Skip to content

Commit dba7357

Browse files
paldepindCopilot
andcommitted
JS: Package native TypeScript runtimes by distribution
Split the wrapper and native runtimes into common and architecture-specific archives. Select and verify pinned platform packages, preserve executable permissions, and use ZIP response files on Windows. Add installed-package smoke tests covering platform selection, archive layout, runtime compatibility, and the merged distribution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9f12f7cb-26aa-405b-a1f0-9dcc239778cb
1 parent f63b850 commit dba7357

4 files changed

Lines changed: 411 additions & 10 deletions

File tree

‎javascript/BUILD.bazel‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@ codeql_pack(
3333
"//javascript/extractor:tools-extractor",
3434
"//javascript/resources",
3535
],
36-
zips = {"//javascript/extractor/lib/typescript": "tools"},
36+
arch_overrides = ["tools/typescript-parser-wrapper/node_modules/@typescript"],
37+
zips = {
38+
"//javascript/extractor/lib/typescript": "tools",
39+
"//javascript/extractor/lib/typescript:native": "tools/typescript-parser-wrapper/node_modules/@typescript",
40+
},
3741
)

‎javascript/extractor/lib/typescript/BUILD.bazel‎

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
1-
# Builds a zip file of the compiled typscript-parser-wrapper and its dependencies.
1+
load("@rules_python//python:defs.bzl", "py_test")
2+
load("//misc/bazel:os.bzl", "codeql_platform_select")
3+
load("//misc/bazel:pkg.bzl", "codeql_pack", "codeql_pack_group")
4+
5+
DISTRIBUTION = codeql_platform_select(
6+
linux64 = "linux64",
7+
linux_arm64 = "linux-arm64",
8+
osx64 = "osx64",
9+
win64 = "win64",
10+
)
11+
12+
# Keep the JavaScript API common, but ship native executables in the arch package.
213
genrule(
3-
name = "typescript",
14+
name = "build",
415
srcs = [
516
"tsconfig.json",
617
"package.json",
718
"package-lock.json",
19+
"package-runtime.mjs",
820
] + glob(["src/**"]),
9-
outs = ["javascript.zip"],
10-
cmd = "\n".join([
21+
outs = [
22+
"javascript.zip",
23+
"native.zip",
24+
],
25+
cmd = "DISTRIBUTION=" + DISTRIBUTION + "\n" + "\n".join([
1126
# the original working directory is not preserved anywhere, but needs to be accessible, as
1227
# all paths are relative to this
1328
# unfortunately, we need to change the working directory to run npm.
@@ -25,20 +40,35 @@ genrule(
2540
"rm -rf $(RULEDIR)/inputs",
2641
"cp -L -R $$(dirname $(execpath package.json)) $(RULEDIR)/inputs",
2742
"cd $(RULEDIR)/inputs",
28-
"$$NPM install",
43+
"$$NPM ci",
2944
"$$NPM run build",
3045
"rm -rf node_modules",
3146
# Install again with only runtime deps
32-
"$$NPM install --prod",
47+
"$$NPM ci --omit=dev --omit=optional --ignore-scripts",
48+
# Optional dependencies are host-filtered even with --force. Install the
49+
# selected packages explicitly, retaining the lockfile's
50+
# versions/integrities.
51+
"$$NPM install --omit=dev --ignore-scripts --no-save --force $$(node package-runtime.mjs packages $$DISTRIBUTION)",
52+
# Copy native binaries into `native/` and remove the explicitly
53+
# installed native packages from `node_modules`.
54+
"node package-runtime.mjs stage $$DISTRIBUTION",
3355
"mv node_modules build/",
56+
# Node needs the ESM package boundary in the installed wrapper too.
57+
"cp package.json build/",
3458
"mkdir -p typescript-parser-wrapper",
3559
"mv build/* typescript-parser-wrapper",
36-
"OUT=$$BAZEL_ROOT/$@",
60+
"OUT=$$BAZEL_ROOT/$(execpath javascript.zip)",
61+
"NATIVE_OUT=$$BAZEL_ROOT/$(execpath native.zip)",
3762
"case $$OSTYPE in",
38-
" cygwin|msys|win32) OUT=$$(cygpath -w $$OUT);;",
63+
" cygwin|msys|win32) OUT=$$(cygpath -w $$OUT); NATIVE_OUT=$$(cygpath -w $$NATIVE_OUT);;",
3964
"esac",
4065
"",
41-
"$$BAZEL_ROOT/$(execpath @bazel_tools//tools/zip:zipper) cC $$OUT $$(find typescript-parser-wrapper -name '*' -print)",
66+
# Use response files to stay below Windows' command-line length limit.
67+
"find typescript-parser-wrapper -print > wrapper-files",
68+
"$$BAZEL_ROOT/$(execpath @bazel_tools//tools/zip:zipper) cC $$OUT @wrapper-files",
69+
"cd native",
70+
"find typescript-* -print > native-files",
71+
"$$BAZEL_ROOT/$(execpath @bazel_tools//tools/zip:zipper) cC $$NATIVE_OUT @native-files",
4272
"rm -rf $$TEMP",
4373
]),
4474
tools = [
@@ -48,3 +78,59 @@ genrule(
4878
],
4979
visibility = ["//visibility:public"],
5080
)
81+
82+
filegroup(
83+
name = "typescript",
84+
srcs = ["javascript.zip"],
85+
visibility = ["//visibility:public"],
86+
)
87+
88+
filegroup(
89+
name = "native",
90+
srcs = ["native.zip"],
91+
visibility = ["//visibility:public"],
92+
)
93+
94+
codeql_pack(
95+
name = "package-test-pack",
96+
srcs = [],
97+
arch_overrides = ["tools/typescript-parser-wrapper/node_modules/@typescript"],
98+
install_dest = None,
99+
pack_prefix = "",
100+
zips = {
101+
":native": "tools/typescript-parser-wrapper/node_modules/@typescript",
102+
":typescript": "tools",
103+
},
104+
)
105+
106+
codeql_pack_group(
107+
name = "package-test-distribution",
108+
srcs = [":package-test-pack"],
109+
skip_installer = True,
110+
)
111+
112+
py_test(
113+
name = "package-test",
114+
size = "small",
115+
srcs = ["test/package_test.py"],
116+
data = [
117+
"package-runtime.mjs",
118+
":native",
119+
":package-test-distribution-arch-zip",
120+
":package-test-distribution-common-zip",
121+
":typescript",
122+
"//misc/ripunzip",
123+
"@nodejs//:node_bin",
124+
],
125+
env = {
126+
"NODE_BIN": "$(rlocationpath @nodejs//:node_bin)",
127+
"PACKAGE_RUNTIME": "$(rlocationpath package-runtime.mjs)",
128+
"RIPUNZIP": "$(rlocationpath //misc/ripunzip)",
129+
"TS_ARCH_ZIP": "$(rlocationpath :package-test-distribution-arch-zip)",
130+
"TS_COMMON_ZIP": "$(rlocationpath :package-test-distribution-common-zip)",
131+
"TS_NATIVE_ZIP": "$(rlocationpath :native)",
132+
"TS_WRAPPER_ZIP": "$(rlocationpath :typescript)",
133+
},
134+
main = "test/package_test.py",
135+
deps = ["@rules_python//python/runfiles"],
136+
)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// This script is used in `BUILD.bazel` to manage the TypeScript native
2+
// executables.
3+
//
4+
// Run it with an `action` argument and a `distribution` argument. On
5+
// - packages <distribution> it prints the pinned native packages to install.
6+
// - stage <distribution> it moves the relevant native executables into
7+
// `native/`, leaving `node_modules` with only the common JavaScript
8+
// dependencies.
9+
10+
import {
11+
chmodSync,
12+
mkdirSync,
13+
readFileSync,
14+
renameSync,
15+
rmSync,
16+
statSync,
17+
} from "node:fs";
18+
import { join } from "node:path";
19+
20+
const platformsByDistribution = {
21+
linux64: ["linux-x64"],
22+
"linux-arm64": ["linux-arm64"],
23+
osx64: ["darwin-arm64", "darwin-x64"],
24+
win64: ["win32-x64"],
25+
};
26+
const [action, distribution] = process.argv.slice(2);
27+
if (process.argv.length !== 4 || !["packages", "stage"].includes(action)) {
28+
throw new Error(
29+
"Usage: node package-runtime.mjs <packages|stage> <distribution>",
30+
);
31+
}
32+
const platforms = platformsByDistribution[distribution];
33+
if (platforms == null) {
34+
throw new Error("Unsupported CodeQL distribution: " + distribution);
35+
}
36+
const readJson = (filename) => JSON.parse(readFileSync(filename, "utf8"));
37+
const typescript = readJson("node_modules/typescript/package.json");
38+
39+
if (action === "packages") {
40+
// Print the native packages for the specified distribution
41+
console.log(
42+
platforms
43+
.map(
44+
(platform) => `@typescript/typescript-${platform}@${typescript.version}`,
45+
)
46+
.join(" "),
47+
);
48+
process.exit(0);
49+
} else {
50+
// Move the native Go executables for the specified distribution into the
51+
// `native/` directory
52+
mkdirSync("native");
53+
for (const platform of platforms) {
54+
const name = "typescript-" + platform;
55+
const source = join("node_modules", "@typescript", name);
56+
const manifest = readJson(join(source, "package.json"));
57+
if (
58+
manifest.name !== "@typescript/" + name ||
59+
manifest.version !== typescript.version ||
60+
typescript.optionalDependencies[manifest.name] !== manifest.version
61+
) {
62+
throw new Error(
63+
"Native runtime does not match TypeScript: " + manifest.name,
64+
);
65+
}
66+
const executableName = platform.startsWith("win32-") ? "tsc.exe" : "tsc";
67+
const executable = join(source, "lib", executableName);
68+
if (!statSync(executable).isFile()) {
69+
throw new Error("Native executable is not a file: " + executable);
70+
}
71+
chmodSync(executable, 0o755);
72+
renameSync(source, join("native", name));
73+
}
74+
rmSync("node_modules/@typescript", { recursive: true });
75+
}

0 commit comments

Comments
 (0)