@absolutejs/absolute 0.20.0-beta.71 → 0.20.0-beta.73
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/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/{add-jy0b6kdf.js → add-kt4qx8sh.js} +5 -5
- package/dist/cli/{compile-4mkh7bst.js → compile-bra4g79g.js} +1 -1
- package/dist/cli/{dev-rbvkxpj7.js → dev-p7s1ej32.js} +224 -1
- package/dist/cli/{doctor-gzhf7nr9.js → doctor-a8afwvf9.js} +1 -1
- package/dist/cli/{generate-j3y5qt2d.js → generate-zy3d9bce.js} +4 -4
- package/dist/cli/{htmx-nmpbk3wn.js → htmx-63kt9qj1.js} +2 -2
- package/dist/cli/{index-jg7m4zpy.js → index-1wgc9e0v.js} +1 -1
- package/dist/cli/{index-6sv0rf9w.js → index-6tnpbjc7.js} +4 -0
- package/dist/cli/{index-2j5d7vt5.js → index-mxmg15c5.js} +2 -2
- package/dist/cli/{index-ybq18wst.js → index-pa3hmzj7.js} +1 -1
- package/dist/cli/index-v1k46q3x.js +11 -0
- package/dist/cli/{index-g5rwcntp.js → index-wspj2mq3.js} +2 -2
- package/dist/cli/index.js +11 -11
- package/dist/cli/{mobile-g6xn9270.js → mobile-a0myydjp.js} +1 -1
- package/dist/cli/{remove-paz2qmx4.js → remove-pwfv4wsd.js} +4 -4
- package/dist/cli/{start-cwy0b5z2.js → start-3nqytx1c.js} +1 -1
- package/dist/cli/typecheck-1yj1wfhw.js +1134 -0
- package/dist/dev/importCostPreload.js +206 -0
- package/dist/dev/importCostReport.js +730 -0
- package/dist/dev/serverBootstrap.js +60 -11
- package/dist/index.js +19 -1
- package/dist/index.js.map +4 -4
- package/dist/src/dev/earlyListener.d.ts +32 -0
- package/dist/src/dev/importCost/deferability.d.ts +11 -0
- package/dist/src/dev/importCost/dominators.d.ts +37 -0
- package/dist/src/dev/importCost/format.d.ts +29 -0
- package/dist/src/dev/importCost/instrument.d.ts +5 -0
- package/dist/src/dev/importCost/moduleGraph.d.ts +13 -0
- package/dist/src/dev/importCost/recorder.d.ts +37 -0
- package/dist/src/dev/importCost/report.d.ts +3 -0
- package/dist/src/dev/importCost/scope.d.ts +10 -0
- package/dist/src/dev/importCost/selfTimes.d.ts +17 -0
- package/dist/src/dev/importCostPreload.d.ts +1 -0
- package/dist/src/dev/importCostReport.d.ts +1 -0
- package/dist/types/globals.d.ts +15 -0
- package/package.json +7 -7
- package/dist/cli/index-fr9x2c4m.js +0 -5
- package/dist/cli/typecheck-j0ytazsm.js +0 -302
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { Server } from 'bun';
|
|
2
2
|
export declare const BOOT_STATUS_HEADER = "X-Absolute-Boot";
|
|
3
|
+
/** Written by the child on fd 3 the instant the real server is bound, so
|
|
4
|
+
* the CLI can close its placeholder. A dedicated pipe rather than stdout:
|
|
5
|
+
* the marker must never reach the developer's terminal or the instance
|
|
6
|
+
* log, and stdout chunks coalesce in ways that make stripping a line out
|
|
7
|
+
* of the forwarded stream unreliable. */
|
|
8
|
+
export declare const PARENT_HANDOFF_MARKER = "absolute:listening";
|
|
9
|
+
/** Set on the child by the CLI when the CLI itself holds the placeholder
|
|
10
|
+
* on the dev port. The child then skips its own placeholder and binds the
|
|
11
|
+
* real server with `reusePort` so the two can overlap. */
|
|
12
|
+
export declare const PARENT_LISTENER_ENV = "ABSOLUTE_PARENT_LISTENER";
|
|
3
13
|
export type EarlyListenerOptions = {
|
|
4
14
|
host: string;
|
|
5
15
|
port: number;
|
|
@@ -7,6 +17,10 @@ export type EarlyListenerOptions = {
|
|
|
7
17
|
cert: string;
|
|
8
18
|
key: string;
|
|
9
19
|
} | null;
|
|
20
|
+
/** Bind with `SO_REUSEPORT` so the child's real server can bind the
|
|
21
|
+
* same port before this placeholder goes away. Only the CLI parent
|
|
22
|
+
* sets it. */
|
|
23
|
+
reusePort?: boolean;
|
|
10
24
|
};
|
|
11
25
|
export type EarlyListener = {
|
|
12
26
|
/** The placeholder server, or null while a bind retry is pending or
|
|
@@ -32,7 +46,25 @@ export declare const buildingResponse: (request: Request, startedAt: number) =>
|
|
|
32
46
|
* `networking` plugin: any `Bun.serve` that targets the placeholder's
|
|
33
47
|
* port first releases it. Installed once per process by the bootstrap. */
|
|
34
48
|
export declare const installEarlyListenerServeGuard: (port: number) => void;
|
|
49
|
+
/** Defensive hand-off for the parent-owned path: any `Bun.serve` that
|
|
50
|
+
* targets the dev port is bound with `reusePort` (so it can come up while
|
|
51
|
+
* the CLI's placeholder is still listening) and reports the bind back to
|
|
52
|
+
* the CLI. The `networking` plugin does both explicitly; this guard is
|
|
53
|
+
* what makes entries that call `.listen()` without it work too, mirroring
|
|
54
|
+
* `installEarlyListenerServeGuard` on the child-owned path. */
|
|
55
|
+
export declare const installParentPortHandoffGuard: (port: number) => void;
|
|
56
|
+
/** `SO_REUSEPORT` is POSIX-only; Windows has no equivalent that lets two
|
|
57
|
+
* processes share a listening socket, so the parent-side placeholder is
|
|
58
|
+
* not offered there and the child's own placeholder stays in charge. */
|
|
59
|
+
export declare const parentListenerSupported: () => boolean;
|
|
60
|
+
/** True when the CLI parent, not this process, owns the placeholder. */
|
|
61
|
+
export declare const parentOwnsDevPort: () => boolean;
|
|
35
62
|
export declare const releaseEarlyListener: () => boolean;
|
|
63
|
+
/** Tell the CLI parent that the real server is bound, so it can close its
|
|
64
|
+
* placeholder. Idempotent, and silent when there is no hand-off pipe (the
|
|
65
|
+
* bootstrap run directly, or a parent that never bound) — the CLI also
|
|
66
|
+
* releases on the ready banner, so a missed signal is not fatal. */
|
|
67
|
+
export declare const signalParentPortHandoff: (port: number) => void;
|
|
36
68
|
/** Bind the placeholder. A failed bind (the previous child's socket is
|
|
37
69
|
* still draining, or the port genuinely conflicts) is retried in the
|
|
38
70
|
* background for a few seconds and then given up on silently — the real
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type EntryImport = {
|
|
2
|
+
line: number;
|
|
3
|
+
/** Why it is not deferrable, when it is not. */
|
|
4
|
+
reason: string | null;
|
|
5
|
+
specifier: string;
|
|
6
|
+
verdict: EntryImportVerdict;
|
|
7
|
+
};
|
|
8
|
+
export type EntryImportVerdict = 'deferrable' | 'side-effect import' | 'type-only' | 'used at module scope';
|
|
9
|
+
/** One verdict per top-level import (and re-export) of the entry, in source
|
|
10
|
+
* order. Pure: give it the entry's text, get the verdicts back. */
|
|
11
|
+
export declare const analyzeEntryImports: (sourceText: string, fileName: string) => EntryImport[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type ImportCandidate = {
|
|
2
|
+
specifier: string;
|
|
3
|
+
/** Module index the specifier resolved to, or `-1` if it never loaded. */
|
|
4
|
+
target: number;
|
|
5
|
+
};
|
|
6
|
+
export type ImportSaving = {
|
|
7
|
+
/** Modules that become unreachable when this edge is removed. */
|
|
8
|
+
count: number;
|
|
9
|
+
savingMs: number;
|
|
10
|
+
specifier: string;
|
|
11
|
+
target: number;
|
|
12
|
+
};
|
|
13
|
+
export type ImportSavings = {
|
|
14
|
+
attributedCount: number;
|
|
15
|
+
attributedMs: number;
|
|
16
|
+
candidates: ImportSaving[];
|
|
17
|
+
reachableCount: number;
|
|
18
|
+
sharedBaseCount: number;
|
|
19
|
+
sharedBaseMs: number;
|
|
20
|
+
totalMs: number;
|
|
21
|
+
};
|
|
22
|
+
export type SavingsInput = {
|
|
23
|
+
candidates: readonly ImportCandidate[];
|
|
24
|
+
edges: ReadonlyArray<readonly number[]>;
|
|
25
|
+
/** Module whose top-level imports the candidates are — the user's entry. */
|
|
26
|
+
entry: number;
|
|
27
|
+
/** Process root (the dev bootstrap), where reachability starts. */
|
|
28
|
+
root: number;
|
|
29
|
+
selfMs: readonly number[];
|
|
30
|
+
};
|
|
31
|
+
/** The modules dominated by the edge `from -> to`: reachable from the root
|
|
32
|
+
* today, unreachable once that edge is gone. */
|
|
33
|
+
export declare const dominatedByEdge: (edges: ReadonlyArray<readonly number[]>, root: number, fromIndex: number, toIndex: number, reachable?: Uint8Array) => number[];
|
|
34
|
+
/** Modules reachable from `root`, optionally with the single edge
|
|
35
|
+
* `skipFrom -> skipTo` removed. */
|
|
36
|
+
export declare const reachableFrom: (edges: ReadonlyArray<readonly number[]>, root: number, skipFrom?: number, skipTo?: number) => Uint8Array<ArrayBuffer>;
|
|
37
|
+
export declare const summarizeImportSavings: (input: SavingsInput) => ImportSavings;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type ImportCostReport = {
|
|
2
|
+
entryLabel: string;
|
|
3
|
+
/** Modules whose exit call never ran; their self time is approximate. */
|
|
4
|
+
incomplete: number;
|
|
5
|
+
/** Out-of-order exits, i.e. top-level `await` interleaving. */
|
|
6
|
+
interleaved: number;
|
|
7
|
+
/** The entry's own body: everything it does after its imports, including
|
|
8
|
+
* the `prepare()` the framework's boot build runs inside. */
|
|
9
|
+
entryBodyMs: number;
|
|
10
|
+
moduleCount: number;
|
|
11
|
+
/** Time that ran outside every instrumented module: CommonJS
|
|
12
|
+
* dependencies, native modules, and the dev server's concurrent boot
|
|
13
|
+
* build. Deliberately not credited to any import. */
|
|
14
|
+
outsideMs: number;
|
|
15
|
+
/** The recorder's own read-and-rewrite time, excluded from every figure
|
|
16
|
+
* above and reported so the numbers can be sanity-checked. */
|
|
17
|
+
overheadMs: number;
|
|
18
|
+
rows: readonly ImportCostRow[];
|
|
19
|
+
sharedBaseCount: number;
|
|
20
|
+
sharedBaseMs: number;
|
|
21
|
+
totalMs: number;
|
|
22
|
+
};
|
|
23
|
+
export type ImportCostRow = {
|
|
24
|
+
count: number;
|
|
25
|
+
savingMs: number;
|
|
26
|
+
specifier: string;
|
|
27
|
+
verdict: string;
|
|
28
|
+
};
|
|
29
|
+
export declare const formatImportCostReport: (report: ImportCostReport) => string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type ImportCostLoader = 'js' | 'jsx' | 'ts' | 'tsx';
|
|
2
|
+
/** The Bun loader to hand back with the rewritten contents. Bun infers
|
|
3
|
+
* CommonJS versus ESM from the source, so `.cjs`/`.mjs` both take `js`. */
|
|
4
|
+
export declare const importCostLoader: (path: string) => "ts" | "js" | "tsx" | "jsx";
|
|
5
|
+
export declare const instrumentModuleSource: (source: string, moduleIndex: number) => string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type StaticGraph = {
|
|
2
|
+
edges: number[][];
|
|
3
|
+
/** Modules that import a file this diagnostic could not instrument — a
|
|
4
|
+
* CommonJS dependency. Bun loads and parses those inside the gap that
|
|
5
|
+
* would otherwise be charged to this module, so its parse time is not
|
|
6
|
+
* its own and must not be credited to an import. */
|
|
7
|
+
hasUninstrumentedImports: Uint8Array;
|
|
8
|
+
};
|
|
9
|
+
/** Adjacency built from re-scanning every loaded module's source. */
|
|
10
|
+
export declare const buildStaticEdges: (moduleIds: readonly string[]) => StaticGraph;
|
|
11
|
+
/** One inferred edge per orphan module: the innermost module whose body was
|
|
12
|
+
* still open when the orphan started evaluating is the one that asked for it. */
|
|
13
|
+
export declare const inferDynamicEdges: (edges: ReadonlyArray<readonly number[]>, events: readonly number[], root: number) => [number, number][];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type ImportCostRecorder = {
|
|
2
|
+
/** The `.absolutejs-hmr-*` copy of the user's entry that the dev
|
|
3
|
+
* bootstrap actually imports. Set by the bootstrap. */
|
|
4
|
+
entryModule: string | null;
|
|
5
|
+
/** The developer's own server entry path, for reporting. */
|
|
6
|
+
entryOriginalModule: string | null;
|
|
7
|
+
/** Flat pairs of `code, timeMs`, where `code = moduleIndex * 4 + kind`
|
|
8
|
+
* and kind is one of `IMPORT_COST_EVENT`. Numbers, not objects: two
|
|
9
|
+
* array pushes per boundary is cheap enough to leave the timings alone. */
|
|
10
|
+
events: number[];
|
|
11
|
+
moduleIds: string[];
|
|
12
|
+
moduleIndexes: Map<string, number>;
|
|
13
|
+
/** Cleared before the report runs so the report's own imports (and any
|
|
14
|
+
* later `--hot` re-evaluation) never land in the measurement. */
|
|
15
|
+
recording: boolean;
|
|
16
|
+
/** The first module of the process — the dev bootstrap. Reachability is
|
|
17
|
+
* rooted here rather than at the user's entry so that anything the
|
|
18
|
+
* bootstrap loads on its own (the framework runtime, via the boot
|
|
19
|
+
* prebuild) is correctly seen as shared rather than as the entry's. */
|
|
20
|
+
rootModule: string | null;
|
|
21
|
+
};
|
|
22
|
+
type ImportCostEventKinds = {
|
|
23
|
+
enter: number;
|
|
24
|
+
exit: number;
|
|
25
|
+
loadEnd: number;
|
|
26
|
+
loadStart: number;
|
|
27
|
+
};
|
|
28
|
+
export declare const IMPORT_COST_ENV = "ABSOLUTE_DEV_IMPORT_COST";
|
|
29
|
+
/** Event kinds packed into the low bits of each event code.
|
|
30
|
+
* `loadStart`/`loadEnd` bracket the plugin's own work so that reading and
|
|
31
|
+
* rewriting a module is charged to nobody, and the gap that follows — Bun
|
|
32
|
+
* parsing that module and resolving its imports — is charged to it. */
|
|
33
|
+
export declare const IMPORT_COST_EVENT: ImportCostEventKinds;
|
|
34
|
+
export declare const IMPORT_COST_EVENT_KINDS = 4;
|
|
35
|
+
export declare const ensureImportCostRecorder: () => ImportCostRecorder;
|
|
36
|
+
export declare const importCostEnabled: () => boolean;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type EsmRegions = {
|
|
2
|
+
/** Directories inside an ESM region that switch back to CommonJS. */
|
|
3
|
+
cjsPrefixes: string[];
|
|
4
|
+
/** Directories whose subtree is ESM. */
|
|
5
|
+
esmPrefixes: string[];
|
|
6
|
+
};
|
|
7
|
+
/** The ESM regions of an app's installed dependencies. */
|
|
8
|
+
export declare const esmRegions: (root: string) => EsmRegions;
|
|
9
|
+
/** The `onLoad` filter: app sources, `.mjs`, and files in ESM regions. */
|
|
10
|
+
export declare const instrumentableFilter: (regions: EsmRegions) => RegExp;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type SelfTimes = {
|
|
2
|
+
evalMs: number[];
|
|
3
|
+
/** Timestamp of each module's first enter, or `NaN` if it never ran. */
|
|
4
|
+
firstEnterMs: number[];
|
|
5
|
+
incomplete: number[];
|
|
6
|
+
interleaved: number;
|
|
7
|
+
/** Time spent inside the plugin's own read-and-rewrite, charged to nobody. */
|
|
8
|
+
overheadMs: number;
|
|
9
|
+
/** Time with no module open and nothing being parsed: uninstrumented
|
|
10
|
+
* CommonJS dependencies, native modules, and the dev server's own
|
|
11
|
+
* concurrent work. Deliberately credited to no import. */
|
|
12
|
+
unownedMs: number;
|
|
13
|
+
parseMs: number[];
|
|
14
|
+
/** Modules' own parse plus evaluation, exclusive of their children. */
|
|
15
|
+
selfMs: number[];
|
|
16
|
+
};
|
|
17
|
+
export declare const computeSelfTimes: (events: readonly number[], moduleCount: number) => SelfTimes;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { reportImportCost } from './importCost/report';
|
package/dist/types/globals.d.ts
CHANGED
|
@@ -9,6 +9,16 @@ declare global {
|
|
|
9
9
|
* watchers and can observe entry/configuration edits. */
|
|
10
10
|
var __absoluteEntryWatcherReady: boolean | undefined;
|
|
11
11
|
var __absoluteEntryCopies: Set<string> | undefined;
|
|
12
|
+
/** Import-cost diagnostic (`ABSOLUTE_DEV_IMPORT_COST=1`). The recorder is
|
|
13
|
+
* filled by the preloaded `onLoad` plugin, read by the report, and
|
|
14
|
+
* written to by the dev bootstrap — three separate bundles, so it lives
|
|
15
|
+
* here rather than in module state. Stays unset unless the flag is on. */
|
|
16
|
+
var __absoluteImportCost:
|
|
17
|
+
| import('../src/dev/importCost/recorder').ImportCostRecorder
|
|
18
|
+
| undefined;
|
|
19
|
+
var __absoluteImportCostEnter: ((moduleIndex: number) => void) | undefined;
|
|
20
|
+
var __absoluteImportCostExit: ((moduleIndex: number) => void) | undefined;
|
|
21
|
+
var __absoluteImportCostReport: (() => Promise<void>) | undefined;
|
|
12
22
|
var __absoluteEntryBootstrapSequence: number | undefined;
|
|
13
23
|
var __absoluteEntryCleanupRegistered: boolean | undefined;
|
|
14
24
|
var __absoluteEntryWatcherStarted: boolean | undefined;
|
|
@@ -53,6 +63,11 @@ declare global {
|
|
|
53
63
|
| import('../src/dev/earlyListener').EarlyListener
|
|
54
64
|
| undefined;
|
|
55
65
|
var __absoluteEarlyListenerServeGuard: boolean | undefined;
|
|
66
|
+
/** Parent-owned hand-off (see `src/dev/earlyListener.ts`): the CLI holds
|
|
67
|
+
* the "Building…" placeholder, so this child skips its own, binds the
|
|
68
|
+
* real server with `reusePort` and reports the bind back exactly once. */
|
|
69
|
+
var __absoluteParentPortHandoffGuard: boolean | undefined;
|
|
70
|
+
var __absoluteParentHandoffSignalled: boolean | undefined;
|
|
56
71
|
/** Label of the boot step currently running (shown on the placeholder
|
|
57
72
|
* page). See `src/dev/bootLifecycle.ts`. */
|
|
58
73
|
var __absoluteBootPhase: string | undefined;
|
package/package.json
CHANGED
|
@@ -300,12 +300,12 @@
|
|
|
300
300
|
"main": "./dist/index.js",
|
|
301
301
|
"name": "@absolutejs/absolute",
|
|
302
302
|
"optionalDependencies": {
|
|
303
|
-
"@absolutejs/native-darwin-arm64": "0.20.0-beta.
|
|
304
|
-
"@absolutejs/native-darwin-x64": "0.20.0-beta.
|
|
305
|
-
"@absolutejs/native-linux-arm64": "0.20.0-beta.
|
|
306
|
-
"@absolutejs/native-linux-x64": "0.20.0-beta.
|
|
307
|
-
"@absolutejs/native-windows-arm64": "0.20.0-beta.
|
|
308
|
-
"@absolutejs/native-windows-x64": "0.20.0-beta.
|
|
303
|
+
"@absolutejs/native-darwin-arm64": "0.20.0-beta.73",
|
|
304
|
+
"@absolutejs/native-darwin-x64": "0.20.0-beta.73",
|
|
305
|
+
"@absolutejs/native-linux-arm64": "0.20.0-beta.73",
|
|
306
|
+
"@absolutejs/native-linux-x64": "0.20.0-beta.73",
|
|
307
|
+
"@absolutejs/native-windows-arm64": "0.20.0-beta.73",
|
|
308
|
+
"@absolutejs/native-windows-x64": "0.20.0-beta.73"
|
|
309
309
|
},
|
|
310
310
|
"overrides": {
|
|
311
311
|
"@sinclair/typebox": "0.34.52",
|
|
@@ -525,7 +525,7 @@
|
|
|
525
525
|
]
|
|
526
526
|
}
|
|
527
527
|
},
|
|
528
|
-
"version": "0.20.0-beta.
|
|
528
|
+
"version": "0.20.0-beta.73",
|
|
529
529
|
"workspaces": [
|
|
530
530
|
"tests/fixtures/*",
|
|
531
531
|
"tests/fixtures/_packages/*"
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
getWorkspaceServices,
|
|
4
|
-
isWorkspaceConfig,
|
|
5
|
-
loadConfig,
|
|
6
|
-
loadRawConfig
|
|
7
|
-
} from "./index-06cmrcdq.js";
|
|
8
|
-
import {
|
|
9
|
-
ANSI_ESCAPE_CODE
|
|
10
|
-
} from "./index-czwrxgdj.js";
|
|
11
|
-
import"./index-mywsk201.js";
|
|
12
|
-
|
|
13
|
-
// src/cli/scripts/typecheck.ts
|
|
14
|
-
import { resolve, join } from "path";
|
|
15
|
-
import { existsSync, readFileSync } from "fs";
|
|
16
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
17
|
-
var isCommandService = (service) => service.kind === "command" || Array.isArray(service.command);
|
|
18
|
-
var resolveConfigPath = (configPath) => resolve(configPath ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
|
|
19
|
-
var getTypecheckTargets = async (configPath) => {
|
|
20
|
-
if (!existsSync(resolveConfigPath(configPath))) {
|
|
21
|
-
const defaultService = {};
|
|
22
|
-
return [defaultService];
|
|
23
|
-
}
|
|
24
|
-
const rawConfig = await loadRawConfig(configPath);
|
|
25
|
-
if (!isWorkspaceConfig(rawConfig)) {
|
|
26
|
-
return [await loadConfig(configPath)];
|
|
27
|
-
}
|
|
28
|
-
return Object.values(getWorkspaceServices(rawConfig)).filter((service) => !isCommandService(service));
|
|
29
|
-
};
|
|
30
|
-
var STACK_OVERFLOW = "Maximum call stack size exceeded";
|
|
31
|
-
var DEEP_STACK_KB = 4000;
|
|
32
|
-
var spawnChecker = async (name, command) => {
|
|
33
|
-
const proc = Bun.spawn(command, {
|
|
34
|
-
stderr: "pipe",
|
|
35
|
-
stdout: "pipe"
|
|
36
|
-
});
|
|
37
|
-
const [stdout, stderr] = await Promise.all([
|
|
38
|
-
new Response(proc.stdout).text(),
|
|
39
|
-
new Response(proc.stderr).text()
|
|
40
|
-
]);
|
|
41
|
-
const exitCode = await proc.exited;
|
|
42
|
-
return { exitCode, name, output: (stdout + stderr).trim() };
|
|
43
|
-
};
|
|
44
|
-
var run = async (name, command) => {
|
|
45
|
-
const first = await spawnChecker(name, command);
|
|
46
|
-
if (!first.output.includes(STACK_OVERFLOW))
|
|
47
|
-
return first;
|
|
48
|
-
const [bin, ...args] = command;
|
|
49
|
-
if (bin === undefined)
|
|
50
|
-
return first;
|
|
51
|
-
const deep = await spawnChecker(name, [
|
|
52
|
-
"node",
|
|
53
|
-
`--stack-size=${DEEP_STACK_KB}`,
|
|
54
|
-
bin,
|
|
55
|
-
...args
|
|
56
|
-
]).catch(() => first);
|
|
57
|
-
return deep.output.includes(STACK_OVERFLOW) ? first : deep;
|
|
58
|
-
};
|
|
59
|
-
var shellEscape = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
|
60
|
-
var runShell = async (name, command) => run(name, ["/bin/bash", "-lc", command]);
|
|
61
|
-
var findBin = (name) => {
|
|
62
|
-
const local = resolve("node_modules", ".bin", name);
|
|
63
|
-
return existsSync(local) ? local : null;
|
|
64
|
-
};
|
|
65
|
-
var ANSI_COLOR_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[[0-9;]*m`, "g");
|
|
66
|
-
var ANSI_PURPLE_REGEX = `${String.fromCharCode(ANSI_ESCAPE_CODE)}[35m`;
|
|
67
|
-
var ANSI_CYAN_REGEX = new RegExp(`^${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[36m|\\t`);
|
|
68
|
-
var ANSI_TOKEN_END_REGEX = new RegExp(`${String.fromCharCode(ANSI_ESCAPE_CODE)}\\[3[69]m`);
|
|
69
|
-
var stripAnsi = (str) => str.replace(ANSI_COLOR_REGEX, "");
|
|
70
|
-
var formatSvelteOutput = (output) => {
|
|
71
|
-
const cwd = `${process.cwd()}/`;
|
|
72
|
-
const summaryMatch = stripAnsi(output).match(/svelte-check found (\d+) error/);
|
|
73
|
-
const errorCount = summaryMatch ? parseInt(summaryMatch[1] ?? "0", 10) : 0;
|
|
74
|
-
const formatted = output.split(`
|
|
75
|
-
`).filter((line) => {
|
|
76
|
-
const plain = stripAnsi(line);
|
|
77
|
-
return !plain.startsWith("Loading svelte-check") && !plain.startsWith("Getting Svelte") && !plain.startsWith("====") && !plain.startsWith("svelte-check found") && !/^\d+ (START|COMPLETED)/.test(plain) && plain.trim() !== "";
|
|
78
|
-
}).flatMap((line) => {
|
|
79
|
-
const result = line.replaceAll(cwd, "");
|
|
80
|
-
const plain = stripAnsi(result);
|
|
81
|
-
const pathMatch = plain.match(/^(\S+\.svelte):(\d+:\d+)$/);
|
|
82
|
-
if (pathMatch) {
|
|
83
|
-
return [
|
|
84
|
-
`\x1B[96m${pathMatch[1]}\x1B[0m:\x1B[93m${pathMatch[2]}\x1B[0m`
|
|
85
|
-
];
|
|
86
|
-
}
|
|
87
|
-
if (result.includes(ANSI_PURPLE_REGEX)) {
|
|
88
|
-
const plainLine = stripAnsi(result);
|
|
89
|
-
const before = stripAnsi(result.split(ANSI_PURPLE_REGEX)[0] ?? "");
|
|
90
|
-
const token = stripAnsi((result.split(ANSI_PURPLE_REGEX)[1] ?? "").split(ANSI_TOKEN_END_REGEX)[0] ?? "");
|
|
91
|
-
if (!token)
|
|
92
|
-
return [result];
|
|
93
|
-
const expanded = before.replace(/\t/g, " ");
|
|
94
|
-
const expandedLine = plainLine.replace(/\t/g, " ");
|
|
95
|
-
const underline = "~".repeat(token.length);
|
|
96
|
-
return [
|
|
97
|
-
`\x1B[0m${expandedLine}`,
|
|
98
|
-
`${" ".repeat(expanded.length)}\x1B[91m${underline}\x1B[0m`
|
|
99
|
-
];
|
|
100
|
-
}
|
|
101
|
-
if (ANSI_CYAN_REGEX.test(result) && !result.includes("Error") && !result.includes(ANSI_PURPLE_REGEX)) {
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
104
|
-
return [result];
|
|
105
|
-
}).join(`
|
|
106
|
-
`);
|
|
107
|
-
if (errorCount > 0) {
|
|
108
|
-
const suffix = errorCount === 1 ? "" : "s";
|
|
109
|
-
return `${formatted}
|
|
110
|
-
|
|
111
|
-
Found ${errorCount} error${suffix}.`;
|
|
112
|
-
}
|
|
113
|
-
return formatted;
|
|
114
|
-
};
|
|
115
|
-
var ABSOLUTE_INTERNAL_EXCLUDES = [
|
|
116
|
-
".absolutejs/**/*",
|
|
117
|
-
"**/build/**/*",
|
|
118
|
-
"**/generated/**/*"
|
|
119
|
-
];
|
|
120
|
-
var resolveAbsoluteTypeFile = (fileName) => {
|
|
121
|
-
const candidates = [
|
|
122
|
-
resolve("node_modules/@absolutejs/absolute/dist/types", fileName),
|
|
123
|
-
resolve(import.meta.dir, "../types", fileName),
|
|
124
|
-
resolve(import.meta.dir, "../../types", fileName),
|
|
125
|
-
resolve(import.meta.dir, "../../../types", fileName)
|
|
126
|
-
];
|
|
127
|
-
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
|
128
|
-
};
|
|
129
|
-
var ABSOLUTE_TYPECHECK_FILES = [
|
|
130
|
-
resolveAbsoluteTypeFile("style-module-shim.d.ts")
|
|
131
|
-
];
|
|
132
|
-
var readProjectTsconfig = () => {
|
|
133
|
-
try {
|
|
134
|
-
return JSON.parse(readFileSync(resolve("tsconfig.json"), "utf-8"));
|
|
135
|
-
} catch {
|
|
136
|
-
return {};
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
var toGeneratedConfigPath = (path) => path.startsWith("/") ? path : `../${path}`;
|
|
140
|
-
var getProjectTypecheckIncludes = () => {
|
|
141
|
-
const config = readProjectTsconfig();
|
|
142
|
-
const includes = Array.isArray(config.include) && config.include.length > 0 ? config.include : ["**/*"];
|
|
143
|
-
const files = Array.isArray(config.files) ? config.files : [];
|
|
144
|
-
return [
|
|
145
|
-
...includes.map(toGeneratedConfigPath),
|
|
146
|
-
...files.map(toGeneratedConfigPath),
|
|
147
|
-
...ABSOLUTE_TYPECHECK_FILES
|
|
148
|
-
];
|
|
149
|
-
};
|
|
150
|
-
var getProjectTypecheckExcludes = () => {
|
|
151
|
-
const config = readProjectTsconfig();
|
|
152
|
-
const excludes = Array.isArray(config.exclude) ? config.exclude : [];
|
|
153
|
-
return [
|
|
154
|
-
...new Set([
|
|
155
|
-
...ABSOLUTE_INTERNAL_EXCLUDES.map(toGeneratedConfigPath),
|
|
156
|
-
...excludes.map(toGeneratedConfigPath)
|
|
157
|
-
])
|
|
158
|
-
];
|
|
159
|
-
};
|
|
160
|
-
var buildVueTscCheck = async (cacheDir) => {
|
|
161
|
-
const vueTscBin = findBin("vue-tsc");
|
|
162
|
-
if (!vueTscBin) {
|
|
163
|
-
console.error("\x1B[31m\u2717\x1B[0m vue-tsc is required for Vue type checking. Install it: bun add -d vue-tsc");
|
|
164
|
-
process.exit(1);
|
|
165
|
-
}
|
|
166
|
-
const vueTsconfigPath = join(cacheDir, "tsconfig.vue-check.json");
|
|
167
|
-
await writeFile(vueTsconfigPath, JSON.stringify({
|
|
168
|
-
compilerOptions: {
|
|
169
|
-
rootDir: ".."
|
|
170
|
-
},
|
|
171
|
-
exclude: getProjectTypecheckExcludes(),
|
|
172
|
-
extends: resolve("tsconfig.json"),
|
|
173
|
-
include: getProjectTypecheckIncludes()
|
|
174
|
-
}, null, "\t"));
|
|
175
|
-
const base = [
|
|
176
|
-
vueTscBin,
|
|
177
|
-
"--noEmit",
|
|
178
|
-
"--project",
|
|
179
|
-
resolve(vueTsconfigPath),
|
|
180
|
-
"--pretty"
|
|
181
|
-
];
|
|
182
|
-
const cached = await run("vue-tsc", [
|
|
183
|
-
...base,
|
|
184
|
-
"--incremental",
|
|
185
|
-
"--tsBuildInfoFile",
|
|
186
|
-
join(cacheDir, "vue-tsc.tsbuildinfo")
|
|
187
|
-
]);
|
|
188
|
-
if (cached.exitCode === 0 || cached.output.length > 0)
|
|
189
|
-
return cached;
|
|
190
|
-
return run("vue-tsc", base);
|
|
191
|
-
};
|
|
192
|
-
var buildAngularCheck = async (cacheDir, angularDir) => {
|
|
193
|
-
const ngcBin = findBin("ngc");
|
|
194
|
-
if (!ngcBin) {
|
|
195
|
-
console.error("\x1B[31m\u2717\x1B[0m @angular/compiler-cli is required for Angular type checking. Install it: bun add -d @angular/compiler-cli");
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
|
-
const angularTsconfigPath = join(cacheDir, "tsconfig.angular-check.json");
|
|
199
|
-
await writeFile(angularTsconfigPath, JSON.stringify({
|
|
200
|
-
angularCompilerOptions: {
|
|
201
|
-
strictTemplates: true
|
|
202
|
-
},
|
|
203
|
-
compilerOptions: {
|
|
204
|
-
noEmit: true,
|
|
205
|
-
rootDir: ".."
|
|
206
|
-
},
|
|
207
|
-
exclude: ABSOLUTE_INTERNAL_EXCLUDES.map(toGeneratedConfigPath),
|
|
208
|
-
extends: resolve("tsconfig.json"),
|
|
209
|
-
include: [`../${angularDir}/**/*`]
|
|
210
|
-
}, null, "\t"));
|
|
211
|
-
return runShell("ngc", `${shellEscape(ngcBin)} -p ${shellEscape(resolve(angularTsconfigPath))}`);
|
|
212
|
-
};
|
|
213
|
-
var buildTscCheck = (cacheDir) => {
|
|
214
|
-
const tscBin = findBin("tsc");
|
|
215
|
-
if (!tscBin) {
|
|
216
|
-
console.error("\x1B[31m\u2717\x1B[0m typescript is required for type checking. Install it: bun add -d typescript");
|
|
217
|
-
process.exit(1);
|
|
218
|
-
}
|
|
219
|
-
const tscConfigPath = join(cacheDir, "tsconfig.typecheck.json");
|
|
220
|
-
return writeFile(tscConfigPath, JSON.stringify({
|
|
221
|
-
compilerOptions: {
|
|
222
|
-
rootDir: ".."
|
|
223
|
-
},
|
|
224
|
-
exclude: getProjectTypecheckExcludes(),
|
|
225
|
-
extends: resolve("tsconfig.json"),
|
|
226
|
-
include: getProjectTypecheckIncludes()
|
|
227
|
-
}, null, "\t")).then(() => run("tsc", [
|
|
228
|
-
tscBin,
|
|
229
|
-
"--noEmit",
|
|
230
|
-
"--project",
|
|
231
|
-
resolve(tscConfigPath),
|
|
232
|
-
"--incremental",
|
|
233
|
-
"--tsBuildInfoFile",
|
|
234
|
-
join(cacheDir, "tsc.tsbuildinfo"),
|
|
235
|
-
"--pretty"
|
|
236
|
-
]));
|
|
237
|
-
};
|
|
238
|
-
var buildSvelteCheck = async (cacheDir, svelteDir) => {
|
|
239
|
-
const svelteBin = findBin("svelte-check");
|
|
240
|
-
if (!svelteBin) {
|
|
241
|
-
console.error("\x1B[31m\u2717\x1B[0m svelte-check is required for Svelte type checking. Install it: bun add -d svelte-check");
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
244
|
-
const svelteTsconfigPath = join(cacheDir, "tsconfig.svelte-check.json");
|
|
245
|
-
await writeFile(svelteTsconfigPath, JSON.stringify({
|
|
246
|
-
extends: resolve("tsconfig.json"),
|
|
247
|
-
files: ABSOLUTE_TYPECHECK_FILES,
|
|
248
|
-
include: [`../${svelteDir}/**/*`]
|
|
249
|
-
}, null, "\t"));
|
|
250
|
-
return run("svelte-check", [
|
|
251
|
-
svelteBin,
|
|
252
|
-
"--tsconfig",
|
|
253
|
-
resolve(svelteTsconfigPath),
|
|
254
|
-
"--threshold",
|
|
255
|
-
"error",
|
|
256
|
-
"--compiler-warnings",
|
|
257
|
-
"css-unused-selector:ignore",
|
|
258
|
-
"--output",
|
|
259
|
-
"human-verbose",
|
|
260
|
-
"--color"
|
|
261
|
-
]);
|
|
262
|
-
};
|
|
263
|
-
var typecheck = async (configPath) => {
|
|
264
|
-
const targets = await getTypecheckTargets(configPath);
|
|
265
|
-
const hasAngular = targets.some((config) => Boolean(config.angularDirectory));
|
|
266
|
-
const hasSvelte = targets.some((config) => Boolean(config.svelteDirectory));
|
|
267
|
-
const hasVue = targets.some((config) => Boolean(config.vueDirectory));
|
|
268
|
-
const svelteDirs = [
|
|
269
|
-
...new Set(targets.map((config) => config.svelteDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
|
|
270
|
-
];
|
|
271
|
-
const angularDirs = [
|
|
272
|
-
...new Set(targets.map((config) => config.angularDirectory).filter((dir) => typeof dir === "string" && dir.length > 0))
|
|
273
|
-
];
|
|
274
|
-
const cacheDir = ".absolutejs";
|
|
275
|
-
await mkdir(cacheDir, { recursive: true });
|
|
276
|
-
const checks = [];
|
|
277
|
-
checks.push(hasVue ? buildVueTscCheck(cacheDir) : buildTscCheck(cacheDir));
|
|
278
|
-
for (const svelteDir of hasSvelte ? svelteDirs : []) {
|
|
279
|
-
checks.push(buildSvelteCheck(cacheDir, svelteDir));
|
|
280
|
-
}
|
|
281
|
-
for (const angularDir of hasAngular ? angularDirs : []) {
|
|
282
|
-
checks.push(buildAngularCheck(cacheDir, angularDir));
|
|
283
|
-
}
|
|
284
|
-
const results = await Promise.all(checks);
|
|
285
|
-
const failed = results.filter((res) => res.exitCode !== 0);
|
|
286
|
-
if (failed.length === 0) {
|
|
287
|
-
console.log("\x1B[32m\u2713\x1B[0m Typecheck passed");
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
for (const result of failed) {
|
|
291
|
-
console.error(`
|
|
292
|
-
\x1B[31m[${result.name}]\x1B[0m`);
|
|
293
|
-
const output = result.name === "svelte-check" ? formatSvelteOutput(result.output) : result.output;
|
|
294
|
-
console.error(output);
|
|
295
|
-
}
|
|
296
|
-
console.error(`
|
|
297
|
-
\x1B[31m\u2717\x1B[0m Typecheck failed: ${failed.map((res) => res.name).join(", ")}`);
|
|
298
|
-
process.exit(1);
|
|
299
|
-
};
|
|
300
|
-
export {
|
|
301
|
-
typecheck
|
|
302
|
-
};
|