mirror of
https://github.com/nodejs/node.git
synced 2025-05-16 07:06:47 +00:00

This enables a --loader flag for Node, which can provide custom "resolve" and "dynamicInstantiate" methods for custom ES module loading. In the process, module providers have been converted from classes into functions and the module APIs have been made to pass URL strings over objects. PR-URL: https://github.com/nodejs/node/pull/15445 Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Timothy Gu <timothygu99@gmail.com>
123 lines
3.7 KiB
JavaScript
123 lines
3.7 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const internalCJSModule = require('internal/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
|
|
loaders.set('cjs', async (url) => {
|
|
return createDynamicModule(['default'], url, (reflect) => {
|
|
debug(`Loading CJSModule ${url}`);
|
|
const CJSModule = require('module');
|
|
const pathname = internalURLModule.getPathFromURL(new URL(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 = search(specifier, parentURL);
|
|
|
|
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));
|
|
}
|
|
};
|