├── .eslintrc.json ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── package.json └── test ├── fixtures ├── basic.js ├── local.js └── shaders │ ├── frag.glsl │ ├── local.glsl │ └── my-function.glsl └── index.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es2022": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": 13, 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "comma-dangle": ["error", "never"], 13 | "indent": ["error", 4, { "SwitchCase": 1 }], 14 | "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], 15 | "linebreak-style": ["error", "unix"], 16 | "object-curly-spacing": ["error", "always"], 17 | "object-shorthand": ["error", "always"], 18 | "quotes": ["error", "single"], 19 | "semi": ["error", "always"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | package-lock.json 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintrc.json 3 | test 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright © 2017-2019 Patrick Schroen 4 | Copyright © 2019-2023 glslify contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rollup-plugin-glslify 2 | 3 | [![NPM Package][npm]][npm-url] 4 | [![NPM Package][lgtm]][lgtm-url] 5 | 6 | Import GLSL strings with [glslify](https://github.com/glslify/glslify) (a node.js-style module system for GLSL). 7 | 8 | ```js 9 | import frag from './shaders/frag.glsl'; 10 | console.log(frag); 11 | ``` 12 | 13 | ## Installation 14 | 15 | ```sh 16 | npm i -D rollup-plugin-glslify 17 | 18 | # or 19 | 20 | yarn add -D rollup-plugin-glslify 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```js 26 | // rollup.config.js 27 | import glslify from 'rollup-plugin-glslify'; 28 | 29 | export default { 30 | // ... 31 | plugins: [ 32 | glslify() 33 | ] 34 | }; 35 | ``` 36 | 37 | ## Options 38 | 39 | ```js 40 | glslify(options) 41 | ``` 42 | 43 | ```js 44 | { 45 | // Default 46 | include: [ 47 | '**/*.vs', 48 | '**/*.fs', 49 | '**/*.vert', 50 | '**/*.frag', 51 | '**/*.glsl' 52 | ], 53 | 54 | // Undefined by default 55 | exclude: 'node_modules/**', 56 | 57 | // Enabled by default 58 | compress: true 59 | 60 | // The compress option also accepts a function with its first argument 61 | // being the string containing the glslified shader code. 62 | // The function is expected to return a string (or object) - the compressed shader 63 | } 64 | ``` 65 | 66 | [glslify API options](https://github.com/glslify/glslify#var-src--glslcompilesrc-opts) 67 | 68 | ## Changelog 69 | 70 | * [Releases](https://github.com/glslify/rollup-plugin-glslify/releases) 71 | 72 | ## License 73 | 74 | [MIT](LICENSE) 75 | 76 | 77 | [npm]: https://img.shields.io/npm/v/rollup-plugin-glslify.svg 78 | [npm-url]: https://www.npmjs.com/package/rollup-plugin-glslify 79 | [lgtm]: https://img.shields.io/lgtm/alerts/github/glslify/rollup-plugin-glslify.svg 80 | [lgtm-url]: https://lgtm.com/projects/g/glslify/rollup-plugin-glslify 81 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { createFilter } from '@rollup/pluginutils'; 2 | import { Plugin } from 'rollup'; 3 | 4 | interface Options { 5 | include: Parameters[0]; 6 | exclude: Parameters[1]; 7 | 8 | compress: boolean | ((code: string) => string | Record); 9 | } 10 | 11 | declare const glslify: (options?: Partial) => Plugin; 12 | 13 | export default glslify; 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author pschroen / https://ufo.ai/ 3 | */ 4 | 5 | import { dirname } from 'node:path'; 6 | import { createFilter } from '@rollup/pluginutils'; 7 | import { compile } from 'glslify'; 8 | 9 | function compressShader(code) { 10 | // Based on https://github.com/vwochnik/rollup-plugin-glsl 11 | // Modified to remove multiline comments. See #16 12 | 13 | let needNewline = false; 14 | return code.replace(/\\(?:\r\n|\n\r|\n|\r)|\/\*.*?\*\/|\/\/(?:\\(?:\r\n|\n\r|\n|\r)|[^\n\r])*/gs, '').split(/\n+/).reduce((result, line) => { 15 | line = line.trim().replace(/\s{2,}|\t/, ' '); // lgtm[js/incomplete-sanitization] 16 | if (line.charAt(0) === '#') { 17 | if (needNewline) { 18 | result.push('\n'); 19 | } 20 | result.push(line, '\n'); 21 | needNewline = false; 22 | } else { 23 | result.push(line.replace(/\s*({|}|=|\*|,|\+|\/|>|<|&|\||\[|\]|\(|\)|-|!|;)\s*/g, '$1')); 24 | needNewline = true; 25 | } 26 | return result; 27 | }, []).join('').replace(/\n+/g, '\n'); 28 | } 29 | 30 | export default function glslify(userOptions = {}) { 31 | const options = Object.assign( 32 | { 33 | include: [ 34 | '**/*.vs', 35 | '**/*.fs', 36 | '**/*.vert', 37 | '**/*.frag', 38 | '**/*.glsl' 39 | ] 40 | }, 41 | userOptions 42 | ); 43 | 44 | const filter = createFilter(options.include, options.exclude); 45 | 46 | return { 47 | name: 'glslify', 48 | transform(code, id) { 49 | if (!filter(id)) return; 50 | 51 | const fileOptions = Object.assign( 52 | { 53 | basedir: dirname(id) 54 | }, 55 | options 56 | ); 57 | 58 | code = compile(code, fileOptions); 59 | 60 | if (typeof options.compress === 'function') { 61 | code = options.compress(code); 62 | } else if (options.compress !== false) { 63 | code = compressShader(code); 64 | } 65 | 66 | return { 67 | code: `export default ${JSON.stringify(code)}; // eslint-disable-line`, 68 | map: { mappings: '' } 69 | }; 70 | } 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-plugin-glslify", 3 | "version": "2.0.0", 4 | "description": "Import GLSL strings with glslify", 5 | "homepage": "https://github.com/glslify/rollup-plugin-glslify#readme", 6 | "keywords": [ 7 | "rollup-plugin", 8 | "glsl", 9 | "glslify", 10 | "shaders" 11 | ], 12 | "author": "pschroen", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/glslify/rollup-plugin-glslify" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/glslify/rollup-plugin-glslify/issues" 19 | }, 20 | "license": "MIT", 21 | "type": "module", 22 | "main": "index.js", 23 | "scripts": { 24 | "lint": "eslint .", 25 | "test": "ava" 26 | }, 27 | "dependencies": { 28 | "@rollup/pluginutils": "^5.0.2", 29 | "glslify": "^7.1.1" 30 | }, 31 | "devDependencies": { 32 | "ava": "^5.1.1", 33 | "eslint": "^8.33.0", 34 | "glsl-noise": "0.0.0", 35 | "rollup": "^3.12.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/fixtures/basic.js: -------------------------------------------------------------------------------- 1 | import frag from './shaders/frag.glsl'; 2 | console.log(frag); // eslint-disable-line 3 | -------------------------------------------------------------------------------- /test/fixtures/local.js: -------------------------------------------------------------------------------- 1 | import local from './shaders/local.glsl'; 2 | console.log(local); // eslint-disable-line 3 | -------------------------------------------------------------------------------- /test/fixtures/shaders/frag.glsl: -------------------------------------------------------------------------------- 1 | #pragma glslify: noise = require('glsl-noise/simplex/3d') 2 | 3 | precision mediump float; 4 | varying vec3 vpos; 5 | void main () { 6 | gl_FragColor = vec4(noise(vpos*25.0),1); 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/shaders/local.glsl: -------------------------------------------------------------------------------- 1 | #pragma glslify: topDot = require(./my-function.glsl) 2 | 3 | topDot(vec3(0, 1, 0)); // 1 4 | -------------------------------------------------------------------------------- /test/fixtures/shaders/my-function.glsl: -------------------------------------------------------------------------------- 1 | float myFunction(vec3 normal) { 2 | return dot(vec3(0, 1, 0), normal); 3 | } 4 | 5 | #pragma glslify: export(myFunction) 6 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import { rollup } from 'rollup'; 2 | import test from 'ava'; 3 | 4 | import glslify from '../index.js'; 5 | 6 | test('compressed', async t => { 7 | const bundle = await rollup({ 8 | input: 'test/fixtures/basic.js', 9 | plugins: [glslify()] 10 | }); 11 | 12 | const { output } = await bundle.generate({ format: 'es' }); 13 | const code = output[0].code; 14 | 15 | t.is(!code.includes('// Description'), true); 16 | t.is(code.includes('taylorInvSqrt'), true); 17 | }); 18 | 19 | test('compressed | custom function', async t => { 20 | const bundle = await rollup({ 21 | input: 'test/fixtures/basic.js', 22 | plugins: [glslify({ compress: code => code + '\n\n// test custom function' })] 23 | }); 24 | 25 | const { output } = await bundle.generate({ format: 'es' }); 26 | const code = output[0].code; 27 | 28 | t.is(code.includes('// Description'), true); 29 | t.is(code.includes('taylorInvSqrt'), true); 30 | t.is(code.includes('// test custom function'), true); 31 | }); 32 | 33 | test('uncompressed', async t => { 34 | const bundle = await rollup({ 35 | input: 'test/fixtures/basic.js', 36 | plugins: [glslify({ compress: false })] 37 | }); 38 | 39 | const { output } = await bundle.generate({ format: 'es' }); 40 | const code = output[0].code; 41 | 42 | t.is(code.includes('// Description'), true); 43 | t.is(code.includes('taylorInvSqrt'), true); 44 | }); 45 | 46 | test('local module', async t => { 47 | const bundle = await rollup({ 48 | input: 'test/fixtures/local.js', 49 | plugins: [glslify({ compress: false })] 50 | }); 51 | 52 | const { output } = await bundle.generate({ format: 'es' }); 53 | const code = output[0].code; 54 | 55 | t.is(code.includes('myFunction'), true); 56 | }); 57 | --------------------------------------------------------------------------------