Skip to content
This repository has been archived by the owner on Mar 31, 2024. It is now read-only.

Commit

Permalink
[kbn/optimizer] fix windows compatibility (elastic#69304)
Browse files Browse the repository at this point in the history
Co-authored-by: spalger <spalger@users.noreply.github.com>
  • Loading branch information
Spencer and spalger authored Jun 16, 2020
1 parent efbb4cc commit db1df7b
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 26 deletions.
66 changes: 53 additions & 13 deletions packages/kbn-optimizer/src/optimizer/observe_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { inspect } from 'util';

import execa from 'execa';
import * as Rx from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { map, takeUntil, first, ignoreElements } from 'rxjs/operators';

import { isWorkerMsg, WorkerConfig, WorkerMsg, Bundle, BundleRefs } from '../common';

Expand Down Expand Up @@ -68,19 +68,11 @@ if (inspectFlagIndex !== -1) {

function usingWorkerProc<T>(
config: OptimizerConfig,
workerConfig: WorkerConfig,
bundles: Bundle[],
fn: (proc: execa.ExecaChildProcess) => Rx.Observable<T>
) {
return Rx.using(
(): ProcResource => {
const args = [
JSON.stringify(workerConfig),
JSON.stringify(bundles.map((b) => b.toSpec())),
BundleRefs.fromBundles(config.bundles).toSpecJson(),
];

const proc = execa.node(require.resolve('../worker/run_worker'), args, {
const proc = execa.node(require.resolve('../worker/run_worker'), [], {
nodeOptions: [
...(inspectFlag && config.inspectWorkers
? [`${inspectFlag}=${inspectPortCounter++}`]
Expand Down Expand Up @@ -129,6 +121,51 @@ function observeStdio$(stream: Readable, name: WorkerStdio['stream']) {
);
}

/**
* We used to pass configuration to the worker as JSON encoded arguments, but they
* grew too large for argv, especially on Windows, so we had to move to an async init
* where we send the args over IPC. To keep the logic simple we basically mock the
* argv behavior and don't use complicated messages or anything so that state can
* be initialized in the worker before most of the code is run.
*/
function initWorker(
proc: execa.ExecaChildProcess,
config: OptimizerConfig,
workerConfig: WorkerConfig,
bundles: Bundle[]
) {
const msg$ = Rx.fromEvent<[unknown]>(proc, 'message').pipe(
// validate the initialization messages from the process
map(([msg]) => {
if (typeof msg === 'string') {
switch (msg) {
case 'init':
return 'init' as const;
case 'ready':
return 'ready' as const;
}
}

throw new Error(`unexpected message from worker while initializing: [${inspect(msg)}]`);
})
);

return Rx.concat(
msg$.pipe(first((msg) => msg === 'init')),
Rx.defer(() => {
proc.send({
args: [
JSON.stringify(workerConfig),
JSON.stringify(bundles.map((b) => b.toSpec())),
BundleRefs.fromBundles(config.bundles).toSpecJson(),
],
});
return [];
}),
msg$.pipe(first((msg) => msg === 'ready'))
).pipe(ignoreElements());
}

/**
* Start a worker process with the specified `workerConfig` and
* `bundles` and return an observable of the events related to
Expand All @@ -140,10 +177,11 @@ export function observeWorker(
workerConfig: WorkerConfig,
bundles: Bundle[]
): Rx.Observable<WorkerMsg | WorkerStatus> {
return usingWorkerProc(config, workerConfig, bundles, (proc) => {
let lastMsg: WorkerMsg;
return usingWorkerProc(config, (proc) => {
const init$ = initWorker(proc, config, workerConfig, bundles);

return Rx.merge(
let lastMsg: WorkerMsg;
const worker$: Rx.Observable<WorkerMsg | WorkerStatus> = Rx.merge(
Rx.of({
type: 'worker started',
bundles,
Expand Down Expand Up @@ -201,5 +239,7 @@ export function observeWorker(
)
)
);

return Rx.concat(init$, worker$);
});
}
10 changes: 7 additions & 3 deletions packages/kbn-optimizer/src/worker/entry_point_creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
* under the License.
*/

module.exports = function ({ entries }: { entries: Array<{ importId: string; relPath: string }> }) {
const lines = entries.map(({ importId, relPath }) => [
`__kbnBundles__.define('${importId}', __webpack_require__, require.resolve('./${relPath}'))`,
module.exports = function ({
entries,
}: {
entries: Array<{ importId: string; requirePath: string }>;
}) {
const lines = entries.map(({ importId, requirePath }) => [
`__kbnBundles__.define('${importId}', __webpack_require__, require.resolve('${requirePath}'))`,
]);

return {
Expand Down
38 changes: 32 additions & 6 deletions packages/kbn-optimizer/src/worker/run_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
* under the License.
*/

import { inspect } from 'util';

import * as Rx from 'rxjs';
import { take, mergeMap } from 'rxjs/operators';

import {
parseBundles,
Expand Down Expand Up @@ -80,15 +83,38 @@ setInterval(() => {
}
}, 1000).unref();

function assertInitMsg(msg: unknown): asserts msg is { args: string[] } {
if (typeof msg !== 'object' || !msg) {
throw new Error(`expected init message to be an object: ${inspect(msg)}`);
}

const { args } = msg as Record<string, unknown>;
if (!args || !Array.isArray(args) || !args.every((a) => typeof a === 'string')) {
throw new Error(
`expected init message to have an 'args' property that's an array of strings: ${inspect(msg)}`
);
}
}

Rx.defer(() => {
const workerConfig = parseWorkerConfig(process.argv[2]);
const bundles = parseBundles(process.argv[3]);
const bundleRefs = BundleRefs.parseSpec(process.argv[4]);
process.send!('init');

return Rx.fromEvent<[unknown]>(process as any, 'message').pipe(
take(1),
mergeMap(([msg]) => {
assertInitMsg(msg);
process.send!('ready');

const workerConfig = parseWorkerConfig(msg.args[0]);
const bundles = parseBundles(msg.args[1]);
const bundleRefs = BundleRefs.parseSpec(msg.args[2]);

// set BROWSERSLIST_ENV so that style/babel loaders see it before running compilers
process.env.BROWSERSLIST_ENV = workerConfig.browserslistEnv;
// set BROWSERSLIST_ENV so that style/babel loaders see it before running compilers
process.env.BROWSERSLIST_ENV = workerConfig.browserslistEnv;

return runCompilers(workerConfig, bundles, bundleRefs);
return runCompilers(workerConfig, bundles, bundleRefs);
})
);
}).subscribe(
(msg) => {
send(msg);
Expand Down
14 changes: 10 additions & 4 deletions packages/kbn-optimizer/src/worker/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,16 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker:
entries: bundle.publicDirNames.map((name) => {
const absolute = Path.resolve(bundle.contextDir, name);
const newContext = Path.dirname(ENTRY_CREATOR);
return {
importId: `${bundle.type}/${bundle.id}/${name}`,
relPath: Path.relative(newContext, absolute),
};
const importId = `${bundle.type}/${bundle.id}/${name}`;

// relative path from context of the ENTRY_CREATOR, with linux path separators
let requirePath = Path.relative(newContext, absolute).split('\\').join('/');
if (!requirePath.startsWith('.')) {
// ensure requirePath is identified by node as relative
requirePath = `./${requirePath}`;
}

return { importId, requirePath };
}),
},
},
Expand Down

0 comments on commit db1df7b

Please sign in to comment.