Skip to content

Commit

Permalink
es modules: implement npm spec
Browse files Browse the repository at this point in the history
- add -m, --module for preloading esm
- Refactored startup for readability.
- We can now syntax-check ESM files in addition to commonjs files.
- We now handle file path resolution the way the commonjs code path does.
- Removed --loader.
- Removed frozen "safe" promises and maps.
  • Loading branch information
chrisdickinson authored and ceejbot committed Jan 12, 2018
1 parent 1385e1b commit 1d771f5
Show file tree
Hide file tree
Showing 109 changed files with 833 additions and 1,020 deletions.
10 changes: 5 additions & 5 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ parser: babel-eslint
parserOptions:
sourceType: script

overrides:
- files: ["doc/api/esm.md", "*.mjs", "test/es-module/test-esm-example-loader.js"]
parserOptions:
sourceType: module

rules:
# Possible Errors
# http://eslint.org/docs/rules/#possible-errors
Expand Down Expand Up @@ -209,3 +204,8 @@ globals:
LTTNG_NET_SERVER_CONNECTION: false
LTTNG_NET_STREAM_END: false
internalBinding: false

overrides:
- files: [ "esm.md" ]
parserOptions:
sourceType: module
241 changes: 139 additions & 102 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@
'ExperimentalWarning', undefined);
}


// There are various modes that Node can run in. The most common two
// are running from a script and running the REPL - but there are a few
// others like the debugger or running --eval arguments. Here we decide
Expand All @@ -123,8 +122,11 @@
NativeModule.require('_third_party_main');
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_THIRD_PARTY_MAIN_END);
});
return;
}

} else if (process.argv[1] === 'inspect' || process.argv[1] === 'debug') {
// Are we debugging?
if (process.argv[1] === 'inspect' || process.argv[1] === 'debug') {
if (process.argv[1] === 'debug') {
process.emitWarning(
'`node debug` is deprecated. Please use `node inspect` instead.',
Expand All @@ -135,115 +137,149 @@
process.nextTick(function() {
NativeModule.require('internal/deps/node-inspect/lib/_inspect').start();
});
return;
}

} else if (process.profProcess) {
// Are we handling profiling output?
if (process.profProcess) {
NativeModule.require('internal/v8_prof_processor');
return;
}

} else {
// There is user code to be run

// If this is a worker in cluster mode, start up the communication
// channel. This needs to be done before any user code gets executed
// (including preload modules).
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START);
const cluster = NativeModule.require('cluster');
cluster._setupWorker();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END);
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
// If we reach here, we have user code to run. What sort of user code?
const isClusterWorker = process.argv[1] && process.env.NODE_UNIQUE_ID;
const isEvalTarget = process._eval != null && !process._forceRepl;
const isFileTarget = process.argv[1] && process.argv[1] !== '-';

// If this is a worker in cluster mode, start up the communication
// channel. This needs to be done before any user code gets executed
// (including preload modules).
if (isClusterWorker) {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_START);
const cluster = NativeModule.require('cluster');
cluster._setupWorker();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_CLUSTER_SETUP_END);
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}

let loadUserFile = () => {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
// make process.argv[1] into a full path
const path = NativeModule.require('path');
process.argv[1] = path.resolve(process.argv[1]);

const Module = NativeModule.require('module');

// check if user passed `-c` or `--check` arguments to Node.
if (process._syntax_check_only != null) {
const fs = NativeModule.require('fs');
// read the source
const filename = Module._resolveFilename(process.argv[1]);
var source = fs.readFileSync(filename, 'utf-8');
checkScriptSyntax(source, filename);
process.exit(0);
}

if (process._eval != null && !process._forceRepl) {
return Module;
};

if (isEvalTarget || !isFileTarget) {
loadUserFile = () => {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END);
// User passed '-e' or '--eval' arguments to Node without '-i' or
// '--interactive'
};
}

perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
preloadModules();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
const MaybeModule = loadUserFile();

if (isEvalTarget) {
// User passed '-e' or '--eval' arguments to Node without '-i' or
// '--interactive'
preloadModules(() => {
const internalModule = NativeModule.require('internal/module');
internalModule.addBuiltinLibsToObject(global);
evalScript('[eval]');
} else if (process.argv[1] && process.argv[1] !== '-') {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
// make process.argv[1] into a full path
const path = NativeModule.require('path');
process.argv[1] = path.resolve(process.argv[1]);
});
} else if (isFileTarget) {
preloadModules(() => MaybeModule.runMain());
} else {
preloadModules(() => runRepl());
}

const Module = NativeModule.require('module');
// This is the end of the startup code.

// check if user passed `-c` or `--check` arguments to Node.
if (process._syntax_check_only != null) {
const fs = NativeModule.require('fs');
// read the source
const filename = Module._resolveFilename(process.argv[1]);
var source = fs.readFileSync(filename, 'utf-8');
checkScriptSyntax(source, filename);
process.exit(0);
// Load preload modules
function preloadModules(next) {
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
if (process._preload_modules) {
NativeModule.require('module')
._preloadModules(process._preload_modules);
}

if (process._preload_es_modules) {
NativeModule.require('module')
._preloadESModules(process._preload_es_modules).then(() => {
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
next();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
}).catch((err) => {
process._rawDebug(err.stack);
process.exit(1);
});
return;
}

perf.markMilestone(NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
next();
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
}
}

function runRepl() {
// If -i or --interactive were passed, or stdin is a TTY.
if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
// REPL
const cliRepl = NativeModule.require('internal/repl');
cliRepl.createInternalRepl(process.env, function(err, repl) {
if (err) {
throw err;
}
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END);
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
preloadModules();
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
Module.runMain();
} else {
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_START);
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_MODULE_LOAD_END);
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_START);
preloadModules();
perf.markMilestone(
NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END);
// If -i or --interactive were passed, or stdin is a TTY.
if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
// REPL
const cliRepl = NativeModule.require('internal/repl');
cliRepl.createInternalRepl(process.env, function(err, repl) {
if (err) {
throw err;
}
repl.on('exit', function() {
if (repl._flushing) {
repl.pause();
return repl.once('flushHistory', function() {
process.exit();
});
}
repl.on('exit', function() {
if (repl._flushing) {
repl.pause();
return repl.once('flushHistory', function() {
process.exit();
});
});

if (process._eval != null) {
// User passed '-e' or '--eval'
evalScript('[eval]');
}
} else {
// Read all of stdin - execute it.
process.stdin.setEncoding('utf8');
process.exit();
});
});

var code = '';
process.stdin.on('data', function(d) {
code += d;
});
if (process._eval != null) {
// User passed '-e' or '--eval'
evalScript('[eval]');
}
} else {
// Read all of stdin - execute it.
process.stdin.setEncoding('utf8');

process.stdin.on('end', function() {
if (process._syntax_check_only != null) {
checkScriptSyntax(code, '[stdin]');
} else {
process._eval = code;
evalScript('[stdin]');
}
});
var code = '';
process.stdin.on('data', function(d) {
code += d;
});

process.stdin.on('end', function() {
if (process._syntax_check_only != null) {
checkScriptSyntax(code, '[stdin]');
} else {
process._eval = code;
evalScript('[stdin]');
}
}
});
}
perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE);
}

function setupProcessObject() {
Expand Down Expand Up @@ -487,26 +523,27 @@
process._tickCallback();
}

// Load preload modules
function preloadModules() {
if (process._preload_modules) {
NativeModule.require('module')._preloadModules(process._preload_modules);
}
}

function checkScriptSyntax(source, filename) {
// We need to determine the kind of module this is (commonjs or esm)
// and load it the right way.

const Module = NativeModule.require('module');
const vm = NativeModule.require('vm');
const internalModule = NativeModule.require('internal/module');
const { ModuleWrap } = NativeModule.require('internal/loader/ModuleWrap');

// remove Shebang
const mode = Module.guessModuleMode(filename, source);
source = internalModule.stripShebang(source);
// remove BOM
source = internalModule.stripBOM(source);
// wrap it
source = Module.wrap(source);
// compile the script, this will throw if it fails
new vm.Script(source, { displayErrors: true, filename });
if (mode === 'esm') {
new ModuleWrap(source, filename);
// this throws if a syntax error
} else {
// We are legacy commons.
source = Module.wrap(source);
// Compile the script. This will throw if it fails.
new vm.Script(source, { displayErrors: true, filename });
}
}

// Below you find a minimal module system, which is used to load the node
Expand Down
3 changes: 3 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ E('ERR_ASYNC_TYPE', 'Invalid name for async "type": %s');
E('ERR_BUFFER_OUT_OF_BOUNDS', bufferOutOfBounds);
E('ERR_BUFFER_TOO_LARGE',
`Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes`);
E('ERR_CANNOT_IMPORT_LEGACY_MODULE',
'Cannot import module previously loaded as legacy module: %s');
E('ERR_CANNOT_REQUIRE_ESM', 'Cannot require module previously loaded as ES module: %s');
E('ERR_CANNOT_WATCH_SIGINT', 'Cannot watch for SIGINT signals');
E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received');
E('ERR_CHILD_PROCESS_IPC_REQUIRED',
Expand Down
Loading

0 comments on commit 1d771f5

Please sign in to comment.