Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to ESM #284

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,13 @@
"extends": ["eslint:recommended", "plugin:import/errors", "plugin:import/warnings"],
"plugins": ["import"],
"parserOptions": {
"ecmaVersion": 11
"ecmaVersion": 13,
"sourceType": "module"
},
"rules": {
"eqeqeq": "error",
"no-trailing-spaces": "error",
"prefer-arrow-callback": "error",
"semi": "error"
},
"overrides": [
{
"files": ["./**/*.mjs"],
"parserOptions": {
"sourceType": "module"
}
}
]
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ There are a couple of command-line options that can be used to control which fil
- `--respawn` - Keep watching for changes after the script has exited
- `--timestamp` - The timestamp format to use for logging restarts
- `--vm` - Load files using Node's VM
- `--worker` - Hook into worker_threads.Worker constructor

## Passing arguments to node

Expand Down
13 changes: 0 additions & 13 deletions bin/node-dev

This file was deleted.

8 changes: 8 additions & 0 deletions bin/node-dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env node

import dev from '../lib/index.js';
import cli from '../lib/cli.js';

const { script, scriptArgs, nodeArgs, opts } = cli(process.argv);

dev(script, scriptArgs, nodeArgs, opts);
8 changes: 3 additions & 5 deletions lib/cfg.js → lib/cfg.cjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
const { existsSync, readFileSync } = require('fs');
const { dirname, resolve } = require('path');

const resolveMain = require('./resolve-main');

const defaultConfig = {
clear: false,
debounce: 10,
Expand All @@ -21,7 +19,8 @@ const defaultConfig = {
poll: false,
respawn: false,
timestamp: 'HH:MM:ss',
vm: true
vm: true,
worker: true
};

function read(dir) {
Expand All @@ -30,8 +29,7 @@ function read(dir) {
}

function getConfig(script) {
const main = resolveMain(script);
const dir = main ? dirname(main) : '.';
const dir = resolve(dirname(script));

return Object.assign(
defaultConfig,
Expand Down
6 changes: 2 additions & 4 deletions lib/clear.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
const control = '\u001bc';
const clearFactory = clear => (clear ? () => process.stdout.write(control) : () => {});

module.exports = { clearFactory, control };
export const control = '\u001bc';
export const clearFactory = clear => (clear ? () => process.stdout.write(control) : () => {});
10 changes: 5 additions & 5 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const assert = require('assert');
const minimist = require('minimist');
const { resolve } = require('path');
import assert from 'assert';
import minimist from 'minimist';
import { resolve } from 'path';

const { getConfig } = require('./cfg');
import { getConfig } from './cfg.cjs';

const arrayify = v => (Array.isArray(v) ? [...v] : [v]);
const argify = key => ({ arg: `--${key}`, key });
Expand Down Expand Up @@ -55,7 +55,7 @@ const unknownFactory = args => arg => {
key && !nodeDevNumber.includes(key) && args.push({ arg, key });
};

module.exports = argv => {
export default argv => {
const nodeCustomArgs = [];
const args = argv.slice(2).filter(nodeCustomFactory(nodeCustomArgs));

Expand Down
60 changes: 0 additions & 60 deletions lib/hook.js

This file was deleted.

6 changes: 2 additions & 4 deletions lib/ignore.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,5 @@ const getPrefix = mod => {

const isPrefixOf = value => prefix => value.startsWith(prefix);

const configureDeps = deps => required => deps !== -1 && getLevel(required) > deps;
const configureIgnore = ignore => required => ignore.some(isPrefixOf(required));

module.exports = { configureDeps, configureIgnore };
export const configureDeps = deps => required => deps !== -1 && getLevel(required) > deps;
export const configureIgnore = ignore => required => ignore.some(isPrefixOf(required));
55 changes: 30 additions & 25 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const { fork } = require('child_process');
const filewatcher = require('filewatcher');
const { join } = require('path');
const semver = require('semver');
const { pathToFileURL } = require('url');

const { clearFactory } = require('./clear');
const { configureDeps, configureIgnore } = require('./ignore');
const ipc = require('./ipc');
const localPath = require('./local-path');
const logFactory = require('./log');
const notifyFactory = require('./notify');
const resolveMain = require('./resolve-main');

module.exports = function (
import { fork } from 'child_process';
import filewatcher from 'filewatcher';
import { createRequire } from 'module';
import semver from 'semver';

import { clearFactory } from './clear.js';
import { configureDeps, configureIgnore } from './ignore.js';
import ipc from './ipc.cjs';
import logFactory from './log.cjs';
import notifyFactory from './notify.js';

const require = createRequire(import.meta.url);
const resolveHook = hook => require.resolve('./require/' + hook);

export default function (
script,
scriptArgs,
nodeArgs,
Expand All @@ -21,13 +21,16 @@ module.exports = function (
debounce,
dedupe,
deps,
fork: patchFork,
graceful_ipc: gracefulIPC,
ignore,
interval,
notify: notifyEnabled,
poll: forcePolling,
respawn,
timestamp
timestamp,
vm: patchVm,
worker: patchWorker
}
) {
if (!script) {
Expand Down Expand Up @@ -55,9 +58,6 @@ module.exports = function (
const isIgnored = configureIgnore(ignore);
const isTooDeep = configureDeps(deps);

// Run ./dedupe.js as preload script
if (dedupe) process.env.NODE_DEV_PRELOAD = localPath('dedupe');

const watcher = filewatcher({ debounce, forcePolling, interval });
let isPaused = false;

Expand Down Expand Up @@ -92,20 +92,25 @@ module.exports = function (
function start() {
isPaused = false;

const args = nodeArgs.slice();
const execArgv = nodeArgs.slice();

args.push(`--require=${resolveMain(localPath('wrap'))}`);
execArgv.push(`--require=${resolveHook('suppress-experimental-warnings')}`);
if (dedupe) execArgv.push(`--require=${resolveHook('dedupe')}`);
execArgv.push(`--require=${resolveHook('patch')}`);
if (patchFork) execArgv.push(`--require=${resolveHook('patch-fork')}`);
if (patchVm) execArgv.push(`--require=${resolveHook('patch-vm')}`);
if (patchWorker) execArgv.push(`--require=${resolveHook('patch-worker')}`);

const loaderName = semver.satisfies(process.version, '>=16.12.0') ? 'load' : 'get-format';

const loaderURL = pathToFileURL(resolveMain(localPath(join('loaders', `${loaderName}.mjs`))));
const loaderURL = new URL(`./loaders/${loaderName}.mjs`, import.meta.url);

args.push(`--experimental-loader=${loaderURL.href}`);
execArgv.push(`--experimental-loader=${loaderURL.href}`);

child = fork(script, scriptArgs, {
cwd: process.cwd(),
env: process.env,
execArgv: args
execArgv
});

if (respawn) {
Expand Down Expand Up @@ -158,4 +163,4 @@ module.exports = function (

clearOutput();
start();
};
}
52 changes: 52 additions & 0 deletions lib/ipc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { once } = require('events');
const { Worker } = require('worker_threads');

const logFactory = require('./log.cjs');

const cmd = 'NODE_DEV';
const log = logFactory({});
let nodeDevPort;

exports.on = (src, prop, cb) => {
src.on('internalMessage', m => {
if (m.cmd === cmd && prop in m) cb(m);
});
};

exports.relay = src => {
if (src instanceof Worker) {
// create a separate message channel for node-dev
const { port1, port2 } = new MessageChannel();
port1.unref();
port1.on('message', exports.send);
src.postMessage({ cmd, port: port2 }, [port2]);
} else {
src.on('internalMessage', m => {
if (process.connected && m.cmd === cmd) process.send(m);
});
}
};

exports.send = m => {
if (process.connected) {
process.send({ ...m, cmd });
} else if (nodeDevPort) {
// this has doesn't seem to have a race condition in testing
// but just in case, the log statement below should notify of it
nodeDevPort.postMessage({ ...m, cmd });
} else {
log.warn(
`node-dev: The module ${m.required} was imported from an orphaned child process or worker thread`
);
}
};

exports.receiveMessagePort = async src => {
// the first message received by this thread should contain the parent port
const [m] = await once(src, 'message');
if (m && m.cmd === cmd) {
nodeDevPort = m.port;
} else {
log.warn('node-dev: unexpected message on the parentPort', m);
}
};
43 changes: 39 additions & 4 deletions lib/ipc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
const { once } = require('events');
const { MessageChannel, Worker } = require('worker_threads');

const logFactory = require('./log.cjs');

let nodeDevPort;
const cmd = 'NODE_DEV';
const log = logFactory({});

exports.on = (src, prop, cb) => {
src.on('internalMessage', m => {
Expand All @@ -7,11 +14,39 @@ exports.on = (src, prop, cb) => {
};

exports.relay = src => {
src.on('internalMessage', m => {
if (process.connected && m.cmd === cmd) process.send(m);
});
if (src instanceof Worker) {
// create a separate message channel for node-dev
const { port1, port2 } = new MessageChannel();
port1.unref();
port1.on('message', exports.send);
src.postMessage({ cmd, port: port2 }, [port2]);
} else {
src.on('internalMessage', m => {
if (process.connected && m.cmd === cmd) process.send(m);
});
}
};

exports.send = m => {
if (process.connected) process.send({ ...m, cmd });
if (nodeDevPort) {
// this has doesn't seem to have a race condition in testing
// but just in case, the log statement below should notify of it
nodeDevPort.postMessage({ ...m, cmd });
} else if (process.connected) {
process.send({ ...m, cmd });
} else {
log.warn(
`node-dev: The module ${m.required} was imported from an orphaned child process or worker thread`
);
}
};

exports.receiveMessagePort = async src => {
// the first message received by this thread should contain the parent port
const [m] = await once(src, 'message');
if (m && m.cmd === cmd) {
nodeDevPort = m.port;
} else {
log.warn('node-dev: unexpected message on the parentPort', m);
}
};
2 changes: 1 addition & 1 deletion lib/loaders/get-format.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import { send } from './ipc.mjs';
import { send } from '../ipc.cjs';

const require = createRequire(import.meta.url);

Expand Down
Loading