mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Merge base branch
This commit is contained in:
@@ -636,7 +636,13 @@ export function collectNpmImports(lowerer: Lowerer, parts: FileParts[]): void {
|
||||
// graph: resolveImport follows its node_modules link to the loaded
|
||||
// TypeScript entry. Its internal `.js` spellings have already been
|
||||
// resolved to `.ts` by that graph.
|
||||
if (!isRelativeSpecifier(spec) && resolveImport(lowerer.program, fp.sf, spec) !== null) {
|
||||
const projectDep = isRelativeSpecifier(spec)
|
||||
? null
|
||||
: resolveImport(lowerer.program, fp.sf, spec);
|
||||
if (projectDep !== null) {
|
||||
// A Node island package that tsconfig `paths` maps to its installed
|
||||
// directory resolves to its declarations: it loads in Node.
|
||||
if (projectDep.isDeclarationFile) collectIslandImport(lowerer, fp.sf, stmt, spec);
|
||||
continue;
|
||||
}
|
||||
const npm = resolveNpmImport(fp.sf.fileName, spec);
|
||||
|
||||
@@ -5754,6 +5754,16 @@ function preflight7(load: LoadResult): {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// A re-export from a Node island package that tsconfig `paths` maps
|
||||
// to its installed directory: its declarations are no program
|
||||
// module (the lowering reports the island boundary).
|
||||
if (
|
||||
projectReexport !== null &&
|
||||
projectReexport.isDeclarationFile &&
|
||||
!isRelativeSpecifier(fromSpec) &&
|
||||
isFrontendIsland(packageNameOfSpecifier(fromSpec))
|
||||
)
|
||||
continue;
|
||||
const reDep = projectReexport;
|
||||
// `export * as ns from "./m"` re-exports the module NAMESPACE
|
||||
// object under a name: importers' `x.ns.member` reads resolve
|
||||
@@ -5876,6 +5886,16 @@ function preflight7(load: LoadResult): {
|
||||
// Node's startup crash with Node's exact message; what keeps a compile
|
||||
// fence is unsupported builtin or types-only surface.
|
||||
if (isBare && npmStaticDep === null) {
|
||||
// A Node island package that tsconfig `paths` maps to its
|
||||
// installed directory resolves to its declarations here: it loads
|
||||
// in Node, typed by them, like an island package resolved through
|
||||
// node_modules (scope-B NI-4).
|
||||
if (
|
||||
projDep !== null &&
|
||||
projDep.isDeclarationFile &&
|
||||
isFrontendIsland(packageNameOfSpecifier(spec))
|
||||
)
|
||||
continue;
|
||||
if (projDep !== null && projDep.isDeclarationFile) {
|
||||
diags.push(
|
||||
unsupportedDiag(
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6839-node-island-path-mapped/main.ts"
|
||||
],
|
||||
"diags": [
|
||||
{
|
||||
"code": "SC1010",
|
||||
"message": "the 'pathkit' import (it resolves only to type declarations — there is no runtime implementation to compile) is not supported yet",
|
||||
"loc": {
|
||||
"file": "<repo>/tests/corpus/6839-node-island-path-mapped/main.ts",
|
||||
"start": 234,
|
||||
"end": 294
|
||||
},
|
||||
"milestone": "M4",
|
||||
"hint": "relative imports (./file, ../dir/file), package.json-mediated project imports (#alias via the imports field, self-name references via exports), statically compiled npm packages, and the built-in fs, fs/promises, path, os, url, crypto, zlib, child_process, net, http, tls, https, http2, dgram, dns, dns/promises, util, util/types, string_decoder, querystring, readline, events, stream, stream/promises, stream/consumers, stream/web, buffer, assert, assert/strict, worker_threads, cluster, tty, async_hooks, timers, timers/promises, diagnostics_channel, perf_hooks, module, v8, and console modules (bare or node:-prefixed) and node:test (node:-prefixed only, like in Node) are supported"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// @requires-libnode
|
||||
// tsconfig `paths` maps an island package to its installed directory (a
|
||||
// monorepo pinning one copy): its import resolves to the package's
|
||||
// declarations, which type the program, and the package loads in Node.
|
||||
import { Paths, heavier, route, type Route } from "pathkit";
|
||||
|
||||
interface Table<T extends Route> {
|
||||
title: string;
|
||||
rows: T[];
|
||||
}
|
||||
|
||||
function render<T extends Route>(table: Table<T>): string {
|
||||
return `${table.title}: ${table.rows.map((r) => r.label()).join(", ")}`;
|
||||
}
|
||||
|
||||
const routes: Route[] = [route("/a", 1), route("/b", 3), route("/c", 2)];
|
||||
const table: Table<Route> = { title: "routes", rows: routes };
|
||||
console.log(render(table));
|
||||
const top = heavier(routes[0]!, routes[1]!);
|
||||
console.log(top.rank, top.label(), top === routes[1], routes.length);
|
||||
console.log(Paths.join("api", "v1"));
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
export interface Route {
|
||||
readonly path: string;
|
||||
readonly weight: number;
|
||||
label(): string;
|
||||
}
|
||||
export type Weighted<T extends Route> = T & { readonly rank: number };
|
||||
export declare function route(path: string, weight: number): Route;
|
||||
export declare function heavier<T extends Route>(a: T, b: T): Weighted<T>;
|
||||
import * as Paths from "./paths.mjs";
|
||||
export { Paths };
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// The loader picks its part with a computed require() specifier, which
|
||||
// scriptc does not compile natively: the package runs in a Node island.
|
||||
import { createRequire } from "node:module";
|
||||
const load = createRequire(import.meta.url);
|
||||
const { Route } = ["./route.cjs"].map((p) => load(p))[0];
|
||||
export function route(path, weight) {
|
||||
return new Route(path, weight);
|
||||
}
|
||||
export function heavier(a, b) {
|
||||
return Object.assign(b.weight > a.weight ? b : a, { rank: 1 });
|
||||
}
|
||||
import * as Paths from "./paths.mjs";
|
||||
export { Paths };
|
||||
+1
@@ -0,0 +1 @@
|
||||
export declare function join(...parts: string[]): string;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export function join(...parts) {
|
||||
return "/" + parts.join("/");
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
class Route {
|
||||
constructor(path, weight) {
|
||||
this.path = path;
|
||||
this.weight = weight;
|
||||
}
|
||||
label() {
|
||||
return this.path + "@" + this.weight;
|
||||
}
|
||||
}
|
||||
exports.Route = Route;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "pathkit",
|
||||
"version": "2.0.0",
|
||||
"type": "module",
|
||||
"types": "./build/index.d.mts",
|
||||
"exports": { ".": { "import": "./build/index.mjs", "default": "./build/index.mjs" } }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"pathkit": ["./node_modules/pathkit"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Re-exports from an island package that tsconfig `paths` maps to its
|
||||
// installed directory: the type-only one is erased, the value one reports
|
||||
// the island boundary, and the package's declarations never compile as a
|
||||
// program module. The test builds it beside corpus 6839's pathkit package.
|
||||
export type { Route } from "pathkit";
|
||||
export { route } from "pathkit";
|
||||
console.log("loaded");
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 3300, "san": 30000 }
|
||||
@@ -354,3 +354,22 @@ test("recursive JSON types cross into Node", async () => {
|
||||
const ir = JSON.stringify(deserializeModule(readFileSync(outPath, "utf8")));
|
||||
expect(ir).toContain("nodeIsland.callSync");
|
||||
}, 300_000);
|
||||
|
||||
// Needs no libnode pack: the build fails before linking.
|
||||
test("re-exporting a path-mapped island package reports the island boundary", async () => {
|
||||
const dir = join(outDir, "path-mapped-reexport");
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
const corpus = join(repoRoot, "tests/corpus/6839-node-island-path-mapped");
|
||||
cpSync(join(corpus, "node_modules"), join(dir, "node_modules"), { recursive: true });
|
||||
copyFileSync(join(corpus, "tsconfig.json"), join(dir, "tsconfig.json"));
|
||||
copyFileSync(
|
||||
join(repoRoot, "tests/fixtures/node-island/path-mapped-reexport.ts"),
|
||||
join(dir, "main.ts"),
|
||||
);
|
||||
const result = await compile(join(dir, "main.ts"), { outPath: join(dir, "main"), outDir: dir });
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) return;
|
||||
expect(result.diagnostics.map((d) => `${d.code} ${d.loc.file.slice(dir.length + 1)}`)).toEqual([
|
||||
"SC8010 main.ts",
|
||||
]);
|
||||
}, 300_000);
|
||||
|
||||
Reference in New Issue
Block a user