@lite-fsm/cli 0.1.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/LICENSE +21 -0
- package/dist/bin/lite-fsm.d.cts +2 -0
- package/dist/bin/lite-fsm.d.ts +2 -0
- package/dist/bin/lite-fsm.js +12 -0
- package/dist/cli/context.d.cts +21 -0
- package/dist/cli/context.d.ts +21 -0
- package/dist/cli/context.js +0 -0
- package/dist/cli/create-program.d.cts +6 -0
- package/dist/cli/create-program.d.ts +6 -0
- package/dist/cli/create-program.js +48 -0
- package/dist/cli/diagnostics.d.cts +15 -0
- package/dist/cli/diagnostics.d.ts +15 -0
- package/dist/cli/diagnostics.js +13 -0
- package/dist/cli/node-fs.d.cts +2 -0
- package/dist/cli/node-fs.d.ts +2 -0
- package/dist/cli/node-fs.js +33 -0
- package/dist/cli/result.d.cts +6 -0
- package/dist/cli/result.d.ts +6 -0
- package/dist/cli/result.js +8 -0
- package/dist/export-graph/command.d.cts +8 -0
- package/dist/export-graph/command.d.ts +8 -0
- package/dist/export-graph/command.js +30 -0
- package/dist/export-graph/export-document.d.cts +38 -0
- package/dist/export-graph/export-document.d.ts +38 -0
- package/dist/export-graph/export-document.js +35 -0
- package/dist/export-graph/options.d.cts +21 -0
- package/dist/export-graph/options.d.ts +21 -0
- package/dist/export-graph/options.js +40 -0
- package/dist/export-graph/run-export-graph.d.cts +10 -0
- package/dist/export-graph/run-export-graph.d.ts +10 -0
- package/dist/export-graph/run-export-graph.js +53 -0
- package/dist/export-graph/source-bundle.d.cts +12 -0
- package/dist/export-graph/source-bundle.d.ts +12 -0
- package/dist/export-graph/source-bundle.js +48 -0
- package/dist/export-graph/write-output.d.cts +10 -0
- package/dist/export-graph/write-output.d.ts +10 -0
- package/dist/export-graph/write-output.js +24 -0
- package/dist/output/format-diagnostics.d.cts +6 -0
- package/dist/output/format-diagnostics.d.ts +6 -0
- package/dist/output/format-diagnostics.js +33 -0
- package/dist/output/stable-json.d.cts +1 -0
- package/dist/output/stable-json.d.ts +1 -0
- package/dist/output/stable-json.js +72 -0
- package/dist/project/build-project-graph.d.cts +19 -0
- package/dist/project/build-project-graph.d.ts +19 -0
- package/dist/project/build-project-graph.js +64 -0
- package/dist/project/create-project-host.d.cts +4 -0
- package/dist/project/create-project-host.d.ts +4 -0
- package/dist/project/create-project-host.js +11 -0
- package/dist/project/module-resolver.d.cts +15 -0
- package/dist/project/module-resolver.d.ts +15 -0
- package/dist/project/module-resolver.js +111 -0
- package/dist/project/source-cache.d.cts +11 -0
- package/dist/project/source-cache.d.ts +11 -0
- package/dist/project/source-cache.js +55 -0
- package/dist/project/tsconfig.d.cts +15 -0
- package/dist/project/tsconfig.d.ts +15 -0
- package/dist/project/tsconfig.js +116 -0
- package/dist/visualize/command.d.cts +9 -0
- package/dist/visualize/command.d.ts +9 -0
- package/dist/visualize/command.js +35 -0
- package/dist/visualize/http.d.cts +11 -0
- package/dist/visualize/http.d.ts +11 -0
- package/dist/visualize/http.js +65 -0
- package/dist/visualize/open-browser.d.cts +22 -0
- package/dist/visualize/open-browser.d.ts +22 -0
- package/dist/visualize/open-browser.js +45 -0
- package/dist/visualize/options.d.cts +16 -0
- package/dist/visualize/options.d.ts +16 -0
- package/dist/visualize/options.js +51 -0
- package/dist/visualize/routes.d.cts +2 -0
- package/dist/visualize/routes.d.ts +2 -0
- package/dist/visualize/routes.js +24 -0
- package/dist/visualize/run-visualize.d.cts +32 -0
- package/dist/visualize/run-visualize.d.ts +32 -0
- package/dist/visualize/run-visualize.js +81 -0
- package/dist/visualize/server.d.cts +40 -0
- package/dist/visualize/server.d.ts +40 -0
- package/dist/visualize/server.js +90 -0
- package/dist/visualize/session.d.cts +34 -0
- package/dist/visualize/session.d.ts +34 -0
- package/dist/visualize/session.js +137 -0
- package/dist/visualize/static-assets.d.cts +17 -0
- package/dist/visualize/static-assets.d.ts +17 -0
- package/dist/visualize/static-assets.js +87 -0
- package/dist/visualize/types.d.cts +65 -0
- package/dist/visualize/types.d.ts +65 -0
- package/dist/visualize/types.js +0 -0
- package/dist/visualizer/assets/MachineCanvasGraph-CHpVij2M.css +1 -0
- package/dist/visualizer/assets/MachineCanvasGraph-DuWIhVsu.js +30 -0
- package/dist/visualizer/assets/index-CZYnsrxd.js +42381 -0
- package/dist/visualizer/assets/index-DMdcz3e2.css +2 -0
- package/dist/visualizer/index.html +13 -0
- package/package.json +57 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { cliDiagnostic } from "../cli/diagnostics.js";
|
|
3
|
+
import { normalizeAbsolutePath, normalizePath } from "../project/source-cache.js";
|
|
4
|
+
const sourcePath = (projectRoot, fileName) => normalizeAbsolutePath(resolve(projectRoot, fileName));
|
|
5
|
+
const createProjectGraphSourceBundle = (context, projectRoot, files) => {
|
|
6
|
+
const sourceFiles = [];
|
|
7
|
+
for (const file of files) {
|
|
8
|
+
const absolutePath = sourcePath(projectRoot, file.fileName);
|
|
9
|
+
try {
|
|
10
|
+
if (!context.fs.fileExists(absolutePath)) {
|
|
11
|
+
return {
|
|
12
|
+
ok: false,
|
|
13
|
+
diagnostics: [
|
|
14
|
+
cliDiagnostic(
|
|
15
|
+
"LFC_SOURCE_BUNDLE_FILE_UNREADABLE",
|
|
16
|
+
"error",
|
|
17
|
+
`Source file '${file.fileName}' could not be embedded in the graph export.`,
|
|
18
|
+
{ file: normalizePath(absolutePath) }
|
|
19
|
+
)
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
sourceFiles.push({
|
|
24
|
+
fileName: file.fileName,
|
|
25
|
+
language: file.language,
|
|
26
|
+
hash: file.hash,
|
|
27
|
+
text: context.fs.readFile(absolutePath)
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
+
return {
|
|
32
|
+
ok: false,
|
|
33
|
+
diagnostics: [
|
|
34
|
+
cliDiagnostic(
|
|
35
|
+
"LFC_SOURCE_BUNDLE_FILE_UNREADABLE",
|
|
36
|
+
"error",
|
|
37
|
+
`Source file '${file.fileName}' could not be embedded in the graph export: ${message}`,
|
|
38
|
+
{ file: normalizePath(absolutePath) }
|
|
39
|
+
)
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { ok: true, sources: { files: sourceFiles } };
|
|
45
|
+
};
|
|
46
|
+
export {
|
|
47
|
+
createProjectGraphSourceBundle
|
|
48
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CliContext } from "../cli/context.js";
|
|
2
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
3
|
+
export type WriteOutputResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
outputPath: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
diagnostics: CliDiagnostic[];
|
|
9
|
+
};
|
|
10
|
+
export declare const writeOutput: (context: CliContext, outputPath: string, contents: string) => WriteOutputResult;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CliContext } from "../cli/context.js";
|
|
2
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
3
|
+
export type WriteOutputResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
outputPath: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
diagnostics: CliDiagnostic[];
|
|
9
|
+
};
|
|
10
|
+
export declare const writeOutput: (context: CliContext, outputPath: string, contents: string) => WriteOutputResult;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { cliDiagnostic } from "../cli/diagnostics.js";
|
|
3
|
+
import { normalizeAbsolutePath } from "../project/source-cache.js";
|
|
4
|
+
const writeOutput = (context, outputPath, contents) => {
|
|
5
|
+
const absoluteOutputPath = normalizeAbsolutePath(resolve(context.cwd, outputPath));
|
|
6
|
+
try {
|
|
7
|
+
context.fs.mkdir(dirname(absoluteOutputPath), { recursive: true });
|
|
8
|
+
context.fs.writeFile(absoluteOutputPath, contents);
|
|
9
|
+
return { ok: true, outputPath: absoluteOutputPath };
|
|
10
|
+
} catch (error) {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
return {
|
|
13
|
+
ok: false,
|
|
14
|
+
diagnostics: [
|
|
15
|
+
cliDiagnostic("LFC_WRITE_FAILED", "error", `Failed to write graph export: ${message}`, {
|
|
16
|
+
file: absoluteOutputPath
|
|
17
|
+
})
|
|
18
|
+
]
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
writeOutput
|
|
24
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GraphDiagnostic } from "@lite-fsm/graph";
|
|
2
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
3
|
+
type PrintableDiagnostic = CliDiagnostic | GraphDiagnostic;
|
|
4
|
+
export declare const formatDiagnostic: (diagnostic: PrintableDiagnostic) => string;
|
|
5
|
+
export declare const formatDiagnostics: (diagnostics: readonly PrintableDiagnostic[]) => string;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GraphDiagnostic } from "@lite-fsm/graph";
|
|
2
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
3
|
+
type PrintableDiagnostic = CliDiagnostic | GraphDiagnostic;
|
|
4
|
+
export declare const formatDiagnostic: (diagnostic: PrintableDiagnostic) => string;
|
|
5
|
+
export declare const formatDiagnostics: (diagnostics: readonly PrintableDiagnostic[]) => string;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const diagnosticFile = (diagnostic) => {
|
|
2
|
+
if ("file" in diagnostic && diagnostic.file) return diagnostic.file;
|
|
3
|
+
if (!diagnostic.loc || !("fileName" in diagnostic.loc)) return void 0;
|
|
4
|
+
return diagnostic.loc.fileName;
|
|
5
|
+
};
|
|
6
|
+
const diagnosticLine = (diagnostic) => {
|
|
7
|
+
if (!diagnostic.loc) return void 0;
|
|
8
|
+
if ("line" in diagnostic.loc) return diagnostic.loc.line;
|
|
9
|
+
return diagnostic.loc.start.line;
|
|
10
|
+
};
|
|
11
|
+
const diagnosticColumn = (diagnostic) => {
|
|
12
|
+
if (!diagnostic.loc) return void 0;
|
|
13
|
+
if ("line" in diagnostic.loc) return "column" in diagnostic.loc ? diagnostic.loc.column : void 0;
|
|
14
|
+
return diagnostic.loc.start.column;
|
|
15
|
+
};
|
|
16
|
+
const formatDiagnostic = (diagnostic) => {
|
|
17
|
+
const file = diagnosticFile(diagnostic);
|
|
18
|
+
const line = diagnosticLine(diagnostic);
|
|
19
|
+
const column = diagnosticColumn(diagnostic);
|
|
20
|
+
const location = file ? ` ${file}${line === void 0 ? "" : `:${line}:${column ?? 1}`}` : "";
|
|
21
|
+
const hint = "hint" in diagnostic && diagnostic.hint ? `
|
|
22
|
+
hint: ${diagnostic.hint}` : "";
|
|
23
|
+
return `${diagnostic.severity.toUpperCase()} ${diagnostic.code}${location}: ${diagnostic.message}${hint}`;
|
|
24
|
+
};
|
|
25
|
+
const formatDiagnostics = (diagnostics) => {
|
|
26
|
+
if (diagnostics.length === 0) return "";
|
|
27
|
+
return `${diagnostics.map(formatDiagnostic).join("\n")}
|
|
28
|
+
`;
|
|
29
|
+
};
|
|
30
|
+
export {
|
|
31
|
+
formatDiagnostic,
|
|
32
|
+
formatDiagnostics
|
|
33
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const stringifyStableJson: (value: unknown) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const stringifyStableJson: (value: unknown) => string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const KEY_ORDER = [
|
|
2
|
+
"version",
|
|
3
|
+
"createdBy",
|
|
4
|
+
"package",
|
|
5
|
+
"entry",
|
|
6
|
+
"path",
|
|
7
|
+
"tsconfigPath",
|
|
8
|
+
"graph",
|
|
9
|
+
"source",
|
|
10
|
+
"filename",
|
|
11
|
+
"language",
|
|
12
|
+
"kind",
|
|
13
|
+
"entryFileName",
|
|
14
|
+
"files",
|
|
15
|
+
"fileName",
|
|
16
|
+
"roles",
|
|
17
|
+
"hash",
|
|
18
|
+
"machines",
|
|
19
|
+
"managers",
|
|
20
|
+
"diagnostics",
|
|
21
|
+
"code",
|
|
22
|
+
"severity",
|
|
23
|
+
"message",
|
|
24
|
+
"machineId",
|
|
25
|
+
"loc",
|
|
26
|
+
"start",
|
|
27
|
+
"end",
|
|
28
|
+
"line",
|
|
29
|
+
"column",
|
|
30
|
+
"offset",
|
|
31
|
+
"hint",
|
|
32
|
+
"id",
|
|
33
|
+
"index",
|
|
34
|
+
"variableName",
|
|
35
|
+
"exportName",
|
|
36
|
+
"managerKeys",
|
|
37
|
+
"machineRefs",
|
|
38
|
+
"key",
|
|
39
|
+
"machineId",
|
|
40
|
+
"states",
|
|
41
|
+
"transitions",
|
|
42
|
+
"emissions",
|
|
43
|
+
"reducerCases"
|
|
44
|
+
];
|
|
45
|
+
const keyRanks = new Map(KEY_ORDER.map((key, index) => [key, index]));
|
|
46
|
+
const orderedKeys = (value) => {
|
|
47
|
+
return Object.keys(value).sort((left, right) => {
|
|
48
|
+
const leftRank = keyRanks.get(left) ?? Number.MAX_SAFE_INTEGER;
|
|
49
|
+
const rightRank = keyRanks.get(right) ?? Number.MAX_SAFE_INTEGER;
|
|
50
|
+
return leftRank === rightRank ? left.localeCompare(right) : leftRank - rightRank;
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const isPlainObject = (value) => {
|
|
54
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
|
+
};
|
|
56
|
+
const stableValue = (value) => {
|
|
57
|
+
if (Array.isArray(value)) return value.map(stableValue);
|
|
58
|
+
if (!isPlainObject(value)) return value;
|
|
59
|
+
const next = {};
|
|
60
|
+
for (const key of orderedKeys(value)) {
|
|
61
|
+
const child = value[key];
|
|
62
|
+
if (child !== void 0) next[key] = stableValue(child);
|
|
63
|
+
}
|
|
64
|
+
return next;
|
|
65
|
+
};
|
|
66
|
+
const stringifyStableJson = (value) => {
|
|
67
|
+
return `${JSON.stringify(stableValue(value), null, 2)}
|
|
68
|
+
`;
|
|
69
|
+
};
|
|
70
|
+
export {
|
|
71
|
+
stringifyStableJson
|
|
72
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type LiteFsmGraphProjectResult } from "@lite-fsm/graph";
|
|
2
|
+
import type { CliContext } from "../cli/context.js";
|
|
3
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
4
|
+
export type ProjectGraphBuildOptions = {
|
|
5
|
+
entry: string;
|
|
6
|
+
tsconfig?: string;
|
|
7
|
+
};
|
|
8
|
+
export type ProjectGraphBuildResult = {
|
|
9
|
+
project: {
|
|
10
|
+
entryPath: string;
|
|
11
|
+
absoluteEntryPath: string;
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
tsconfigPath?: string;
|
|
14
|
+
};
|
|
15
|
+
graphResult?: LiteFsmGraphProjectResult;
|
|
16
|
+
diagnostics: CliDiagnostic[];
|
|
17
|
+
blocking: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare const buildProjectGraph: (context: CliContext, options: ProjectGraphBuildOptions) => ProjectGraphBuildResult;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type LiteFsmGraphProjectResult } from "@lite-fsm/graph";
|
|
2
|
+
import type { CliContext } from "../cli/context.js";
|
|
3
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
4
|
+
export type ProjectGraphBuildOptions = {
|
|
5
|
+
entry: string;
|
|
6
|
+
tsconfig?: string;
|
|
7
|
+
};
|
|
8
|
+
export type ProjectGraphBuildResult = {
|
|
9
|
+
project: {
|
|
10
|
+
entryPath: string;
|
|
11
|
+
absoluteEntryPath: string;
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
tsconfigPath?: string;
|
|
14
|
+
};
|
|
15
|
+
graphResult?: LiteFsmGraphProjectResult;
|
|
16
|
+
diagnostics: CliDiagnostic[];
|
|
17
|
+
blocking: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare const buildProjectGraph: (context: CliContext, options: ProjectGraphBuildOptions) => ProjectGraphBuildResult;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { relative, resolve } from "node:path";
|
|
2
|
+
import { compileLiteFsmGraphProject } from "@lite-fsm/graph";
|
|
3
|
+
import { cliDiagnostic, hasBlockingCliDiagnostics } from "../cli/diagnostics.js";
|
|
4
|
+
import { createProjectHost } from "./create-project-host.js";
|
|
5
|
+
import { createProjectModuleResolver } from "./module-resolver.js";
|
|
6
|
+
import { createSourceCache, normalizeAbsolutePath, normalizePath } from "./source-cache.js";
|
|
7
|
+
import { resolveProjectTsconfig } from "./tsconfig.js";
|
|
8
|
+
const toDisplayPath = (path, cwd) => {
|
|
9
|
+
const relativePath = normalizePath(relative(cwd, path));
|
|
10
|
+
return relativePath === "" || !relativePath.startsWith("../") && relativePath !== ".." ? relativePath || "." : normalizePath(path);
|
|
11
|
+
};
|
|
12
|
+
const buildProjectGraph = (context, options) => {
|
|
13
|
+
const absoluteEntryPath = normalizeAbsolutePath(resolve(context.cwd, options.entry));
|
|
14
|
+
const tsconfig = resolveProjectTsconfig(context, {
|
|
15
|
+
entryFileName: absoluteEntryPath,
|
|
16
|
+
explicitTsconfigPath: options.tsconfig
|
|
17
|
+
});
|
|
18
|
+
const project = {
|
|
19
|
+
entryPath: toDisplayPath(absoluteEntryPath, normalizeAbsolutePath(context.cwd)),
|
|
20
|
+
absoluteEntryPath,
|
|
21
|
+
projectRoot: tsconfig.projectRoot,
|
|
22
|
+
tsconfigPath: tsconfig.tsconfigPath ? toDisplayPath(tsconfig.tsconfigPath, normalizeAbsolutePath(context.cwd)) : void 0
|
|
23
|
+
};
|
|
24
|
+
if (tsconfig.blocking) {
|
|
25
|
+
return {
|
|
26
|
+
project,
|
|
27
|
+
diagnostics: tsconfig.diagnostics,
|
|
28
|
+
blocking: true
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const sourceCache = createSourceCache(context.fs);
|
|
33
|
+
const resolver = createProjectModuleResolver({
|
|
34
|
+
compilerOptions: tsconfig.compilerOptions,
|
|
35
|
+
projectRoot: tsconfig.projectRoot,
|
|
36
|
+
sourceCache
|
|
37
|
+
});
|
|
38
|
+
const host = createProjectHost(sourceCache, resolver);
|
|
39
|
+
const graphResult = compileLiteFsmGraphProject({
|
|
40
|
+
entryFileName: absoluteEntryPath,
|
|
41
|
+
projectRoot: tsconfig.projectRoot,
|
|
42
|
+
host
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
project,
|
|
46
|
+
graphResult,
|
|
47
|
+
diagnostics: tsconfig.diagnostics,
|
|
48
|
+
blocking: hasBlockingCliDiagnostics(tsconfig.diagnostics)
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
return {
|
|
53
|
+
project,
|
|
54
|
+
diagnostics: [
|
|
55
|
+
...tsconfig.diagnostics,
|
|
56
|
+
cliDiagnostic("LFC_GRAPH_PROJECT_FAILED", "error", `Project graph build failed unexpectedly: ${message}`)
|
|
57
|
+
],
|
|
58
|
+
blocking: true
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
export {
|
|
63
|
+
buildProjectGraph
|
|
64
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LiteFsmGraphProjectHost } from "@lite-fsm/graph";
|
|
2
|
+
import type { ProjectModuleResolver } from "./module-resolver.js";
|
|
3
|
+
import type { SourceCache } from "./source-cache.js";
|
|
4
|
+
export declare const createProjectHost: (sourceCache: SourceCache, resolver: ProjectModuleResolver) => LiteFsmGraphProjectHost;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LiteFsmGraphProjectHost } from "@lite-fsm/graph";
|
|
2
|
+
import type { ProjectModuleResolver } from "./module-resolver.js";
|
|
3
|
+
import type { SourceCache } from "./source-cache.js";
|
|
4
|
+
export declare const createProjectHost: (sourceCache: SourceCache, resolver: ProjectModuleResolver) => LiteFsmGraphProjectHost;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { LiteFsmGraphProjectModuleResolution } from "@lite-fsm/graph";
|
|
3
|
+
import type { SourceCache } from "./source-cache.js";
|
|
4
|
+
export type ProjectModuleResolver = {
|
|
5
|
+
resolveModule(input: {
|
|
6
|
+
fromFileName: string;
|
|
7
|
+
moduleSpecifier: string;
|
|
8
|
+
}): LiteFsmGraphProjectModuleResolution;
|
|
9
|
+
};
|
|
10
|
+
export type ProjectModuleResolverOptions = {
|
|
11
|
+
compilerOptions: ts.CompilerOptions;
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
sourceCache: SourceCache;
|
|
14
|
+
};
|
|
15
|
+
export declare const createProjectModuleResolver: ({ compilerOptions, projectRoot, sourceCache, }: ProjectModuleResolverOptions) => ProjectModuleResolver;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { LiteFsmGraphProjectModuleResolution } from "@lite-fsm/graph";
|
|
3
|
+
import type { SourceCache } from "./source-cache.js";
|
|
4
|
+
export type ProjectModuleResolver = {
|
|
5
|
+
resolveModule(input: {
|
|
6
|
+
fromFileName: string;
|
|
7
|
+
moduleSpecifier: string;
|
|
8
|
+
}): LiteFsmGraphProjectModuleResolution;
|
|
9
|
+
};
|
|
10
|
+
export type ProjectModuleResolverOptions = {
|
|
11
|
+
compilerOptions: ts.CompilerOptions;
|
|
12
|
+
projectRoot: string;
|
|
13
|
+
sourceCache: SourceCache;
|
|
14
|
+
};
|
|
15
|
+
export declare const createProjectModuleResolver: ({ compilerOptions, projectRoot, sourceCache, }: ProjectModuleResolverOptions) => ProjectModuleResolver;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { extname, isAbsolute, relative, resolve } from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { normalizeAbsolutePath, normalizePath } from "./source-cache.js";
|
|
4
|
+
const SUPPORTED_EXTENSION = ".ts";
|
|
5
|
+
const isRelativeSpecifier = (moduleSpecifier) => {
|
|
6
|
+
return moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../");
|
|
7
|
+
};
|
|
8
|
+
const isCoreSpecifier = (moduleSpecifier) => {
|
|
9
|
+
return moduleSpecifier === "@lite-fsm/core" || moduleSpecifier === "lite-fsm";
|
|
10
|
+
};
|
|
11
|
+
const explicitExtension = (moduleSpecifier) => {
|
|
12
|
+
const segments = moduleSpecifier.split("/");
|
|
13
|
+
const lastSegment = segments[segments.length - 1] ?? moduleSpecifier;
|
|
14
|
+
const extension = extname(lastSegment);
|
|
15
|
+
return extension === "" ? void 0 : extension;
|
|
16
|
+
};
|
|
17
|
+
const isInsideRoot = (fileName, projectRoot) => {
|
|
18
|
+
const relativePath = normalizePath(relative(projectRoot, fileName));
|
|
19
|
+
return relativePath === "" || !relativePath.startsWith("../") && relativePath !== ".." && !isAbsolute(relativePath);
|
|
20
|
+
};
|
|
21
|
+
const pathPatternToRegExp = (pattern) => {
|
|
22
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
23
|
+
return new RegExp(`^${escaped}$`);
|
|
24
|
+
};
|
|
25
|
+
const matchesPaths = (moduleSpecifier, paths) => {
|
|
26
|
+
if (!paths) return false;
|
|
27
|
+
return Object.keys(paths).some((pattern) => pathPatternToRegExp(pattern).test(moduleSpecifier));
|
|
28
|
+
};
|
|
29
|
+
const isBaseUrlCandidate = (moduleSpecifier, baseUrl) => {
|
|
30
|
+
return Boolean(baseUrl) && !moduleSpecifier.startsWith("@") && !moduleSpecifier.startsWith("lite-fsm/");
|
|
31
|
+
};
|
|
32
|
+
const firstSpecifierSegment = (moduleSpecifier) => {
|
|
33
|
+
const separatorIndex = moduleSpecifier.indexOf("/");
|
|
34
|
+
return separatorIndex === -1 ? moduleSpecifier : moduleSpecifier.slice(0, separatorIndex);
|
|
35
|
+
};
|
|
36
|
+
const hasBaseUrlProjectPrefix = (moduleSpecifier, baseUrl, sourceCache) => {
|
|
37
|
+
const candidate = normalizeAbsolutePath(resolve(baseUrl, firstSpecifierSegment(moduleSpecifier)));
|
|
38
|
+
return sourceCache.fileExists(`${candidate}.ts`) || sourceCache.directoryExists(candidate);
|
|
39
|
+
};
|
|
40
|
+
const resolutionHost = (sourceCache) => ({
|
|
41
|
+
fileExists(path) {
|
|
42
|
+
return sourceCache.fileExists(path);
|
|
43
|
+
},
|
|
44
|
+
/* v8 ignore next 3 -- TypeScript only asks readFile for package metadata/declaration strategies outside the CLI source traversal path. */
|
|
45
|
+
readFile(path) {
|
|
46
|
+
return sourceCache.readSource(path);
|
|
47
|
+
},
|
|
48
|
+
directoryExists(path) {
|
|
49
|
+
return sourceCache.directoryExists(path);
|
|
50
|
+
},
|
|
51
|
+
/* v8 ignore next 3 -- realpath is supplied for TypeScript host completeness; current supported resolver paths do not require it. */
|
|
52
|
+
realpath(path) {
|
|
53
|
+
return sourceCache.realpath(path);
|
|
54
|
+
},
|
|
55
|
+
getCurrentDirectory() {
|
|
56
|
+
return "/";
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const resolutionFromFileName = (moduleSpecifier, fileName, projectRoot) => {
|
|
60
|
+
const extension = extname(fileName);
|
|
61
|
+
if (extension !== SUPPORTED_EXTENSION) {
|
|
62
|
+
return {
|
|
63
|
+
kind: "unsupported-extension",
|
|
64
|
+
moduleSpecifier,
|
|
65
|
+
/* v8 ignore next -- unsupported resolved modules always have an extension in supported TypeScript resolution. */
|
|
66
|
+
extension: extension || "unknown"
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (!isInsideRoot(fileName, projectRoot)) return { kind: "external", moduleSpecifier };
|
|
70
|
+
return { kind: "resolved", fileName };
|
|
71
|
+
};
|
|
72
|
+
const createProjectModuleResolver = ({
|
|
73
|
+
compilerOptions,
|
|
74
|
+
projectRoot,
|
|
75
|
+
sourceCache
|
|
76
|
+
}) => {
|
|
77
|
+
const normalizedProjectRoot = normalizeAbsolutePath(projectRoot);
|
|
78
|
+
const host = resolutionHost(sourceCache);
|
|
79
|
+
return {
|
|
80
|
+
resolveModule({ fromFileName, moduleSpecifier }) {
|
|
81
|
+
if (isCoreSpecifier(moduleSpecifier)) return { kind: "core", moduleSpecifier };
|
|
82
|
+
if (moduleSpecifier.startsWith("lite-fsm/")) return { kind: "external", moduleSpecifier };
|
|
83
|
+
const extension = explicitExtension(moduleSpecifier);
|
|
84
|
+
if (extension && extension !== SUPPORTED_EXTENSION) {
|
|
85
|
+
return { kind: "unsupported-extension", moduleSpecifier, extension };
|
|
86
|
+
}
|
|
87
|
+
const relativeSpecifier = isRelativeSpecifier(moduleSpecifier);
|
|
88
|
+
const pathsCandidate = matchesPaths(moduleSpecifier, compilerOptions.paths);
|
|
89
|
+
const baseUrl = compilerOptions.baseUrl;
|
|
90
|
+
const baseUrlCandidate = isBaseUrlCandidate(moduleSpecifier, baseUrl);
|
|
91
|
+
const projectLocalCandidate = relativeSpecifier || pathsCandidate || baseUrlCandidate;
|
|
92
|
+
if (!projectLocalCandidate) return { kind: "external", moduleSpecifier };
|
|
93
|
+
const resolvedModule = ts.resolveModuleName(
|
|
94
|
+
moduleSpecifier,
|
|
95
|
+
normalizeAbsolutePath(fromFileName),
|
|
96
|
+
compilerOptions,
|
|
97
|
+
host
|
|
98
|
+
).resolvedModule;
|
|
99
|
+
if (!resolvedModule) {
|
|
100
|
+
const unresolvedProjectLocal = relativeSpecifier || pathsCandidate || baseUrlCandidate && hasBaseUrlProjectPrefix(moduleSpecifier, baseUrl, sourceCache);
|
|
101
|
+
return unresolvedProjectLocal ? { kind: "not-found", moduleSpecifier } : { kind: "external", moduleSpecifier };
|
|
102
|
+
}
|
|
103
|
+
if (resolvedModule.isExternalLibraryImport) return { kind: "external", moduleSpecifier };
|
|
104
|
+
const resolvedFileName = normalizeAbsolutePath(resolve(resolvedModule.resolvedFileName));
|
|
105
|
+
return resolutionFromFileName(moduleSpecifier, resolvedFileName, normalizedProjectRoot);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
export {
|
|
110
|
+
createProjectModuleResolver
|
|
111
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CliFileSystem } from "../cli/context.js";
|
|
2
|
+
export declare const normalizeAbsolutePath: (path: string) => string;
|
|
3
|
+
export declare const normalizePath: (path: string) => string;
|
|
4
|
+
export type SourceCache = {
|
|
5
|
+
readSource(path: string): string | undefined;
|
|
6
|
+
fileExists(path: string): boolean;
|
|
7
|
+
directoryExists(path: string): boolean;
|
|
8
|
+
realpath(path: string): string;
|
|
9
|
+
sourceHash(path: string): string | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare const createSourceCache: (fs: CliFileSystem) => SourceCache;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CliFileSystem } from "../cli/context.js";
|
|
2
|
+
export declare const normalizeAbsolutePath: (path: string) => string;
|
|
3
|
+
export declare const normalizePath: (path: string) => string;
|
|
4
|
+
export type SourceCache = {
|
|
5
|
+
readSource(path: string): string | undefined;
|
|
6
|
+
fileExists(path: string): boolean;
|
|
7
|
+
directoryExists(path: string): boolean;
|
|
8
|
+
realpath(path: string): string;
|
|
9
|
+
sourceHash(path: string): string | undefined;
|
|
10
|
+
};
|
|
11
|
+
export declare const createSourceCache: (fs: CliFileSystem) => SourceCache;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { extname, resolve } from "node:path";
|
|
3
|
+
const toPosixPath = (path) => path.replace(/\\/g, "/");
|
|
4
|
+
const normalizeAbsolutePath = (path) => toPosixPath(resolve(path));
|
|
5
|
+
const normalizePath = (path) => toPosixPath(path);
|
|
6
|
+
const createSourceCache = (fs) => {
|
|
7
|
+
const sourceByPath = /* @__PURE__ */ new Map();
|
|
8
|
+
const fileExistsByPath = /* @__PURE__ */ new Map();
|
|
9
|
+
const directoryExistsByPath = /* @__PURE__ */ new Map();
|
|
10
|
+
const normalizeKey = (path) => normalizeAbsolutePath(path);
|
|
11
|
+
const fileExists = (path) => {
|
|
12
|
+
const key = normalizeKey(path);
|
|
13
|
+
const cached = fileExistsByPath.get(key);
|
|
14
|
+
if (cached !== void 0) return cached;
|
|
15
|
+
const exists = fs.fileExists(key);
|
|
16
|
+
fileExistsByPath.set(key, exists);
|
|
17
|
+
return exists;
|
|
18
|
+
};
|
|
19
|
+
const directoryExists = (path) => {
|
|
20
|
+
const key = normalizeKey(path);
|
|
21
|
+
const cached = directoryExistsByPath.get(key);
|
|
22
|
+
if (cached !== void 0) return cached;
|
|
23
|
+
const exists = fs.directoryExists(key);
|
|
24
|
+
directoryExistsByPath.set(key, exists);
|
|
25
|
+
return exists;
|
|
26
|
+
};
|
|
27
|
+
const readSource = (path) => {
|
|
28
|
+
const key = normalizeKey(path);
|
|
29
|
+
if (extname(key) !== ".ts") return void 0;
|
|
30
|
+
if (sourceByPath.has(key)) return sourceByPath.get(key);
|
|
31
|
+
const source = fileExists(key) ? fs.readFile(key) : void 0;
|
|
32
|
+
sourceByPath.set(key, source);
|
|
33
|
+
return source;
|
|
34
|
+
};
|
|
35
|
+
const sourceHash = (path) => {
|
|
36
|
+
const source = readSource(path);
|
|
37
|
+
if (source === void 0) return void 0;
|
|
38
|
+
return createHash("sha256").update(source).digest("hex");
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
readSource,
|
|
42
|
+
fileExists,
|
|
43
|
+
directoryExists,
|
|
44
|
+
realpath(path) {
|
|
45
|
+
const key = normalizeKey(path);
|
|
46
|
+
return fs.realpath ? normalizeAbsolutePath(fs.realpath(key)) : key;
|
|
47
|
+
},
|
|
48
|
+
sourceHash
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export {
|
|
52
|
+
createSourceCache,
|
|
53
|
+
normalizeAbsolutePath,
|
|
54
|
+
normalizePath
|
|
55
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { CliContext } from "../cli/context.js";
|
|
3
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
4
|
+
export type ProjectTsconfig = {
|
|
5
|
+
tsconfigPath?: string;
|
|
6
|
+
projectRoot: string;
|
|
7
|
+
compilerOptions: ts.CompilerOptions;
|
|
8
|
+
diagnostics: CliDiagnostic[];
|
|
9
|
+
blocking: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ResolveProjectTsconfigOptions = {
|
|
12
|
+
entryFileName: string;
|
|
13
|
+
explicitTsconfigPath?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const resolveProjectTsconfig: (context: CliContext, options: ResolveProjectTsconfigOptions) => ProjectTsconfig;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import type { CliContext } from "../cli/context.js";
|
|
3
|
+
import type { CliDiagnostic } from "../cli/diagnostics.js";
|
|
4
|
+
export type ProjectTsconfig = {
|
|
5
|
+
tsconfigPath?: string;
|
|
6
|
+
projectRoot: string;
|
|
7
|
+
compilerOptions: ts.CompilerOptions;
|
|
8
|
+
diagnostics: CliDiagnostic[];
|
|
9
|
+
blocking: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ResolveProjectTsconfigOptions = {
|
|
12
|
+
entryFileName: string;
|
|
13
|
+
explicitTsconfigPath?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const resolveProjectTsconfig: (context: CliContext, options: ResolveProjectTsconfigOptions) => ProjectTsconfig;
|