switch Svelte compilation to worker model

based on changes from upstream rules_svelte

Their code was using run_node() instead of ctx.actions.run(), which
seems to create a new worker for every CPU core, instead of respecting
the standard limit of 4.
This commit is contained in:
Damien Elmes 2021-03-20 20:42:29 +10:00
parent 8cfd28bd00
commit 0cc06ad03b
5 changed files with 144 additions and 106 deletions

View file

@ -8,6 +8,7 @@
"devDependencies": { "devDependencies": {
"@bazel/rollup": "=3.2.2", "@bazel/rollup": "=3.2.2",
"@bazel/typescript": "=3.2.2", "@bazel/typescript": "=3.2.2",
"@bazel/worker": "=3.2.2",
"@pyoner/svelte-types": "^3.4.4-2", "@pyoner/svelte-types": "^3.4.4-2",
"@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-node-resolve": "^9.0.0",

View file

@ -1,24 +1,16 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
nodejs_binary( nodejs_binary(
name = "typescript", name = "svelte_bin",
data = [
"@npm//typescript",
],
entry_point = "@npm//:node_modules/typescript/lib/tsc.js",
visibility = ["//visibility:public"],
)
nodejs_binary(
name = "svelte",
data = [ data = [
":svelte.js", ":svelte.js",
"@npm//@bazel/worker",
"@npm//sass", "@npm//sass",
"@npm//svelte", "@npm//svelte",
"@npm//svelte-preprocess", "@npm//svelte-preprocess",
"@npm//svelte2tsx", "@npm//svelte2tsx",
"@npm//typescript",
], ],
entry_point = ":svelte.js", entry_point = ":svelte.js",
templated_args = ["--bazel_patch_module_resolver"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )

View file

@ -1,65 +1,38 @@
load("@npm//svelte-check:index.bzl", _svelte_check = "svelte_check_test") load("@npm//svelte-check:index.bzl", _svelte_check = "svelte_check_test")
load("@build_bazel_rules_nodejs//:providers.bzl", "declaration_info", "run_node")
"Implementation of the svelte rule"
load("@build_bazel_rules_nodejs//:providers.bzl", "declaration_info")
SvelteFilesInfo = provider("transitive_sources")
def get_transitive_srcs(srcs, deps):
return depset(
srcs,
transitive = [dep[SvelteFilesInfo].transitive_sources for dep in deps],
)
def _svelte(ctx): def _svelte(ctx):
base = ctx.attr.name + ".svelte" args = ctx.actions.args()
temp = ctx.actions.declare_directory(base + ".temp") args.use_param_file("@%s", use_always = True)
temptsx = temp.path + "/" + base + ".tsx" args.set_param_file_format("multiline")
ctx.actions.run_shell(
mnemonic = "Svelte", temp_ts_path = ctx.actions.declare_file(ctx.attr.name + ".svelte.ts")
command = """\
{svelte} {input} {output_js} {temp} && \ args.add(ctx.file.entry_point.path)
{tsc} {tsc_args} {temptsx} {shims} && \ args.add(ctx.outputs.mjs.path)
mv {temp}/{base}.d.ts {output_def} && \ args.add(ctx.outputs.dts.path)
rm {temptsx}""".format( args.add(temp_ts_path)
svelte = ctx.executable._svelte.path, args.add_all(ctx.files._shims)
input = ctx.file.entry_point.path,
output_js = ctx.outputs.build.path, ctx.actions.run(
tsc = ctx.executable._typescript.path, execution_requirements = {"supports-workers": "1"},
output_def = ctx.outputs.buildDef.path, executable = ctx.executable._svelte_bin,
temp = temp.path, outputs = [ctx.outputs.mjs, ctx.outputs.dts, temp_ts_path],
temptsx = temptsx,
base = base,
tsc_args = "--jsx preserve --emitDeclarationOnly --declaration --skipLibCheck",
shims = " ".join([f.path for f in ctx.files._shims]),
),
outputs = [ctx.outputs.build, ctx.outputs.buildDef, temp],
inputs = [ctx.file.entry_point] + ctx.files._shims, inputs = [ctx.file.entry_point] + ctx.files._shims,
tools = [ctx.executable._svelte, ctx.executable._typescript], mnemonic = "Svelte",
arguments = [args],
) )
trans_srcs = get_transitive_srcs(ctx.files.srcs + [ctx.outputs.build, ctx.outputs.buildDef], ctx.attr.deps)
return [ return [
declaration_info(depset([ctx.outputs.buildDef]), deps = [ctx.attr._shims]), declaration_info(depset([ctx.outputs.dts]), deps = [ctx.attr._shims]),
SvelteFilesInfo(transitive_sources = trans_srcs),
DefaultInfo(files = trans_srcs),
] ]
svelte = rule( svelte = rule(
implementation = _svelte, implementation = _svelte,
attrs = { attrs = {
"entry_point": attr.label(allow_single_file = True), "entry_point": attr.label(allow_single_file = True),
"deps": attr.label_list(), "_svelte_bin": attr.label(
"srcs": attr.label_list(allow_files = True), default = Label("//ts/svelte:svelte_bin"),
"_svelte": attr.label(
default = Label("//ts/svelte:svelte"),
executable = True,
cfg = "host",
),
"_typescript": attr.label(
default = Label("//ts/svelte:typescript"),
executable = True, executable = True,
cfg = "host", cfg = "host",
), ),
@ -69,8 +42,8 @@ svelte = rule(
), ),
}, },
outputs = { outputs = {
"build": "%{name}.svelte.mjs", "mjs": "%{name}.svelte.mjs",
"buildDef": "%{name}.svelte.d.ts", "dts": "%{name}.svelte.d.ts",
}, },
) )

View file

@ -1,55 +1,120 @@
const fs = require("fs"); const fs = require("fs");
const process = require("process"); const process = require("process");
const path = require("path"); const path = require("path");
const worker = require("@bazel/worker");
const input = process.argv[2];
const outputJs = process.argv[3];
const temp = process.argv[4];
const svelte = require("svelte/compiler.js"); const svelte = require("svelte/compiler.js");
const source = fs.readFileSync(input, "utf8");
const preprocessOptions = require("svelte-preprocess")({});
preprocessOptions.filename = input;
const svelte2tsx = require("svelte2tsx"); const svelte2tsx = require("svelte2tsx");
const preprocess = require("svelte-preprocess");
const ts = require("typescript");
let tsoutput = svelte2tsx(source, { const tsOptions = {
filename: input, jsx: "preserve",
strictMode: true, declaration: true,
isTsFile: true, emitDeclarationOnly: true,
}); skipLibCheck: true,
let codeLines = tsoutput.code.split("\n"); };
// replace the "///<reference types="svelte" />" with a line const tsHost = ts.createCompilerHost(tsOptions);
// turning off checking, as we'll use svelte-check for that
codeLines[0] = "// @ts-nocheck"; function writeFile(file, data) {
const outputBase = path.basename(outputJs.replace(".mjs", ".tsx")); return new Promise((resolve, reject) => {
const outputTs = path.join(temp, outputBase); fs.writeFile(file, data, (err) => {
fs.writeFileSync(outputTs, codeLines.join("\n")); if (err) {
reject(err);
return;
}
resolve();
});
});
}
function readFile(file) {
return new Promise((resolve, reject) => {
fs.readFile(file, "utf8", (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
function buildDeclarations(svelteTsFile, shims) {
const createdFiles = {}; //test2
tsHost.writeFile = (fileName, contents) => (createdFiles[fileName] = contents);
const program = ts.createProgram([svelteTsFile, ...shims], tsOptions, tsHost);
program.emit();
const dtsSource = createdFiles[svelteTsFile.replace(".ts", ".d.ts")];
return dtsSource;
}
async function writeDts(tsPath, dtsPath, shims) {
const dtsSource = buildDeclarations(tsPath, shims);
await writeFile(dtsPath, dtsSource);
}
async function writeTs(svelteSource, sveltePath, tsPath) {
let tsSource = svelte2tsx(svelteSource, {
filename: sveltePath,
strictMode: true,
isTsFile: true,
});
let codeLines = tsSource.code.split("\n");
// replace the "///<reference types="svelte" />" with a line
// turning off checking, as we'll use svelte-check for that
codeLines[0] = "// @ts-nocheck";
await writeFile(tsPath, codeLines.join("\n"));
}
//test
async function writeJs(source, inputFilename, outputPath) {
const preprocessOptions = preprocess({});
preprocessOptions.filename = inputFilename;
svelte.preprocess(source, preprocessOptions).then(
(processed) => {
let result;
try { try {
result = svelte.compile(processed.toString(), { const processed = await svelte.preprocess(source, preprocessOptions);
format: "esm", const result = svelte.compile(processed.toString(), {
generate: "dom", format: "esm",
filename: outputJs, generate: "dom",
}); filename: outputPath,
});
// warnings are an error
if (result.warnings.length > 0) {
console.log(`warnings during compile: ${result.warnings}`);
return;
}
const outputSource = result.js.code;
await writeFile(outputPath, outputSource);
} catch (err) { } catch (err) {
console.log(`compile failed: ${err}`); console.log(`compile failed: ${err}`);
return; return;
}
if (result.warnings.length > 0) {
console.log(`warnings during compile: ${result.warnings}`);
return;
} }
}
let code = result.js.code; async function compileSvelte(args) {
fs.writeFileSync(outputJs, code); const [sveltePath, mjsPath, dtsPath, tempTsPath, ...shims] = args;
}, const svelteSource = await readFile(sveltePath);
(error) => {
console.log(`preprocess failed: ${error}`); await writeTs(svelteSource, sveltePath, tempTsPath);
} await writeDts(tempTsPath, dtsPath, shims);
); await writeJs(svelteSource, sveltePath, mjsPath);
return true;
}
function main(args) {
if (worker.runAsWorker(process.argv)) {
worker.log("Svelte running as a Bazel worker");
worker.runWorkerLoop(compileSvelte);
} else {
const paramFile = process.argv[2].replace(/^@/, "");
const commandLineArgs = fs.readFileSync(paramFile, "utf-8").trim().split("\n");
console.log("Svelte running as a standalone process");
compileSvelte(commandLineArgs);
}
}
if (require.main === module) {
main(process.argv.slice(2));
process.exitCode = 0;
}

View file

@ -43,6 +43,13 @@
source-map-support "0.5.9" source-map-support "0.5.9"
tsutils "2.27.2" tsutils "2.27.2"
"@bazel/worker@=3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@bazel/worker/-/worker-3.2.2.tgz#16bb809aaa59bf077836da5860bfbc478c66b835"
integrity sha512-fFEdePL4r2LWOzaIVOHOP89HUHZ/cEVZCb1d7SW55uaNEIo0l9iNOx+5thpAn0Woga51LBsMADuYNPk/hmSeIg==
dependencies:
protobufjs "6.8.8"
"@fluent/bundle@^0.15.1": "@fluent/bundle@^0.15.1":
version "0.15.1" version "0.15.1"
resolved "https://registry.yarnpkg.com/@fluent/bundle/-/bundle-0.15.1.tgz#95d3b9f836ac138b6ee8480ef8d0547dd59195b1" resolved "https://registry.yarnpkg.com/@fluent/bundle/-/bundle-0.15.1.tgz#95d3b9f836ac138b6ee8480ef8d0547dd59195b1"