├── test
├── fixtures
│ ├── import-1.glsl
│ ├── include-1.glsl
│ ├── hex.glsl
│ ├── nest-conflict-2.glsl
│ ├── import-3.glsl
│ ├── include-3.glsl
│ ├── import-2.glsl
│ ├── include-2.glsl
│ ├── nest-conflict-1.glsl
│ ├── test01.frag
│ ├── include-entry.glsl
│ ├── import-entry.glsl
│ └── nest-conflict-entry.glsl
├── export.test.ts
├── index.test.ts
├── cli.test.ts
├── _util.ts
├── source-map.test.ts
└── import.test.ts
├── .prettierrc.js
├── typings
├── index.d.ts
├── modules.d.ts
└── core.d.ts
├── src
├── index.ts
├── tokens-to-string.ts
├── glslify.ts
├── topo-sort.ts
├── glslify-import.ts
└── glslify-bundle.ts
├── .travis.yml
├── renovate.json
├── .eslintrc
├── tsconfig.json
├── .gitignore
├── README.md
├── bin
└── cli.js
└── package.json
/test/fixtures/import-1.glsl:
--------------------------------------------------------------------------------
1 | const float b = 2.0;
2 |
--------------------------------------------------------------------------------
/test/fixtures/include-1.glsl:
--------------------------------------------------------------------------------
1 | const float b = 2.0;
2 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | tabWidth: 4
3 | };
4 |
--------------------------------------------------------------------------------
/test/fixtures/hex.glsl:
--------------------------------------------------------------------------------
1 | void main() {
2 | gl_FragColor = #FFFF;
3 | }
4 |
--------------------------------------------------------------------------------
/test/fixtures/nest-conflict-2.glsl:
--------------------------------------------------------------------------------
1 | const float d = 1.0;
2 |
3 | #pragma glslify: export(d)
4 |
--------------------------------------------------------------------------------
/test/fixtures/import-3.glsl:
--------------------------------------------------------------------------------
1 | const float f = 6.0;
2 | const float g = 7.0;
3 | const float h = 8.0;
4 |
--------------------------------------------------------------------------------
/test/fixtures/include-3.glsl:
--------------------------------------------------------------------------------
1 | const float f = 6.0;
2 | const float g = 7.0;
3 | const float h = 8.0;
4 |
--------------------------------------------------------------------------------
/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/test/fixtures/import-2.glsl:
--------------------------------------------------------------------------------
1 | const float d = 4.0;
2 | #pragma glslify: import(./import-3.glsl)
3 | const float e = 5.0;
4 |
--------------------------------------------------------------------------------
/test/fixtures/include-2.glsl:
--------------------------------------------------------------------------------
1 | const float d = 4.0;
2 | #pragma glslify: import("./include-3.glsl")
3 | const float e = 5.0;
4 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as glslify from "./glslify";
2 | export const compile = glslify.compile;
3 | export const file = glslify.file;
4 |
--------------------------------------------------------------------------------
/test/fixtures/nest-conflict-1.glsl:
--------------------------------------------------------------------------------
1 | #pragma glslify: b = require(./nest-conflict-2.glsl)
2 |
3 | const float d;
4 | const vec2 c = vec2(2.0, b);
5 |
6 | #pragma glslify: export(c)
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "stable"
4 | - "lts/*"
5 | cache:
6 | directories:
7 | - node_modules
8 | script:
9 | - npm ci
10 | - npm run ci
11 |
--------------------------------------------------------------------------------
/test/fixtures/test01.frag:
--------------------------------------------------------------------------------
1 | #pragma glslify: noise = require("glsl-noise/simplex/3d")
2 | precision mediump float;
3 | varying vec3 vpos;
4 | void main () {
5 | gl_FragColor = vec4(noise(vpos*25.0),1);
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/include-entry.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | const float a = 1.0;
3 | #include "./include-1.glsl"
4 | const float c = 3.0;
5 | #include "include-2.glsl"
6 |
7 | void main() {
8 | gl_FragColor = vec4(a, b, c, d);
9 | }
10 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["config:base"],
3 | "labels": ["renovate"],
4 | "automerge": true,
5 | "major": {
6 | "automerge": false
7 | },
8 | "minor": {
9 | "groupName": "minor dependencies"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/fixtures/import-entry.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | const float a = 1.0;
3 | #pragma glslify: import(./import-1.glsl)
4 | const float c = 3.0;
5 | #pragma glslify: import('import-2.glsl')
6 |
7 | void main() {
8 | gl_FragColor = vec4(a, b, c, d);
9 | }
10 |
--------------------------------------------------------------------------------
/test/fixtures/nest-conflict-entry.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | const float d;
4 |
5 | #pragma glslify: b = require(./nest-conflict-2.glsl)
6 | #pragma glslify: a = require(./nest-conflict-1.glsl)
7 |
8 | void main() {
9 | gl_FragColor = vec4(a, b, 1);
10 | }
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "jest/globals": true
5 | },
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:prettier/recommended",
9 | "plugin:@typescript-eslint/recommended"
10 | ],
11 | "plugins": ["@typescript-eslint", "jest"],
12 | "parser": "@typescript-eslint/parser",
13 | "parserOptions": {
14 | "sourceType": "module",
15 | "project": "./tsconfig.json"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/test/export.test.ts:
--------------------------------------------------------------------------------
1 | import * as glslify from "..";
2 | import { file, compile } from "..";
3 |
4 | test("ES modules import", async (): Promise => {
5 | expect(typeof glslify.compile).toEqual("function");
6 | expect(typeof glslify.file).toEqual("function");
7 | expect(glslify.compile).toEqual(compile);
8 | expect(glslify.file).toEqual(file);
9 | });
10 |
11 | test("CommonJS require", async (): Promise => {
12 | const g = require("..");
13 | expect(typeof g.compile).toEqual("function");
14 | expect(typeof g.file).toEqual("function");
15 | });
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2015",
4 | "module": "commonjs",
5 | "lib": ["es2015", "es2017", "dom"],
6 | "sourceMap": true,
7 | "outDir": "lib",
8 | "removeComments": true,
9 | "strict": true,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "noImplicitReturns": true,
13 | "noFallthroughCasesInSwitch": true,
14 | "moduleResolution": "node",
15 | "esModuleInterop": true
16 | },
17 | "include": ["src", "typings"],
18 | "exclude": ["examples"]
19 | }
20 |
--------------------------------------------------------------------------------
/test/index.test.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import { file, compile } from "../src/glslify";
3 |
4 | test("node string", async (): Promise => {
5 | var output = await compile(
6 | [
7 | ' #pragma glslify: noise = require("glsl-noise/simplex/3d")',
8 | " precision mediump float;",
9 | " varying vec3 vpos;",
10 | " void main () {",
11 | " gl_FragColor = vec4(noise(vpos*25.0),1);",
12 | " }"
13 | ].join("\n")
14 | );
15 | expect(output).toMatch(/taylorInvSqrt/); // contains parts of the file
16 | });
17 |
18 | test("node file", async (): Promise => {
19 | var output = await file(path.resolve(__dirname, "fixtures/test01.frag"));
20 | expect(output).toMatch(/taylorInvSqrt/); // contains parts of the file
21 | });
22 |
--------------------------------------------------------------------------------
/src/tokens-to-string.ts:
--------------------------------------------------------------------------------
1 | import * as sourceMap from "source-map";
2 | import * as convert from "convert-source-map";
3 |
4 | export default function tokensToString(tokens: Token[]): string {
5 | const output: string[] = [];
6 | const map = new sourceMap.SourceMapGenerator();
7 |
8 | let line = 1;
9 | let column = 1;
10 |
11 | tokens.forEach((token): void => {
12 | if (token.type === "eof") return;
13 |
14 | output.push(token.data);
15 |
16 | const sourceFile = token.source;
17 | const originalPos = token.original;
18 | if (!sourceFile || !originalPos) {
19 | return;
20 | }
21 |
22 | const tokenMap = {
23 | source: sourceFile,
24 | original: {
25 | line: originalPos.line,
26 | column: originalPos.column
27 | },
28 | generated: {
29 | line: line,
30 | column: column
31 | }
32 | };
33 | map.addMapping(tokenMap);
34 |
35 | const lines = token.data.split(/\r\n|\r|\n/);
36 | if (lines.length > 1) {
37 | // if token has multiple lines
38 | line += lines.length - 1;
39 | column = lines[lines.length - 1].length + 1;
40 | } else {
41 | column += token.data.length;
42 | }
43 | });
44 |
45 | const src = output.join("");
46 | const mapJSON = map.toString();
47 | const mapComment = convert.fromJSON(mapJSON).toComment();
48 |
49 | return src + "\n" + mapComment;
50 | }
51 |
--------------------------------------------------------------------------------
/typings/modules.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "glsl-tokenizer/string" {
4 | function tokenize(arg: string): Token[];
5 | export = tokenize;
6 | }
7 |
8 | declare module "glsl-inject-defines" {
9 | type Defs = {
10 | [key: string]: number | string;
11 | };
12 | function inject(src: string, defs: Defs): string;
13 | export = inject;
14 | }
15 |
16 | declare module "glsl-token-defines" {
17 | function defines(tokens: Token[]): Token[];
18 | export = defines;
19 | }
20 |
21 | declare module "glsl-token-descope" {
22 | function descope(tokens: Token[], callback: any): Token[];
23 | export = descope;
24 | }
25 |
26 | declare module "glsl-token-scope" {
27 | function scope(tokens: Token[]): Token[];
28 | export = scope;
29 | }
30 |
31 | declare module "glsl-token-string" {
32 | function string(tokens: Token[]): string;
33 | export = string;
34 | }
35 |
36 | declare module "glsl-token-depth" {
37 | function depth(tokens: Token[]): Token[];
38 | export = depth;
39 | }
40 |
41 | declare module "shallow-copy" {
42 | function copy(object: T): T;
43 | export = copy;
44 | }
45 |
46 | declare module "glslify-deps" {
47 | function deps(opts: DepperOptions): Depper;
48 | export = deps;
49 | }
50 |
51 | declare module "glsl-tokenizer" {
52 | function tokenize(src: string): Token[];
53 | export = tokenize;
54 | }
55 |
56 | declare module "glsl-resolve" {
57 | function resolve(path: string, opts: { basedir: string }): Promise;
58 | export = resolve;
59 | }
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | !node_modules
2 | ### https://raw.github.com/github/gitignore/6531e382923e8e4d89b069a045e1c2a5d4be6321/Node.gitignore
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | # Runtime data
12 | pids
13 | *.pid
14 | *.seed
15 | *.pid.lock
16 |
17 | # Directory for instrumented libs generated by jscoverage/JSCover
18 | lib-cov
19 |
20 | # Coverage directory used by tools like istanbul
21 | coverage
22 |
23 | # nyc test coverage
24 | .nyc_output
25 |
26 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
27 | .grunt
28 |
29 | # Bower dependency directory (https://bower.io/)
30 | bower_components
31 |
32 | # node-waf configuration
33 | .lock-wscript
34 |
35 | # Compiled binary addons (https://nodejs.org/api/addons.html)
36 | build/Release
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 | .env.test
60 |
61 | # parcel-bundler cache (https://parceljs.org/)
62 | .cache
63 |
64 | # next.js build output
65 | .next
66 |
67 | # nuxt.js build output
68 | .nuxt
69 |
70 | # vuepress build output
71 | .vuepress/dist
72 |
73 | # Serverless directories
74 | .serverless/
75 |
76 | # FuseBox cache
77 | .fusebox/
78 |
79 | # DynamoDB Local files
80 | .dynamodb/
81 |
82 | # I don't wanna add package lock to git
83 | package-lock.json
84 |
85 | lib/**/*
86 |
--------------------------------------------------------------------------------
/typings/core.d.ts:
--------------------------------------------------------------------------------
1 | type DepsInfo = {
2 | id: number;
3 | deps: { [name: string]: number };
4 | file: string;
5 | source: string;
6 | entry: boolean;
7 |
8 | // Property added by Bundle.preprocess
9 | parsed?: {
10 | tokens: Token[];
11 | imports: DepImport[];
12 | exports: string;
13 | };
14 | };
15 |
16 | type DepImport = {
17 | name: string;
18 | path: string;
19 | target: any;
20 | maps: any;
21 | index: number;
22 | };
23 |
24 | type DepsHash = {
25 | [id: number]: DepsInfo;
26 | };
27 |
28 | type DepperOptions = {
29 | cwd?: string;
30 | readFile?: Function;
31 | resolve?: Function;
32 | files?: any;
33 | };
34 | type DepperCallback = (err: Error, result: DepsInfo[]) => void;
35 | type DepperTransformOptions = any;
36 |
37 | type Depper = {
38 | add(filename: string, callback: DepperCallback): void;
39 | inline(source: string, basedir: string, callback: DepperCallback): void;
40 | transform(transform: string, options: DepperTransformOptions): void;
41 | on(event: "file", callback: (filename: string) => void): void;
42 | };
43 |
44 | type PostTransform = {
45 | name: string;
46 | opts: any;
47 | };
48 |
49 | type Token = {
50 | type:
51 | | "block-comment"
52 | | "line-comment"
53 | | "preprocessor"
54 | | "operator"
55 | | "float"
56 | | "ident"
57 | | "builtin"
58 | | "eof"
59 | | "integer"
60 | | "whitespace"
61 | | "keyword";
62 | data: string;
63 | position: number;
64 | line: number;
65 | column: number;
66 |
67 | source?: string;
68 | original?: {
69 | line: number;
70 | column: number;
71 | };
72 | };
73 |
74 | interface MapPos {
75 | line: number;
76 | column: number;
77 | name: string | null;
78 | source: string | null;
79 | }
80 |
--------------------------------------------------------------------------------
/test/cli.test.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path";
2 | import * as cp from "child_process";
3 | import * as fs from "fs";
4 | import * as tmp from "tmp";
5 | import p = require("pify");
6 |
7 | const cmd = path.resolve(__dirname, "../bin/cli.js");
8 | const exec = (...args: string[]) => {
9 | const proc = cp.spawnSync(cmd, args);
10 | return proc.stdout.toString();
11 | };
12 |
13 | const test01 = `
14 | #pragma glslify: noise = require("glsl-noise/simplex/3d")
15 | precision mediump float;
16 | varying vec3 vpos;
17 | void main () {
18 | gl_FragColor = vec4(noise(vpos*25.0),1);",
19 | }
20 | `;
21 |
22 | test("CLI version", async (): Promise => {
23 | const output = exec("-v");
24 | expect(output).toEqual(exec("--version"));
25 | });
26 |
27 | test("CLI help", async (): Promise => {
28 | const output = exec("-h");
29 | expect(output).toEqual(exec("--help"));
30 | expect(output).toMatch("Usage");
31 | expect(output).toMatch("Example");
32 | });
33 |
34 | test("CLI read file and write to STDOUT", async (): Promise => {
35 | const input = path.resolve(__dirname, "fixtures/test01.frag");
36 | expect(exec(input)).toMatch(/taylorInvSqrt/);
37 | });
38 |
39 | test("CLI read file and write to file", async (): Promise => {
40 | const input = path.resolve(__dirname, "fixtures/test01.frag");
41 | const dst = tmp.fileSync().name;
42 | exec(input, "-o", dst);
43 |
44 | const output = await p(fs.readFile)(dst, "utf8");
45 | expect(output).toMatch(/taylorInvSqrt/);
46 | });
47 |
48 | test("CLI read STDIN and write to STDOUT", async (): Promise => {
49 | const proc = cp.spawnSync(cmd, [], { input: test01 });
50 |
51 | const output = proc.stdout.toString();
52 | expect(output).toMatch(/taylorInvSqrt/);
53 | });
54 |
55 | test("CLI read STDIN and write to file", async (): Promise => {
56 | const dst = tmp.fileSync().name;
57 | cp.spawnSync(cmd, ["-o", dst], { input: test01 });
58 |
59 | const output = await p(fs.readFile)(dst, "utf8");
60 | expect(output).toMatch(/taylorInvSqrt/);
61 | });
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # glslify-lite
2 |
3 | [](https://travis-ci.org/fand/glslify-lite) [](https://codecov.io/gh/fand/glslify-lite) 
4 |
5 | A fast, lightweight fork of [glslify](https://github.com/glslify/glslify).
6 | Intended to provide more useful APIs for linters, live coding apps, etc.
7 |
8 | ## Why?
9 |
10 | glslify is great, but has some problems especially in realtime usage such as linters, live coding, etc.
11 |
12 | - Synchronous, blocking API by design
13 | - No support for sourcemaps
14 |
15 | glslify-lite overcomes these problems.
16 | However, we don't provide completely same features as glslify.
17 |
18 | | | glslify | glslify-lite |
19 | | ----------------------- | :-----: | :-----------: |
20 | | API | Sync | Async |
21 | | Sourcemaps | - | ✅ |
22 | | Output code is clean | ✅ | - |
23 | | Transformer support | ✅ | Only built-in |
24 | | Tagged template literal | ✅ | - |
25 | | Browserify | ✅ | - |
26 |
27 | ## Install
28 |
29 | ```
30 | npm i glslify-lite
31 | ```
32 |
33 | ## Usage
34 |
35 | ### CLI
36 |
37 | The CLI can take a file as its first argument, and output to a file using the -o flag:
38 |
39 | ```
40 | glslify-lite index.glsl -o output.glsl
41 | ```
42 |
43 | It can also read input from stdin and output to stdout:
44 |
45 | ```
46 | cat index.glsl | glslify-lite > output.glsl
47 | ```
48 |
49 | ### API
50 |
51 | #### glslify.compile(src, opts): Promise
52 |
53 | Compile a shader string from a string `src`.
54 |
55 | Optionally provide:
56 |
57 | - `opts.basedir` - directory to resolve relative paths in `src`
58 |
59 | #### glslify.file(filename, opts): Promise
60 |
61 | Compile a shader from a `filename`.
62 |
63 | Optionally provide:
64 |
65 | - `opts.basedir` - directory to resolve relative paths in `src`
66 |
67 | ## LICENSE
68 |
69 | MIT
70 |
--------------------------------------------------------------------------------
/bin/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const fs = require("fs");
3 | const path = require("path");
4 | const meow = require("meow");
5 | const getStdin = require("get-stdin");
6 | const p = require("pify");
7 | const glslify = require("..");
8 |
9 | const cli = meow(
10 | `
11 | Usage
12 | $ glslify-lite -o