@beignet/cli 0.0.52 → 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 +26 -0
- package/README.md +131 -1
- package/dist/analysis/layers.d.ts +2 -0
- package/dist/analysis/layers.d.ts.map +1 -1
- package/dist/analysis/layers.js +5 -0
- 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 +39 -10
- package/dist/analysis/port-wiring.js.map +1 -1
- 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 +166 -21
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts.map +1 -1
- package/dist/app-map-changes.js +19 -7
- package/dist/app-map-changes.js.map +1 -1
- package/dist/app-map.d.ts.map +1 -1
- package/dist/app-map.js +58 -55
- package/dist/app-map.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/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +89 -21
- package/dist/inspect.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +106 -38
- 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/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/shared.js +3 -3
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +42 -0
- package/src/analysis/layers.ts +9 -0
- package/src/analysis/port-wiring.ts +66 -10
- package/src/analysis/source-index.ts +56 -4
- package/src/analysis/workspace-routes.ts +385 -0
- package/src/analysis/workspace.ts +281 -34
- package/src/app-map-changes.ts +31 -5
- package/src/app-map.ts +75 -75
- package/src/config.ts +33 -0
- package/src/index.ts +25 -0
- package/src/inspect.ts +123 -16
- package/src/lint.ts +146 -43
- package/src/make/shared.ts +1 -1
- package/src/make.ts +31 -1
- package/src/provider-add.ts +2 -2
- package/src/provider-audit.ts +30 -10
- package/src/templates/shared.ts +3 -3
|
@@ -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 {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
resolveConfig,
|
|
10
10
|
} from "../config.js";
|
|
11
11
|
import {
|
|
12
|
+
isAnalysisSourceFile,
|
|
12
13
|
isAnalysisSourceFileExtension,
|
|
13
14
|
sourceFileScriptKind,
|
|
14
15
|
} from "./source-files.js";
|
|
@@ -22,8 +23,23 @@ const ignoredDirectoryNames = new Set([
|
|
|
22
23
|
"node_modules",
|
|
23
24
|
]);
|
|
24
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
|
+
|
|
25
40
|
export type AnalysisWorkspace = {
|
|
26
41
|
targetDir: string;
|
|
42
|
+
projects: AnalysisProject[];
|
|
27
43
|
files: string[];
|
|
28
44
|
fileSet: Set<string>;
|
|
29
45
|
config: ResolvedBeignetConfig;
|
|
@@ -44,24 +60,70 @@ export async function createAnalysisWorkspace(
|
|
|
44
60
|
): Promise<AnalysisWorkspace> {
|
|
45
61
|
const targetDir = path.resolve(cwd);
|
|
46
62
|
await assertDirectory(targetDir);
|
|
47
|
-
const
|
|
48
|
-
const fileSet = new Set(files);
|
|
63
|
+
const appFiles = await listProjectFiles(targetDir);
|
|
49
64
|
const config = options.config
|
|
50
65
|
? resolveConfig(options.config)
|
|
51
|
-
: await loadBeignetConfig(targetDir,
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
targetDir,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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();
|
|
61
121
|
const sources = new Map<string, string>();
|
|
62
122
|
const sourceFiles = new Map<string, ts.SourceFile>();
|
|
63
123
|
|
|
64
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}`);
|
|
65
127
|
const cached = sources.get(file);
|
|
66
128
|
if (cached !== undefined) return cached;
|
|
67
129
|
|
|
@@ -72,6 +134,7 @@ export async function createAnalysisWorkspace(
|
|
|
72
134
|
|
|
73
135
|
return {
|
|
74
136
|
targetDir,
|
|
137
|
+
projects,
|
|
75
138
|
files,
|
|
76
139
|
fileSet,
|
|
77
140
|
config,
|
|
@@ -136,7 +199,11 @@ export async function listProjectFiles(targetDir: string): Promise<string[]> {
|
|
|
136
199
|
export function resolveLocalSourceFile(
|
|
137
200
|
workspace: Pick<
|
|
138
201
|
AnalysisWorkspace,
|
|
139
|
-
|
|
202
|
+
| "compilerOptions"
|
|
203
|
+
| "fileSet"
|
|
204
|
+
| "moduleResolutionCache"
|
|
205
|
+
| "targetDir"
|
|
206
|
+
| "projects"
|
|
140
207
|
>,
|
|
141
208
|
importerFile: string,
|
|
142
209
|
importPath: string,
|
|
@@ -147,14 +214,36 @@ export function resolveLocalSourceFile(
|
|
|
147
214
|
const file = normalizePath(
|
|
148
215
|
path.relative(workspace.targetDir, resolved.resolvedFileName),
|
|
149
216
|
);
|
|
150
|
-
if (
|
|
151
|
-
|
|
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;
|
|
152
237
|
}
|
|
153
238
|
|
|
154
239
|
export function resolveLocalModulePath(
|
|
155
240
|
workspace: Pick<
|
|
156
241
|
AnalysisWorkspace,
|
|
157
|
-
|
|
242
|
+
| "compilerOptions"
|
|
243
|
+
| "fileSet"
|
|
244
|
+
| "moduleResolutionCache"
|
|
245
|
+
| "targetDir"
|
|
246
|
+
| "projects"
|
|
158
247
|
>,
|
|
159
248
|
importerFile: string,
|
|
160
249
|
importPath: string,
|
|
@@ -170,16 +259,34 @@ export function resolveLocalModulePath(
|
|
|
170
259
|
return undefined;
|
|
171
260
|
}
|
|
172
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
|
+
|
|
173
283
|
if (importPath.startsWith(".")) {
|
|
174
284
|
return normalizePath(path.join(path.dirname(importerFile), importPath));
|
|
175
285
|
}
|
|
176
286
|
|
|
177
|
-
const baseUrl = compilerPathsBase(
|
|
178
|
-
workspace.compilerOptions,
|
|
179
|
-
workspace.targetDir,
|
|
180
|
-
);
|
|
287
|
+
const baseUrl = compilerPathsBase(project.compilerOptions, project.targetDir);
|
|
181
288
|
for (const [pattern, targets] of Object.entries(
|
|
182
|
-
|
|
289
|
+
project.compilerOptions.paths ?? {},
|
|
183
290
|
)) {
|
|
184
291
|
if (pattern === "*") continue;
|
|
185
292
|
const wildcardValue = modulePatternWildcard(pattern, importPath);
|
|
@@ -191,14 +298,22 @@ export function resolveLocalModulePath(
|
|
|
191
298
|
const relative = normalizePath(
|
|
192
299
|
path.relative(workspace.targetDir, absolute),
|
|
193
300
|
);
|
|
194
|
-
if (
|
|
301
|
+
if (
|
|
302
|
+
workspace.projects.length > 1 ||
|
|
303
|
+
workspace.projects.some((candidate) =>
|
|
304
|
+
within(absolute, candidate.targetDir),
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
return relative;
|
|
195
308
|
}
|
|
196
309
|
}
|
|
197
310
|
|
|
198
311
|
const relative = normalizePath(
|
|
199
312
|
path.relative(workspace.targetDir, path.resolve(baseUrl, importPath)),
|
|
200
313
|
);
|
|
201
|
-
return
|
|
314
|
+
return workspace.projects.some((candidate) =>
|
|
315
|
+
within(path.resolve(workspace.targetDir, relative), candidate.targetDir),
|
|
316
|
+
)
|
|
202
317
|
? relative
|
|
203
318
|
: undefined;
|
|
204
319
|
}
|
|
@@ -206,13 +321,21 @@ export function resolveLocalModulePath(
|
|
|
206
321
|
export function isLocalModuleSpecifier(
|
|
207
322
|
workspace: Pick<
|
|
208
323
|
AnalysisWorkspace,
|
|
209
|
-
|
|
324
|
+
| "compilerOptions"
|
|
325
|
+
| "fileSet"
|
|
326
|
+
| "moduleResolutionCache"
|
|
327
|
+
| "targetDir"
|
|
328
|
+
| "projects"
|
|
210
329
|
>,
|
|
211
330
|
importerFile: string,
|
|
212
331
|
importPath: string,
|
|
213
332
|
): boolean {
|
|
214
333
|
const extension = path.posix.extname(importPath);
|
|
215
|
-
if (
|
|
334
|
+
if (
|
|
335
|
+
!importPath.startsWith("#") &&
|
|
336
|
+
extension &&
|
|
337
|
+
!isAnalysisSourceFileExtension(extension)
|
|
338
|
+
) {
|
|
216
339
|
return false;
|
|
217
340
|
}
|
|
218
341
|
if (importPath.startsWith(".")) return true;
|
|
@@ -222,12 +345,33 @@ export function isLocalModuleSpecifier(
|
|
|
222
345
|
const file = normalizePath(
|
|
223
346
|
path.relative(workspace.targetDir, resolved.resolvedFileName),
|
|
224
347
|
);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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;
|
|
228
362
|
}
|
|
229
363
|
|
|
230
|
-
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 ?? {});
|
|
231
375
|
if (
|
|
232
376
|
pathPatterns.some(
|
|
233
377
|
(pattern) =>
|
|
@@ -239,7 +383,7 @@ export function isLocalModuleSpecifier(
|
|
|
239
383
|
return true;
|
|
240
384
|
}
|
|
241
385
|
|
|
242
|
-
const baseUrl =
|
|
386
|
+
const baseUrl = project.compilerOptions.baseUrl;
|
|
243
387
|
if (!baseUrl) return false;
|
|
244
388
|
const firstSegment = importPath.split("/")[0];
|
|
245
389
|
const prefix = normalizePath(
|
|
@@ -254,17 +398,18 @@ export function isLocalModuleSpecifier(
|
|
|
254
398
|
function resolveModule(
|
|
255
399
|
workspace: Pick<
|
|
256
400
|
AnalysisWorkspace,
|
|
257
|
-
"compilerOptions" | "moduleResolutionCache" | "targetDir"
|
|
401
|
+
"compilerOptions" | "moduleResolutionCache" | "targetDir" | "projects"
|
|
258
402
|
>,
|
|
259
403
|
importerFile: string,
|
|
260
404
|
importPath: string,
|
|
261
405
|
): ts.ResolvedModuleFull | undefined {
|
|
406
|
+
const project = projectForFile(workspace, importerFile);
|
|
262
407
|
return ts.resolveModuleName(
|
|
263
408
|
importPath,
|
|
264
409
|
path.join(workspace.targetDir, importerFile),
|
|
265
|
-
|
|
410
|
+
project.compilerOptions,
|
|
266
411
|
ts.sys,
|
|
267
|
-
|
|
412
|
+
project.moduleResolutionCache,
|
|
268
413
|
).resolvedModule;
|
|
269
414
|
}
|
|
270
415
|
|
|
@@ -341,3 +486,105 @@ function compilerPathsBase(
|
|
|
341
486
|
compilerOptions.baseUrl ?? optionsWithPathBase.pathsBasePath ?? targetDir
|
|
342
487
|
);
|
|
343
488
|
}
|
|
489
|
+
|
|
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
|
+
);
|
|
590
|
+
}
|
package/src/app-map-changes.ts
CHANGED
|
@@ -216,8 +216,22 @@ export async function mapAppChanges(
|
|
|
216
216
|
createAnalysisWorkspace(targetDir),
|
|
217
217
|
resolveWorkspaceScope(git.repositoryRoot, canonicalTargetDir),
|
|
218
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
|
+
}
|
|
219
226
|
const governingFiles = new Set(
|
|
220
|
-
|
|
227
|
+
[
|
|
228
|
+
...workspace.compilerConfigFiles,
|
|
229
|
+
...workspace.projects.flatMap((project) =>
|
|
230
|
+
project.config.configFile
|
|
231
|
+
? [path.join(project.targetDir, project.config.configFile)]
|
|
232
|
+
: [],
|
|
233
|
+
),
|
|
234
|
+
]
|
|
221
235
|
.map((file) =>
|
|
222
236
|
path.resolve(
|
|
223
237
|
canonicalTargetDir,
|
|
@@ -285,6 +299,11 @@ export async function mapAppChanges(
|
|
|
285
299
|
changedFiles,
|
|
286
300
|
git.repositoryRoot,
|
|
287
301
|
canonicalTargetDir,
|
|
302
|
+
workspace.projects
|
|
303
|
+
.slice(1)
|
|
304
|
+
.map((project) =>
|
|
305
|
+
normalizePath(path.relative(git.repositoryRoot, project.canonicalDir)),
|
|
306
|
+
),
|
|
288
307
|
);
|
|
289
308
|
for (const change of appSourceChanges) {
|
|
290
309
|
const directNodes = nodesByFile.get(change.file) ?? [];
|
|
@@ -750,6 +769,7 @@ function appRelativeSourceChanges(
|
|
|
750
769
|
files: AppChangedFile[],
|
|
751
770
|
repositoryRoot: string,
|
|
752
771
|
canonicalTargetDir: string,
|
|
772
|
+
sourcePackages: string[],
|
|
753
773
|
): Array<{
|
|
754
774
|
file: string;
|
|
755
775
|
repositoryPath: string;
|
|
@@ -764,9 +784,15 @@ function appRelativeSourceChanges(
|
|
|
764
784
|
status: GitChangedFileStatus;
|
|
765
785
|
}> = [];
|
|
766
786
|
for (const changed of files) {
|
|
767
|
-
if (changed.scope !== "app")
|
|
787
|
+
if (changed.scope !== "app" && changed.scope !== "workspace-dependency")
|
|
788
|
+
continue;
|
|
768
789
|
for (const repositoryPath of changedFilePaths(changed)) {
|
|
769
|
-
if (
|
|
790
|
+
if (
|
|
791
|
+
![targetPrefix, ...sourcePackages].some((prefix) =>
|
|
792
|
+
isWithin(repositoryPath, prefix),
|
|
793
|
+
)
|
|
794
|
+
)
|
|
795
|
+
continue;
|
|
770
796
|
const file = targetPrefix
|
|
771
797
|
? normalizePath(path.posix.relative(targetPrefix, repositoryPath))
|
|
772
798
|
: repositoryPath;
|
|
@@ -1378,8 +1404,8 @@ function appWideChangeReasons(
|
|
|
1378
1404
|
const message =
|
|
1379
1405
|
scope === "workspace-dependency"
|
|
1380
1406
|
? scoped.length === 1
|
|
1381
|
-
? `
|
|
1382
|
-
: `${scoped.length}
|
|
1407
|
+
? `Local source dependency ${changedFile} changed.`
|
|
1408
|
+
: `${scoped.length} local source dependency files changed, including ${changedFile}.`
|
|
1383
1409
|
: scoped.length === 1
|
|
1384
1410
|
? `Governing file ${changedFile} changed.`
|
|
1385
1411
|
: `${scoped.length} governing files changed, including ${changedFile}.`;
|