@beignet/cli 0.0.51 → 0.0.53
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/CHANGELOG.md +46 -0
- package/README.md +182 -19
- package/dist/analysis/layers.d.ts +2 -0
- package/dist/analysis/layers.d.ts.map +1 -1
- package/dist/analysis/layers.js +9 -2
- package/dist/analysis/layers.js.map +1 -1
- package/dist/analysis/port-wiring.d.ts +4 -2
- package/dist/analysis/port-wiring.d.ts.map +1 -1
- package/dist/analysis/port-wiring.js +41 -11
- package/dist/analysis/port-wiring.js.map +1 -1
- package/dist/analysis/source-files.d.ts +15 -0
- package/dist/analysis/source-files.d.ts.map +1 -0
- package/dist/analysis/source-files.js +52 -0
- package/dist/analysis/source-files.js.map +1 -0
- package/dist/analysis/source-index.d.ts +2 -1
- package/dist/analysis/source-index.d.ts.map +1 -1
- package/dist/analysis/source-index.js +34 -5
- package/dist/analysis/source-index.js.map +1 -1
- package/dist/analysis/workspace-routes.d.ts +8 -0
- package/dist/analysis/workspace-routes.d.ts.map +1 -0
- package/dist/analysis/workspace-routes.js +291 -0
- package/dist/analysis/workspace-routes.js.map +1 -0
- package/dist/analysis/workspace.d.ts +24 -3
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +184 -43
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts.map +1 -1
- package/dist/app-map-changes.js +24 -14
- package/dist/app-map-changes.js.map +1 -1
- package/dist/app-map.d.ts.map +1 -1
- package/dist/app-map.js +87 -81
- package/dist/app-map.js.map +1 -1
- package/dist/check.d.ts +12 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +92 -25
- package/dist/check.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -1
- package/dist/git-changes.d.ts +17 -1
- package/dist/git-changes.d.ts.map +1 -1
- package/dist/git-changes.js +118 -84
- package/dist/git-changes.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -3
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +424 -129
- package/dist/inspect.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +108 -43
- package/dist/lint.js.map +1 -1
- package/dist/make/shared.js +1 -1
- package/dist/make.js +18 -1
- package/dist/make.js.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/provider-add.js +2 -2
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +22 -6
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +36 -13
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.js +5 -5
- package/dist/templates/db/mysql.d.ts.map +1 -1
- package/dist/templates/db/mysql.js +51 -22
- package/dist/templates/db/mysql.js.map +1 -1
- package/dist/templates/db/postgres.d.ts.map +1 -1
- package/dist/templates/db/postgres.js +60 -20
- package/dist/templates/db/postgres.js.map +1 -1
- package/dist/templates/db/sqlite.d.ts.map +1 -1
- package/dist/templates/db/sqlite.js +51 -23
- package/dist/templates/db/sqlite.js.map +1 -1
- package/dist/templates/shadcn.js +1 -1
- package/dist/templates/shadcn.js.map +1 -1
- package/dist/templates/shared.js +6 -6
- package/dist/templates/shared.js.map +1 -1
- package/dist/templates/testing.d.ts.map +1 -1
- package/dist/templates/testing.js +7 -19
- package/dist/templates/testing.js.map +1 -1
- package/dist/test-discovery.d.ts +9 -0
- package/dist/test-discovery.d.ts.map +1 -0
- package/dist/test-discovery.js +40 -0
- package/dist/test-discovery.js.map +1 -0
- package/package.json +8 -5
- package/skills/app-structure/SKILL.md +74 -7
- package/src/analysis/layers.ts +13 -2
- package/src/analysis/port-wiring.ts +68 -11
- package/src/analysis/source-files.ts +61 -0
- package/src/analysis/source-index.ts +56 -4
- package/src/analysis/workspace-routes.ts +385 -0
- package/src/analysis/workspace.ts +302 -59
- package/src/app-map-changes.ts +41 -13
- package/src/app-map.ts +114 -103
- package/src/check.ts +115 -25
- package/src/config.ts +33 -0
- package/src/git-changes.ts +218 -86
- package/src/index.ts +33 -5
- package/src/inspect.ts +608 -156
- package/src/lint.ts +152 -48
- package/src/make/shared.ts +1 -1
- package/src/make.ts +31 -1
- package/src/mcp.ts +1 -1
- package/src/provider-add.ts +2 -2
- package/src/provider-audit.ts +30 -10
- package/src/templates/agents.ts +36 -13
- package/src/templates/base.ts +5 -5
- package/src/templates/db/mysql.ts +51 -22
- package/src/templates/db/postgres.ts +60 -20
- package/src/templates/db/sqlite.ts +51 -23
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +6 -6
- package/src/templates/testing.ts +12 -19
- package/src/test-discovery.ts +53 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
1
|
+
import { readdir, readFile, realpath, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import ts from "typescript";
|
|
4
4
|
import {
|
|
@@ -8,6 +8,11 @@ import {
|
|
|
8
8
|
type ResolvedBeignetConfig,
|
|
9
9
|
resolveConfig,
|
|
10
10
|
} from "../config.js";
|
|
11
|
+
import {
|
|
12
|
+
isAnalysisSourceFile,
|
|
13
|
+
isAnalysisSourceFileExtension,
|
|
14
|
+
sourceFileScriptKind,
|
|
15
|
+
} from "./source-files.js";
|
|
11
16
|
|
|
12
17
|
const ignoredDirectoryNames = new Set([
|
|
13
18
|
".git",
|
|
@@ -18,8 +23,23 @@ const ignoredDirectoryNames = new Set([
|
|
|
18
23
|
"node_modules",
|
|
19
24
|
]);
|
|
20
25
|
|
|
26
|
+
export type AnalysisProject = {
|
|
27
|
+
targetDir: string;
|
|
28
|
+
canonicalDir: string;
|
|
29
|
+
prefix: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
dependencies: Set<string>;
|
|
32
|
+
localDependencies: Set<string>;
|
|
33
|
+
config: ResolvedBeignetConfig;
|
|
34
|
+
files: string[];
|
|
35
|
+
compilerOptions: ts.CompilerOptions;
|
|
36
|
+
compilerConfigFiles: string[];
|
|
37
|
+
moduleResolutionCache: ts.ModuleResolutionCache;
|
|
38
|
+
};
|
|
39
|
+
|
|
21
40
|
export type AnalysisWorkspace = {
|
|
22
41
|
targetDir: string;
|
|
42
|
+
projects: AnalysisProject[];
|
|
23
43
|
files: string[];
|
|
24
44
|
fileSet: Set<string>;
|
|
25
45
|
config: ResolvedBeignetConfig;
|
|
@@ -40,24 +60,70 @@ export async function createAnalysisWorkspace(
|
|
|
40
60
|
): Promise<AnalysisWorkspace> {
|
|
41
61
|
const targetDir = path.resolve(cwd);
|
|
42
62
|
await assertDirectory(targetDir);
|
|
43
|
-
const
|
|
44
|
-
const fileSet = new Set(files);
|
|
63
|
+
const appFiles = await listProjectFiles(targetDir);
|
|
45
64
|
const config = options.config
|
|
46
65
|
? resolveConfig(options.config)
|
|
47
|
-
: await loadBeignetConfig(targetDir,
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
targetDir,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
: await loadBeignetConfig(targetDir, appFiles);
|
|
67
|
+
const app = await createProject(targetDir, targetDir, appFiles, config);
|
|
68
|
+
const projects = [app];
|
|
69
|
+
for (const directory of config.workspace.packages) {
|
|
70
|
+
const packageDir = path.resolve(targetDir, directory);
|
|
71
|
+
await assertDirectory(packageDir);
|
|
72
|
+
const canonical = await realpath(packageDir);
|
|
73
|
+
for (const project of projects) {
|
|
74
|
+
const existing = project.canonicalDir;
|
|
75
|
+
if (within(canonical, existing) || within(existing, canonical)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Workspace package ${directory} overlaps ${project.prefix || "the app"}. Package roots must be separate directories.`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const packageFiles = await listProjectFiles(packageDir);
|
|
82
|
+
const packageConfig = await loadBeignetConfig(packageDir, packageFiles);
|
|
83
|
+
const project = await createProject(
|
|
84
|
+
targetDir,
|
|
85
|
+
packageDir,
|
|
86
|
+
packageFiles,
|
|
87
|
+
packageConfig,
|
|
88
|
+
);
|
|
89
|
+
if (!project.name)
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Workspace package ${directory} must have a name in package.json.`,
|
|
92
|
+
);
|
|
93
|
+
if (projects.some((candidate) => candidate.name === project.name)) {
|
|
94
|
+
throw new Error(`Duplicate workspace package name ${project.name}.`);
|
|
95
|
+
}
|
|
96
|
+
projects.push(project);
|
|
97
|
+
}
|
|
98
|
+
const reachable = new Set([app]);
|
|
99
|
+
for (const project of reachable) {
|
|
100
|
+
for (const candidate of projects.slice(1)) {
|
|
101
|
+
if (candidate.name && project.dependencies.has(candidate.name))
|
|
102
|
+
reachable.add(candidate);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const project of projects.slice(1)) {
|
|
106
|
+
if (!reachable.has(project))
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Workspace package ${project.name} is not a declared dependency of the app or its configured source packages.`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const files = projects
|
|
112
|
+
.flatMap((project) =>
|
|
113
|
+
project.files.map((file) => projectPath(project, file)),
|
|
114
|
+
)
|
|
115
|
+
.sort();
|
|
116
|
+
const fileSet = new Set(files);
|
|
117
|
+
const { compilerOptions, moduleResolutionCache } = app;
|
|
118
|
+
const compilerConfigFiles = [
|
|
119
|
+
...new Set(projects.flatMap((project) => project.compilerConfigFiles)),
|
|
120
|
+
].sort();
|
|
57
121
|
const sources = new Map<string, string>();
|
|
58
122
|
const sourceFiles = new Map<string, ts.SourceFile>();
|
|
59
123
|
|
|
60
124
|
const readSource = async (file: string): Promise<string> => {
|
|
125
|
+
if (!fileSet.has(file))
|
|
126
|
+
throw new Error(`Source file is outside the analysis workspace: ${file}`);
|
|
61
127
|
const cached = sources.get(file);
|
|
62
128
|
if (cached !== undefined) return cached;
|
|
63
129
|
|
|
@@ -68,6 +134,7 @@ export async function createAnalysisWorkspace(
|
|
|
68
134
|
|
|
69
135
|
return {
|
|
70
136
|
targetDir,
|
|
137
|
+
projects,
|
|
71
138
|
files,
|
|
72
139
|
fileSet,
|
|
73
140
|
config,
|
|
@@ -84,7 +151,7 @@ export async function createAnalysisWorkspace(
|
|
|
84
151
|
await readSource(file),
|
|
85
152
|
ts.ScriptTarget.Latest,
|
|
86
153
|
true,
|
|
87
|
-
|
|
154
|
+
sourceFileScriptKind(file),
|
|
88
155
|
);
|
|
89
156
|
sourceFiles.set(file, sourceFile);
|
|
90
157
|
return sourceFile;
|
|
@@ -132,7 +199,11 @@ export async function listProjectFiles(targetDir: string): Promise<string[]> {
|
|
|
132
199
|
export function resolveLocalSourceFile(
|
|
133
200
|
workspace: Pick<
|
|
134
201
|
AnalysisWorkspace,
|
|
135
|
-
|
|
202
|
+
| "compilerOptions"
|
|
203
|
+
| "fileSet"
|
|
204
|
+
| "moduleResolutionCache"
|
|
205
|
+
| "targetDir"
|
|
206
|
+
| "projects"
|
|
136
207
|
>,
|
|
137
208
|
importerFile: string,
|
|
138
209
|
importPath: string,
|
|
@@ -143,14 +214,36 @@ export function resolveLocalSourceFile(
|
|
|
143
214
|
const file = normalizePath(
|
|
144
215
|
path.relative(workspace.targetDir, resolved.resolvedFileName),
|
|
145
216
|
);
|
|
146
|
-
if (
|
|
147
|
-
|
|
217
|
+
if (
|
|
218
|
+
workspace.fileSet.has(file) &&
|
|
219
|
+
(!projectForFile(workspace, file).prefix || isAnalysisSourceFile(file))
|
|
220
|
+
)
|
|
221
|
+
return file;
|
|
222
|
+
const canonicalFile =
|
|
223
|
+
ts.sys.realpath?.(resolved.resolvedFileName) ?? resolved.resolvedFileName;
|
|
224
|
+
for (const project of workspace.projects) {
|
|
225
|
+
if (!within(canonicalFile, project.canonicalDir)) continue;
|
|
226
|
+
const candidate = projectPath(
|
|
227
|
+
project,
|
|
228
|
+
path.relative(project.canonicalDir, canonicalFile),
|
|
229
|
+
);
|
|
230
|
+
if (
|
|
231
|
+
workspace.fileSet.has(candidate) &&
|
|
232
|
+
(!project.prefix || isAnalysisSourceFile(candidate))
|
|
233
|
+
)
|
|
234
|
+
return candidate;
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
148
237
|
}
|
|
149
238
|
|
|
150
239
|
export function resolveLocalModulePath(
|
|
151
240
|
workspace: Pick<
|
|
152
241
|
AnalysisWorkspace,
|
|
153
|
-
|
|
242
|
+
| "compilerOptions"
|
|
243
|
+
| "fileSet"
|
|
244
|
+
| "moduleResolutionCache"
|
|
245
|
+
| "targetDir"
|
|
246
|
+
| "projects"
|
|
154
247
|
>,
|
|
155
248
|
importerFile: string,
|
|
156
249
|
importPath: string,
|
|
@@ -166,16 +259,34 @@ export function resolveLocalModulePath(
|
|
|
166
259
|
return undefined;
|
|
167
260
|
}
|
|
168
261
|
|
|
262
|
+
const project = projectForFile(workspace, importerFile);
|
|
263
|
+
if (
|
|
264
|
+
workspace.projects.length > 1 &&
|
|
265
|
+
(importPath.startsWith(".") ||
|
|
266
|
+
importPath.startsWith("#") ||
|
|
267
|
+
Object.keys(project.compilerOptions.paths ?? {}).some(
|
|
268
|
+
(pattern) =>
|
|
269
|
+
pattern !== "*" && modulePatternMatches(pattern, importPath),
|
|
270
|
+
))
|
|
271
|
+
) {
|
|
272
|
+
const resolved = resolveModule(workspace, importerFile, importPath);
|
|
273
|
+
if (resolved)
|
|
274
|
+
return normalizePath(
|
|
275
|
+
path.relative(
|
|
276
|
+
workspace.targetDir,
|
|
277
|
+
ts.sys.realpath?.(resolved.resolvedFileName) ??
|
|
278
|
+
resolved.resolvedFileName,
|
|
279
|
+
),
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
169
283
|
if (importPath.startsWith(".")) {
|
|
170
284
|
return normalizePath(path.join(path.dirname(importerFile), importPath));
|
|
171
285
|
}
|
|
172
286
|
|
|
173
|
-
const baseUrl = compilerPathsBase(
|
|
174
|
-
workspace.compilerOptions,
|
|
175
|
-
workspace.targetDir,
|
|
176
|
-
);
|
|
287
|
+
const baseUrl = compilerPathsBase(project.compilerOptions, project.targetDir);
|
|
177
288
|
for (const [pattern, targets] of Object.entries(
|
|
178
|
-
|
|
289
|
+
project.compilerOptions.paths ?? {},
|
|
179
290
|
)) {
|
|
180
291
|
if (pattern === "*") continue;
|
|
181
292
|
const wildcardValue = modulePatternWildcard(pattern, importPath);
|
|
@@ -187,14 +298,22 @@ export function resolveLocalModulePath(
|
|
|
187
298
|
const relative = normalizePath(
|
|
188
299
|
path.relative(workspace.targetDir, absolute),
|
|
189
300
|
);
|
|
190
|
-
if (
|
|
301
|
+
if (
|
|
302
|
+
workspace.projects.length > 1 ||
|
|
303
|
+
workspace.projects.some((candidate) =>
|
|
304
|
+
within(absolute, candidate.targetDir),
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
return relative;
|
|
191
308
|
}
|
|
192
309
|
}
|
|
193
310
|
|
|
194
311
|
const relative = normalizePath(
|
|
195
312
|
path.relative(workspace.targetDir, path.resolve(baseUrl, importPath)),
|
|
196
313
|
);
|
|
197
|
-
return
|
|
314
|
+
return workspace.projects.some((candidate) =>
|
|
315
|
+
within(path.resolve(workspace.targetDir, relative), candidate.targetDir),
|
|
316
|
+
)
|
|
198
317
|
? relative
|
|
199
318
|
: undefined;
|
|
200
319
|
}
|
|
@@ -202,17 +321,20 @@ export function resolveLocalModulePath(
|
|
|
202
321
|
export function isLocalModuleSpecifier(
|
|
203
322
|
workspace: Pick<
|
|
204
323
|
AnalysisWorkspace,
|
|
205
|
-
|
|
324
|
+
| "compilerOptions"
|
|
325
|
+
| "fileSet"
|
|
326
|
+
| "moduleResolutionCache"
|
|
327
|
+
| "targetDir"
|
|
328
|
+
| "projects"
|
|
206
329
|
>,
|
|
207
330
|
importerFile: string,
|
|
208
331
|
importPath: string,
|
|
209
332
|
): boolean {
|
|
210
333
|
const extension = path.posix.extname(importPath);
|
|
211
334
|
if (
|
|
335
|
+
!importPath.startsWith("#") &&
|
|
212
336
|
extension &&
|
|
213
|
-
!
|
|
214
|
-
extension,
|
|
215
|
-
)
|
|
337
|
+
!isAnalysisSourceFileExtension(extension)
|
|
216
338
|
) {
|
|
217
339
|
return false;
|
|
218
340
|
}
|
|
@@ -223,12 +345,33 @@ export function isLocalModuleSpecifier(
|
|
|
223
345
|
const file = normalizePath(
|
|
224
346
|
path.relative(workspace.targetDir, resolved.resolvedFileName),
|
|
225
347
|
);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
348
|
+
if (
|
|
349
|
+
workspace.fileSet.has(file) ||
|
|
350
|
+
resolveLocalSourceFile(workspace, importerFile, importPath)
|
|
351
|
+
)
|
|
352
|
+
return true;
|
|
353
|
+
// Package-import aliases can resolve to an omitted workspace through an
|
|
354
|
+
// install symlink. Keep normal dependencies inside node_modules external.
|
|
355
|
+
const canonical =
|
|
356
|
+
ts.sys.realpath?.(resolved.resolvedFileName) ?? resolved.resolvedFileName;
|
|
357
|
+
if (
|
|
358
|
+
importPath.startsWith("#") &&
|
|
359
|
+
!normalizePath(canonical).split("/").includes("node_modules")
|
|
360
|
+
)
|
|
361
|
+
return true;
|
|
229
362
|
}
|
|
230
363
|
|
|
231
|
-
const
|
|
364
|
+
const project = projectForFile(workspace, importerFile);
|
|
365
|
+
if (
|
|
366
|
+
workspace.projects.some(
|
|
367
|
+
(candidate) =>
|
|
368
|
+
candidate.name &&
|
|
369
|
+
(importPath === candidate.name ||
|
|
370
|
+
importPath.startsWith(`${candidate.name}/`)),
|
|
371
|
+
)
|
|
372
|
+
)
|
|
373
|
+
return true;
|
|
374
|
+
const pathPatterns = Object.keys(project.compilerOptions.paths ?? {});
|
|
232
375
|
if (
|
|
233
376
|
pathPatterns.some(
|
|
234
377
|
(pattern) =>
|
|
@@ -240,7 +383,7 @@ export function isLocalModuleSpecifier(
|
|
|
240
383
|
return true;
|
|
241
384
|
}
|
|
242
385
|
|
|
243
|
-
const baseUrl =
|
|
386
|
+
const baseUrl = project.compilerOptions.baseUrl;
|
|
244
387
|
if (!baseUrl) return false;
|
|
245
388
|
const firstSegment = importPath.split("/")[0];
|
|
246
389
|
const prefix = normalizePath(
|
|
@@ -255,17 +398,18 @@ export function isLocalModuleSpecifier(
|
|
|
255
398
|
function resolveModule(
|
|
256
399
|
workspace: Pick<
|
|
257
400
|
AnalysisWorkspace,
|
|
258
|
-
"compilerOptions" | "moduleResolutionCache" | "targetDir"
|
|
401
|
+
"compilerOptions" | "moduleResolutionCache" | "targetDir" | "projects"
|
|
259
402
|
>,
|
|
260
403
|
importerFile: string,
|
|
261
404
|
importPath: string,
|
|
262
405
|
): ts.ResolvedModuleFull | undefined {
|
|
406
|
+
const project = projectForFile(workspace, importerFile);
|
|
263
407
|
return ts.resolveModuleName(
|
|
264
408
|
importPath,
|
|
265
409
|
path.join(workspace.targetDir, importerFile),
|
|
266
|
-
|
|
410
|
+
project.compilerOptions,
|
|
267
411
|
ts.sys,
|
|
268
|
-
|
|
412
|
+
project.moduleResolutionCache,
|
|
269
413
|
).resolvedModule;
|
|
270
414
|
}
|
|
271
415
|
|
|
@@ -298,28 +442,32 @@ function resolveCompilerConfiguration(targetDir: string): {
|
|
|
298
442
|
compilerOptions: ts.CompilerOptions;
|
|
299
443
|
} {
|
|
300
444
|
const compilerConfigFiles = new Set<string>();
|
|
301
|
-
const configPath =
|
|
302
|
-
|
|
303
|
-
ts.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
445
|
+
const configPath = ["tsconfig.json", "jsconfig.json"]
|
|
446
|
+
.map((file) => path.join(targetDir, file))
|
|
447
|
+
.find(ts.sys.fileExists);
|
|
448
|
+
const compilerOptions = configPath
|
|
449
|
+
? (ts.getParsedCommandLineOfConfigFile(
|
|
450
|
+
configPath,
|
|
451
|
+
{},
|
|
452
|
+
{
|
|
453
|
+
...ts.sys,
|
|
454
|
+
readFile(file) {
|
|
455
|
+
compilerConfigFiles.add(path.resolve(file));
|
|
456
|
+
return ts.sys.readFile(file);
|
|
457
|
+
},
|
|
458
|
+
onUnRecoverableConfigFileDiagnostic: () => {},
|
|
311
459
|
},
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
460
|
+
)?.options ?? {})
|
|
461
|
+
: {};
|
|
462
|
+
const analysisOptions = { ...compilerOptions, allowJs: true };
|
|
315
463
|
|
|
316
464
|
return {
|
|
317
465
|
compilerConfigFiles: [...compilerConfigFiles].sort(),
|
|
318
|
-
compilerOptions:
|
|
319
|
-
?
|
|
466
|
+
compilerOptions: analysisOptions.paths
|
|
467
|
+
? analysisOptions
|
|
320
468
|
: {
|
|
321
|
-
...
|
|
322
|
-
baseUrl:
|
|
469
|
+
...analysisOptions,
|
|
470
|
+
baseUrl: analysisOptions.baseUrl ?? targetDir,
|
|
323
471
|
paths: {
|
|
324
472
|
"@/*": ["*"],
|
|
325
473
|
},
|
|
@@ -339,9 +487,104 @@ function compilerPathsBase(
|
|
|
339
487
|
);
|
|
340
488
|
}
|
|
341
489
|
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
490
|
+
async function createProject(
|
|
491
|
+
appDir: string,
|
|
492
|
+
targetDir: string,
|
|
493
|
+
files: string[],
|
|
494
|
+
config: ResolvedBeignetConfig,
|
|
495
|
+
): Promise<AnalysisProject> {
|
|
496
|
+
const manifest: unknown = files.includes("package.json")
|
|
497
|
+
? JSON.parse(await readFile(path.join(targetDir, "package.json"), "utf8"))
|
|
498
|
+
: {};
|
|
499
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
500
|
+
throw new Error(`Invalid package.json in ${targetDir}.`);
|
|
501
|
+
}
|
|
502
|
+
const data = manifest as Record<string, unknown>;
|
|
503
|
+
const dependencies = new Set<string>();
|
|
504
|
+
const localDependencies = new Set<string>();
|
|
505
|
+
for (const key of [
|
|
506
|
+
"dependencies",
|
|
507
|
+
"devDependencies",
|
|
508
|
+
"peerDependencies",
|
|
509
|
+
"optionalDependencies",
|
|
510
|
+
]) {
|
|
511
|
+
const value = data[key];
|
|
512
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
513
|
+
for (const [name, specifier] of Object.entries(value)) {
|
|
514
|
+
dependencies.add(name);
|
|
515
|
+
if (
|
|
516
|
+
typeof specifier === "string" &&
|
|
517
|
+
/^(workspace|link|file):/.test(specifier) &&
|
|
518
|
+
!specifier.endsWith(".tgz")
|
|
519
|
+
)
|
|
520
|
+
localDependencies.add(name);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const { compilerConfigFiles, compilerOptions } =
|
|
525
|
+
resolveCompilerConfiguration(targetDir);
|
|
526
|
+
return {
|
|
527
|
+
targetDir,
|
|
528
|
+
canonicalDir: await realpath(targetDir),
|
|
529
|
+
prefix: normalizePath(path.relative(appDir, targetDir)),
|
|
530
|
+
...(typeof data.name === "string" && data.name.trim()
|
|
531
|
+
? { name: data.name }
|
|
532
|
+
: {}),
|
|
533
|
+
dependencies,
|
|
534
|
+
localDependencies,
|
|
535
|
+
config,
|
|
536
|
+
files,
|
|
537
|
+
compilerConfigFiles,
|
|
538
|
+
compilerOptions,
|
|
539
|
+
moduleResolutionCache: ts.createModuleResolutionCache(
|
|
540
|
+
targetDir,
|
|
541
|
+
ts.sys.useCaseSensitiveFileNames
|
|
542
|
+
? (file) => file
|
|
543
|
+
: (file) => file.toLowerCase(),
|
|
544
|
+
compilerOptions,
|
|
545
|
+
),
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export function projectForFile(
|
|
550
|
+
workspace: Pick<AnalysisWorkspace, "projects" | "targetDir">,
|
|
551
|
+
file: string,
|
|
552
|
+
): AnalysisProject {
|
|
553
|
+
return findProjectForFile(workspace, file) ?? workspace.projects[0];
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export function findProjectForFile(
|
|
557
|
+
workspace: Pick<AnalysisWorkspace, "projects" | "targetDir">,
|
|
558
|
+
file: string,
|
|
559
|
+
): AnalysisProject | undefined {
|
|
560
|
+
const absolute = path.resolve(workspace.targetDir, file);
|
|
561
|
+
return workspace.projects.find((project) =>
|
|
562
|
+
within(absolute, project.targetDir),
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function projectPath(project: AnalysisProject, file: string): string {
|
|
567
|
+
return normalizePath(path.join(project.prefix, file));
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export function projectSource(
|
|
571
|
+
workspace: Pick<AnalysisWorkspace, "projects" | "targetDir">,
|
|
572
|
+
file: string,
|
|
573
|
+
): { project: AnalysisProject; file: string } {
|
|
574
|
+
const project = projectForFile(workspace, file);
|
|
575
|
+
return {
|
|
576
|
+
project,
|
|
577
|
+
file: normalizePath(
|
|
578
|
+
path.relative(project.targetDir, path.resolve(workspace.targetDir, file)),
|
|
579
|
+
),
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function within(file: string, directory: string): boolean {
|
|
584
|
+
const relative = path.relative(directory, file);
|
|
585
|
+
return (
|
|
586
|
+
relative !== ".." &&
|
|
587
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
588
|
+
!path.isAbsolute(relative)
|
|
589
|
+
);
|
|
347
590
|
}
|
package/src/app-map-changes.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { readFile, realpath } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
isAnalysisSourceFile,
|
|
5
|
+
stripSourceFileExtension,
|
|
6
|
+
} from "./analysis/source-files.js";
|
|
3
7
|
import {
|
|
4
8
|
type LocalImportReference,
|
|
5
9
|
localImportReferences,
|
|
@@ -212,8 +216,22 @@ export async function mapAppChanges(
|
|
|
212
216
|
createAnalysisWorkspace(targetDir),
|
|
213
217
|
resolveWorkspaceScope(git.repositoryRoot, canonicalTargetDir),
|
|
214
218
|
]);
|
|
219
|
+
for (const project of workspace.projects.slice(1)) {
|
|
220
|
+
if (isAbsoluteWithin(project.canonicalDir, git.repositoryRoot)) {
|
|
221
|
+
workspaceScope.dependencyDirectories.add(
|
|
222
|
+
normalizePath(path.relative(git.repositoryRoot, project.canonicalDir)),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
215
226
|
const governingFiles = new Set(
|
|
216
|
-
|
|
227
|
+
[
|
|
228
|
+
...workspace.compilerConfigFiles,
|
|
229
|
+
...workspace.projects.flatMap((project) =>
|
|
230
|
+
project.config.configFile
|
|
231
|
+
? [path.join(project.targetDir, project.config.configFile)]
|
|
232
|
+
: [],
|
|
233
|
+
),
|
|
234
|
+
]
|
|
217
235
|
.map((file) =>
|
|
218
236
|
path.resolve(
|
|
219
237
|
canonicalTargetDir,
|
|
@@ -281,6 +299,11 @@ export async function mapAppChanges(
|
|
|
281
299
|
changedFiles,
|
|
282
300
|
git.repositoryRoot,
|
|
283
301
|
canonicalTargetDir,
|
|
302
|
+
workspace.projects
|
|
303
|
+
.slice(1)
|
|
304
|
+
.map((project) =>
|
|
305
|
+
normalizePath(path.relative(git.repositoryRoot, project.canonicalDir)),
|
|
306
|
+
),
|
|
284
307
|
);
|
|
285
308
|
for (const change of appSourceChanges) {
|
|
286
309
|
const directNodes = nodesByFile.get(change.file) ?? [];
|
|
@@ -746,6 +769,7 @@ function appRelativeSourceChanges(
|
|
|
746
769
|
files: AppChangedFile[],
|
|
747
770
|
repositoryRoot: string,
|
|
748
771
|
canonicalTargetDir: string,
|
|
772
|
+
sourcePackages: string[],
|
|
749
773
|
): Array<{
|
|
750
774
|
file: string;
|
|
751
775
|
repositoryPath: string;
|
|
@@ -760,9 +784,15 @@ function appRelativeSourceChanges(
|
|
|
760
784
|
status: GitChangedFileStatus;
|
|
761
785
|
}> = [];
|
|
762
786
|
for (const changed of files) {
|
|
763
|
-
if (changed.scope !== "app")
|
|
787
|
+
if (changed.scope !== "app" && changed.scope !== "workspace-dependency")
|
|
788
|
+
continue;
|
|
764
789
|
for (const repositoryPath of changedFilePaths(changed)) {
|
|
765
|
-
if (
|
|
790
|
+
if (
|
|
791
|
+
![targetPrefix, ...sourcePackages].some((prefix) =>
|
|
792
|
+
isWithin(repositoryPath, prefix),
|
|
793
|
+
)
|
|
794
|
+
)
|
|
795
|
+
continue;
|
|
766
796
|
const file = targetPrefix
|
|
767
797
|
? normalizePath(path.posix.relative(targetPrefix, repositoryPath))
|
|
768
798
|
: repositoryPath;
|
|
@@ -794,7 +824,7 @@ async function addImportConsumers(args: {
|
|
|
794
824
|
reference: LocalImportReference;
|
|
795
825
|
}>
|
|
796
826
|
>();
|
|
797
|
-
const sourceFiles = args.workspace.files.filter(
|
|
827
|
+
const sourceFiles = args.workspace.files.filter(isAnalysisSourceFile);
|
|
798
828
|
await Promise.all(
|
|
799
829
|
sourceFiles.map(async (file) => {
|
|
800
830
|
for (const reference of await localImportReferences(
|
|
@@ -826,7 +856,9 @@ async function addImportConsumers(args: {
|
|
|
826
856
|
);
|
|
827
857
|
}
|
|
828
858
|
|
|
829
|
-
for (const change of args.changes.filter((item) =>
|
|
859
|
+
for (const change of args.changes.filter((item) =>
|
|
860
|
+
isAnalysisSourceFile(item.file),
|
|
861
|
+
)) {
|
|
830
862
|
const queue: Array<{
|
|
831
863
|
confidence: "exact" | "partial";
|
|
832
864
|
depth: number;
|
|
@@ -1002,7 +1034,7 @@ function addUnmappedChangeGaps(args: {
|
|
|
1002
1034
|
const represented = reasons.some(
|
|
1003
1035
|
(reason) => reason.changedFile === change.repositoryPath,
|
|
1004
1036
|
);
|
|
1005
|
-
const sourceFile =
|
|
1037
|
+
const sourceFile = isAnalysisSourceFile(change.file);
|
|
1006
1038
|
if (sourceFile && change.status === "deleted") {
|
|
1007
1039
|
args.gaps.push({
|
|
1008
1040
|
code: "deleted_source_unresolved",
|
|
@@ -1289,7 +1321,7 @@ function scopeSort(scope: AppChangeFileScope): number {
|
|
|
1289
1321
|
}
|
|
1290
1322
|
|
|
1291
1323
|
function moduleStem(file: string): string {
|
|
1292
|
-
return normalizePath(file)
|
|
1324
|
+
return stripSourceFileExtension(normalizePath(file));
|
|
1293
1325
|
}
|
|
1294
1326
|
|
|
1295
1327
|
function resolvedModuleKey(file: string): string {
|
|
@@ -1333,10 +1365,6 @@ function highestPriorityScope(
|
|
|
1333
1365
|
);
|
|
1334
1366
|
}
|
|
1335
1367
|
|
|
1336
|
-
function isSourceFile(file: string): boolean {
|
|
1337
|
-
return /\.(?:c|m)?[jt]sx?$/.test(file) && !file.endsWith(".d.ts");
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
1368
|
function isDocumentationPath(file: string, roots: string[]): boolean {
|
|
1341
1369
|
const normalized = normalizePath(file);
|
|
1342
1370
|
const basename = path.posix.basename(normalized);
|
|
@@ -1376,8 +1404,8 @@ function appWideChangeReasons(
|
|
|
1376
1404
|
const message =
|
|
1377
1405
|
scope === "workspace-dependency"
|
|
1378
1406
|
? scoped.length === 1
|
|
1379
|
-
? `
|
|
1380
|
-
: `${scoped.length}
|
|
1407
|
+
? `Local source dependency ${changedFile} changed.`
|
|
1408
|
+
: `${scoped.length} local source dependency files changed, including ${changedFile}.`
|
|
1381
1409
|
: scoped.length === 1
|
|
1382
1410
|
? `Governing file ${changedFile} changed.`
|
|
1383
1411
|
: `${scoped.length} governing files changed, including ${changedFile}.`;
|