From 11f824f1577e8ccc4ee621220242eaf89aa4cf6a Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Thu, 21 Jun 2018 16:51:00 +0200 Subject: [PATCH 1/9] Added test fixtures for EffectComposer, CopyShader & RenderPass --- test/fixtures.spec.js | 12 ++++++++++++ test/fixtures/copyshader.js | 6 ++++++ test/fixtures/effectcomposer.js | 7 +++++++ test/fixtures/renderpass.js | 7 +++++++ 4 files changed, 32 insertions(+) create mode 100644 test/fixtures/copyshader.js create mode 100644 test/fixtures/effectcomposer.js create mode 100644 test/fixtures/renderpass.js diff --git a/test/fixtures.spec.js b/test/fixtures.spec.js index eedf168..88c51ef 100644 --- a/test/fixtures.spec.js +++ b/test/fixtures.spec.js @@ -118,6 +118,18 @@ it('With examples', async() => { await testFixture('./with-examples.js', false, 'function function function'); }); +it('EffectComposer', async() => { + await testFixture('./effectcomposer.js', false, 'function function function'); +}); + +it('RenderPass', async() => { + await testFixture('./renderpass.js', false, 'function function'); +}); + +it('CopyShader', async() => { + await testFixture('./copyshader.js', false, 'object string string'); +}); + it('Invalid path', async() => { const errors = await testFixture('./wrong-examples.js', true, ''); expect(errors.length).toBe(1, 'Has one error'); diff --git a/test/fixtures/copyshader.js b/test/fixtures/copyshader.js new file mode 100644 index 0000000..09508fe --- /dev/null +++ b/test/fixtures/copyshader.js @@ -0,0 +1,6 @@ +import {uniforms, vertexShader, fragmentShader} from 'three/examples/js/shaders/CopyShader'; + +const $div = document.createElement('div'); +$div.setAttribute('id', 'fixture'); +$div.innerText = ` ${typeof uniforms} ${typeof vertexShader} ${typeof fragmentShader}`; +document.body.appendChild($div); diff --git a/test/fixtures/effectcomposer.js b/test/fixtures/effectcomposer.js new file mode 100644 index 0000000..d375fc4 --- /dev/null +++ b/test/fixtures/effectcomposer.js @@ -0,0 +1,7 @@ +import {Vector3} from 'three'; +import {EffectComposer, Pass} from 'three/examples/js/postprocessing/EffectComposer'; + +const $div = document.createElement('div'); +$div.setAttribute('id', 'fixture'); +$div.innerText = ` ${typeof Vector3} ${typeof EffectComposer} ${typeof Pass}`; +document.body.appendChild($div); diff --git a/test/fixtures/renderpass.js b/test/fixtures/renderpass.js new file mode 100644 index 0000000..6dbf403 --- /dev/null +++ b/test/fixtures/renderpass.js @@ -0,0 +1,7 @@ +import {Vector3} from 'three'; +import RenderPass from 'three/examples/js/postprocessing/RenderPass'; + +const $div = document.createElement('div'); +$div.setAttribute('id', 'fixture'); +$div.innerText = ` ${typeof Vector3} ${typeof RenderPass}`; +document.body.appendChild($div); From 889e54d070a149974b64b916f65e2f15075cd91b Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Thu, 21 Jun 2018 16:52:39 +0200 Subject: [PATCH 2/9] The plugin now uses a local loader --- package.json | 3 +-- src/Loader.js | 32 ++++++++++++++++++++++++++++++++ src/Plugin.js | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/Loader.js diff --git a/package.json b/package.json index fc38663..2ce31cd 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,7 @@ }, "homepage": "https://github.com/wildpeaks/package-three-webpack-plugin#readme", "dependencies": { - "exports-loader": "0.7.0", - "imports-loader": "0.8.0" + "loader-utils": "1.1.0" }, "devDependencies": { "@wildpeaks/eslint-config-commonjs": "4.9.0", diff --git a/src/Loader.js b/src/Loader.js new file mode 100644 index 0000000..9a40930 --- /dev/null +++ b/src/Loader.js @@ -0,0 +1,32 @@ +'use strict'; +const {getOptions} = require('loader-utils'); + + +function Loader(source){ + const options = getOptions(this); + let code = `'use strict';\nconst THREE = require('three');\n`; + + const optionsRequires = options.requires; + if (Array.isArray(optionsRequires)){ + code += optionsRequires.map(moduleId => `require(${JSON.stringify(moduleId)});`).join('\n'); + } + + code += `${source}\n`; + + const optionsExports = options.exports; + if (typeof optionsExports === 'string'){ + code += `module.exports = ${optionsExports};\n`; + } else if ((typeof optionsExports === 'object') && (optionsExports !== null)){ + const lines = []; + for (const id in optionsExports){ + const exportId = optionsExports[id]; + lines.push(`${JSON.stringify(id)}: ${exportId}`); + } + code += 'module.exports = {' + lines.join(',') + '}'; // eslint-disable-line prefer-template + } + + return code; +} + + +module.exports = Loader; diff --git a/src/Plugin.js b/src/Plugin.js index 37e30e2..8535cb2 100644 --- a/src/Plugin.js +++ b/src/Plugin.js @@ -1,6 +1,8 @@ /* eslint-env node */ 'use strict'; const PLUGIN_ID = 'wildpeaks-three'; +const Loader = require.resolve('./Loader'); + class Plugin { apply(compiler){ // eslint-disable-line class-methods-use-this @@ -9,8 +11,35 @@ class Plugin { const {loaders, rawRequest} = data; if (rawRequest.startsWith('three/examples/js/')){ const exportId = rawRequest.split('/').pop(); - loaders.push('imports-loader?THREE=three'); - loaders.push(`exports-loader?THREE.${exportId}`); + + if (rawRequest === 'three/examples/js/postprocessing/EffectComposer'){ + loaders.push({ + loader: Loader, + options: { + exports: { + EffectComposer: 'THREE.EffectComposer', + Pass: 'THREE.Pass' + } + } + }); + } else if (rawRequest.startsWith('three/examples/js/postprocessing/')){ + loaders.push({ + loader: Loader, + options: { + requires: [ + 'three/examples/js/postprocessing/EffectComposer' + ], + exports: `THREE.${exportId}` + } + }); + } else { + loaders.push({ + loader: Loader, + options: { + exports: `THREE.${exportId}` + } + }); + } } return data; }); From 4983494c722eecec99930e994d4503435d18f1a8 Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Thu, 21 Jun 2018 16:56:27 +0200 Subject: [PATCH 3/9] Using `var` instead of `const` Just in case the user has no transforms and targets es5 --- src/Loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Loader.js b/src/Loader.js index 9a40930..750b437 100644 --- a/src/Loader.js +++ b/src/Loader.js @@ -4,7 +4,7 @@ const {getOptions} = require('loader-utils'); function Loader(source){ const options = getOptions(this); - let code = `'use strict';\nconst THREE = require('three');\n`; + let code = `'use strict';\nvar THREE = require('three');\n`; const optionsRequires = options.requires; if (Array.isArray(optionsRequires)){ From ba533994db36d7bfca9150f8c3a09c2f2f6ff469 Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Thu, 21 Jun 2018 18:10:25 +0200 Subject: [PATCH 4/9] Breaking change: the loader exports as named export instead of `default` Because EffectComposer might not be the only file that exports multiple classes, and it would be inconsistent to export some files as default and other files as named. Also, if they're ever truly converted to ES Modules, they would most likely use named exports like package `three`. --- README.md | 2 +- src/Loader.js | 4 +--- src/Plugin.js | 8 ++++++-- test/fixtures.spec.js | 2 +- test/fixtures/copyshader.js | 4 ++-- test/fixtures/renderpass.js | 2 +- test/fixtures/with-examples.js | 7 ++----- test/fixtures/{wrong-examples.js.js => wrong-examples.js} | 2 +- 8 files changed, 15 insertions(+), 16 deletions(-) rename test/fixtures/{wrong-examples.js.js => wrong-examples.js} (77%) diff --git a/README.md b/README.md index 3761984..c41834e 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ You can now import the classes in your application: import {Scene, WebGLRenderer} from 'three'; // Import from "three/examples/js" for addditional classes -import OrbitControls from 'three/examples/js/controls/OrbitControls'; +import {OrbitControls} from 'three/examples/js/controls/OrbitControls'; // Use the imported classes const scene = new Scene(); diff --git a/src/Loader.js b/src/Loader.js index 750b437..43bba76 100644 --- a/src/Loader.js +++ b/src/Loader.js @@ -14,9 +14,7 @@ function Loader(source){ code += `${source}\n`; const optionsExports = options.exports; - if (typeof optionsExports === 'string'){ - code += `module.exports = ${optionsExports};\n`; - } else if ((typeof optionsExports === 'object') && (optionsExports !== null)){ + if ((typeof optionsExports === 'object') && (optionsExports !== null)){ const lines = []; for (const id in optionsExports){ const exportId = optionsExports[id]; diff --git a/src/Plugin.js b/src/Plugin.js index 8535cb2..87948d8 100644 --- a/src/Plugin.js +++ b/src/Plugin.js @@ -29,14 +29,18 @@ class Plugin { requires: [ 'three/examples/js/postprocessing/EffectComposer' ], - exports: `THREE.${exportId}` + exports: { + [exportId]: `THREE.${exportId}` + } } }); } else { loaders.push({ loader: Loader, options: { - exports: `THREE.${exportId}` + exports: { + [exportId]: `THREE.${exportId}` + } } }); } diff --git a/test/fixtures.spec.js b/test/fixtures.spec.js index 88c51ef..670542c 100644 --- a/test/fixtures.spec.js +++ b/test/fixtures.spec.js @@ -127,7 +127,7 @@ it('RenderPass', async() => { }); it('CopyShader', async() => { - await testFixture('./copyshader.js', false, 'object string string'); + await testFixture('./copyshader.js', false, 'object object string string'); }); it('Invalid path', async() => { diff --git a/test/fixtures/copyshader.js b/test/fixtures/copyshader.js index 09508fe..dc6a4f2 100644 --- a/test/fixtures/copyshader.js +++ b/test/fixtures/copyshader.js @@ -1,6 +1,6 @@ -import {uniforms, vertexShader, fragmentShader} from 'three/examples/js/shaders/CopyShader'; +import {CopyShader} from 'three/examples/js/shaders/CopyShader'; const $div = document.createElement('div'); $div.setAttribute('id', 'fixture'); -$div.innerText = ` ${typeof uniforms} ${typeof vertexShader} ${typeof fragmentShader}`; +$div.innerText = `${typeof CopyShader} ${typeof CopyShader.uniforms} ${typeof CopyShader.vertexShader} ${typeof CopyShader.fragmentShader}`; document.body.appendChild($div); diff --git a/test/fixtures/renderpass.js b/test/fixtures/renderpass.js index 6dbf403..388b444 100644 --- a/test/fixtures/renderpass.js +++ b/test/fixtures/renderpass.js @@ -1,5 +1,5 @@ import {Vector3} from 'three'; -import RenderPass from 'three/examples/js/postprocessing/RenderPass'; +import {RenderPass} from 'three/examples/js/postprocessing/RenderPass'; const $div = document.createElement('div'); $div.setAttribute('id', 'fixture'); diff --git a/test/fixtures/with-examples.js b/test/fixtures/with-examples.js index 739c86c..8bc4112 100644 --- a/test/fixtures/with-examples.js +++ b/test/fixtures/with-examples.js @@ -1,9 +1,6 @@ import {Vector3} from 'three'; -import OrbitControls from "three/examples/js/controls/OrbitControls"; -import OBJLoader from "three/examples/js/loaders/OBJLoader"; - -console.log('[OrbitControls]', OrbitControls); -console.log('[OBJLoader]', OBJLoader); +import {OrbitControls} from 'three/examples/js/controls/OrbitControls'; +import {OBJLoader} from 'three/examples/js/loaders/OBJLoader'; const $div = document.createElement('div'); $div.setAttribute('id', 'fixture'); diff --git a/test/fixtures/wrong-examples.js.js b/test/fixtures/wrong-examples.js similarity index 77% rename from test/fixtures/wrong-examples.js.js rename to test/fixtures/wrong-examples.js index 204c21f..4af272a 100644 --- a/test/fixtures/wrong-examples.js.js +++ b/test/fixtures/wrong-examples.js @@ -1,5 +1,5 @@ import {Vector3} from 'three'; -import OBJLoader from "three/examples/js/fake/OBJLoader"; +import OBJLoader from 'three/examples/js/fake/OBJLoader'; const $div = document.createElement('div'); $div.setAttribute('id', 'fixture'); From fd3d28acccaf3811cc575402c60f23a59f6a9887 Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Thu, 21 Jun 2018 18:12:03 +0200 Subject: [PATCH 5/9] Just removed some whitespace --- src/Plugin.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Plugin.js b/src/Plugin.js index 87948d8..70f47d3 100644 --- a/src/Plugin.js +++ b/src/Plugin.js @@ -3,7 +3,6 @@ const PLUGIN_ID = 'wildpeaks-three'; const Loader = require.resolve('./Loader'); - class Plugin { apply(compiler){ // eslint-disable-line class-methods-use-this compiler.hooks.normalModuleFactory.tap(PLUGIN_ID, normalModuleFactory => { @@ -11,7 +10,6 @@ class Plugin { const {loaders, rawRequest} = data; if (rawRequest.startsWith('three/examples/js/')){ const exportId = rawRequest.split('/').pop(); - if (rawRequest === 'three/examples/js/postprocessing/EffectComposer'){ loaders.push({ loader: Loader, From 8893c0e3903a04a937a18c72b6c83c1faadc46d4 Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Fri, 22 Jun 2018 13:18:29 +0200 Subject: [PATCH 6/9] Added a few Typescript test fixtures --- .gitattributes | 1 + .gitignore | 2 ++ globals.d.ts | 21 +++++++++++++++++++++ package.json | 3 +++ test/fixtures.spec.js | 23 +++++++++++++++++++++++ test/fixtures/ts-renderpass.ts | 7 +++++++ test/fixtures/ts-with-examples.ts | 8 ++++++++ tsconfig.json | 21 +++++++++++++++++++++ 8 files changed, 86 insertions(+) create mode 100644 globals.d.ts create mode 100644 test/fixtures/ts-renderpass.ts create mode 100644 test/fixtures/ts-with-examples.ts create mode 100644 tsconfig.json diff --git a/.gitattributes b/.gitattributes index d36a461..4f9aada 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,5 +8,6 @@ LICENSE text *.md text *.js text +*.ts text *.json text *.yml text diff --git a/.gitignore b/.gitignore index 6d30f40..73ccc6a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,9 @@ !/.vscode !/src !/test +!/globals.d.ts !/package.json +!/tsconfig.json #-----------------------------------------------------------------------------# # But make sure to ignore those regardless: diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000..6852dd2 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,21 @@ + +declare module 'three/examples/js/controls/OrbitControls' { + export const OrbitControls: typeof THREE.OrbitControls; +} + +declare module 'three/examples/js/loaders/OBJLoader' { + export const OBJLoader: typeof THREE.OBJLoader; +} + +declare module 'three/examples/js/postprocessing/EffectComposer' { + export const EffectComposer: typeof THREE.EffectComposer; + export const Pass: typeof THREE.Pass; +} + +declare module 'three/examples/js/postprocessing/RenderPass' { + export const RenderPass: typeof THREE.RenderPass; +} + +declare module 'three/examples/js/shaders/CopyShader' { + export const CopyShader: typeof THREE.CopyShader; +} diff --git a/package.json b/package.json index 2ce31cd..0c6ee60 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "loader-utils": "1.1.0" }, "devDependencies": { + "@types/three": "0.92.7", "@wildpeaks/eslint-config-commonjs": "4.9.0", "eslint": "4.19.1", "express": "4.16.3", @@ -36,6 +37,8 @@ "puppeteer": "1.5.0", "rimraf": "2.6.2", "three": "0.93.0", + "ts-loader": "4.4.1", + "typescript": "2.9.2", "webpack": "4.12.0" }, "peerDependencies": { diff --git a/test/fixtures.spec.js b/test/fixtures.spec.js index 670542c..433e1bb 100644 --- a/test/fixtures.spec.js +++ b/test/fixtures.spec.js @@ -51,6 +51,21 @@ async function testFixture(entry, expectError, expectText){ path: outputFolder, filename: '[name].js' }, + module: { + rules: entry.endsWith('.ts') ? [ + { + test: /\.(ts|js)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true + } + } + ] + } + ] : [] + }, plugins: [ new HtmlWebpackPlugin(), new Plugin() @@ -135,3 +150,11 @@ it('Invalid path', async() => { expect(errors.length).toBe(1, 'Has one error'); expect(errors[0] instanceof ModuleNotFoundError).toBe(true, 'The error is a ModuleNotFoundError'); }); + +it('Typescript: With examples', async() => { + await testFixture('./ts-with-examples.ts', false, 'function function function'); +}); + +it('Typescript: RenderPass', async() => { + await testFixture('./ts-renderpass.ts', false, 'function function'); +}); diff --git a/test/fixtures/ts-renderpass.ts b/test/fixtures/ts-renderpass.ts new file mode 100644 index 0000000..858a9e7 --- /dev/null +++ b/test/fixtures/ts-renderpass.ts @@ -0,0 +1,7 @@ +import {Vector3} from 'three'; +import {RenderPass} from 'three/examples/js/postprocessing/RenderPass'; + +const $div: HTMLDivElement = document.createElement('div'); +$div.setAttribute('id', 'fixture'); +$div.innerText = ` ${typeof Vector3} ${typeof RenderPass}`; +document.body.appendChild($div); diff --git a/test/fixtures/ts-with-examples.ts b/test/fixtures/ts-with-examples.ts new file mode 100644 index 0000000..1c7b9d2 --- /dev/null +++ b/test/fixtures/ts-with-examples.ts @@ -0,0 +1,8 @@ +import {Vector3} from 'three'; +import {OrbitControls} from 'three/examples/js/controls/OrbitControls'; +import {OBJLoader} from 'three/examples/js/loaders/OBJLoader'; + +const $div: HTMLDivElement = document.createElement('div'); +$div.setAttribute('id', 'fixture'); +$div.innerText = `${typeof Vector3} ${typeof OBJLoader} ${typeof OrbitControls}`; +document.body.appendChild($div); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ee5c632 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "newLine": "LF", + "alwaysStrict": true, + "noEmitOnError": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictNullChecks": true, + "preserveConstEnums": true, + "removeComments": true, + "sourceMap": true, + "module": "esnext", + "target": "es5", + "lib": ["es2017", "dom"], + "allowJs": true + } +} From a0cc306a23b4485236cd33c94c869565a695d4d5 Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Fri, 22 Jun 2018 13:28:08 +0200 Subject: [PATCH 7/9] Updated package version to `2.0.0-beta` and resolves #4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c6ee60..dd0dc33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wildpeaks/three-webpack-plugin", - "version": "1.0.0", + "version": "2.0.0-beta", "description": "Webpack plugin to use Three.js \"examples\" classes that aren't ES Modules", "author": "Cecile Muller", "license": "MIT", From 70613c55f19aa493cee133c2047bde4a4744f986 Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Fri, 22 Jun 2018 14:48:29 +0200 Subject: [PATCH 8/9] Updated package version to `2.0.0` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dd0dc33..dcb958c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wildpeaks/three-webpack-plugin", - "version": "2.0.0-beta", + "version": "2.0.0", "description": "Webpack plugin to use Three.js \"examples\" classes that aren't ES Modules", "author": "Cecile Muller", "license": "MIT", From 7060d7d30439b4ccba8dcfd548a59a622377d94e Mon Sep 17 00:00:00 2001 From: Cecile Muller Date: Fri, 22 Jun 2018 15:00:08 +0200 Subject: [PATCH 9/9] Added `declare module` example in README --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index c41834e..4b219be 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,18 @@ const scene = new Scene(); const renderer = new WebGLRenderer(); const controls = new OrbitControls(); ```` + + +## Typescript + +Until definitions are integrated directly in `@types/three`, add a file `globals.d.ts` +at the root of your project to specify the types of the imports, e.g.: + +````ts +declare module 'three/examples/js/controls/OrbitControls' { + export const OrbitControls: typeof THREE.OrbitControls; +} +```` + +Note that this is *not* required for compiling to JS, it improves Intellisense in your code editor. +