├── .gitignore ├── LICENSE.md ├── README.md ├── example ├── entry.js ├── index.html ├── shader.frag ├── shader.vert └── webpack.config.js ├── glslify-loader.js ├── package.json └── test ├── fixtures ├── config-inline.js ├── config-post.js ├── config-simple.js ├── config-transform.js ├── entry-inline.js ├── entry-post.js ├── entry-simple.js ├── entry-transform.js ├── shader-export.glsl ├── shader-fancy-import.glsl ├── shader-hex.glsl └── shader-simple.glsl ├── index.js └── utils └── readWebpack.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors 2 | .idea 3 | .vscode 4 | 5 | # Node / Yarn / Npm 6 | node_modules 7 | npm-debug.log* 8 | yarn.lock 9 | package-lock.json 10 | 11 | # Logs and Temporary files 12 | .tmp 13 | logs 14 | *.log 15 | 16 | # MacOS 17 | .DS_Store 18 | .AppleDouble 19 | .LSOverride 20 | Icon 21 | ._* 22 | 23 | # Misc 24 | coverage -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2014 [stackgl](http://github.com/stackgl/) contributors 5 | 6 | *stackgl contributors listed at * 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glslify-loader 2 | 3 | [glslify](http://github.com/stackgl/glslify) loader module for [webpack](http://webpack.github.io/). 4 | 5 | ## Installation 6 | ```sh 7 | npm install glslify-loader 8 | ``` 9 | 10 | Generally, you'll want to use this alongside webpack's [raw-loader](https://github.com/webpack-contrib/raw-loader) module: 11 | ```sh 12 | npm install raw-loader 13 | ``` 14 | 15 | ## Usage 16 | 17 | [Documentation: Using Loaders in Webpack](https://webpack.js.org/concepts/loaders/#configuration) 18 | 19 | ##### Configuration file 20 | 21 | ```js 22 | module.exports = { 23 | rules: [ 24 | { 25 | test: /\.(glsl|vs|fs|vert|frag)$/, 26 | exclude: /node_modules/, 27 | use: [ 28 | 'raw-loader', 29 | 'glslify-loader' 30 | ] 31 | } 32 | ] 33 | } 34 | ``` 35 | 36 | ##### Inline 37 | 38 | ```js 39 | // Using require 40 | const source = require('raw-loader!glslify-loader!./my-shader.glsl') 41 | 42 | // Using ES6 import statement 43 | import source from 'raw-loader!glslify-loader!./my-shader.glsl' 44 | ``` 45 | 46 | ##### Speficy source transforms 47 | See [Glslify Source Transforms](https://github.com/glslify/glslify#source-transforms) for details. 48 | 49 | ```js 50 | module.exports = { 51 | rules: [ 52 | { 53 | test: /\.(glsl|frag|vert)$/, 54 | exclude: /node_modules/, 55 | use: [ 56 | 'raw-loader', 57 | { 58 | loader: 'glslify-loader' 59 | options: { 60 | transform: [ 61 | ['glslify-hex', { 'option-1': true, 'option-2': 42 }] 62 | ] 63 | } 64 | } 65 | ] 66 | } 67 | ] 68 | } 69 | ``` 70 | 71 | 72 | ## Contributing 73 | 74 | See [stackgl/contributing](https://github.com/stackgl/contributing) for details. 75 | 76 | ## License 77 | 78 | MIT. See [LICENSE.md](http://github.com/stackgl/glslify-loader/blob/master/LICENSE.md) for details. 79 | -------------------------------------------------------------------------------- /example/entry.js: -------------------------------------------------------------------------------- 1 | var regl = require('regl')() 2 | 3 | var fragment = require('./shader.frag') 4 | var vertex = require('./shader.vert') 5 | 6 | var size = [0, 0] 7 | window.addEventListener('resize', resize) 8 | resize() 9 | 10 | // hot module reloading for the shader files 11 | if (module.hot) { 12 | module.hot.accept('./shader.frag', function () { fragment = require('./shader.frag') }) 13 | module.hot.accept('./shader.vert', function () { vertex = require('./shader.vert') }) 14 | } 15 | 16 | var draw = regl({ 17 | frag: function () { return fragment }, 18 | vert: function () { return vertex }, 19 | attributes: { 20 | position: [ 21 | -2, 0, 22 | 0, -2, 23 | 2, 2 24 | ] 25 | }, 26 | uniforms: { 27 | time: function (f) { return 0.01 * f.tick }, 28 | resolution: function () { return size } 29 | }, 30 | depth: { 31 | enable: false 32 | }, 33 | count: 3 34 | }) 35 | 36 | regl.frame(function () { 37 | regl.clear({ color: [0, 0, 0, 1] }) 38 | draw() 39 | }) 40 | 41 | function resize () { 42 | size[0] = window.innerWidth 43 | size[1] = window.innerHeight 44 | } 45 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | glslify-loader 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/shader.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #ifdef GL_OES_standard_derivatives 4 | #extension GL_OES_standard_derivatives : enable 5 | #endif 6 | 7 | #pragma glslify: aastep = require('glsl-aastep') 8 | 9 | varying vec2 vUv; 10 | 11 | uniform vec2 resolution; 12 | uniform float time; 13 | 14 | // You can edit these values to see your changes without a page refresh 15 | const float size = 0.3; 16 | const vec3 boxColor = vec3(0.950, 0.982, 0.470); 17 | const vec3 bgColor = vec3(0.159, 0.164, 0.215); 18 | const float speed = 5.9; 19 | 20 | vec2 rotate(vec2 v, float a) { 21 | float s = sin(a); 22 | float c = cos(a); 23 | mat2 m = mat2(c, -s, s, c); 24 | return m * v; 25 | } 26 | 27 | float getAngle(float time) { 28 | return sin(time * speed) * 0.8 + time * 1.3; 29 | } 30 | 31 | void main () { 32 | 33 | vec2 box = vUv.xy - 0.5; 34 | if (resolution.y >= resolution.x) { 35 | box.y *= resolution.y / resolution.x; 36 | } else { 37 | box.x *= resolution.x / resolution.y; 38 | } 39 | 40 | float ang = getAngle(time); 41 | ang += box.y * (ang - getAngle(time - 0.01)) * 10.; 42 | box = rotate(box, ang); 43 | box += 0.5; 44 | 45 | float s = 0.5 - size * 0.5; 46 | float inBox = aastep(s, box.x) 47 | * aastep(s, box.y) 48 | * aastep(s, 1.0 - box.x) 49 | * aastep(s, 1.0 - box.y); 50 | 51 | 52 | vec3 color = mix(bgColor, boxColor, inBox); 53 | 54 | gl_FragColor = vec4(color, 1.0); 55 | } -------------------------------------------------------------------------------- /example/shader.vert: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | attribute vec2 position; 4 | varying vec2 vUv; 5 | 6 | void main () { 7 | vUv = position; 8 | gl_Position = vec4(1.0 - 2.0 * position, 0, 1); 9 | } -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: { 6 | bundle: [path.join(__dirname, 'entry.js')], 7 | }, 8 | output: { 9 | path: __dirname, 10 | publicPath: '', 11 | filename: '[name].js', 12 | }, 13 | module: { 14 | rules: [{ 15 | test: /\.(glsl|frag|vert)$/, 16 | exclude: [/node_modules/], 17 | use: [ 18 | 'raw-loader', 19 | path.resolve(__dirname, '..', 'glslify-loader.js') 20 | ] 21 | }] 22 | }, 23 | serve: { 24 | content: __dirname, 25 | dev: { stats: 'minimal' } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /glslify-loader.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const loaderUtils = require('loader-utils') 3 | const resolve = require('resolve') 4 | const deps = require('glslify-deps') 5 | const bundle = require('glslify-bundle') 6 | 7 | module.exports = function glslifyLoader (content) { 8 | this.cacheable && this.cacheable() 9 | 10 | const depper = deps() 11 | const callback = this.async() 12 | 13 | // Setup options 14 | const options = Object.assign({ 15 | basedir: path.dirname(this.resourcePath), 16 | transform: [] 17 | }, loaderUtils.getOptions(this)) 18 | 19 | // Handle transforms from options 20 | const transforms = Array.isArray(options.transform) ? options.transform : [] 21 | const postTransforms = [] 22 | transforms.forEach(transform => { 23 | if (!Array.isArray(transform)) transform = [String(transform)] 24 | const name = transform[0] 25 | const opts = transform[1] || {} 26 | // Keep post-transforms for later 27 | if (opts.post) postTransforms.push({ name, opts }) 28 | else depper.transform(name, opts) 29 | }) 30 | 31 | // Build the dependency graph 32 | depper.inline(content, options.basedir, (err, tree) => { 33 | if (err) return error(err) 34 | // Make webpack watch each subdependencies 35 | tree && tree.forEach(file => !file.entry && this.addDependency(file.file)) 36 | // Bundle the glsl output 37 | const output = String(bundle(tree)) 38 | // Start applying post transforms 39 | nextPostTransform(null, output) 40 | }) 41 | 42 | // Iterate over each post transforms 43 | function nextPostTransform (err, output) { 44 | if (err) return error(err) 45 | const transform = postTransforms.shift() 46 | if (!transform) return done(output) 47 | resolve(transform.name, { basedir: options.basedir }, (err, target) => { 48 | if (err) return error(err) 49 | require(target)(null, output, transform.opts, nextPostTransform) 50 | }) 51 | } 52 | 53 | function error (err) { 54 | callback(err, null) 55 | } 56 | 57 | function done (output) { 58 | callback(null, output) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glslify-loader", 3 | "version": "2.0.0", 4 | "description": "glslify loader module for webpack", 5 | "main": "glslify-loader.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "npm run tape --silent", 9 | "tape": "tape test/index.js | tap-spec", 10 | "example": "webpack-serve ./example/webpack.config.js" 11 | }, 12 | "author": { 13 | "name": "Hugh Kennedy", 14 | "email": "hughskennedy@gmail.com", 15 | "url": "http://hughsk.io/" 16 | }, 17 | "files": [], 18 | "dependencies": { 19 | "glslify-bundle": "^5.0.0", 20 | "glslify-deps": "^1.3.0", 21 | "loader-utils": "^1.1.0", 22 | "resolve": "^1.3.3" 23 | }, 24 | "devDependencies": { 25 | "glsl-aastep": "^1.0.1", 26 | "glsl-noise": "^0.0.0", 27 | "glslify-fancy-imports": "^1.0.1", 28 | "glslify-hex": "^2.1.1", 29 | "memory-fs": "^0.4.1", 30 | "raw-loader": "^0.5.1", 31 | "regl": "^1.3.6", 32 | "require-from-string": "^2.0.1", 33 | "tap-spec": "^5.0.0", 34 | "tape": "^4.9.0", 35 | "webpack": "^4.1.0", 36 | "webpack-serve": "^1.0.4" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "git://github.com/stackgl/glslify-loader.git" 41 | }, 42 | "keywords": [ 43 | "ecosystem:stackgl" 44 | ], 45 | "homepage": "https://github.com/stackgl/glslify-loader", 46 | "bugs": { 47 | "url": "https://github.com/stackgl/glslify-loader/issues" 48 | }, 49 | "engines": { 50 | "node": ">=6" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/fixtures/config-inline.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | target: 'node', 3 | mode: 'production' 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/config-post.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | target: 'node', 5 | mode: 'production', 6 | module: { 7 | rules: [{ 8 | test: /\.glsl$/, 9 | exclude: [/node_modules/], 10 | use: [ 11 | 'raw-loader', 12 | { 13 | loader: path.resolve(__dirname, '../../glslify-loader.js'), 14 | options: { 15 | transform: [ 16 | ['glslify-hex', { post: true }] 17 | ] 18 | } 19 | } 20 | ] 21 | }] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/fixtures/config-simple.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | target: 'node', 5 | mode: 'production', 6 | module: { 7 | rules: [{ 8 | test: /\.glsl$/, 9 | exclude: [/node_modules/], 10 | use: [ 11 | 'raw-loader', 12 | path.resolve(__dirname, '../../glslify-loader.js') 13 | ] 14 | }] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/config-transform.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | target: 'node', 5 | mode: 'production', 6 | module: { 7 | rules: [{ 8 | test: /\.glsl$/, 9 | exclude: [/node_modules/], 10 | use: [ 11 | 'raw-loader', 12 | { 13 | loader: path.resolve(__dirname, '../../glslify-loader.js'), 14 | options: { 15 | transform: ['glslify-fancy-imports'] 16 | } 17 | } 18 | ] 19 | }] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/fixtures/entry-inline.js: -------------------------------------------------------------------------------- 1 | const shader = require('raw-loader!../../glslify-loader!./shader-simple.glsl') 2 | module.exports = shader 3 | -------------------------------------------------------------------------------- /test/fixtures/entry-post.js: -------------------------------------------------------------------------------- 1 | const shader = require('./shader-hex.glsl') 2 | module.exports = shader 3 | -------------------------------------------------------------------------------- /test/fixtures/entry-simple.js: -------------------------------------------------------------------------------- 1 | const shader = require('./shader-simple.glsl') 2 | module.exports = shader 3 | -------------------------------------------------------------------------------- /test/fixtures/entry-transform.js: -------------------------------------------------------------------------------- 1 | const shader = require('./shader-fancy-import.glsl') 2 | module.exports = shader 3 | -------------------------------------------------------------------------------- /test/fixtures/shader-export.glsl: -------------------------------------------------------------------------------- 1 | float myFunction(vec3 normal) { 2 | vec3 hello = vec3(1, 0, 0) 3 | return dot(hello, normal); 4 | } 5 | 6 | #pragma glslify: export(myFunction) -------------------------------------------------------------------------------- /test/fixtures/shader-fancy-import.glsl: -------------------------------------------------------------------------------- 1 | import myFunction from './shader-export' 2 | 3 | void main () { 4 | gl_FragColor = vec4(1, 0, 0, 1); 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/shader-hex.glsl: -------------------------------------------------------------------------------- 1 | void main () { 2 | gl_FragColor = #00FF00; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/shader-simple.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | #pragma glslify: noise = require(glsl-noise/classic/2d) 4 | #pragma glslify: myFunction = require(./shader-export) 5 | 6 | void main () { 7 | gl_FragColor = vec4(1, 0, 0, 1); 8 | } 9 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const webpack = require('./utils/readWebpack') 3 | 4 | test('simple glsl file with local & external dependencies', t => { 5 | webpack('config-simple.js', 'entry-simple.js') 6 | .then(data => { 7 | t.ok(~data.indexOf('#define GLSLIFY 1'), '#define GLSLIFY 1 was added') 8 | t.ok(~data.indexOf('GLSL textureless classic 2D noise "cnoise"'), 'node module glsl-noise was included') 9 | t.ok(~data.indexOf('cnoise('), 'cnoise was not renamed') 10 | t.ok(~data.indexOf('vec3 hello ='), 'local dependency shader-export.glsl was included') 11 | t.end() 12 | }) 13 | .catch(err => t.fail(err)) 14 | }) 15 | 16 | test('glslify-loader using a transform option (glslify-fancy-imports)', t => { 17 | webpack('config-transform.js', 'entry-transform.js') 18 | .then(data => { 19 | t.ok(~data.indexOf('vec3 hello ='), 'local dependency shader-export.glsl was included') 20 | t.end() 21 | }) 22 | .catch(err => t.fail(err)) 23 | }) 24 | 25 | test('glslify-loader using post transforms (glslify-hex)', t => { 26 | webpack('config-post.js', 'entry-post.js') 27 | .then(data => { 28 | t.ok(!~data.indexOf('#00FF00'), '#00FF00 transformed away with glslify-hex') 29 | t.end() 30 | }) 31 | .catch(err => t.fail(err)) 32 | }) 33 | 34 | 35 | test('inline use', t => { 36 | webpack('config-inline.js', 'entry-inline.js') 37 | .then(data => { 38 | t.ok(~data.indexOf('#define GLSLIFY 1'), '#define GLSLIFY 1 was added') 39 | t.ok(~data.indexOf('GLSL textureless classic 2D noise "cnoise"'), 'node module glsl-noise was included') 40 | t.end() 41 | }) 42 | .catch(err => t.fail(err)) 43 | }) 44 | -------------------------------------------------------------------------------- /test/utils/readWebpack.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const MemoryFileSystem = require('memory-fs') 4 | const requireFromString = require('require-from-string') 5 | 6 | const FIXTURESROOT = path.join(__dirname, '..', 'fixtures') 7 | 8 | module.exports = function readWebpack (configpath, entrypath) { 9 | return new Promise((resolve, reject) => { 10 | const config = require(path.resolve(FIXTURESROOT, configpath)) 11 | config.entry = path.resolve(FIXTURESROOT, entrypath) 12 | if (!config.output) config.output = {} 13 | Object.assign(config.output, { 14 | path: '/', 15 | filename: 'bundle.js', 16 | libraryTarget: 'umd' 17 | }) 18 | 19 | const compiler = webpack(config) 20 | const memfs = new MemoryFileSystem() 21 | compiler.outputFileSystem = memfs 22 | compiler.run((err, stats) => { 23 | if (err) return reject(err) 24 | if (stats.compilation.errors && stats.compilation.errors.length > 0) { 25 | return reject(stats.compilation.errors[0]) 26 | } 27 | const bundleContent = String(memfs.readFileSync('/bundle.js')) 28 | const output = requireFromString(bundleContent).trim() 29 | resolve(output) 30 | }) 31 | }) 32 | } 33 | --------------------------------------------------------------------------------