mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
Respect explicit static library package selections
- Compile opted-in runtime packages when external type declarations are installed. - Preserve static dependency checks across package subpaths and scoped imports.
This commit is contained in:
@@ -112,6 +112,9 @@ function detectAutoPackages(
|
||||
// never npm candidates. Auto keeps its original path (the
|
||||
// @types/node answer skips them below), byte-for-byte.
|
||||
if (mode === "lib" && canonicalBuiltinModule(spec) !== null) continue;
|
||||
// Explicit attempts name the runtime package. Types-first resolution
|
||||
// may instead identify its @types twin, which has no runtime to admit.
|
||||
if (judged?.has(packageNameOfSpecifier(spec))) continue;
|
||||
const npm = resolveNpmImport(sf.fileName, spec);
|
||||
if (npm !== null && isNodeTypesPath(npm.typesFile)) continue;
|
||||
if (npm === null) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { expect, test } from "vitest";
|
||||
import { compileLibrary } from "@scriptc/compiler";
|
||||
|
||||
test.each(["sample", "@scope/sample"])("explicit library attempts use %s runtime code with installed declaration twins", async (name) => {
|
||||
const dir = await mkdtemp("/tmp/scriptc-library-static-types-");
|
||||
try {
|
||||
const pkg = join(dir, "node_modules", name);
|
||||
const types = join(dir, "node_modules/@types", name.startsWith("@") ? "scope__sample" : name);
|
||||
await mkdir(pkg, { recursive: true });
|
||||
await mkdir(types, { recursive: true });
|
||||
await writeFile(join(dir, "package.json"), '{"type":"module"}');
|
||||
await writeFile(join(pkg, "package.json"), JSON.stringify({ name, version: "1.0.0", type: "module", exports: { ".": "./index.js", "./value": "./index.js" } }));
|
||||
await writeFile(join(pkg, "index.js"), "export function calculate(value) { return value * 3 + 1; }\n");
|
||||
await writeFile(join(types, "package.json"), JSON.stringify({ name: `@types/${name.startsWith("@") ? "scope__sample" : name}`, version: "1.0.0", types: "index.d.ts" }));
|
||||
const declaration = "export function calculate(value: number): number;\n";
|
||||
await writeFile(join(types, "index.d.ts"), declaration);
|
||||
await writeFile(join(types, "value.d.ts"), declaration);
|
||||
await writeFile(join(dir, "entry.ts"), `import { calculate } from '${name}/value';\nexport function result(): number { return calculate(7); }\n`);
|
||||
const profile = {
|
||||
profile_format: 1, name: "static-types", entry: "entry.ts", emission: "llvm", npm_static: [name],
|
||||
abi: { prefix: "st_", init_symbol: "st_init", sink_register_symbol: "st_sink", collect_symbol: "st_collect" },
|
||||
exports: [{ export: "result", symbol: "st_result", params: [], returns: "f64" }],
|
||||
};
|
||||
const profilePath = join(dir, "profile.json");
|
||||
await writeFile(profilePath, JSON.stringify(profile));
|
||||
const result = await compileLibrary({ profilePath, outDir: join(dir, "out") });
|
||||
if (!result.ok) throw new Error(JSON.stringify(result.diagnostics));
|
||||
if (process.env["SCRIPTC_PORTABLE_ONLY"] !== "1") {
|
||||
const source = join(dir, "host.c"), binary = join(dir, "host");
|
||||
await writeFile(source, '#include <stdio.h>\nextern void st_init(void); extern double st_result(void);\nint main(void) { st_init(); printf("%.0f\\n", st_result()); }\n');
|
||||
execFileSync("clang", [source, result.archivePath, "-lm", "-o", binary]);
|
||||
const actual = execFileSync(binary, { encoding: "utf8" });
|
||||
const reference = execFileSync(process.execPath, ["--input-type=module", "-e", `import { result } from ${JSON.stringify(join(dir, "entry.ts"))}; console.log(result());`], { encoding: "utf8" });
|
||||
expect(actual).toBe(reference);
|
||||
expect(actual).toBe("22\n");
|
||||
}
|
||||
// Opting in to one runtime package does not admit its untyped dependency.
|
||||
const dependency = join(dir, "node_modules/unchecked-dependency");
|
||||
await mkdir(dependency, { recursive: true });
|
||||
await writeFile(join(dependency, "package.json"), '{"name":"unchecked-dependency","version":"1.0.0","type":"module","main":"index.js"}');
|
||||
await writeFile(join(dependency, "index.js"), 'export function adjust(value) { return value; }');
|
||||
await writeFile(join(pkg, "index.js"), "import { adjust } from 'unchecked-dependency'; export function calculate(value) { return adjust(value) * 3 + 1; }\n");
|
||||
const refused = await compileLibrary({ profilePath, outDir: join(dir, "refused") });
|
||||
expect(refused.ok).toBe(false);
|
||||
if (!refused.ok) expect(refused.diagnostics.some((d) => d.code === "SC4020" && d.message.includes("unchecked-dependency"))).toBe(true);
|
||||
expect(await readFile(join(types, "index.d.ts"), "utf8")).toBe(declaration);
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user