Skip to content

Commit

Permalink
fix: various nits with process lifetime and streaming
Browse files Browse the repository at this point in the history
  • Loading branch information
cha0s committed Feb 21, 2024
1 parent 8fed3a3 commit e0ed998
Show file tree
Hide file tree
Showing 16 changed files with 1,215 additions and 833 deletions.
1,857 changes: 1,067 additions & 790 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"packages/*"
],
"devDependencies": {
"@npmcli/arborist": "^7.3.1",
"chalk": "^4.1.2"
"@npmcli/arborist": "^7.3.1"
}
}
1 change: 1 addition & 0 deletions packages/build/build/default.eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ module.exports = async (flecks) => ({
'import/prefer-default-export': 'off',
'jsx-a11y/control-has-associated-label': ['error', {assert: 'either'}],
'jsx-a11y/label-has-associated-control': ['error', {assert: 'either'}],
'max-classes-per-file': ['error', {ignoreExpressions: true}],
'no-param-reassign': ['error', {props: false}],
'no-plusplus': 'off',
'no-shadow': 'off',
Expand Down
31 changes: 26 additions & 5 deletions packages/build/build/hooks/@flecks/build.commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {
} = require('@babel/types');
const addPathsToYml = require('@flecks/core/build/add-paths-to-yml');
const D = require('@flecks/core/build/debug');
const {prefixLines} = require('@flecks/core/build/stream');
const {
add,
binaryPath,
Expand All @@ -28,6 +29,7 @@ const {
spawnWith,
writeFile,
} = require('@flecks/core/src/server');
const chalk = require('chalk');
const chokidar = require('chokidar');
const {glob} = require('glob');
const {paperwork} = require('precinct');
Expand Down Expand Up @@ -224,24 +226,43 @@ exports.hook = (program, flecks) => {
'--mode', (production && !hot) ? 'production' : 'development',
];
const options = {
// @todo This kills the pnpm. Let's use a real IPC channel.
useFork: true,
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
...rest,
env: {
DEBUG_COLORS: process.stdout.isTTY,
FLECKS_BUILD_IS_PRODUCTION: production,
FORCE_COLOR: process.stdout.isTTY,
...(target ? {FLECKS_CORE_BUILD_LIST: target} : {}),
...(hot ? {FLECKS_ENV__flecks_server__hot: 'true'} : {}),
...rest.env,
},
};
const spawnWithPrefixedLines = (cmd, options) => {
const child = spawnWith(cmd, options);
if (
'pipe' === options.stdio
|| (Array.isArray(options.stdio) && options.stdio[0] === 'pipe')
) {
prefixLines(child.stdout, chalk.green('[📦] '))
.pipe(process.stdout);
}
if (
'pipe' === options.stdio
|| (Array.isArray(options.stdio) && options.stdio[1] === 'pipe')
) {
prefixLines(child.stderr, chalk.green('[📦] '))
.pipe(process.stderr);
}
return child;
};
if (!watch) {
return spawnWith(cmd, options);
return spawnWithPrefixedLines(cmd, options);
}
try {
await access(join(FLECKS_CORE_ROOT, 'build/flecks.yml'));
}
catch (error) {
return spawnWith(cmd, options);
return spawnWithPrefixedLines(cmd, options);
}
await rootsDependencies(flecks.roots, flecks.resolver);
const watched = Object.keys(dependencies);
Expand All @@ -259,7 +280,7 @@ exports.hook = (program, flecks) => {
});
let webpack;
const spawnWebpack = () => {
webpack = spawnWith(cmd, options);
webpack = spawnWithPrefixedLines(cmd, options);
webpack.on('message', (message) => {
switch (message.type) {
case 'kill':
Expand Down
1 change: 1 addition & 0 deletions packages/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"babel-merge": "^3.0.0",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"chalk": "^4.1.2",
"chokidar": "^3.5.3",
"commander": "11.1.0",
"copy-webpack-plugin": "^11.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/build/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = (name) => {
D.formatters.o = undefined;
D.formatters.O = undefined;
}
const type = 'web' === process.env.FLECKS_CORE_BUILD_TARGET ? 'debug' : 'error';
const type = 'undefined' !== typeof window ? 'log' : 'error';
// eslint-disable-next-line no-console
D.log = console[type].bind(console);
}
Expand Down
56 changes: 51 additions & 5 deletions packages/core/build/stream.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// eslint-disable-next-line max-classes-per-file, import/no-extraneous-dependencies
const {Buffer} = require('buffer');
const {EOL} = require('os');
const {Transform, Writable} = require('stream');

const {dump: dumpYml, load: loadYml} = require('js-yaml');
const JsonParse = require('jsonparse');
const {Transform, Writable} = require('stream');

const linebreak = /\r?\n/;

exports.JsonStream = class JsonStream extends Transform {

Expand Down Expand Up @@ -46,17 +50,43 @@ exports.JsonStream.PrettyPrint = class extends exports.JsonStream {

};

exports.pipesink = (stream) => {
exports.LineStream = class LineStream extends Transform {

constructor(encoding = 'utf8') {
super();
this.encoding = encoding;
this.buffer = '';
}

// eslint-disable-next-line no-underscore-dangle
_transform(chunk, encoding, done) {
const string = chunk.toString(this.encoding);
if (!string.match(linebreak)) {
this.buffer += string;
done();
return;
}
const parts = (this.buffer + string).split(linebreak);
this.buffer = parts.pop();
for (let i = 0; i < parts.length; ++i) {
this.push(parts[i]);
}
done();
}

};

exports.pipesink = (stream, {concat = Buffer.concat} = {}) => {
class Sink extends Writable {

constructor() {
super();
this.buffers = [];
this.chunks = [];
}

// eslint-disable-next-line no-underscore-dangle
_write(chunk, encoding, done) {
this.buffers.push(chunk);
this.chunks.push(chunk);
done();
}

Expand All @@ -66,11 +96,27 @@ exports.pipesink = (stream) => {
stream.pipe(sink);
stream.on('error', reject);
stream.on('end', () => {
resolve(Buffer.concat(sink.buffers));
resolve(concat(sink.chunks));
});
});
};

exports.prefixLines = (stream, prefix) => (
stream
.pipe(new exports.LineStream())
.pipe(new class Stdio extends Transform {

// eslint-disable-next-line no-underscore-dangle, class-methods-use-this
_transform(chunk, encoding, done) {
this.push(prefix);
this.push(chunk);
this.push(EOL);
done();
}

}())
);

exports.YamlStream = class YamlStream extends Transform {

constructor(decorator, options = {dump: {}, load: {}}) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/hooks/@flecks/web.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
exports.hook = async (req, flecks) => ({
id: flecks.get('@flecks/core.id'),
hi: 'foo',
});
13 changes: 6 additions & 7 deletions packages/core/src/server/process.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const {exec, fork, spawn} = require('child_process');
const {exec, spawn} = require('child_process');
const {
access,
constants: {X_OK},
Expand Down Expand Up @@ -60,15 +60,14 @@ exports.run = (cmd, {suppressError = true} = {}) => (
const children = [];

exports.spawnWith = (cmd, opts = {}) => {
const {useFork, ...rest} = opts;
debug("%sing: '%s'", useFork ? 'fork' : 'spawn', cmd.join(' '));
debugSilly('with options: %O', rest);
const child = (useFork ? fork : spawn)(cmd[0], cmd.slice(1), {
debug("spawning: '%s'", cmd.join(' '));
debugSilly('with options: %O', opts);
const child = spawn(cmd[0], cmd.slice(1), {
stdio: 'inherit',
...rest,
...opts,
env: {
...process.env,
...rest.env,
...opts.env,
},
});
children.push(child);
Expand Down
7 changes: 3 additions & 4 deletions packages/fleck/build/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const debug = D('@flecks/build.commands');

const {
FLECKS_CORE_ROOT = process.cwd(),
TERM,
} = process.env;

module.exports = (program, flecks) => {
Expand Down Expand Up @@ -49,7 +48,7 @@ module.exports = (program, flecks) => {
const filename = await flecks.resolveBuildConfig('test.webpack.config.js', '@flecks/build');
const config = {test: await require(filename)(env, argv, flecks)};
await flecks.configureBuilds(config, env, argv);
if (!config.test.entry) {
if (0 === Object.entries(config.test.entry).length) {
return undefined;
}
// Remove the previous test(s).
Expand All @@ -59,8 +58,8 @@ module.exports = (program, flecks) => {
'test',
{
env: {
DEBUG_COLORS: 'dumb' !== TERM,
FORCE_COLOR: 'dumb' !== TERM,
DEBUG_COLORS: process.stdout.isTTY,
FORCE_COLOR: process.stdout.isTTY,
},
production,
stdio: watch ? 'inherit' : 'pipe',
Expand Down
24 changes: 20 additions & 4 deletions packages/server/build/start.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const cluster = require('cluster');
const {join} = require('path');

const {prefixLines} = require('@flecks/core/build/stream');
const chalk = require('chalk');

class StartServerPlugin {

pluginName = 'StartServerPlugin';
Expand All @@ -27,21 +30,27 @@ class StartServerPlugin {
apply(compiler) {
const {options: {exec, signal}, pluginName} = this;
const logger = compiler.getInfrastructureLogger(pluginName);
let lastStartHadErrors = false;
compiler.hooks.afterEmit.tapPromise(pluginName, async (compilation) => {
if (compilation.errors.length > 0) {
lastStartHadErrors = true;
return;
}
if (this.worker && this.worker.isConnected()) {
if (signal) {
if (signal && !lastStartHadErrors) {
process.kill(
this.worker.process.pid,
true === signal ? 'SIGUSR2' : signal,
);
return undefined;
return;
}
const promise = new Promise((resolve) => {
this.worker.on('disconnect', resolve);
});
this.worker.disconnect();
await promise;
}
lastStartHadErrors = false;
let entryPoint;
if (!exec) {
entryPoint = compilation.getPath(Object.keys(compilation.assets)[0]);
Expand All @@ -55,7 +64,7 @@ class StartServerPlugin {
else {
entryPoint = exec(compilation);
}
return this.startServer(join(compiler.options.output.path, entryPoint));
await this.startServer(join(compiler.options.output.path, entryPoint));
});
compiler.hooks.shouldEmit.tap(pluginName, (compilation) => {
const entryPoints = Object.keys(compilation.assets);
Expand Down Expand Up @@ -92,9 +101,16 @@ class StartServerPlugin {
exec,
execArgv,
args,
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
...(inspectPort && {inspectPort}),
});
this.worker = cluster.fork(env);
this.worker = cluster.fork({
...env,
});
['stdout', 'stderr'].forEach((stream) => {
prefixLines(this.worker.process[stream], chalk.blue('[SRV] '))
.pipe(process[stream]);
});
this.worker.on('exit', (code) => {
if (killOnExit && !this.worker.exitedAfterDisconnect) {
process.send({type: 'kill', payload: code});
Expand Down
3 changes: 2 additions & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"server.js"
],
"dependencies": {
"@flecks/core": "^4.2.3"
"@flecks/core": "^4.2.3",
"chalk": "^4.1.2"
},
"devDependencies": {
"@flecks/build": "^4.1.3",
Expand Down
3 changes: 0 additions & 3 deletions packages/server/test/helpers/start-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {createApplication} from './create-application';

const {
FLECKS_CORE_ROOT = process.cwd(),
TERM,
} = process.env;

class SocketWrapper {
Expand Down Expand Up @@ -135,12 +134,10 @@ export async function startServer({
stdio: 'pipe',
...opts,
env: {
DEBUG_COLORS: 'dumb' !== TERM,
FLECKS_ENV__flecks_server__stats: '{"preset": "none"}',
FLECKS_ENV__flecks_server__start: true,
FLECKS_CORE_ROOT: path,
FLECKS_SERVER_TEST_SOCKET: socketPath,
FORCE_COLOR: 'dumb' !== TERM,
NODE_ENV: 'test',
NODE_PATH: join(FLECKS_CORE_ROOT, '..', '..', 'node_modules'),
...opts.env,
Expand Down
4 changes: 4 additions & 0 deletions packages/web/build/hooks/@flecks/core.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ exports.hook = () => ({
* (webpack-dev-server) Port to bind. Defaults to random port.
*/
devPort: 0,
/**
* (webpack-dev-server) Set up a proxy to the dev server. Defaults to `false` in production.
*/
devProxyWds: undefined,
/**
* (webpack-dev-server) Webpack stats output.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"before-build-webpack": "^0.2.13",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"chalk": "^4.1.2",
"clean-webpack-plugin": "4.0.0",
"compression": "^1.7.4",
"css-loader": "^6.8.1",
Expand Down
Loading

0 comments on commit e0ed998

Please sign in to comment.