module: refactor loader
PR-URL: https://github.com/nodejs/node/pull/16874 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
This commit is contained in:
parent
85739b6c5b
commit
921fb84687
@ -1,9 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {
|
const { ModuleWrap } = internalBinding('module_wrap');
|
||||||
ModuleWrap,
|
|
||||||
setImportModuleDynamicallyCallback
|
|
||||||
} = internalBinding('module_wrap');
|
|
||||||
const debug = require('util').debuglog('esm');
|
const debug = require('util').debuglog('esm');
|
||||||
const ArrayJoin = Function.call.bind(Array.prototype.join);
|
const ArrayJoin = Function.call.bind(Array.prototype.join);
|
||||||
const ArrayMap = Function.call.bind(Array.prototype.map);
|
const ArrayMap = Function.call.bind(Array.prototype.map);
|
||||||
@ -60,8 +57,4 @@ const createDynamicModule = (exports, url = '', evaluate) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = createDynamicModule;
|
||||||
createDynamicModule,
|
|
||||||
setImportModuleDynamicallyCallback,
|
|
||||||
ModuleWrap
|
|
||||||
};
|
|
84
lib/internal/loader/DefaultResolve.js
Normal file
84
lib/internal/loader/DefaultResolve.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { URL } = require('url');
|
||||||
|
const CJSmodule = require('module');
|
||||||
|
const internalURLModule = require('internal/url');
|
||||||
|
const internalFS = require('internal/fs');
|
||||||
|
const NativeModule = require('native_module');
|
||||||
|
const { extname } = require('path');
|
||||||
|
const { realpathSync } = require('fs');
|
||||||
|
const preserveSymlinks = !!process.binding('config').preserveSymlinks;
|
||||||
|
const errors = require('internal/errors');
|
||||||
|
const { resolve: moduleWrapResolve } = internalBinding('module_wrap');
|
||||||
|
const StringStartsWith = Function.call.bind(String.prototype.startsWith);
|
||||||
|
|
||||||
|
const realpathCache = new Map();
|
||||||
|
|
||||||
|
function search(target, base) {
|
||||||
|
if (base === undefined) {
|
||||||
|
// We cannot search without a base.
|
||||||
|
throw new errors.Error('ERR_MISSING_MODULE', target);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return moduleWrapResolve(target, base);
|
||||||
|
} catch (e) {
|
||||||
|
e.stack; // cause V8 to generate stack before rethrow
|
||||||
|
let error = e;
|
||||||
|
try {
|
||||||
|
const questionedBase = new URL(base);
|
||||||
|
const tmpMod = new CJSmodule(questionedBase.pathname, null);
|
||||||
|
tmpMod.paths = CJSmodule._nodeModulePaths(
|
||||||
|
new URL('./', questionedBase).pathname);
|
||||||
|
const found = CJSmodule._resolveFilename(target, tmpMod);
|
||||||
|
error = new errors.Error('ERR_MODULE_RESOLUTION_LEGACY', target,
|
||||||
|
base, found);
|
||||||
|
} catch (problemChecking) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensionFormatMap = {
|
||||||
|
__proto__: null,
|
||||||
|
'.mjs': 'esm',
|
||||||
|
'.json': 'json',
|
||||||
|
'.node': 'addon',
|
||||||
|
'.js': 'commonjs'
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolve(specifier, parentURL) {
|
||||||
|
if (NativeModule.nonInternalExists(specifier)) {
|
||||||
|
return {
|
||||||
|
url: specifier,
|
||||||
|
format: 'builtin'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let url;
|
||||||
|
try {
|
||||||
|
url = search(specifier, parentURL);
|
||||||
|
} catch (e) {
|
||||||
|
if (typeof e.message === 'string' &&
|
||||||
|
StringStartsWith(e.message, 'Cannot find module'))
|
||||||
|
e.code = 'MODULE_NOT_FOUND';
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preserveSymlinks) {
|
||||||
|
const real = realpathSync(internalURLModule.getPathFromURL(url), {
|
||||||
|
[internalFS.realpathCacheKey]: realpathCache
|
||||||
|
});
|
||||||
|
const old = url;
|
||||||
|
url = internalURLModule.getURLFromFilePath(real);
|
||||||
|
url.search = old.search;
|
||||||
|
url.hash = old.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ext = extname(url.pathname);
|
||||||
|
return { url: `${url}`, format: extensionFormatMap[ext] || ext };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = resolve;
|
||||||
|
// exported for tests
|
||||||
|
module.exports.search = search;
|
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { getURLFromFilePath, URL } = require('internal/url');
|
const { getURLFromFilePath, URL } = require('internal/url');
|
||||||
|
const errors = require('internal/errors');
|
||||||
const {
|
|
||||||
createDynamicModule,
|
|
||||||
setImportModuleDynamicallyCallback
|
|
||||||
} = require('internal/loader/ModuleWrap');
|
|
||||||
|
|
||||||
const ModuleMap = require('internal/loader/ModuleMap');
|
const ModuleMap = require('internal/loader/ModuleMap');
|
||||||
const ModuleJob = require('internal/loader/ModuleJob');
|
const ModuleJob = require('internal/loader/ModuleJob');
|
||||||
const ModuleRequest = require('internal/loader/ModuleRequest');
|
const defaultResolve = require('internal/loader/DefaultResolve');
|
||||||
const errors = require('internal/errors');
|
const createDynamicModule = require('internal/loader/CreateDynamicModule');
|
||||||
|
const translators = require('internal/loader/Translators');
|
||||||
|
const { setImportModuleDynamicallyCallback } = internalBinding('module_wrap');
|
||||||
|
const FunctionBind = Function.call.bind(Function.prototype.bind);
|
||||||
|
|
||||||
const debug = require('util').debuglog('esm');
|
const debug = require('util').debuglog('esm');
|
||||||
|
|
||||||
// Returns a file URL for the current working directory.
|
// Returns a file URL for the current working directory.
|
||||||
@ -40,105 +40,101 @@ function normalizeReferrerURL(referrer) {
|
|||||||
* the main module and everything in its dependency graph. */
|
* the main module and everything in its dependency graph. */
|
||||||
class Loader {
|
class Loader {
|
||||||
constructor(base = getURLStringForCwd()) {
|
constructor(base = getURLStringForCwd()) {
|
||||||
if (typeof base !== 'string') {
|
if (typeof base !== 'string')
|
||||||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'base', 'string');
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'base', 'string');
|
||||||
}
|
|
||||||
|
|
||||||
this.moduleMap = new ModuleMap();
|
|
||||||
this.base = base;
|
this.base = base;
|
||||||
|
|
||||||
|
// methods which translate input code or other information
|
||||||
|
// into es modules
|
||||||
|
this.translators = translators;
|
||||||
|
|
||||||
|
// registry of loaded modules, akin to `require.cache`
|
||||||
|
this.moduleMap = new ModuleMap();
|
||||||
|
|
||||||
// The resolver has the signature
|
// The resolver has the signature
|
||||||
// (specifier : string, parentURL : string, defaultResolve)
|
// (specifier : string, parentURL : string, defaultResolve)
|
||||||
// -> Promise<{ url : string,
|
// -> Promise<{ url : string, format: string }>
|
||||||
// format: anything in Loader.validFormats }>
|
|
||||||
// where defaultResolve is ModuleRequest.resolve (having the same
|
// where defaultResolve is ModuleRequest.resolve (having the same
|
||||||
// signature itself).
|
// signature itself).
|
||||||
// If `.format` on the returned value is 'dynamic', .dynamicInstantiate
|
// If `.format` on the returned value is 'dynamic', .dynamicInstantiate
|
||||||
// will be used as described below.
|
// will be used as described below.
|
||||||
this.resolver = ModuleRequest.resolve;
|
this._resolve = defaultResolve;
|
||||||
// This hook is only called when resolve(...).format is 'dynamic' and has
|
// This hook is only called when resolve(...).format is 'dynamic' and
|
||||||
// the signature
|
// has the signature
|
||||||
// (url : string) -> Promise<{ exports: { ... }, execute: function }>
|
// (url : string) -> Promise<{ exports: { ... }, execute: function }>
|
||||||
// Where `exports` is an object whose property names define the exported
|
// Where `exports` is an object whose property names define the exported
|
||||||
// names of the generated module. `execute` is a function that receives
|
// names of the generated module. `execute` is a function that receives
|
||||||
// an object with the same keys as `exports`, whose values are get/set
|
// an object with the same keys as `exports`, whose values are get/set
|
||||||
// functions for the actual exported values.
|
// functions for the actual exported values.
|
||||||
this.dynamicInstantiate = undefined;
|
this._dynamicInstantiate = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
hook({ resolve = ModuleRequest.resolve, dynamicInstantiate }) {
|
|
||||||
// Use .bind() to avoid giving access to the Loader instance when it is
|
|
||||||
// called as this.resolver(...);
|
|
||||||
this.resolver = resolve.bind(null);
|
|
||||||
this.dynamicInstantiate = dynamicInstantiate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typechecking wrapper around .resolver().
|
|
||||||
async resolve(specifier, parentURL = this.base) {
|
async resolve(specifier, parentURL = this.base) {
|
||||||
if (typeof parentURL !== 'string') {
|
if (typeof parentURL !== 'string')
|
||||||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'parentURL', 'string');
|
||||||
'parentURL', 'string');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { url, format } = await this.resolver(specifier, parentURL,
|
const { url, format } =
|
||||||
ModuleRequest.resolve);
|
await this._resolve(specifier, parentURL, defaultResolve);
|
||||||
|
|
||||||
if (!Loader.validFormats.includes(format)) {
|
if (typeof url !== 'string')
|
||||||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'format',
|
|
||||||
Loader.validFormats);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof url !== 'string') {
|
|
||||||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string');
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string');
|
||||||
}
|
|
||||||
|
|
||||||
if (format === 'builtin') {
|
if (typeof format !== 'string')
|
||||||
|
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'format', 'string');
|
||||||
|
|
||||||
|
if (format === 'builtin')
|
||||||
return { url: `node:${url}`, format };
|
return { url: `node:${url}`, format };
|
||||||
}
|
|
||||||
|
|
||||||
if (format !== 'dynamic') {
|
if (format !== 'dynamic' && !url.startsWith('file:'))
|
||||||
if (!ModuleRequest.loaders.has(format)) {
|
throw new errors.Error('ERR_INVALID_PROTOCOL', url, 'file:');
|
||||||
throw new errors.Error('ERR_UNKNOWN_MODULE_FORMAT', format);
|
|
||||||
}
|
|
||||||
if (!url.startsWith('file:')) {
|
|
||||||
throw new errors.Error('ERR_INVALID_PROTOCOL', url, 'file:');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { url, format };
|
return { url, format };
|
||||||
}
|
}
|
||||||
|
|
||||||
// May create a new ModuleJob instance if one did not already exist.
|
async import(specifier, parent = this.base) {
|
||||||
|
const job = await this.getModuleJob(specifier, parent);
|
||||||
|
const module = await job.run();
|
||||||
|
return module.namespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
hook({ resolve, dynamicInstantiate }) {
|
||||||
|
// Use .bind() to avoid giving access to the Loader instance when called.
|
||||||
|
if (resolve !== undefined)
|
||||||
|
this._resolve = FunctionBind(resolve, null);
|
||||||
|
if (dynamicInstantiate !== undefined)
|
||||||
|
this._dynamicInstantiate = FunctionBind(dynamicInstantiate, null);
|
||||||
|
}
|
||||||
|
|
||||||
async getModuleJob(specifier, parentURL = this.base) {
|
async getModuleJob(specifier, parentURL = this.base) {
|
||||||
const { url, format } = await this.resolve(specifier, parentURL);
|
const { url, format } = await this.resolve(specifier, parentURL);
|
||||||
let job = this.moduleMap.get(url);
|
let job = this.moduleMap.get(url);
|
||||||
if (job === undefined) {
|
if (job !== undefined)
|
||||||
let loaderInstance;
|
return job;
|
||||||
if (format === 'dynamic') {
|
|
||||||
const { dynamicInstantiate } = this;
|
|
||||||
if (typeof dynamicInstantiate !== 'function') {
|
|
||||||
throw new errors.Error('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK');
|
|
||||||
}
|
|
||||||
|
|
||||||
loaderInstance = async (url) => {
|
let loaderInstance;
|
||||||
const { exports, execute } = await dynamicInstantiate(url);
|
if (format === 'dynamic') {
|
||||||
return createDynamicModule(exports, url, (reflect) => {
|
if (typeof this._dynamicInstantiate !== 'function')
|
||||||
debug(`Loading custom loader ${url}`);
|
throw new errors.Error('ERR_MISSING_DYNAMIC_INTSTANTIATE_HOOK');
|
||||||
execute(reflect.exports);
|
|
||||||
});
|
loaderInstance = async (url) => {
|
||||||
};
|
debug(`Translating dynamic ${url}`);
|
||||||
} else {
|
const { exports, execute } = await this._dynamicInstantiate(url);
|
||||||
loaderInstance = ModuleRequest.loaders.get(format);
|
return createDynamicModule(exports, url, (reflect) => {
|
||||||
}
|
debug(`Loading dynamic ${url}`);
|
||||||
job = new ModuleJob(this, url, loaderInstance);
|
execute(reflect.exports);
|
||||||
this.moduleMap.set(url, job);
|
});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (!translators.has(format))
|
||||||
|
throw new errors.RangeError('ERR_UNKNOWN_MODULE_FORMAT', format);
|
||||||
|
|
||||||
|
loaderInstance = translators.get(format);
|
||||||
}
|
}
|
||||||
return job;
|
|
||||||
}
|
|
||||||
|
|
||||||
async import(specifier, parentURL = this.base) {
|
job = new ModuleJob(this, url, loaderInstance);
|
||||||
const job = await this.getModuleJob(specifier, parentURL);
|
this.moduleMap.set(url, job);
|
||||||
const module = await job.run();
|
return job;
|
||||||
return module.namespace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static registerImportDynamicallyCallback(loader) {
|
static registerImportDynamicallyCallback(loader) {
|
||||||
@ -147,6 +143,6 @@ class Loader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loader.validFormats = ['esm', 'cjs', 'builtin', 'addon', 'json', 'dynamic'];
|
|
||||||
Object.setPrototypeOf(Loader.prototype, null);
|
Object.setPrototypeOf(Loader.prototype, null);
|
||||||
module.exports = Loader;
|
module.exports = Loader;
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const internalCJSModule = require('internal/module');
|
|
||||||
const CJSModule = require('module');
|
|
||||||
const internalURLModule = require('internal/url');
|
|
||||||
const internalFS = require('internal/fs');
|
|
||||||
const NativeModule = require('native_module');
|
|
||||||
const { extname, _makeLong } = require('path');
|
|
||||||
const { URL } = require('url');
|
|
||||||
const { realpathSync } = require('fs');
|
|
||||||
const preserveSymlinks = !!process.binding('config').preserveSymlinks;
|
|
||||||
const {
|
|
||||||
ModuleWrap,
|
|
||||||
createDynamicModule
|
|
||||||
} = require('internal/loader/ModuleWrap');
|
|
||||||
const errors = require('internal/errors');
|
|
||||||
|
|
||||||
const search = require('internal/loader/search');
|
|
||||||
const asyncReadFile = require('util').promisify(require('fs').readFile);
|
|
||||||
const debug = require('util').debuglog('esm');
|
|
||||||
|
|
||||||
const realpathCache = new Map();
|
|
||||||
|
|
||||||
const loaders = new Map();
|
|
||||||
exports.loaders = loaders;
|
|
||||||
|
|
||||||
// Strategy for loading a standard JavaScript module
|
|
||||||
loaders.set('esm', async (url) => {
|
|
||||||
const source = `${await asyncReadFile(new URL(url))}`;
|
|
||||||
debug(`Loading StandardModule ${url}`);
|
|
||||||
return {
|
|
||||||
module: new ModuleWrap(internalCJSModule.stripShebang(source), url),
|
|
||||||
reflect: undefined
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Strategy for loading a node-style CommonJS module
|
|
||||||
const isWindows = process.platform === 'win32';
|
|
||||||
const winSepRegEx = /\//g;
|
|
||||||
loaders.set('cjs', async (url) => {
|
|
||||||
const pathname = internalURLModule.getPathFromURL(new URL(url));
|
|
||||||
const module = CJSModule._cache[
|
|
||||||
isWindows ? pathname.replace(winSepRegEx, '\\') : pathname];
|
|
||||||
if (module && module.loaded) {
|
|
||||||
const ctx = createDynamicModule(['default'], url, undefined);
|
|
||||||
ctx.reflect.exports.default.set(module.exports);
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
return createDynamicModule(['default'], url, (reflect) => {
|
|
||||||
debug(`Loading CJSModule ${url}`);
|
|
||||||
CJSModule._load(pathname);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Strategy for loading a node builtin CommonJS module that isn't
|
|
||||||
// through normal resolution
|
|
||||||
loaders.set('builtin', async (url) => {
|
|
||||||
return createDynamicModule(['default'], url, (reflect) => {
|
|
||||||
debug(`Loading BuiltinModule ${url}`);
|
|
||||||
const exports = NativeModule.require(url.substr(5));
|
|
||||||
reflect.exports.default.set(exports);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
loaders.set('addon', async (url) => {
|
|
||||||
const ctx = createDynamicModule(['default'], url, (reflect) => {
|
|
||||||
debug(`Loading NativeModule ${url}`);
|
|
||||||
const module = { exports: {} };
|
|
||||||
const pathname = internalURLModule.getPathFromURL(new URL(url));
|
|
||||||
process.dlopen(module, _makeLong(pathname));
|
|
||||||
reflect.exports.default.set(module.exports);
|
|
||||||
});
|
|
||||||
return ctx;
|
|
||||||
});
|
|
||||||
|
|
||||||
loaders.set('json', async (url) => {
|
|
||||||
return createDynamicModule(['default'], url, (reflect) => {
|
|
||||||
debug(`Loading JSONModule ${url}`);
|
|
||||||
const pathname = internalURLModule.getPathFromURL(new URL(url));
|
|
||||||
const content = fs.readFileSync(pathname, 'utf8');
|
|
||||||
try {
|
|
||||||
const exports = JSON.parse(internalCJSModule.stripBOM(content));
|
|
||||||
reflect.exports.default.set(exports);
|
|
||||||
} catch (err) {
|
|
||||||
err.message = pathname + ': ' + err.message;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.resolve = (specifier, parentURL) => {
|
|
||||||
if (NativeModule.nonInternalExists(specifier)) {
|
|
||||||
return {
|
|
||||||
url: specifier,
|
|
||||||
format: 'builtin'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let url;
|
|
||||||
try {
|
|
||||||
url = search(specifier, parentURL);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message && e.message.startsWith('Cannot find module'))
|
|
||||||
e.code = 'MODULE_NOT_FOUND';
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.protocol !== 'file:') {
|
|
||||||
throw new errors.Error('ERR_INVALID_PROTOCOL',
|
|
||||||
url.protocol, 'file:');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preserveSymlinks) {
|
|
||||||
const real = realpathSync(internalURLModule.getPathFromURL(url), {
|
|
||||||
[internalFS.realpathCacheKey]: realpathCache
|
|
||||||
});
|
|
||||||
const old = url;
|
|
||||||
url = internalURLModule.getURLFromFilePath(real);
|
|
||||||
url.search = old.search;
|
|
||||||
url.hash = old.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ext = extname(url.pathname);
|
|
||||||
switch (ext) {
|
|
||||||
case '.mjs':
|
|
||||||
return { url: `${url}`, format: 'esm' };
|
|
||||||
case '.json':
|
|
||||||
return { url: `${url}`, format: 'json' };
|
|
||||||
case '.node':
|
|
||||||
return { url: `${url}`, format: 'addon' };
|
|
||||||
case '.js':
|
|
||||||
return { url: `${url}`, format: 'cjs' };
|
|
||||||
default:
|
|
||||||
throw new errors.Error('ERR_UNKNOWN_FILE_EXTENSION',
|
|
||||||
internalURLModule.getPathFromURL(url));
|
|
||||||
}
|
|
||||||
};
|
|
92
lib/internal/loader/Translators.js
Normal file
92
lib/internal/loader/Translators.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { ModuleWrap } = internalBinding('module_wrap');
|
||||||
|
const NativeModule = require('native_module');
|
||||||
|
const internalCJSModule = require('internal/module');
|
||||||
|
const CJSModule = require('module');
|
||||||
|
const internalURLModule = require('internal/url');
|
||||||
|
const createDynamicModule = require('internal/loader/CreateDynamicModule');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { _makeLong } = require('path');
|
||||||
|
const { SafeMap } = require('internal/safe_globals');
|
||||||
|
const { URL } = require('url');
|
||||||
|
const debug = require('util').debuglog('esm');
|
||||||
|
const readFileAsync = require('util').promisify(fs.readFile);
|
||||||
|
const readFileSync = fs.readFileSync;
|
||||||
|
const StringReplace = Function.call.bind(String.prototype.replace);
|
||||||
|
const JsonParse = JSON.parse;
|
||||||
|
|
||||||
|
const translators = new SafeMap();
|
||||||
|
module.exports = translators;
|
||||||
|
|
||||||
|
// Stragety for loading a standard JavaScript module
|
||||||
|
translators.set('esm', async (url) => {
|
||||||
|
const source = `${await readFileAsync(new URL(url))}`;
|
||||||
|
debug(`Translating StandardModule ${url}`);
|
||||||
|
return {
|
||||||
|
module: new ModuleWrap(internalCJSModule.stripShebang(source), url),
|
||||||
|
reflect: undefined
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Strategy for loading a node-style CommonJS module
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
const winSepRegEx = /\//g;
|
||||||
|
translators.set('commonjs', async (url) => {
|
||||||
|
debug(`Translating CJSModule ${url}`);
|
||||||
|
const pathname = internalURLModule.getPathFromURL(new URL(url));
|
||||||
|
const module = CJSModule._cache[
|
||||||
|
isWindows ? StringReplace(pathname, winSepRegEx, '\\') : pathname];
|
||||||
|
if (module && module.loaded) {
|
||||||
|
const ctx = createDynamicModule(['default'], url);
|
||||||
|
ctx.reflect.exports.default.set(module.exports);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
return createDynamicModule(['default'], url, () => {
|
||||||
|
debug(`Loading CJSModule ${url}`);
|
||||||
|
// we don't care about the return val of _load here because Module#load
|
||||||
|
// will handle it for us by checking the loader registry and filling the
|
||||||
|
// exports like above
|
||||||
|
CJSModule._load(pathname);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Strategy for loading a node builtin CommonJS module that isn't
|
||||||
|
// through normal resolution
|
||||||
|
translators.set('builtin', async (url) => {
|
||||||
|
debug(`Translating BuiltinModule ${url}`);
|
||||||
|
return createDynamicModule(['default'], url, (reflect) => {
|
||||||
|
debug(`Loading BuiltinModule ${url}`);
|
||||||
|
const exports = NativeModule.require(url.slice(5));
|
||||||
|
reflect.exports.default.set(exports);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stragety for loading a node native module
|
||||||
|
translators.set('addon', async (url) => {
|
||||||
|
debug(`Translating NativeModule ${url}`);
|
||||||
|
return createDynamicModule(['default'], url, (reflect) => {
|
||||||
|
debug(`Loading NativeModule ${url}`);
|
||||||
|
const module = { exports: {} };
|
||||||
|
const pathname = internalURLModule.getPathFromURL(new URL(url));
|
||||||
|
process.dlopen(module, _makeLong(pathname));
|
||||||
|
reflect.exports.default.set(module.exports);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stragety for loading a JSON file
|
||||||
|
translators.set('json', async (url) => {
|
||||||
|
debug(`Translating JSONModule ${url}`);
|
||||||
|
return createDynamicModule(['default'], url, (reflect) => {
|
||||||
|
debug(`Loading JSONModule ${url}`);
|
||||||
|
const pathname = internalURLModule.getPathFromURL(new URL(url));
|
||||||
|
const content = readFileSync(pathname, 'utf8');
|
||||||
|
try {
|
||||||
|
const exports = JsonParse(internalCJSModule.stripBOM(content));
|
||||||
|
reflect.exports.default.set(exports);
|
||||||
|
} catch (err) {
|
||||||
|
err.message = pathname + ': ' + err.message;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -1,31 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const { URL } = require('url');
|
|
||||||
const CJSmodule = require('module');
|
|
||||||
const errors = require('internal/errors');
|
|
||||||
const { resolve } = internalBinding('module_wrap');
|
|
||||||
|
|
||||||
module.exports = (target, base) => {
|
|
||||||
if (base === undefined) {
|
|
||||||
// We cannot search without a base.
|
|
||||||
throw new errors.Error('ERR_MISSING_MODULE', target);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return resolve(target, base);
|
|
||||||
} catch (e) {
|
|
||||||
e.stack; // cause V8 to generate stack before rethrow
|
|
||||||
let error = e;
|
|
||||||
try {
|
|
||||||
const questionedBase = new URL(base);
|
|
||||||
const tmpMod = new CJSmodule(questionedBase.pathname, null);
|
|
||||||
tmpMod.paths = CJSmodule._nodeModulePaths(
|
|
||||||
new URL('./', questionedBase).pathname);
|
|
||||||
const found = CJSmodule._resolveFilename(target, tmpMod);
|
|
||||||
error = new errors.Error('ERR_MODULE_RESOLUTION_LEGACY', target,
|
|
||||||
base, found);
|
|
||||||
} catch (problemChecking) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
@ -45,7 +45,7 @@ module.exports = Module;
|
|||||||
// these are below module.exports for the circular reference
|
// these are below module.exports for the circular reference
|
||||||
const Loader = require('internal/loader/Loader');
|
const Loader = require('internal/loader/Loader');
|
||||||
const ModuleJob = require('internal/loader/ModuleJob');
|
const ModuleJob = require('internal/loader/ModuleJob');
|
||||||
const { createDynamicModule } = require('internal/loader/ModuleWrap');
|
const createDynamicModule = require('internal/loader/CreateDynamicModule');
|
||||||
let ESMLoader;
|
let ESMLoader;
|
||||||
|
|
||||||
function stat(filename) {
|
function stat(filename) {
|
||||||
|
8
node.gyp
8
node.gyp
@ -101,11 +101,11 @@
|
|||||||
'lib/internal/inspector_async_hook.js',
|
'lib/internal/inspector_async_hook.js',
|
||||||
'lib/internal/linkedlist.js',
|
'lib/internal/linkedlist.js',
|
||||||
'lib/internal/loader/Loader.js',
|
'lib/internal/loader/Loader.js',
|
||||||
'lib/internal/loader/ModuleMap.js',
|
'lib/internal/loader/CreateDynamicModule.js',
|
||||||
|
'lib/internal/loader/DefaultResolve.js',
|
||||||
'lib/internal/loader/ModuleJob.js',
|
'lib/internal/loader/ModuleJob.js',
|
||||||
'lib/internal/loader/ModuleWrap.js',
|
'lib/internal/loader/ModuleMap.js',
|
||||||
'lib/internal/loader/ModuleRequest.js',
|
'lib/internal/loader/Translators.js',
|
||||||
'lib/internal/loader/search.js',
|
|
||||||
'lib/internal/safe_globals.js',
|
'lib/internal/safe_globals.js',
|
||||||
'lib/internal/net.js',
|
'lib/internal/net.js',
|
||||||
'lib/internal/module.js',
|
'lib/internal/module.js',
|
||||||
|
@ -10,7 +10,7 @@ const { URL } = require('url');
|
|||||||
const Loader = require('internal/loader/Loader');
|
const Loader = require('internal/loader/Loader');
|
||||||
const ModuleMap = require('internal/loader/ModuleMap');
|
const ModuleMap = require('internal/loader/ModuleMap');
|
||||||
const ModuleJob = require('internal/loader/ModuleJob');
|
const ModuleJob = require('internal/loader/ModuleJob');
|
||||||
const { createDynamicModule } = require('internal/loader/ModuleWrap');
|
const createDynamicModule = require('internal/loader/CreateDynamicModule');
|
||||||
|
|
||||||
const stubModuleUrl = new URL('file://tmp/test');
|
const stubModuleUrl = new URL('file://tmp/test');
|
||||||
const stubModule = createDynamicModule(['default'], stubModuleUrl);
|
const stubModule = createDynamicModule(['default'], stubModuleUrl);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
|
|
||||||
const search = require('internal/loader/search');
|
const { search } = require('internal/loader/DefaultResolve');
|
||||||
const errors = require('internal/errors');
|
const errors = require('internal/errors');
|
||||||
|
|
||||||
common.expectsError(
|
common.expectsError(
|
||||||
|
@ -3,19 +3,9 @@ import module from 'module';
|
|||||||
const builtins = new Set(
|
const builtins = new Set(
|
||||||
Object.keys(process.binding('natives')).filter(str =>
|
Object.keys(process.binding('natives')).filter(str =>
|
||||||
/^(?!(?:internal|node|v8)\/)/.test(str))
|
/^(?!(?:internal|node|v8)\/)/.test(str))
|
||||||
)
|
);
|
||||||
|
|
||||||
export function resolve (specifier, base, defaultResolver) {
|
export function dynamicInstantiate(url) {
|
||||||
if (builtins.has(specifier)) {
|
|
||||||
return {
|
|
||||||
url: `node:${specifier}`,
|
|
||||||
format: 'dynamic'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return defaultResolver(specifier, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function dynamicInstantiate (url) {
|
|
||||||
const builtinInstance = module._load(url.substr(5));
|
const builtinInstance = module._load(url.substr(5));
|
||||||
const builtinExports = ['default', ...Object.keys(builtinInstance)];
|
const builtinExports = ['default', ...Object.keys(builtinInstance)];
|
||||||
return {
|
return {
|
||||||
@ -27,3 +17,13 @@ export async function dynamicInstantiate (url) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolve(specifier, base, defaultResolver) {
|
||||||
|
if (builtins.has(specifier)) {
|
||||||
|
return {
|
||||||
|
url: `node:${specifier}`,
|
||||||
|
format: 'dynamic'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return defaultResolver(specifier, base);
|
||||||
|
}
|
||||||
|
@ -3,5 +3,5 @@ file:///*/test/message/esm_display_syntax_error.mjs:3
|
|||||||
await async () => 0;
|
await async () => 0;
|
||||||
^^^^^
|
^^^^^
|
||||||
SyntaxError: Unexpected reserved word
|
SyntaxError: Unexpected reserved word
|
||||||
at loaders.set (internal/loader/ModuleRequest.js:*:*)
|
at translators.set (internal/loader/Translators.js:*:*)
|
||||||
at <anonymous>
|
at <anonymous>
|
||||||
|
@ -3,5 +3,5 @@ file:///*/test/fixtures/es-module-loaders/syntax-error.mjs:2
|
|||||||
await async () => 0;
|
await async () => 0;
|
||||||
^^^^^
|
^^^^^
|
||||||
SyntaxError: Unexpected reserved word
|
SyntaxError: Unexpected reserved word
|
||||||
at loaders.set (internal/loader/ModuleRequest.js:*:*)
|
at translators.set (internal/loader/Translators.js:*:*)
|
||||||
at <anonymous>
|
at <anonymous>
|
||||||
|
@ -5,7 +5,7 @@ const common = require('../common');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const ModuleMap = require('internal/loader/ModuleMap');
|
const ModuleMap = require('internal/loader/ModuleMap');
|
||||||
|
|
||||||
// ModuleMap.get, ModuleMap.has and ModuleMap.set should only accept string
|
// ModuleMap.get, ModuleMap.has and ModuleMap.set should only accept string
|
||||||
// values as url argument.
|
// values as url argument.
|
||||||
{
|
{
|
||||||
const errorReg = common.expectsError({
|
const errorReg = common.expectsError({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user