From cd5f669a1cdae3e43877d581e2e0dcf52a57ec98 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Tue, 9 Jul 2019 13:23:49 -0700 Subject: [PATCH 1/9] refactor: switch from standard-things/esm to native BREAKING CHANGE: now if you want to use `.mjs` files, you should run with `NODE_OPTIONS=--experimental-modules` on Node 12.x+. Also, native ES module loading is NOT compatible with coffeescript/register Fixes: https://github.com/groupon/nilo/issues/11 --- .eslintignore | 1 + lib/esm/import.js | 40 ++++++++++++++++++++++++++ lib/esm/index.js | 48 +++++++++++++++++++++++++++++++ lib/project.js | 65 ++++++++++++++++++------------------------ package-lock.json | 5 ---- package.json | 1 - test/project.test.js | 67 +++++++++++++++++++++++++++++--------------- 7 files changed, 161 insertions(+), 66 deletions(-) create mode 100644 lib/esm/import.js create mode 100644 lib/esm/index.js diff --git a/.eslintignore b/.eslintignore index c58b395..504728e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ /node_modules /tmp +/lib/esm/import.js diff --git a/lib/esm/import.js b/lib/esm/import.js new file mode 100644 index 0000000..79cd7d3 --- /dev/null +++ b/lib/esm/import.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, Groupon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of GROUPON nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +'use strict'; + +// put this in its own file so that it can barf on require if needed + +/** + * @param {string} id + * @returns {Promise} + */ +module.exports = id => import(id); diff --git a/lib/esm/index.js b/lib/esm/index.js new file mode 100644 index 0000000..1e4b348 --- /dev/null +++ b/lib/esm/index.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019, Groupon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of GROUPON nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +'use strict'; + +const debug = require('debug')('esm'); + +/** @type {(id: string) => Promise} */ +let importESM; +try { + importESM = require('./import'); +} catch (err) { + debug('failed to load esm import: ', err); + importESM = () => Promise.reject(err); +} + +exports.importESM = importESM; +exports.supportsESM = () => + importESM('../../package.json').then(() => true, () => false); diff --git a/lib/project.js b/lib/project.js index cbd4478..0490f84 100644 --- a/lib/project.js +++ b/lib/project.js @@ -35,11 +35,10 @@ const path = require('path'); const Module = require('module'); const util = require('util'); +const { URL } = require('url'); const debug = require('debug')('nilo:project'); // @ts-ignore -const esm = require('esm'); -// @ts-ignore const globCallback = require('glob'); const glob = util.promisify(globCallback); @@ -48,27 +47,18 @@ const glob = util.promisify(globCallback); const rawModulePaths = Module['_nodeModulePaths']; const getModulePaths = /** @type {(from: string) => string[]} */ (rawModulePaths); +const { importESM } = require('./esm'); + /** - * @typedef {import('./typedefs').InterfaceFile} InterfaceFile + * @param {string} reSTR */ +function escapeRE(reSTR) { + return reSTR.replace(/[$^*()+\[\]{}\\|.?]/g, '\\$&'); +} /** - * @param {object} esmResult - * @param {string} specifier + * @typedef {import('./typedefs').InterfaceFile} InterfaceFile */ -function guessNamespace(esmResult, specifier) { - if (specifier.endsWith('.mjs')) return esmResult; - - // "Heuristic" to figure out if it kinda looks like a namespace - if ( - esmResult !== null && - typeof esmResult === 'object' && - 'default' in esmResult - ) - return esmResult; - - return { default: esmResult }; -} class Project { /** @@ -91,27 +81,12 @@ class Project { this._pkgJson = null; this._globCache = {}; this._globStatCache = {}; - - this._importESM = esm(this.app, { - cjs: { - cache: false, - esModule: false, - extensions: false, - mutableNamespace: false, - namedExports: false, - paths: false, - vars: false, - dedefault: false, - topLevelReturn: false, - }, - mode: 'strict', - }); } /** * @param {string} pattern * @param {object} [options] - * @returns {string[]} + * @returns {Promise} */ cachedGlob(pattern, options = {}) { return glob( @@ -188,9 +163,22 @@ class Project { * @param {string} id * @returns {Promise} */ - import(id) { - const esmResult = this._importESM(id); - return guessNamespace(esmResult, id); + async import(id) { + debug('import', id); + // if it's a package spec, assume we can't load it as a ES module + // for now + if (!/^(\.\.?)?\//.test(id)) return { default: this.require(id) }; + + try { + return await importESM( + /^\.\.?\//.test(id) + ? new URL(id, `file://${this.app.filename}`).toString() + : id + ); + } catch (err) { + if (err.message !== 'Not supported') throw err; + return { default: this.require(id) }; + } } /** @@ -204,7 +192,8 @@ class Project { // Still throw errors unrelated to finding the module if (e.code !== 'MODULE_NOT_FOUND') throw e; // Do *not* ignore failing requires of subsequent files - if (!e.message.includes(`'${id}'`)) throw e; + const re = new RegExp(`(^|[\\s'"])${escapeRE(id)}([\\s'"]|$)`); + if (!re.test(e.message)) throw e; return null; } diff --git a/package-lock.json b/package-lock.json index 265dcbb..47d7d27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -756,11 +756,6 @@ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, - "esm": { - "version": "3.2.20", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.20.tgz", - "integrity": "sha512-NA92qDA8C/qGX/xMinDGa3+cSPs4wQoFxskRrSnDo/9UloifhONFm4sl4G+JsyCqM007z2K+BfQlH5rMta4K1Q==" - }, "espree": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", diff --git a/package.json b/package.json index 9473a3a..d1905be 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "dependencies": { "commander": "^2.19.0", "debug": "^4.1.1", - "esm": "^3.2.20", "glob": "^7.1.3" }, "devDependencies": { diff --git a/test/project.test.js b/test/project.test.js index 21fc227..de40ba5 100644 --- a/test/project.test.js +++ b/test/project.test.js @@ -9,6 +9,7 @@ const mkdirp = require('mkdirp'); const tmp = require('tmp'); const { Project } = require('../'); +const { supportsESM } = require('../lib/esm'); const FRAMEWORK_DIR = path.resolve(__dirname, '..'); @@ -34,7 +35,7 @@ describe('Project', () => { let project; /** @type {import('tmp').SynchrounousResult} */ let tmpHandle; - before(() => { + before(async () => { tmpHandle = tmp.dirSync({ unsafeCleanup: true }); project = new Project(tmpHandle.name, FRAMEWORK_DIR); @@ -55,11 +56,13 @@ describe('Project', () => { module.exports = 'from lib1'; `, - 'modules/mod1/everywhere.mjs': `\ + }; + if (await supportsESM()) { + files['modules/mod1/everywhere.mjs'] = `\ export default 'from mod1'; export const namedExport = 'forwarded'; -`, - }; +`; + } const pkgNames = ['@some-scope/pkg1', 'dev-dep1', 'hoisted', 'pkg2']; for (const pkgName of pkgNames) { files[`node_modules/${pkgName}/everywhere.js`] = `\ @@ -83,11 +86,23 @@ module.exports = 'from ${pkgName}'; if (tmpHandle) tmpHandle.removeCallback(); }); + afterEach(() => { + // @ts-ignore + project['_globCache'] = {}; + // @ts-ignore + project['_globStatCache'] = {}; + }); + /** * @param {{ specifier: string, [key: string]: any }[]} expected * @param {{ specifier: string, [key: string]: any }[]} actual */ function sortedEqual(expected, actual) { + // native es modules return a namespace which isn't an ordinary object, + // which makes mocha barf on diffing + for (const a of actual) { + if (a.moduleNamespace) a.moduleNamespace = { ...a.moduleNamespace }; + } assert.deepStrictEqual( sortBy(actual, 'specifier'), sortBy(expected, 'specifier') @@ -108,15 +123,19 @@ module.exports = 'from ${pkgName}'; moduleNamespace: { default: 'from lib1' }, defaultExport: 'from lib1', }, - { - specifier: './modules/mod1/everywhere.mjs', - group: 'mod1', - moduleNamespace: { - default: 'from mod1', - namedExport: 'forwarded', - }, - defaultExport: 'from mod1', - }, + ...((await supportsESM()) + ? [ + { + specifier: './modules/mod1/everywhere.mjs', + group: 'mod1', + moduleNamespace: { + default: 'from mod1', + namedExport: 'forwarded', + }, + defaultExport: 'from mod1', + }, + ] + : []), { specifier: '@some-scope/pkg1/everywhere', group: 'pkg1', @@ -139,15 +158,19 @@ module.exports = 'from ${pkgName}'; moduleNamespace: { default: 'from lib1' }, defaultExport: 'from lib1', }, - { - specifier: './modules/mod1/everywhere.mjs', - group: 'mod1', - moduleNamespace: { - default: 'from mod1', - namedExport: 'forwarded', - }, - defaultExport: 'from mod1', - }, + ...((await supportsESM()) + ? [ + { + specifier: './modules/mod1/everywhere.mjs', + group: 'mod1', + moduleNamespace: { + default: 'from mod1', + namedExport: 'forwarded', + }, + defaultExport: 'from mod1', + }, + ] + : []), { specifier: '@some-scope/pkg1/everywhere', group: 'pkg1', From 2c30ee44a491e5bb667be0114f09b97cb486f4d1 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Sat, 13 Jul 2019 11:20:59 -0700 Subject: [PATCH 2/9] chore: fix tests --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index ed664bb..65144c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,13 @@ language: node_js node_js: - '8' - '10' +env: + - NODE_OPTIONS= + - NODE_OPTIONS=--experimental-modules +matrix: + exclude: + - node_js: '8' + env: NODE_OPTIONS=--experimental-modules deploy: - provider: script script: ./node_modules/.bin/nlm release From 83e2d7d9e741e75aaca6bf01870e8f81b1adf497 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Fri, 6 Dec 2019 12:51:21 -0800 Subject: [PATCH 3/9] chore: cleanup travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 65144c9..08569a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ matrix: env: NODE_OPTIONS=--experimental-modules deploy: - provider: script - script: ./node_modules/.bin/nlm release + script: npx nlm release skip_cleanup: true 'on': branch: master From a447de84f039c2c2c1dc80dbe254f09aec6a7b8f Mon Sep 17 00:00:00 2001 From: David Bushong Date: Fri, 6 Dec 2019 14:07:57 -0800 Subject: [PATCH 4/9] fix: node8 support --- lib/esm/index.js | 2 +- lib/project.js | 1 + test/project.test.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/esm/index.js b/lib/esm/index.js index 1e4b348..3f82e99 100644 --- a/lib/esm/index.js +++ b/lib/esm/index.js @@ -40,7 +40,7 @@ try { importESM = require('./import'); } catch (err) { debug('failed to load esm import: ', err); - importESM = () => Promise.reject(err); + importESM = () => Promise.reject(new Error('Not supported')); } exports.importESM = importESM; diff --git a/lib/project.js b/lib/project.js index 0490f84..75b08e7 100644 --- a/lib/project.js +++ b/lib/project.js @@ -205,6 +205,7 @@ class Project { * @returns {T} */ require(id) { + debug('require', id); return this.app.require(id); } diff --git a/test/project.test.js b/test/project.test.js index de40ba5..1744b81 100644 --- a/test/project.test.js +++ b/test/project.test.js @@ -58,6 +58,8 @@ module.exports = 'from lib1'; `, }; if (await supportsESM()) { + // eslint-disable-next-line no-console + console.log(' [esm support enabled]'); files['modules/mod1/everywhere.mjs'] = `\ export default 'from mod1'; export const namedExport = 'forwarded'; From 560896837e94b0a714e9efc830a06750b2000932 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Mon, 9 Dec 2019 11:19:22 -0800 Subject: [PATCH 5/9] docs: clarify mjs support --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1fc2389..79d7ba7 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,11 @@ This function returns an array with one entry for each interface file: relative to the app's root directory. * `group`: The directory the file was found in. +Note that `.mjs` file support requires you to be using a version of node with +builtin support for ES Modules. Currently this is Node 10+ with +the `--experimental-modules` flag. Node 10.x seems to experience +segfaults under certain conditions, so we recommend 12+. + ### Registry A set of three scopes, in order of nesting: From f4ef49c44b94d60e1477eb2c1c8db05027a27ae4 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Mon, 9 Dec 2019 11:21:02 -0800 Subject: [PATCH 6/9] chore: travis: cleanup run conditions --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08569a9..e86c6e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: node_js node_js: - '8' - '10' + - '12' env: - NODE_OPTIONS= - NODE_OPTIONS=--experimental-modules @@ -15,6 +16,4 @@ deploy: skip_cleanup: true 'on': branch: master - node: '10' -before_install: - - npm i -g npm@^6 + node: '12' From 66f1b179c4fcebb922dc63ba8a59493c3e98e920 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Mon, 9 Dec 2019 14:37:50 -0800 Subject: [PATCH 7/9] test: fix order of assert.equal() calls --- test/registry.test.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/registry.test.js b/test/registry.test.js index 854b961..7d38421 100644 --- a/test/registry.test.js +++ b/test/registry.test.js @@ -26,7 +26,7 @@ function assertThrows(fn) { * @param {Error & { code: string }} err */ function assertInvalidError(err) { - assert.equal('INVALID_DEPENDENCY_KEY', err.code); + assert.equal(err.code, 'INVALID_DEPENDENCY_KEY'); } /** @@ -34,7 +34,7 @@ function assertInvalidError(err) { */ function assertMissingError(err) { assert.include(err.message, 'Unknown dependency'); - assert.equal('INVALID_DEPENDENCY_KEY', err.code); + assert.equal(err.code, 'INVALID_DEPENDENCY_KEY'); } /** @@ -42,7 +42,7 @@ function assertMissingError(err) { */ function assertDuplicateError(err) { assert.include(err.message, 'already been registered'); - assert.equal('DUPLICATE_DEPENDENCY_KEY', err.code); + assert.equal(err.code, 'DUPLICATE_DEPENDENCY_KEY'); } describe('Registry', () => { @@ -97,9 +97,9 @@ describe('Registry', () => { it('exposes built-ins', () => { const injector = registry.getActionInjector(req, res, action); - assert.equal(req, injector.get('request')); - assert.equal(res, injector.get('response')); - assert.equal(action, injector.get('action')); + assert.equal(injector.get('request'), req); + assert.equal(injector.get('response'), res); + assert.equal(injector.get('action'), action); }); it('caches deps by request but not by action', () => { @@ -119,13 +119,13 @@ describe('Registry', () => { it('can be inspected', () => { const injector = registry.getActionInjector(req, res, action); assert.equal( - 'Injector { action, byAction, request, response, byReq, Symbol(byReq), constValue }', - inspect(injector) + inspect(injector), + 'Injector { action, byAction, request, response, byReq, Symbol(byReq), constValue }' ); const provider = injector.getProvider(); assert.equal( - 'Provider { Injector { action, byAction, request, response, byReq, Symbol(byReq), constValue } }', - inspect(provider) + inspect(provider), + 'Provider { Injector { action, byAction, request, response, byReq, Symbol(byReq), constValue } }' ); }); }); @@ -138,8 +138,8 @@ describe('Registry', () => { ]) .getActionInjector() .getProvider(); - assert.equal(99, deps.blah); - assert.equal(42, deps.answer); + assert.equal(deps.blah, 99); + assert.equal(deps.answer, 42); }); }); @@ -203,12 +203,12 @@ describe('Registry', () => { it('supports option object syntax', () => { assert.equal( - undefined, - singleton.get({ key: 'missing', optional: true, multiValued: false }) + singleton.get({ key: 'missing', optional: true, multiValued: false }), + undefined ); assert.equal( - EXISTING, - singleton.get({ key: 'existing', optional: true, multiValued: false }) + singleton.get({ key: 'existing', optional: true, multiValued: false }), + EXISTING ); }); @@ -318,17 +318,17 @@ describe('Registry', () => { it('refuses to retrieve a multi-values key when requesting a single value', () => { const err = assertThrows(() => singleton.get('single')); - assert.equal('INCOMPATIBLE_DEPENDENCY_KEY', err.code); + assert.equal(err.code, 'INCOMPATIBLE_DEPENDENCY_KEY'); assert.equal( - 'INCOMPATIBLE_DEPENDENCY_KEY', - assertThrows(() => singleton.getProvider()['single']).code + assertThrows(() => singleton.getProvider()['single']).code, + 'INCOMPATIBLE_DEPENDENCY_KEY' ); }); it('refuses to retrieve a normal key when requesting as multi-valued', () => { const err = assertThrows(() => singleton.get('simple[]')); - assert.equal('INCOMPATIBLE_DEPENDENCY_KEY', err.code); + assert.equal(err.code, 'INCOMPATIBLE_DEPENDENCY_KEY'); }); it('resolves to an empty list if no provider is known', () => { @@ -353,7 +353,7 @@ describe('Registry', () => { it('exposes the index names of the list items', () => { const multi = /** @type {{ c: string }} */ (request.get('multi[]')); - assert.equal('r.c', multi.c); + assert.equal(multi.c, 'r.c'); }); it('does shadowing per index', () => { From d50a30bd0f5f4b7da4e96017b5aa937608cf973c Mon Sep 17 00:00:00 2001 From: David Bushong Date: Mon, 9 Dec 2019 14:48:03 -0800 Subject: [PATCH 8/9] fix: make node 12 work by using createRequire BREAKING CHANGE: Node 8.x is no longer supported --- .eslintrc | 2 +- .travis.yml | 5 ----- lib/project.js | 38 ++++++++------------------------------ 3 files changed, 9 insertions(+), 36 deletions(-) diff --git a/.eslintrc b/.eslintrc index 58ee886..1c0f215 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,3 @@ { - "extends": "groupon/node8" + "extends": "groupon/node10" } diff --git a/.travis.yml b/.travis.yml index e86c6e6..50ff88c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,10 @@ language: node_js node_js: - - '8' - '10' - '12' env: - NODE_OPTIONS= - NODE_OPTIONS=--experimental-modules -matrix: - exclude: - - node_js: '8' - env: NODE_OPTIONS=--experimental-modules deploy: - provider: script script: npx nlm release diff --git a/lib/project.js b/lib/project.js index 75b08e7..0915847 100644 --- a/lib/project.js +++ b/lib/project.js @@ -33,7 +33,8 @@ 'use strict'; const path = require('path'); -const Module = require('module'); +// @ts-ignore +const { createRequireFromPath, createRequire } = require('module'); const util = require('util'); const { URL } = require('url'); @@ -43,9 +44,9 @@ const globCallback = require('glob'); const glob = util.promisify(globCallback); -// @ts-ignore -const rawModulePaths = Module['_nodeModulePaths']; -const getModulePaths = /** @type {(from: string) => string[]} */ (rawModulePaths); +// createRequireFromPath is deprecated Node 10.x; createRequire is official 12+ +/** @type {typeof createRequireFromPath} */ +const createReq = createRequire || createRequireFromPath; const { importESM } = require('./esm'); @@ -69,14 +70,10 @@ class Project { this.root = appDirectory; const appPath = path.resolve(appDirectory, 'app'); - this.app = new Module(''); - this.app.filename = appPath; - this.app.paths = getModulePaths(appPath); + this.require = createReq(appPath); const frameworkPath = path.resolve(frameworkDirectory, 'app'); - this.framework = new Module(''); - this.framework.filename = frameworkPath; - this.framework.paths = getModulePaths(frameworkPath); + this.requireBundled = createReq(frameworkPath); this._pkgJson = null; this._globCache = {}; @@ -172,7 +169,7 @@ class Project { try { return await importESM( /^\.\.?\//.test(id) - ? new URL(id, `file://${this.app.filename}`).toString() + ? new URL(id, `file://${this.root}/app`).toString() : id ); } catch (err) { @@ -199,16 +196,6 @@ class Project { } } - /** - * @template T - * @param {string} id - * @returns {T} - */ - require(id) { - debug('require', id); - return this.app.require(id); - } - /** * @template T * @param {string} id @@ -227,15 +214,6 @@ class Project { } } - /** - * @template T - * @param {string} id - * @returns {T} - */ - requireBundled(id) { - return this.framework.require(id); - } - /** * @template T * @param {string} id From 9b5b7035c0218b33ff2909a65bf439adde2aaf40 Mon Sep 17 00:00:00 2001 From: David Bushong Date: Mon, 9 Dec 2019 14:53:37 -0800 Subject: [PATCH 9/9] fix: node12 inspection of provider See: https://github.com/nodejs/node/pull/26241 --- lib/registry/injector.js | 23 ++++++----------------- test/registry.test.js | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/lib/registry/injector.js b/lib/registry/injector.js index c936a4c..3741522 100644 --- a/lib/registry/injector.js +++ b/lib/registry/injector.js @@ -41,15 +41,7 @@ const { parseDependencyQuery } = require('./query'); /** @typedef {import('../typedefs').DependencyDescriptor} DependencyDescriptor */ /** @typedef {import('../typedefs').DependencyQuery} DependencyQuery */ -const INSPECT = util.inspect.custom || Symbol.for('nodejs.util.inspect.custom'); - -/** - * @param {Injector} injector - */ -function inspectProvider(injector) { - // @ts-ignore - return `Provider { ${injector[INSPECT]()} }`; -} +const INSPECT = util.inspect.custom; const PROVIDER_HANDLER = { /** @@ -59,17 +51,14 @@ const PROVIDER_HANDLER = { get(injector, key) { switch (key) { case 'constructor': - return injector.constructor; - case 'get': - return injector.get; - case 'keys': - return injector.keys; + case 'scope': + return injector[key]; - case INSPECT: { - return inspectProvider.bind(null, injector); - } + case INSPECT: + // @ts-ignore + return injector[INSPECT]; default: return injector.get(key); diff --git a/test/registry.test.js b/test/registry.test.js index 7d38421..e529808 100644 --- a/test/registry.test.js +++ b/test/registry.test.js @@ -125,7 +125,7 @@ describe('Registry', () => { const provider = injector.getProvider(); assert.equal( inspect(provider), - 'Provider { Injector { action, byAction, request, response, byReq, Symbol(byReq), constValue } }' + 'Injector { action, byAction, request, response, byReq, Symbol(byReq), constValue }' ); }); });