Skip to content

Commit

Permalink
Port to ESM
Browse files Browse the repository at this point in the history
  • Loading branch information
kherock committed Jan 16, 2023
1 parent 365e0b7 commit 0f7a7f7
Show file tree
Hide file tree
Showing 58 changed files with 419 additions and 329 deletions.
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);
3 changes: 2 additions & 1 deletion lib/cfg.js → lib/cfg.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const defaultConfig = {
poll: false,
respawn: false,
timestamp: 'HH:MM:ss',
vm: true
vm: true,
worker: true
};

function read(dir) {
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));
52 changes: 30 additions & 22 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
const { fork } = require('child_process');
const filewatcher = require('filewatcher');
const semver = require('semver');
const { pathToFileURL } = require('url');

const { clearFactory } = require('./clear');
const { configureDeps, configureIgnore } = require('./ignore');
const ipc = require('./ipc');
const logFactory = require('./log');
const notifyFactory = require('./notify');

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 @@ -18,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 @@ -52,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 = require.resolve('./dedupe');

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

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

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

args.push(`--require=${require.resolve('./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(require.resolve(`./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 @@ -155,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
5 changes: 0 additions & 5 deletions lib/loaders/ipc.mjs

This file was deleted.

2 changes: 1 addition & 1 deletion lib/loaders/load.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
File renamed without changes.
Loading

0 comments on commit 0f7a7f7

Please sign in to comment.