carveout 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +21 -3
- package/NOTICE.md +2 -1
- package/README.md +248 -473
- package/dist/src/account.d.ts +37 -0
- package/dist/src/account.js +275 -0
- package/dist/src/api.d.ts +162 -0
- package/dist/src/api.js +134 -0
- package/dist/src/carve.d.ts +788 -0
- package/dist/src/carve.js +1721 -125
- package/dist/src/cli.d.ts +10 -0
- package/dist/src/cli.js +674 -47
- package/dist/src/command.d.ts +10 -0
- package/dist/src/core.d.ts +11 -0
- package/dist/src/cut.d.ts +78 -0
- package/dist/src/cut.js +25 -7
- package/dist/src/extract/compiler-config.d.ts +49 -0
- package/dist/src/extract/fixture.d.ts +103 -0
- package/dist/src/extract/hash.d.ts +6 -0
- package/dist/src/extract/package-registry.d.ts +47 -0
- package/dist/src/extract/package-types.d.ts +52 -0
- package/dist/src/extract/surface.d.ts +44 -0
- package/dist/src/extract/typescript.d.ts +180 -0
- package/dist/src/gate.d.ts +55 -0
- package/dist/src/gate.js +1 -0
- package/dist/src/graph.d.ts +142 -0
- package/dist/src/graph.js +4 -3
- package/dist/src/impact.d.ts +113 -0
- package/dist/src/imports.d.ts +67 -0
- package/dist/src/mergeback.d.ts +182 -0
- package/dist/src/mergeback.js +580 -35
- package/dist/src/platform.d.ts +69 -0
- package/dist/src/platform.js +184 -0
- package/dist/src/policy.d.ts +181 -0
- package/dist/src/policy.js +716 -230
- package/dist/src/project/build.d.ts +23 -0
- package/dist/src/project/catalog.d.ts +25 -0
- package/dist/src/project/project.d.ts +287 -0
- package/dist/src/project/project.js +479 -52
- package/dist/src/project/provenance.d.ts +73 -0
- package/dist/src/project/provenance.js +12 -0
- package/dist/src/project/runner.d.ts +19 -0
- package/dist/src/project/secrets.d.ts +64 -0
- package/dist/src/project/vitest-config.d.ts +70 -0
- package/dist/src/project/vitest-config.js +291 -0
- package/dist/src/pseudonyms/aliases.d.ts +107 -0
- package/dist/src/pseudonyms/aliases.js +394 -0
- package/dist/src/pseudonyms/draft.d.ts +64 -0
- package/dist/src/pseudonyms/draft.js +284 -0
- package/dist/src/pseudonyms/folder.d.ts +172 -0
- package/dist/src/pseudonyms/folder.js +527 -0
- package/dist/src/pseudonyms/index.d.ts +11 -0
- package/dist/src/pseudonyms/index.js +11 -0
- package/dist/src/pseudonyms/vocabulary.d.ts +113 -0
- package/dist/src/pseudonyms/vocabulary.js +522 -0
- package/dist/src/readiness.d.ts +71 -0
- package/dist/src/readiness.js +131 -0
- package/dist/src/receipt.d.ts +154 -0
- package/dist/src/receipt.js +296 -0
- package/dist/src/sandbox.d.ts +53 -0
- package/dist/src/sandbox.js +154 -0
- package/dist/src/slice/analysis.d.ts +75 -0
- package/dist/src/slice/externals.d.ts +51 -0
- package/dist/src/slice/graph.d.ts +69 -0
- package/dist/src/slice/manifest.d.ts +262 -0
- package/dist/src/slice/manifest.js +4 -3
- package/dist/src/slice/reach.d.ts +204 -0
- package/dist/src/slice/reach.js +11 -5
- package/dist/src/slice/report.d.ts +49 -0
- package/dist/src/slice/report.js +5 -2
- package/dist/src/specifiers.d.ts +48 -0
- package/dist/src/status.d.ts +46 -0
- package/dist/src/status.js +3 -2
- package/dist/src/test-integrity.d.ts +54 -0
- package/dist/src/test-integrity.js +812 -0
- package/package.json +24 -5
- package/skills/carveout/SKILL.md +518 -0
- package/skills/carveout-gate/SKILL.md +98 -0
- package/SKILL.md +0 -423
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs a tool to completion and returns its stdout. `env` is added to a
|
|
3
|
+
* minimal base, so nothing of the caller's environment but PATH and HOME
|
|
4
|
+
* passes through; an abort kills the whole process group.
|
|
5
|
+
*/
|
|
6
|
+
export declare function command(executable: string, args: readonly string[], options: {
|
|
7
|
+
signal: AbortSignal;
|
|
8
|
+
cwd?: string;
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The pure rules the extractor reads, from one place. */
|
|
2
|
+
export * from "./slice/analysis.js";
|
|
3
|
+
export * from "./slice/graph.js";
|
|
4
|
+
export * from "./slice/reach.js";
|
|
5
|
+
export * from "./slice/report.js";
|
|
6
|
+
export * from "./slice/externals.js";
|
|
7
|
+
export * from "./slice/manifest.js";
|
|
8
|
+
export * from "./project/provenance.js";
|
|
9
|
+
export * from "./project/secrets.js";
|
|
10
|
+
export * from "./project/build.js";
|
|
11
|
+
export * from "./project/project.js";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cut: from the checkout's import graph and its files, the files a
|
|
3
|
+
* slice needs and the shape of their boundary.
|
|
4
|
+
*
|
|
5
|
+
* Walks the graph with `reach`, reads the included files, extracts
|
|
6
|
+
* signatures for the cut modules and the public surface (TypeScript and
|
|
7
|
+
* JavaScript; other languages get names only), compiles the fixture with
|
|
8
|
+
* the stubs in place, and assembles the manifest and the contract. A pure
|
|
9
|
+
* function of the graph, the source bytes and the request, apart from the
|
|
10
|
+
* package types it may fetch.
|
|
11
|
+
*/
|
|
12
|
+
import type { BoundaryContract, ReachResult, SliceExternals, SliceGraph, SliceManifest, SliceParams } from "./core.js";
|
|
13
|
+
import { type PackageTypesFetcher } from "./extract/package-types.js";
|
|
14
|
+
/** Why a cut could not be made at all, as opposed to a blocker it reports. */
|
|
15
|
+
export declare class CutError extends Error {
|
|
16
|
+
}
|
|
17
|
+
/** Bytes of included source one slice may copy. */
|
|
18
|
+
export declare const INCLUDED_BYTES_MAX: number;
|
|
19
|
+
/** Everything a slice of one request is, before anything is written. */
|
|
20
|
+
export interface SliceAnalysis {
|
|
21
|
+
readonly manifest: SliceManifest;
|
|
22
|
+
readonly contract: BoundaryContract;
|
|
23
|
+
readonly contractJson: string;
|
|
24
|
+
readonly stubs: readonly {
|
|
25
|
+
path: string;
|
|
26
|
+
module: string;
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
/** The public surface's declarations and check; see `surfaceFiles`. */
|
|
30
|
+
readonly surface: readonly {
|
|
31
|
+
path: string;
|
|
32
|
+
text: string;
|
|
33
|
+
}[];
|
|
34
|
+
readonly ready: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the slice compiles with the source's noImplicitAny, so the
|
|
37
|
+
* folder can hold its code to it; false when the source has it off.
|
|
38
|
+
*/
|
|
39
|
+
readonly keepsImplicitAny: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The files where the stubs leave an implicit any, when the source holds
|
|
42
|
+
* its code to noImplicitAny: the folder relaxes it for them alone when
|
|
43
|
+
* nothing else in the slice imports them.
|
|
44
|
+
*/
|
|
45
|
+
readonly implicitAnyFiles: readonly string[];
|
|
46
|
+
/** The walk the slice was cut from. */
|
|
47
|
+
readonly walk: ReachResult;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Slices one request: walks the graph, reads the included files, extracts
|
|
51
|
+
* the boundary, compiles the fixture and assembles the manifest. A pure
|
|
52
|
+
* function of the graph, the source bytes and the parameters; the slice
|
|
53
|
+
* tool writes its result, and the scope agent checks its proposals with it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function analyseSlice(input: {
|
|
56
|
+
readonly sourceDir: string;
|
|
57
|
+
readonly graph: SliceGraph;
|
|
58
|
+
readonly params: SliceParams;
|
|
59
|
+
readonly source: {
|
|
60
|
+
readonly commitSha: string | null;
|
|
61
|
+
/** Whether an included file differs from that commit. */
|
|
62
|
+
readonly isDirty: (paths: readonly string[]) => boolean;
|
|
63
|
+
};
|
|
64
|
+
readonly signal: AbortSignal;
|
|
65
|
+
readonly log: (line: string) => void;
|
|
66
|
+
/**
|
|
67
|
+
* The externals of the included files when an index already has them
|
|
68
|
+
* all, as `detectExternals` would give them; null to read them here.
|
|
69
|
+
*/
|
|
70
|
+
readonly externalsOf?: (included: readonly string[]) => SliceExternals | null;
|
|
71
|
+
/**
|
|
72
|
+
* Fetches the types of the public packages the slice reads; without it,
|
|
73
|
+
* or when it fails, every package is untyped.
|
|
74
|
+
*/
|
|
75
|
+
readonly packageTypes?: PackageTypesFetcher;
|
|
76
|
+
/** See `extractTypeScriptBoundary`; true unless told otherwise. */
|
|
77
|
+
readonly widenLiterals?: boolean;
|
|
78
|
+
}): Promise<SliceAnalysis>;
|
package/dist/src/cut.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { constants, readFileSync } from "node:fs";
|
|
13
13
|
import { open, readFile } from "node:fs/promises";
|
|
14
14
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
15
|
-
import { BOUNDARY_CONTRACT_SCHEMA_VERSION, OPERATION_POLICY, SLICE_MANIFEST_SCHEMA_VERSION, SLICE_TOOL_VERSION, canonicalJson, detectExternals, isRegistryVersion, isSecretPath, pathMatcher, purityRules, reach, sliceReadiness, } from "./core.js";
|
|
15
|
+
import { BOUNDARY_CONTRACT_SCHEMA_VERSION, IMPLICIT_ANY_CODE, OPERATION_POLICY, SLICE_MANIFEST_SCHEMA_VERSION, SLICE_TOOL_VERSION, canonicalJson, detectExternals, isPackageName, isRegistryVersion, isSecretPath, pathMatcher, purityRules, reach, sliceReadiness, } from "./core.js";
|
|
16
16
|
import { catalogResolver } from "./project/catalog.js";
|
|
17
17
|
import { testRunnerResolver } from "./project/runner.js";
|
|
18
18
|
import { workspaceModuleResolver, workspacePackages, } from "./extract/compiler-config.js";
|
|
@@ -214,6 +214,7 @@ export async function analyseSlice(input) {
|
|
|
214
214
|
: pathMatcher(params.private);
|
|
215
215
|
const reached = reach(graph, params.entryPoints, params.budget, {
|
|
216
216
|
tests: params.tests,
|
|
217
|
+
...(params.setupFiles === undefined || params.setupFiles.length === 0 ? {} : { roots: params.setupFiles }),
|
|
217
218
|
packageOf: packageFolder(input.sourceDir),
|
|
218
219
|
runnerOf: testRunnerResolver(input.sourceDir, packageFolder(input.sourceDir)),
|
|
219
220
|
...(params.wholePackage === true ? { wholePackage: true } : {}),
|
|
@@ -293,7 +294,9 @@ export async function analyseSlice(input) {
|
|
|
293
294
|
continue;
|
|
294
295
|
if (dependency.status === "dynamic" && carriedTests.has(dependency.file))
|
|
295
296
|
continue;
|
|
296
|
-
|
|
297
|
+
// A path read is a repository path, never a package, however it is
|
|
298
|
+
// spelled (`apps/worker/python/extract.py` is no package named `apps`).
|
|
299
|
+
if (dependency.status !== "dynamic" && dependency.status !== "path" && isBare(dependency.specifier)) {
|
|
297
300
|
if (isNodeBuiltin(dependency.specifier))
|
|
298
301
|
continue;
|
|
299
302
|
if (scripted && workspaceModule(dependency.specifier) !== null)
|
|
@@ -314,7 +317,7 @@ export async function analyseSlice(input) {
|
|
|
314
317
|
detail: dependency.status === "dynamic"
|
|
315
318
|
? "A dynamic import of a computed name cannot be resolved."
|
|
316
319
|
: dependency.status === "path"
|
|
317
|
-
? `${dependency.specifier ?? ""} is read by path, and is a folder or a kind of file a slice does not copy.`
|
|
320
|
+
? `${dependency.specifier ?? ""} is read by path, and is a folder or a kind of file a slice does not copy: cut again with --copy ${dependency.specifier ?? "<path>"} to bring it.`
|
|
318
321
|
: `${dependency.specifier ?? ""} does not resolve to a repository file.`,
|
|
319
322
|
});
|
|
320
323
|
}
|
|
@@ -326,6 +329,15 @@ export async function analyseSlice(input) {
|
|
|
326
329
|
files.add(test);
|
|
327
330
|
packageFiles.set("vitest", files);
|
|
328
331
|
}
|
|
332
|
+
// So does a package whose tests run on Vitest when no test of the slice
|
|
333
|
+
// came along: the tests the contributor writes run on it, as the
|
|
334
|
+
// repository's do. Pinned as the entry points' package pins it.
|
|
335
|
+
if (params.tests && params.testRunner === "vitest") {
|
|
336
|
+
const files = packageFiles.get("vitest") ?? new Set();
|
|
337
|
+
for (const entry of walk.entries)
|
|
338
|
+
files.add(entry);
|
|
339
|
+
packageFiles.set("vitest", files);
|
|
340
|
+
}
|
|
329
341
|
// A file read by path must be there itself; a stub at its path is not it.
|
|
330
342
|
for (const edge of walk.cuts.outbound)
|
|
331
343
|
if (edge.relation === "reads" && !copied(edge.to))
|
|
@@ -343,6 +355,7 @@ export async function analyseSlice(input) {
|
|
|
343
355
|
let surface = [];
|
|
344
356
|
let configs = [];
|
|
345
357
|
let keepsImplicitAny = false;
|
|
358
|
+
let implicitAnyFiles = [];
|
|
346
359
|
let compilation = {
|
|
347
360
|
attempted: false,
|
|
348
361
|
ok: false,
|
|
@@ -397,7 +410,9 @@ export async function analyseSlice(input) {
|
|
|
397
410
|
...pinned,
|
|
398
411
|
...(registry === null ? {} : { registry }),
|
|
399
412
|
});
|
|
400
|
-
|
|
413
|
+
// A module that is no package (`virtual:build-info`) is mocked by its
|
|
414
|
+
// name and installed by none: no pin to look for.
|
|
415
|
+
if (pinned.version === null && isPackageName(name))
|
|
401
416
|
unpinned.push({
|
|
402
417
|
code: "missing_build_input",
|
|
403
418
|
file: from === "" ? null : from,
|
|
@@ -588,11 +603,13 @@ export async function analyseSlice(input) {
|
|
|
588
603
|
const sourceImplicitAny = options["noImplicitAny"] === true ||
|
|
589
604
|
(options["noImplicitAny"] === undefined && options["strict"] === true);
|
|
590
605
|
if (sourceImplicitAny && compilation.ok) {
|
|
591
|
-
//
|
|
592
|
-
const
|
|
606
|
+
// The implicit-any family (TS7xxx but the checks that share it).
|
|
607
|
+
const implicitCode = new RegExp(`^(TS)?${IMPLICIT_ANY_CODE}$`);
|
|
608
|
+
const implicit = compile(fixture, stubModules, false).diagnostics.filter(({ code }) => implicitCode.test(String(code)));
|
|
593
609
|
keepsImplicitAny = implicit.length === 0;
|
|
610
|
+
implicitAnyFiles = [...new Set(implicit.flatMap((d) => (d.file === undefined || d.file === null ? [] : [String(d.file)])))].sort();
|
|
594
611
|
if (!keepsImplicitAny)
|
|
595
|
-
input.log(`noImplicitAny
|
|
612
|
+
input.log(`The folder keeps noImplicitAny; its build does not count the ${implicit.length} implicit any(s) the stubs leave (${implicit
|
|
596
613
|
.slice(0, 3)
|
|
597
614
|
.map((d) => `${d.file ?? ""}:${d.line ?? ""} ${d.code}`)
|
|
598
615
|
.join(", ")}).`);
|
|
@@ -726,6 +743,7 @@ export async function analyseSlice(input) {
|
|
|
726
743
|
surface,
|
|
727
744
|
ready: readiness.ready,
|
|
728
745
|
keepsImplicitAny,
|
|
746
|
+
implicitAnyFiles,
|
|
729
747
|
walk,
|
|
730
748
|
};
|
|
731
749
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
export interface LoadedConfig {
|
|
3
|
+
readonly path: string | null;
|
|
4
|
+
readonly options: ts.CompilerOptions;
|
|
5
|
+
readonly rawOptions: Record<string, unknown> | undefined;
|
|
6
|
+
readonly errors: readonly string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The `packages:` list of a `pnpm-workspace.yaml`, block or flow style.
|
|
10
|
+
* Read by line: the file is a list of globs, and the worker has no YAML
|
|
11
|
+
* parser to spare.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pnpmWorkspacePatterns(text: string): string[];
|
|
14
|
+
/**
|
|
15
|
+
* The repository's own workspace packages, by name, to their directories.
|
|
16
|
+
* A shared base (`@scope/tsconfig/react.json`) is usually one of them, and a
|
|
17
|
+
* snapshot has no node_modules to find it in; and an import of one is the
|
|
18
|
+
* repository's own code, which the slice must never fetch by its name.
|
|
19
|
+
* Every directory a workspace glob names (`workspacePatterns`) that holds a
|
|
20
|
+
* named `package.json`, less those a `!` glob excludes.
|
|
21
|
+
*/
|
|
22
|
+
export declare function workspacePackages(root: string): Map<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* The nearest `tsconfig.json` above a file, read without following `extends`
|
|
25
|
+
* outside the root. A base in an installed package resolves when the
|
|
26
|
+
* package is one of the repository's own workspaces.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadConfig(root: string, file: string,
|
|
29
|
+
/**
|
|
30
|
+
* For a caller loading many files' configurations: the workspaces read
|
|
31
|
+
* once, and each tsconfig loaded once, for that run.
|
|
32
|
+
*/
|
|
33
|
+
shared?: {
|
|
34
|
+
readonly packages?: () => ReadonlyMap<string, string>;
|
|
35
|
+
readonly loaded?: Map<string, LoadedConfig>;
|
|
36
|
+
}): LoadedConfig;
|
|
37
|
+
/** The target an `exports` map gives a package subpath, or null. */
|
|
38
|
+
export declare function exportTarget(exports: unknown, subpath: string): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* The repository source a bare specifier of one of the repository's own
|
|
41
|
+
* packages stands for, by path, or null for any other specifier. The
|
|
42
|
+
* package's `exports` (or `types`, `module`, `main`) name what it ships,
|
|
43
|
+
* which is usually a build output the snapshot does not have; a target
|
|
44
|
+
* under the package's `outDir` is read back under its `rootDir`, as the
|
|
45
|
+
* build wrote it from there, and a script target that is not an output is
|
|
46
|
+
* the source itself. Memoized; the resolver never leaves the root.
|
|
47
|
+
*/
|
|
48
|
+
export declare function workspaceModuleResolver(root: string, packages: () => ReadonlyMap<string, string>): (specifier: string) => string | null;
|
|
49
|
+
export declare function nearestCompilerOptions(root: string, file: string): Record<string, unknown> | undefined;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The isolated compile that tells whether a slice closes.
|
|
3
|
+
*
|
|
4
|
+
* The included files and the generated stubs are type-checked together in
|
|
5
|
+
* memory, at the paths they hold in the repository, with the source's own
|
|
6
|
+
* compiler settings. Nothing else of the repository is visible, so an
|
|
7
|
+
* import the stubs do not cover is an error here rather than a surprise in
|
|
8
|
+
* the sandbox. Packages are declared as `any` for this check, since the
|
|
9
|
+
* worker installs nothing; the sandbox phase installs the pinned versions.
|
|
10
|
+
* Each name the slice reads of a package is declared as a type as well as
|
|
11
|
+
* a value, so a package's types used in the slice do not fail the check:
|
|
12
|
+
* only what the slice itself leaves unresolved does.
|
|
13
|
+
*/
|
|
14
|
+
import ts from "typescript";
|
|
15
|
+
import type { CompilationResult } from "../core.js";
|
|
16
|
+
export declare const DIAGNOSTICS_MAX = 100;
|
|
17
|
+
export declare const SHIM_PATH = "__slice__/packages.d.ts";
|
|
18
|
+
export interface FixtureInput {
|
|
19
|
+
readonly root: string;
|
|
20
|
+
/** Repository-relative path to text: included files, stubs and shims. */
|
|
21
|
+
readonly files: ReadonlyMap<string, string>;
|
|
22
|
+
readonly compilerOptions: ts.CompilerOptions;
|
|
23
|
+
readonly shimmedPackages: readonly string[];
|
|
24
|
+
/** Diagnostic codes not to report: see `UNTYPED_PACKAGE_CODES`. */
|
|
25
|
+
readonly ignoredCodes?: readonly number[];
|
|
26
|
+
/**
|
|
27
|
+
* Bare specifiers of the repository's own packages, to the path in
|
|
28
|
+
* `files` each stands for: no node_modules holds them here.
|
|
29
|
+
*/
|
|
30
|
+
readonly modules?: ReadonlyMap<string, string>;
|
|
31
|
+
/** Diagnostics reported at most; defaults to `DIAGNOSTICS_MAX`. */
|
|
32
|
+
readonly limit?: number;
|
|
33
|
+
/**
|
|
34
|
+
* A directory whose node_modules holds fetched package types, read from
|
|
35
|
+
* disk; a package that resolves there is typed, and its shim yields.
|
|
36
|
+
*/
|
|
37
|
+
readonly packageTypes?: string | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Settings for a compile whose packages are shimmed as `any`. A callback
|
|
41
|
+
* passed to an untyped function has no parameter types to take, which
|
|
42
|
+
* `noImplicitAny` reports though the package's own types would give them.
|
|
43
|
+
*/
|
|
44
|
+
export declare const UNTYPED_PACKAGE_OPTIONS: ts.CompilerOptions;
|
|
45
|
+
/**
|
|
46
|
+
* Errors only a shimmed package raises: TS2347, a type argument given to
|
|
47
|
+
* an untyped call (`useState<string>()`).
|
|
48
|
+
*/
|
|
49
|
+
export declare const UNTYPED_PACKAGE_CODES: readonly number[];
|
|
50
|
+
/**
|
|
51
|
+
* What the slice reads of one package specifier: the names it imports, and
|
|
52
|
+
* under each the members it reads off it (`React.ComponentProps`,
|
|
53
|
+
* `z.infer`), as deep as it reads them. A default import is `default`.
|
|
54
|
+
*/
|
|
55
|
+
export interface PackageUse {
|
|
56
|
+
readonly members: Map<string, PackageUse>;
|
|
57
|
+
}
|
|
58
|
+
export interface PackageUses {
|
|
59
|
+
/** By specifier, as imported. */
|
|
60
|
+
readonly modules: ReadonlyMap<string, PackageUse>;
|
|
61
|
+
/** By global name: `React`. */
|
|
62
|
+
readonly globals: ReadonlyMap<string, PackageUse>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* What the files read of each specifier that `shimmed` says is a package,
|
|
66
|
+
* and of the globals such a package declares (`PACKAGE_GLOBALS`),
|
|
67
|
+
* by the packages' names (`react` covers `react/jsx-runtime`). Read from the
|
|
68
|
+
* syntax alone: a name shadowed in a nested scope may be counted, which
|
|
69
|
+
* only declares one more `any`.
|
|
70
|
+
*/
|
|
71
|
+
export declare function packageUses(files: ReadonlyMap<string, string>, shimmed: (specifier: string) => boolean): PackageUses;
|
|
72
|
+
/**
|
|
73
|
+
* Ambient declarations that make the listed packages resolve as `any`, with
|
|
74
|
+
* Node's own modules and globals as the generated project declares them.
|
|
75
|
+
* A specifier in `uses` gets a module of its own that exports each name the
|
|
76
|
+
* slice reads as both a value and a type, so `import type { X }` and
|
|
77
|
+
* `React.ComponentProps<"button">` check as they would with the package's
|
|
78
|
+
* types; any other is untyped, which only works as a value. A global in
|
|
79
|
+
* `uses` is a namespace of the types read off it. A `typed` package, whose
|
|
80
|
+
* fetched types the compile reads, is declared by patterns that yield to
|
|
81
|
+
* them.
|
|
82
|
+
*/
|
|
83
|
+
export declare function packageShim(packages: readonly string[], { modules: uses, globals }?: PackageUses, typed?: (name: string) => boolean): string;
|
|
84
|
+
/**
|
|
85
|
+
* The part of `packageShim` that declares what the slice reads: a module
|
|
86
|
+
* for each specifier in `uses` and a namespace for each global. A sliced
|
|
87
|
+
* project declares its packages with these too, so it builds against what
|
|
88
|
+
* the slice was checked against; one it `installed` by a pattern, which
|
|
89
|
+
* yields to the package's own types.
|
|
90
|
+
*/
|
|
91
|
+
export declare function packageModules({ modules: uses, globals }: PackageUses, installed?: (specifier: string) => boolean): {
|
|
92
|
+
specifiers: string[];
|
|
93
|
+
text: string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* A compiler message with the checkout's path taken out. Messages name
|
|
97
|
+
* files by absolute path (TS6059 names the project's `rootDir`), and they
|
|
98
|
+
* are written into the folder's record, which may leave the machine: a
|
|
99
|
+
* path under the repository becomes repository-relative, and one under the
|
|
100
|
+
* fetched package types becomes `<package types>/...`.
|
|
101
|
+
*/
|
|
102
|
+
export declare function withoutRoots(message: string, root: string, types?: string | null): string;
|
|
103
|
+
export declare function compileFixture(input: FixtureInput): CompilationResult;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
/** Git's blob id for these bytes: what `git hash-object` would print. */
|
|
3
|
+
export declare function gitBlobId(bytes: Uint8Array): string;
|
|
4
|
+
export declare function sha256(bytes: Uint8Array | string): string;
|
|
5
|
+
/** The two modes Git records for a regular file. */
|
|
6
|
+
export declare function gitFileMode(stats: Pick<Stats, "mode">): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a repository says the packages it imports are fetched from: the
|
|
3
|
+
* URLs its lockfiles resolved them at, and the registries its npm, Yarn
|
|
4
|
+
* and pnpm configuration send their scopes to. These are the facts
|
|
5
|
+
* `resolveScope` in core decides by: a package is installed by its name
|
|
6
|
+
* only when they show it comes from the public registry, since a private
|
|
7
|
+
* registry's package name is anyone's to publish on the public one.
|
|
8
|
+
*
|
|
9
|
+
* Only the repository's own files are read, never a registry: the root
|
|
10
|
+
* `package-lock.json` or `npm-shrinkwrap.json`, `yarn.lock` (classic and
|
|
11
|
+
* Berry), `pnpm-lock.yaml`, `.npmrc`, `.yarnrc`, `.yarnrc.yml`, and the
|
|
12
|
+
* `publishConfig` of the root and workspace manifests. Lockfiles in YAML
|
|
13
|
+
* are read by line, as far as their entries' names and resolutions.
|
|
14
|
+
*/
|
|
15
|
+
import type { PackageRegistry } from "../core.js";
|
|
16
|
+
/** Whether a registry or tarball URL is the public registry's. */
|
|
17
|
+
export declare function isPublicRegistry(url: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* What the repository at `root` says of each package's registry, as a
|
|
20
|
+
* function of its name: `private` when any of its configuration sends the
|
|
21
|
+
* package's scope, or every package, to another registry, or a lockfile
|
|
22
|
+
* resolved it elsewhere; `public` when a lockfile resolved it from the
|
|
23
|
+
* public registry, or pinned it from a registry that the configuration
|
|
24
|
+
* its own package manager reads names as the public one; null when
|
|
25
|
+
* nothing says.
|
|
26
|
+
*
|
|
27
|
+
* One file naming another registry is enough, whichever manager reads it:
|
|
28
|
+
* Berry ignores `.npmrc` and npm ignores `.yarnrc.yml`, so a public
|
|
29
|
+
* registry in one never outweighs a private one in another.
|
|
30
|
+
*/
|
|
31
|
+
export declare function packageRegistries(root: string): (name: string) => PackageRegistry | null;
|
|
32
|
+
/**
|
|
33
|
+
* The integrity each `pnpm-lock.yaml` entry with no tarball URL records,
|
|
34
|
+
* by package name and version. Such an entry came from whatever registry
|
|
35
|
+
* the developer's configuration named, so on its own it shows nothing;
|
|
36
|
+
* its `sha512-` integrity, equal to the one the public registry publishes
|
|
37
|
+
* for the same version, shows it is the public package byte for byte (see
|
|
38
|
+
* `publicByIntegrity`).
|
|
39
|
+
*/
|
|
40
|
+
export declare function lockIntegrities(root: string): Map<string, Map<string, string>>;
|
|
41
|
+
/**
|
|
42
|
+
* Whether every version of `name` the lockfile records has the integrity
|
|
43
|
+
* the public registry publishes for it: then the repository installs the
|
|
44
|
+
* public package, whatever registry served it. One mismatch, or a version
|
|
45
|
+
* the registry does not have, and it shows nothing.
|
|
46
|
+
*/
|
|
47
|
+
export declare function publicByIntegrity(name: string, versions: ReadonlyMap<string, string>, signal: AbortSignal): Promise<boolean>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The declared types of the public packages a slice reads, for the
|
|
3
|
+
* extractor and the fixture compile.
|
|
4
|
+
*
|
|
5
|
+
* A snapshot has no node_modules, so without these every package is
|
|
6
|
+
* untyped: a value whose type the source infers from one (a zod schema)
|
|
7
|
+
* becomes `any` in its stub, and the sandbox, which installs the package,
|
|
8
|
+
* no longer compiles what the source does. Only packages the repository
|
|
9
|
+
* shows come from the public registry are fetched, at the version its
|
|
10
|
+
* lockfile installed (or the range its package.json pins), with install
|
|
11
|
+
* scripts off; the compiler reads their declaration files and nothing of
|
|
12
|
+
* them runs.
|
|
13
|
+
*/
|
|
14
|
+
export interface PackageTypesRequest {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** What npm installs it by: a version, a range or an `npm:` alias. */
|
|
17
|
+
readonly spec: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PackageTypes {
|
|
20
|
+
/** A directory whose `node_modules` holds the packages. */
|
|
21
|
+
readonly directory: string;
|
|
22
|
+
/** What was installed under each requested name. */
|
|
23
|
+
readonly packages: readonly {
|
|
24
|
+
name: string;
|
|
25
|
+
version: string;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
28
|
+
/** The types, or why there are none: the slice then goes on untyped. */
|
|
29
|
+
export type PackageTypesFetcher = (requests: readonly PackageTypesRequest[], signal: AbortSignal) => Promise<PackageTypes | {
|
|
30
|
+
readonly failed: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare const PUBLIC_REGISTRY = "https://registry.npmjs.org/";
|
|
33
|
+
/** How long one fetch may take before the slice goes on without types. */
|
|
34
|
+
export declare const PACKAGE_TYPES_TIMEOUT_MS = 120000;
|
|
35
|
+
/**
|
|
36
|
+
* What the repository's npm lockfile installed under `name` at its root,
|
|
37
|
+
* as npm would install it again: `1.2.3`, or `npm:other@1.2.3` for an
|
|
38
|
+
* alias. Null when no npm lockfile says.
|
|
39
|
+
*/
|
|
40
|
+
export declare function lockedSpec(root: string, name: string): string | null;
|
|
41
|
+
/** `@types/x` for `x`, `@types/scope__x` for `@scope/x`. */
|
|
42
|
+
export declare function typesPackageOf(name: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Fetches with npm into `root`, one directory per set of requests, kept
|
|
45
|
+
* for the next slice that reads the same packages. Nothing of the worker's
|
|
46
|
+
* npm configuration applies: the registry is the public one, the user
|
|
47
|
+
* config an empty file, and no credential is in the environment.
|
|
48
|
+
*/
|
|
49
|
+
export declare function npmPackageTypes(options: {
|
|
50
|
+
readonly root: string;
|
|
51
|
+
readonly timeoutMs?: number;
|
|
52
|
+
}): PackageTypesFetcher;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public surface's types, held where the slice can check them.
|
|
3
|
+
*
|
|
4
|
+
* The interface test asserts that each export outside code imports is
|
|
5
|
+
* still there; it cannot see a changed signature, and a slice whose
|
|
6
|
+
* importers live in other packages passes a change that breaks them. So
|
|
7
|
+
* the cut writes, under `carveout/surface/`, the declarations the slice's
|
|
8
|
+
* own modules had when it was cut (a mirror of their paths, each import
|
|
9
|
+
* pointing at the mirror, the folder's stub or the package it named), and
|
|
10
|
+
* `check.ts`, which holds every export outside code uses to that type:
|
|
11
|
+
*
|
|
12
|
+
* - a value must still be assignable to its old type, so every call and
|
|
13
|
+
* read outside code makes still compiles;
|
|
14
|
+
* - an interface or type alias must be assignable both ways, since
|
|
15
|
+
* outside code both reads and builds it.
|
|
16
|
+
*
|
|
17
|
+
* `npm test` type-checks `check.ts` with the folder's own compiler (see
|
|
18
|
+
* `carveout/test.js`) and reports each error as the export whose type
|
|
19
|
+
* changed. Enums, and classes with private or protected members, are
|
|
20
|
+
* nominal: an identical copy of one is not assignable to the original, so
|
|
21
|
+
* they are not checked. A check that fails before any edit (a type that
|
|
22
|
+
* holds such a class) fails at the slice's baseline and is not counted.
|
|
23
|
+
*/
|
|
24
|
+
import ts from "typescript";
|
|
25
|
+
export interface SurfaceInput {
|
|
26
|
+
readonly program: ts.Program;
|
|
27
|
+
/** Included modules outside code imports, with the names it takes. */
|
|
28
|
+
readonly surface: ReadonlyMap<string, ReadonlySet<string> | "*">;
|
|
29
|
+
readonly included: ReadonlySet<string>;
|
|
30
|
+
/** Cut modules whose stub the folder holds at their declaration path. */
|
|
31
|
+
readonly stubbed: ReadonlySet<string>;
|
|
32
|
+
/** A specifier's module in the repository, or null for a package. */
|
|
33
|
+
readonly resolve: (specifier: string, from: string) => string | null;
|
|
34
|
+
readonly abs: (path: string) => string;
|
|
35
|
+
}
|
|
36
|
+
export interface SurfaceFile {
|
|
37
|
+
readonly path: string;
|
|
38
|
+
readonly text: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The mirror and the check for a slice's public surface; empty when there
|
|
42
|
+
* is nothing to check or a module's declarations cannot be emitted.
|
|
43
|
+
*/
|
|
44
|
+
export declare function surfaceFiles(input: SurfaceInput): SurfaceFile[];
|