-
Notifications
You must be signed in to change notification settings - Fork 522
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(typescript): worker mode for ts_project (#2136)
* feat(typescript): worker mode for ts_project * chore: cleanup and declare protobufjs dependency * fix: do not pass --watch for non-worker mode * fix: do not test worker on windows * fix: log on standalone failures * chore: docs improvements Co-authored-by: Alex Eagle <[email protected]> Co-authored-by: Dan Muller <[email protected]>
- Loading branch information
1 parent
19272ef
commit 9663b85
Showing
11 changed files
with
292 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,15 @@ | ||
{ | ||
"compilerOptions": { | ||
"jsx": "react", | ||
"lib": ["ES2015", "DOM"] | ||
} | ||
} | ||
"lib": [ | ||
"ES2015", | ||
"DOM" | ||
] | ||
}, | ||
// When using ts_project in worker mode, we run outside the Bazel sandbox (unless using --worker_sandboxing). | ||
// We list the files that should be part of this particular compilation to avoid TypeScript discovering others. | ||
"include": [ | ||
"*.tsx", | ||
"*.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# BEGIN-INTERNAL | ||
|
||
load("//internal/common:copy_to_bin.bzl", "copy_to_bin") | ||
load("//third_party/github.com/bazelbuild/bazel-skylib:rules/copy_file.bzl", "copy_file") | ||
|
||
# Copy the proto file to a matching third_party/... nested directory | ||
# so the runtime require() statements still work | ||
_worker_proto_dir = "third_party/github.com/bazelbuild/bazel/src/main/protobuf" | ||
|
||
genrule( | ||
name = "copy_worker_js", | ||
srcs = ["//packages/worker:npm_package"], | ||
outs = ["worker.js"], | ||
cmd = "cp $(execpath //packages/worker:npm_package)/index.js $@", | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
copy_file( | ||
name = "copy_worker_proto", | ||
src = "@build_bazel_rules_typescript//%s:worker_protocol.proto" % _worker_proto_dir, | ||
out = "%s/worker_protocol.proto" % _worker_proto_dir, | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
copy_to_bin( | ||
name = "worker_adapter", | ||
srcs = [ | ||
"worker_adapter.js", | ||
], | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
filegroup( | ||
name = "package_contents", | ||
srcs = [ | ||
"BUILD.bazel", | ||
], | ||
visibility = ["//packages/typescript:__subpackages__"], | ||
) | ||
|
||
# END-INTERNAL | ||
|
||
exports_files([ | ||
"worker_adapter.js", | ||
]) | ||
|
||
filegroup( | ||
name = "worker", | ||
srcs = [ | ||
"third_party/github.com/bazelbuild/bazel/src/main/protobuf/worker_protocol.proto", | ||
"worker.js", | ||
"worker_adapter.js", | ||
], | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* @fileoverview wrapper program around the TypeScript compiler, tsc | ||
* | ||
* It intercepts the Bazel Persistent Worker protocol, using it to remote-control tsc running as a | ||
* child process. In between builds, the tsc process is stopped (akin to ctrl-z in a shell) and then | ||
* resumed (akin to `fg`) when the inputs have changed. | ||
* | ||
* See https://medium.com/@mmorearty/how-to-create-a-persistent-worker-for-bazel-7738bba2cabb | ||
* for more background (note, that is documenting a different implementation) | ||
*/ | ||
const child_process = require('child_process'); | ||
const MNEMONIC = 'TsProject'; | ||
const worker = require('./worker'); | ||
|
||
const workerArg = process.argv.indexOf('--persistent_worker') | ||
if (workerArg > 0) { | ||
process.argv.splice(workerArg, 1, '--watch') | ||
|
||
if (process.platform !== 'linux' && process.platform !== 'darwin') { | ||
throw new Error(`Worker mode is only supported on linux and darwin, not ${process.platform}. | ||
See https://github.com/bazelbuild/rules_nodejs/issues/2277`); | ||
} | ||
} | ||
|
||
const [tscBin, ...tscArgs] = process.argv.slice(2); | ||
|
||
const child = child_process.spawn( | ||
tscBin, | ||
tscArgs, | ||
{stdio: 'pipe'}, | ||
); | ||
function awaitOneBuild() { | ||
child.kill('SIGCONT') | ||
|
||
let buffer = []; | ||
return new Promise((res) => { | ||
function awaitBuild(s) { | ||
buffer.push(s); | ||
|
||
if (s.includes('Watching for file changes.')) { | ||
child.kill('SIGSTOP') | ||
|
||
const success = s.includes('Found 0 errors.'); | ||
res(success); | ||
|
||
child.stdout.removeListener('data', awaitBuild); | ||
|
||
if (!success) { | ||
console.error( | ||
`\nError output from tsc worker:\n\n ${ | ||
buffer.slice(1).map(s => s.toString()).join('').replace(/\n/g, '\n ')}`, | ||
) | ||
} | ||
|
||
buffer = []; | ||
} | ||
}; | ||
child.stdout.on('data', awaitBuild); | ||
}); | ||
} | ||
|
||
async function main() { | ||
// Bazel will pass a special argument to the program when it's running us as a worker | ||
if (workerArg > 0) { | ||
worker.log(`Running ${MNEMONIC} as a Bazel worker`); | ||
|
||
worker.runWorkerLoop(awaitOneBuild); | ||
} else { | ||
// Running standalone so stdout is available as usual | ||
console.log(`Running ${MNEMONIC} as a standalone process`); | ||
console.error( | ||
`Started a new process to perform this action. Your build might be misconfigured, try | ||
--strategy=${MNEMONIC}=worker`); | ||
|
||
const stdoutbuffer = []; | ||
child.stdout.on('data', data => stdoutbuffer.push(data)); | ||
|
||
const stderrbuffer = []; | ||
child.stderr.on('data', data => stderrbuffer.push(data)); | ||
|
||
child.on('exit', code => { | ||
if (code !== 0) { | ||
console.error( | ||
`\nstdout from tsc:\n\n ${ | ||
stdoutbuffer.map(s => s.toString()).join('').replace(/\n/g, '\n ')}`, | ||
) | ||
console.error( | ||
`\nstderr from tsc:\n\n ${ | ||
stderrbuffer.map(s => s.toString()).join('').replace(/\n/g, '\n ')}`, | ||
) | ||
} | ||
process.exit(code) | ||
}); | ||
} | ||
} | ||
|
||
if (require.main === module) { | ||
main(); | ||
} |
Oops, something went wrong.