diff --git a/.eslintignore b/.eslintignore index a843dc4..1c1d90d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ test/fixtures +test/macroFixtures diff --git a/.prettierignore b/.prettierignore index 0a59b7e..bc0e285 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ test/fixtures/**/output.js +test/macroFixtures/**/output.js diff --git a/lib/evaluateExpression.js b/lib/evaluateExpression.js index 9de71fd..0512a52 100644 --- a/lib/evaluateExpression.js +++ b/lib/evaluateExpression.js @@ -1,8 +1,11 @@ +/* eslint-disable no-console */ const { types: t, transformFileSync } = require('@babel/core'); const nodePath = require('path'); function evaluateExpression(path, expression, env) { const evaluateFns = [ + [t.isTaggedTemplateExpression, evaluateTaggedTemplateExpression], + [t.isTemplateLiteral, evaluateTemplateLiteralExpression], [t.isLiteral, evaluateLiteral], [t.isIdentifier, evaluateIdentifier], [t.isMemberExpression, evaluateMemberExpression], @@ -24,10 +27,10 @@ function evaluateExpression(path, expression, env) { } function evaluateLiteral(path, expression, env) { - if (expression.type !== 'TemplateLiteral') { - return expression.value; - } + return expression.value; +} +function evaluateTemplateLiteralExpression(path, expression, env) { const quasis = expression.quasis; const literals = [quasis[0].value.cooked]; const expressions = []; @@ -68,15 +71,14 @@ function evaluateIdentifier(path, expression, env) { const initPath = binding.path.get('init'); if (initPath.node) { - return evaluateExpression(initPath, initPath.node, env); + return evaluateExpression(path, initPath.node, env); } } else if (expression.name === 'undefined') { return undefined; } throw new Error( - `babel-plugin-glsl: cannot resolve glslify() call, unknown or non-initialized identifier "${ - expression.name + `babel-plugin-glsl: cannot resolve glslify() call, unknown or non-initialized identifier "${expression.name }"` ); } @@ -109,8 +111,7 @@ function evaluateImport(importPath, env) { exit(path) { if (!exprToevaluate) { throw new Error( - `babel-plugin-glsl: cannot resolve glslify() call, import "${ - importPath.node.local.name + `babel-plugin-glsl: cannot resolve glslify() call, import "${importPath.node.local.name }" not exported by "${src}"` ); } @@ -129,6 +130,7 @@ function evaluateMemberExpression(path, expression, env) { const { property, object, computed } = expression; if (['window', 'self', 'global', 'globalThis'].includes(object.name)) return expression; + const objectValue = object.name === 'Math' ? Math : evaluateExpression(path, object, env); @@ -166,13 +168,13 @@ function evaluateUnaryExpression(path, expression, env) { // prettier-ignore switch (expression.operator) { - case '+': return +value - case '-': return -value - case '!': return !value - case '~': return ~value - case 'typeof': return typeof value - case 'void': return void value - } + case '+': return +value + case '-': return -value + case '!': return !value + case '~': return ~value + case 'typeof': return typeof value + case 'void': return void value + } throw new Error( `babel-plugin-glsl: unsupported unary operator "${expression.operator}"` ); @@ -186,26 +188,26 @@ function evaluateBinaryExpression(path, expression, env) { // prettier-ignore switch (expression.operator) { - case '+': return left + right - case '-': return left - right - case '*': return left * right - case '&': return left & right - case '/': return left / right - case '%': return left % right - case '|': return left | right - case '^': return left ^ right - case '||': return left || right - case '&&': return left && right - case '<<': return left << right - case '>>': return left >> right - case '>>>': return left >>> right - case '===': return left === right - case '!==': return left !== right - case '<': return left < right - case '>': return right < left - case '<=': return left <= right - case '>=': return left >= right - } + case '+': return left + right + case '-': return left - right + case '*': return left * right + case '&': return left & right + case '/': return left / right + case '%': return left % right + case '|': return left | right + case '^': return left ^ right + case '||': return left || right + case '&&': return left && right + case '<<': return left << right + case '>>': return left >> right + case '>>>': return left >>> right + case '===': return left === right + case '!==': return left !== right + case '<': return left < right + case '>': return right < left + case '<=': return left <= right + case '>=': return left >= right + } throw new Error('babel-plugin-glsl: unsupported binary expression'); } @@ -213,13 +215,13 @@ function evaluateArrayExpression(path, expression, env) { return expression.elements.map(prop => evaluateExpression(path, prop, env)); } -function evaluateObjectExpression(path, expression, babel) { +function evaluateObjectExpression(path, expression, env) { return expression.properties.reduce((result, property) => { if (property.type !== 'ObjectProperty') { throw new Error('babel-plugin-glsl: expected object property'); } - const value = evaluateExpression(path, property.value, babel); + const value = evaluateExpression(path, property.value, env); const key = property.key; if (key.type === 'Identifier') { result[key.name] = value; @@ -232,6 +234,10 @@ function evaluateObjectExpression(path, expression, babel) { }, {}); } +function evaluateTaggedTemplateExpression(path, expression, env) { + return evaluateExpression(path, expression.node.quasi, env); +} + function isPrimitive(val) { return val == null || /^[sbn]/.test(typeof val); } diff --git a/lib/processGlslTag.js b/lib/processGlslTag.js index 0b7c406..ec0f0ca 100644 --- a/lib/processGlslTag.js +++ b/lib/processGlslTag.js @@ -1,30 +1,44 @@ -const { dirname } = require('path'); +const { readFileSync } = require('fs'); +const { dirname, resolve } = require('path'); const { types: t } = require('@babel/core'); const evaluateExpression = require('./evaluateExpression'); const compile = require('./compile'); function processGlslTag(path, state) { const cwd = dirname(state.filename); - const env = { - cwd, - }; - const literal = evaluateExpression(path, path.node.quasi, env); + const env = { cwd }; - if (t.isTemplateLiteral(literal)) { - literal.quasis = literal.quasis.map(quasi => { - quasi.value.raw = `\n${compile(quasi.value.raw, { basedir: cwd })}`; - return quasi; - }); - path.replaceWith(literal); - return; - } else if (typeof literal === 'string') { - const result = compile(literal, { basedir: cwd }); + if ( + t.isCallExpression(path.node) && + t.isStringLiteral(path.node.arguments[0]) + ) { + const filename = resolve( + dirname(state.filename), + path.node.arguments[0].value + ); + const contents = readFileSync(filename, "utf8"); + const result = compile(contents, { basedir: dirname(filename) }); path.replaceWith(t.stringLiteral(result)); + return; } else { - throw new Error( - 'babel-plugin-glsl: string template could not be evaluated' - ); + const literal = evaluateExpression(path, path, env); + if (t.isTemplateLiteral(literal)) { + literal.quasis = literal.quasis.map(quasi => { + quasi.value.raw = `\n${compile(quasi.value.raw, { basedir: cwd })}`; + return quasi; + }); + path.replaceWith(literal); + return; + } else if (typeof literal === 'string') { + const result = compile(literal, { basedir: cwd }); + path.replaceWith(t.stringLiteral(result)); + return; + } } + + throw new Error( + 'babel-plugin-glsl: string template could not be evaluated' + ); } module.exports = processGlslTag; diff --git a/macro.js b/macro.js index c512ce6..5308fc1 100644 --- a/macro.js +++ b/macro.js @@ -1,12 +1,16 @@ const { createMacro, MacroError } = require('babel-plugin-macros'); const processGlslTag = require('./lib/processGlslTag'); -function glslifyMacro({ references, state }) { +function glslifyMacro({ references, state, babel }) { + const t = babel.types; const { default: defaultImport = [] } = references; defaultImport.forEach(referencePath => { const path = referencePath.parentPath; - if (path.type === 'TaggedTemplateExpression') { + if ( + (t.isCallExpression(path.node) && + t.isStringLiteral(path.node.arguments[0])) || + t.isTaggedTemplateExpression(path.node)) { try { processGlslTag(path, state); } catch (e) { diff --git a/test/macro.test.js b/test/macro.test.js index 12fefcf..7799f8b 100644 --- a/test/macro.test.js +++ b/test/macro.test.js @@ -16,5 +16,13 @@ pluginTester({ `, output: '"\\nvoid main () {\\n gl_FragColor = vec4(1, 0, 0, 1);\\n}";', }, + { + code: ` + import glsl from '../macro'; + + glsl("./macroFixtures/externalFile/myshader.frag"); + `, + output: '"#define E 2.718281828459045\\n#define PI 3.141592653589793\\n\\nvoid main () {\\n gl_FragColor = vec4(vec3(1.,0.,0.), 1);\\n}\\n";', + }, ], }); diff --git a/test/macroFixtures/externalFile/code.js b/test/macroFixtures/externalFile/code.js new file mode 100644 index 0000000..1f773b7 --- /dev/null +++ b/test/macroFixtures/externalFile/code.js @@ -0,0 +1,3 @@ +import glsl from 'glslify'; + +glsl("./myshader.frag"); diff --git a/test/macroFixtures/externalFile/defines.glsl b/test/macroFixtures/externalFile/defines.glsl new file mode 100644 index 0000000..1d438ca --- /dev/null +++ b/test/macroFixtures/externalFile/defines.glsl @@ -0,0 +1,2 @@ +#define E 2.718281828459045 +#define PI 3.141592653589793 diff --git a/test/macroFixtures/externalFile/myshader.frag b/test/macroFixtures/externalFile/myshader.frag new file mode 100644 index 0000000..0d39691 --- /dev/null +++ b/test/macroFixtures/externalFile/myshader.frag @@ -0,0 +1,4 @@ +#pragma glslify: import(./defines) +void main () { + gl_FragColor = vec4(#ff0000, 1); +} diff --git a/test/macroFixtures/externalFile/output.js b/test/macroFixtures/externalFile/output.js new file mode 100644 index 0000000..61623c8 --- /dev/null +++ b/test/macroFixtures/externalFile/output.js @@ -0,0 +1 @@ +"\n #define E 2.718281828459045\n#define PI 3.141592653589793\n\n void main () {\n gl_FragColor = vec4(vec3(1.,0.,0.), 1);\n }\n"; \ No newline at end of file diff --git a/test/macroFixtures/externalFile/package.json b/test/macroFixtures/externalFile/package.json new file mode 100644 index 0000000..26c04bd --- /dev/null +++ b/test/macroFixtures/externalFile/package.json @@ -0,0 +1,8 @@ +{ + "glslify": { + "transform": [ + "glslify-hex", + "glslify-import" + ] + } +}