@chrisdudek/yg 4.0.1 → 4.0.2
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/bin.js +183 -205
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2346,6 +2346,10 @@ function normalizeProjectRelativePath(projectRoot, rawPath) {
|
|
|
2346
2346
|
function projectRootFromGraph(yggRootPath) {
|
|
2347
2347
|
return path10.dirname(yggRootPath);
|
|
2348
2348
|
}
|
|
2349
|
+
function resolveFileArg(cwd, repoRoot, rawPath) {
|
|
2350
|
+
const absolute = path10.resolve(cwd, rawPath.trim());
|
|
2351
|
+
return path10.relative(repoRoot, absolute).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2352
|
+
}
|
|
2349
2353
|
|
|
2350
2354
|
// src/core/graph-loader.ts
|
|
2351
2355
|
function toModelPath(absolutePath, modelDir) {
|
|
@@ -2910,8 +2914,160 @@ function formatFileContext(data) {
|
|
|
2910
2914
|
}
|
|
2911
2915
|
|
|
2912
2916
|
// src/core/validator.ts
|
|
2913
|
-
import { readdir as
|
|
2917
|
+
import { readdir as readdir6 } from "fs/promises";
|
|
2918
|
+
import path14 from "path";
|
|
2919
|
+
|
|
2920
|
+
// src/utils/hash.ts
|
|
2921
|
+
import { readFile as readFile14, readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
2914
2922
|
import path13 from "path";
|
|
2923
|
+
import { createHash } from "crypto";
|
|
2924
|
+
import { createRequire } from "module";
|
|
2925
|
+
var require2 = createRequire(import.meta.url);
|
|
2926
|
+
var ignoreFactory = require2("ignore");
|
|
2927
|
+
async function hashFile(filePath) {
|
|
2928
|
+
const content = await readFile14(filePath);
|
|
2929
|
+
return createHash("sha256").update(content).digest("hex");
|
|
2930
|
+
}
|
|
2931
|
+
async function loadRootGitignoreStack(projectRoot) {
|
|
2932
|
+
if (!projectRoot) return [];
|
|
2933
|
+
try {
|
|
2934
|
+
const content = await readFile14(path13.join(projectRoot, ".gitignore"), "utf-8");
|
|
2935
|
+
const matcher = ignoreFactory();
|
|
2936
|
+
matcher.add(content);
|
|
2937
|
+
return [{ basePath: projectRoot, matcher }];
|
|
2938
|
+
} catch {
|
|
2939
|
+
return [];
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
function isIgnoredByStack(candidatePath, stack) {
|
|
2943
|
+
for (const { basePath, matcher } of stack) {
|
|
2944
|
+
const relativePath = path13.relative(basePath, candidatePath);
|
|
2945
|
+
if (relativePath === "" || relativePath.startsWith("..")) continue;
|
|
2946
|
+
if (matcher.ignores(relativePath) || matcher.ignores(relativePath + "/")) return true;
|
|
2947
|
+
}
|
|
2948
|
+
return false;
|
|
2949
|
+
}
|
|
2950
|
+
function hashString(content) {
|
|
2951
|
+
return createHash("sha256").update(content).digest("hex");
|
|
2952
|
+
}
|
|
2953
|
+
async function hashTrackedFiles(projectRoot, trackedFiles, storedFileData, excludePrefixes) {
|
|
2954
|
+
const fileHashes = {};
|
|
2955
|
+
const fileMtimes = {};
|
|
2956
|
+
const gitignoreStack = await loadRootGitignoreStack(projectRoot);
|
|
2957
|
+
const allFiles = [];
|
|
2958
|
+
for (const tf of trackedFiles) {
|
|
2959
|
+
if (tf.syntheticHash) {
|
|
2960
|
+
fileHashes[tf.path] = tf.syntheticHash;
|
|
2961
|
+
continue;
|
|
2962
|
+
}
|
|
2963
|
+
const absPath = path13.join(projectRoot, tf.path);
|
|
2964
|
+
try {
|
|
2965
|
+
const st = await stat4(absPath);
|
|
2966
|
+
if (st.isDirectory()) {
|
|
2967
|
+
const dirEntries = await collectDirectoryFilePaths(absPath, absPath, {
|
|
2968
|
+
projectRoot,
|
|
2969
|
+
gitignoreStack
|
|
2970
|
+
});
|
|
2971
|
+
for (const entry of dirEntries) {
|
|
2972
|
+
allFiles.push({
|
|
2973
|
+
relPath: path13.join(tf.path, entry.relPath).replace(/\\/g, "/").replace(/\/+$/, ""),
|
|
2974
|
+
absPath: entry.absPath,
|
|
2975
|
+
mtimeMs: entry.mtimeMs
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
} else {
|
|
2979
|
+
allFiles.push({ relPath: tf.path, absPath, mtimeMs: st.mtimeMs });
|
|
2980
|
+
}
|
|
2981
|
+
} catch {
|
|
2982
|
+
continue;
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
const filtered = excludePrefixes?.length ? allFiles.filter((entry) => !excludePrefixes.some((prefix) => entry.relPath === prefix || entry.relPath.startsWith(prefix + "/"))) : allFiles;
|
|
2986
|
+
const dirty = [];
|
|
2987
|
+
for (const entry of filtered) {
|
|
2988
|
+
const storedMtime = storedFileData?.mtimes[entry.relPath];
|
|
2989
|
+
const storedHash = storedFileData?.hashes[entry.relPath];
|
|
2990
|
+
if (storedMtime !== void 0 && storedHash !== void 0 && entry.mtimeMs === storedMtime) {
|
|
2991
|
+
fileHashes[entry.relPath] = storedHash;
|
|
2992
|
+
} else {
|
|
2993
|
+
dirty.push(entry);
|
|
2994
|
+
}
|
|
2995
|
+
fileMtimes[entry.relPath] = entry.mtimeMs;
|
|
2996
|
+
}
|
|
2997
|
+
const BATCH_SIZE = 256;
|
|
2998
|
+
for (let i = 0; i < dirty.length; i += BATCH_SIZE) {
|
|
2999
|
+
const batch = dirty.slice(i, i + BATCH_SIZE);
|
|
3000
|
+
const hashes = await Promise.all(batch.map((e) => hashFile(e.absPath)));
|
|
3001
|
+
for (let j = 0; j < batch.length; j++) {
|
|
3002
|
+
fileHashes[batch[j].relPath] = hashes[j];
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
const sorted = Object.entries(fileHashes).sort(([a], [b]) => a.localeCompare(b));
|
|
3006
|
+
const digest = sorted.map(([p2, h]) => `${p2}:${h}`).join("\n");
|
|
3007
|
+
const canonicalHash = hashString(digest);
|
|
3008
|
+
return { canonicalHash, fileHashes, fileMtimes };
|
|
3009
|
+
}
|
|
3010
|
+
async function collectDirectoryFilePaths(directoryPath, rootDirectoryPath, options) {
|
|
3011
|
+
let stack = options.gitignoreStack ?? [];
|
|
3012
|
+
try {
|
|
3013
|
+
const localContent = await readFile14(path13.join(directoryPath, ".gitignore"), "utf-8");
|
|
3014
|
+
const localMatcher = ignoreFactory();
|
|
3015
|
+
localMatcher.add(localContent);
|
|
3016
|
+
stack = [...stack, { basePath: directoryPath, matcher: localMatcher }];
|
|
3017
|
+
} catch {
|
|
3018
|
+
}
|
|
3019
|
+
const entries = await readdir5(directoryPath, { withFileTypes: true });
|
|
3020
|
+
const dirs = [];
|
|
3021
|
+
const files = [];
|
|
3022
|
+
for (const entry of entries) {
|
|
3023
|
+
const absoluteChildPath = path13.join(directoryPath, entry.name);
|
|
3024
|
+
if (isIgnoredByStack(absoluteChildPath, stack)) continue;
|
|
3025
|
+
if (entry.isDirectory()) dirs.push(absoluteChildPath);
|
|
3026
|
+
else if (entry.isFile()) files.push(absoluteChildPath);
|
|
3027
|
+
}
|
|
3028
|
+
const [dirResults, fileStats] = await Promise.all([
|
|
3029
|
+
Promise.all(dirs.map((d) => collectDirectoryFilePaths(d, rootDirectoryPath, {
|
|
3030
|
+
projectRoot: options.projectRoot,
|
|
3031
|
+
gitignoreStack: stack
|
|
3032
|
+
}))),
|
|
3033
|
+
Promise.all(files.map(async (f) => {
|
|
3034
|
+
const fileStat = await stat4(f);
|
|
3035
|
+
return {
|
|
3036
|
+
relPath: path13.relative(rootDirectoryPath, f).replace(/\\/g, "/").replace(/\/+$/, ""),
|
|
3037
|
+
absPath: f,
|
|
3038
|
+
mtimeMs: fileStat.mtimeMs
|
|
3039
|
+
};
|
|
3040
|
+
}))
|
|
3041
|
+
]);
|
|
3042
|
+
const result = [];
|
|
3043
|
+
for (const nested of dirResults) result.push(...nested);
|
|
3044
|
+
result.push(...fileStats);
|
|
3045
|
+
return result;
|
|
3046
|
+
}
|
|
3047
|
+
async function expandMappingPaths(projectRoot, mappingPaths) {
|
|
3048
|
+
const gitignoreStack = await loadRootGitignoreStack(projectRoot);
|
|
3049
|
+
const result = [];
|
|
3050
|
+
for (const mp of mappingPaths) {
|
|
3051
|
+
const absPath = path13.join(projectRoot, mp);
|
|
3052
|
+
try {
|
|
3053
|
+
const st = await stat4(absPath);
|
|
3054
|
+
if (st.isDirectory()) {
|
|
3055
|
+
const dirEntries = await collectDirectoryFilePaths(absPath, absPath, {
|
|
3056
|
+
projectRoot,
|
|
3057
|
+
gitignoreStack
|
|
3058
|
+
});
|
|
3059
|
+
for (const entry of dirEntries) {
|
|
3060
|
+
result.push(path13.join(mp, entry.relPath).replace(/\\/g, "/").replace(/\/+$/, ""));
|
|
3061
|
+
}
|
|
3062
|
+
} else {
|
|
3063
|
+
result.push(mp);
|
|
3064
|
+
}
|
|
3065
|
+
} catch {
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
return result;
|
|
3070
|
+
}
|
|
2915
3071
|
|
|
2916
3072
|
// src/formatters/message-builder.ts
|
|
2917
3073
|
function buildIssueMessage(msg) {
|
|
@@ -2972,10 +3128,11 @@ async function validate(graph, scope = "all") {
|
|
|
2972
3128
|
issues.push(...checkOrphanedAspects(graph));
|
|
2973
3129
|
let filtered = issues;
|
|
2974
3130
|
let nodesScanned = graph.nodes.size;
|
|
2975
|
-
|
|
2976
|
-
|
|
3131
|
+
const normalizedScope = scope.trim().replace(/\\/g, "/").replace(/\/+$/, "");
|
|
3132
|
+
if (normalizedScope !== "all" && normalizedScope) {
|
|
3133
|
+
if (!graph.nodes.has(normalizedScope)) {
|
|
2977
3134
|
const parseError = (graph.nodeParseErrors ?? []).find(
|
|
2978
|
-
(e) => e.nodePath ===
|
|
3135
|
+
(e) => e.nodePath === normalizedScope || normalizedScope.startsWith(e.nodePath + "/")
|
|
2979
3136
|
);
|
|
2980
3137
|
if (parseError) {
|
|
2981
3138
|
return {
|
|
@@ -2990,13 +3147,13 @@ async function validate(graph, scope = "all") {
|
|
|
2990
3147
|
};
|
|
2991
3148
|
}
|
|
2992
3149
|
return {
|
|
2993
|
-
issues: [{ severity: "error", rule: "invalid-scope", message: buildIssueMessage({ what: `Node not found: ${
|
|
3150
|
+
issues: [{ severity: "error", rule: "invalid-scope", message: buildIssueMessage({ what: `Node not found: ${normalizedScope}`, why: "Validation scope references a node that does not exist in the graph.", next: "Check the node path and try again." }) }],
|
|
2994
3151
|
nodesScanned: 0
|
|
2995
3152
|
};
|
|
2996
3153
|
}
|
|
2997
|
-
const scopePrefix =
|
|
2998
|
-
filtered = issues.filter((i) => !i.nodePath || i.nodePath ===
|
|
2999
|
-
nodesScanned = [...graph.nodes.keys()].filter((p2) => p2 ===
|
|
3154
|
+
const scopePrefix = normalizedScope + "/";
|
|
3155
|
+
filtered = issues.filter((i) => !i.nodePath || i.nodePath === normalizedScope || i.nodePath.startsWith(scopePrefix));
|
|
3156
|
+
nodesScanned = [...graph.nodes.keys()].filter((p2) => p2 === normalizedScope || p2.startsWith(scopePrefix)).length;
|
|
3000
3157
|
}
|
|
3001
3158
|
return { issues: filtered, nodesScanned };
|
|
3002
3159
|
}
|
|
@@ -3313,12 +3470,12 @@ function checkMappingOverlap(graph) {
|
|
|
3313
3470
|
}
|
|
3314
3471
|
async function checkMappingPathsExist(graph) {
|
|
3315
3472
|
const issues = [];
|
|
3316
|
-
const projectRoot =
|
|
3473
|
+
const projectRoot = path14.dirname(graph.rootPath);
|
|
3317
3474
|
const { access: access3 } = await import("fs/promises");
|
|
3318
3475
|
for (const [nodePath, node] of graph.nodes) {
|
|
3319
3476
|
const mappingPaths = normalizeMappingPaths(node.meta.mapping);
|
|
3320
3477
|
for (const mp of mappingPaths) {
|
|
3321
|
-
const absPath =
|
|
3478
|
+
const absPath = path14.join(projectRoot, mp);
|
|
3322
3479
|
try {
|
|
3323
3480
|
await access3(absPath);
|
|
3324
3481
|
} catch {
|
|
@@ -3362,13 +3519,13 @@ function checkBrokenFlowRefs(graph) {
|
|
|
3362
3519
|
async function checkWideNodes(graph) {
|
|
3363
3520
|
const issues = [];
|
|
3364
3521
|
const maxFiles = graph.config.quality?.max_mapping_source_files ?? 10;
|
|
3365
|
-
const projectRoot =
|
|
3522
|
+
const projectRoot = path14.dirname(graph.rootPath);
|
|
3366
3523
|
for (const [nodePath, node] of graph.nodes) {
|
|
3367
3524
|
const effectiveAspects = computeEffectiveAspects(node, graph);
|
|
3368
3525
|
if (effectiveAspects.size === 0) continue;
|
|
3369
3526
|
const mappingPaths = normalizeMappingPaths(node.meta.mapping);
|
|
3370
3527
|
if (mappingPaths.length === 0) continue;
|
|
3371
|
-
const sourceFiles = await
|
|
3528
|
+
const sourceFiles = await expandMappingPaths(projectRoot, mappingPaths);
|
|
3372
3529
|
if (sourceFiles.length <= maxFiles) continue;
|
|
3373
3530
|
issues.push({
|
|
3374
3531
|
severity: "warning",
|
|
@@ -3483,9 +3640,9 @@ function checkSchemas(graph) {
|
|
|
3483
3640
|
}
|
|
3484
3641
|
async function checkDirectoriesHaveNodeYaml(graph) {
|
|
3485
3642
|
const issues = [];
|
|
3486
|
-
const modelDir =
|
|
3643
|
+
const modelDir = path14.join(graph.rootPath, "model");
|
|
3487
3644
|
async function scanDir(dirPath, segments) {
|
|
3488
|
-
const entries = (await
|
|
3645
|
+
const entries = (await readdir6(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
3489
3646
|
const hasNodeYaml = entries.some((e) => e.isFile() && e.name === "yg-node.yaml");
|
|
3490
3647
|
const hasFiles = entries.some((e) => e.isFile());
|
|
3491
3648
|
const graphPath = segments.join("/");
|
|
@@ -3507,47 +3664,20 @@ async function checkDirectoriesHaveNodeYaml(graph) {
|
|
|
3507
3664
|
for (const entry of entries) {
|
|
3508
3665
|
if (!entry.isDirectory()) continue;
|
|
3509
3666
|
if (entry.name.startsWith(".")) continue;
|
|
3510
|
-
await scanDir(
|
|
3667
|
+
await scanDir(path14.join(dirPath, entry.name), [...segments, entry.name]);
|
|
3511
3668
|
}
|
|
3512
3669
|
}
|
|
3513
3670
|
try {
|
|
3514
|
-
const rootEntries = (await
|
|
3671
|
+
const rootEntries = (await readdir6(modelDir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
3515
3672
|
for (const entry of rootEntries) {
|
|
3516
3673
|
if (!entry.isDirectory()) continue;
|
|
3517
3674
|
if (entry.name.startsWith(".")) continue;
|
|
3518
|
-
await scanDir(
|
|
3675
|
+
await scanDir(path14.join(modelDir, entry.name), [entry.name]);
|
|
3519
3676
|
}
|
|
3520
3677
|
} catch {
|
|
3521
3678
|
}
|
|
3522
3679
|
return issues;
|
|
3523
3680
|
}
|
|
3524
|
-
async function expandMappingToFiles(projectRoot, mappingPaths) {
|
|
3525
|
-
const files = [];
|
|
3526
|
-
async function collectFiles(absPath) {
|
|
3527
|
-
try {
|
|
3528
|
-
const s = await stat4(absPath);
|
|
3529
|
-
if (s.isFile()) {
|
|
3530
|
-
files.push(absPath);
|
|
3531
|
-
} else if (s.isDirectory()) {
|
|
3532
|
-
const entries = await readdir5(absPath, { withFileTypes: true });
|
|
3533
|
-
for (const entry of entries) {
|
|
3534
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
3535
|
-
const entryPath = path13.join(absPath, entry.name);
|
|
3536
|
-
if (entry.isFile()) {
|
|
3537
|
-
files.push(entryPath);
|
|
3538
|
-
} else if (entry.isDirectory()) {
|
|
3539
|
-
await collectFiles(entryPath);
|
|
3540
|
-
}
|
|
3541
|
-
}
|
|
3542
|
-
}
|
|
3543
|
-
} catch {
|
|
3544
|
-
}
|
|
3545
|
-
}
|
|
3546
|
-
for (const mp of mappingPaths) {
|
|
3547
|
-
await collectFiles(path13.join(projectRoot, mp));
|
|
3548
|
-
}
|
|
3549
|
-
return files;
|
|
3550
|
-
}
|
|
3551
3681
|
function checkMissingDescriptions(graph) {
|
|
3552
3682
|
const issues = [];
|
|
3553
3683
|
for (const [nodePath, node] of graph.nodes) {
|
|
@@ -3815,7 +3945,7 @@ function checkOrphanedAspects(graph) {
|
|
|
3815
3945
|
}
|
|
3816
3946
|
|
|
3817
3947
|
// src/cli/owner.ts
|
|
3818
|
-
import
|
|
3948
|
+
import path15 from "path";
|
|
3819
3949
|
import { access } from "fs/promises";
|
|
3820
3950
|
import chalk2 from "chalk";
|
|
3821
3951
|
function normalizeForMatch(inputPath) {
|
|
@@ -3846,12 +3976,10 @@ function registerOwnerCommand(program2) {
|
|
|
3846
3976
|
const graph = await loadGraph(cwd);
|
|
3847
3977
|
initDebugLog(graph.rootPath, graph.config.debug ?? false);
|
|
3848
3978
|
const repoRoot = projectRootFromGraph(graph.rootPath);
|
|
3849
|
-
const
|
|
3850
|
-
const absolute = path14.resolve(cwd, rawPath);
|
|
3851
|
-
const repoRelative = path14.relative(repoRoot, absolute).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
3979
|
+
const repoRelative = resolveFileArg(cwd, repoRoot, options.file);
|
|
3852
3980
|
const result = findOwner(graph, repoRoot, repoRelative);
|
|
3853
3981
|
if (!result.nodePath) {
|
|
3854
|
-
const absPath =
|
|
3982
|
+
const absPath = path15.resolve(repoRoot, result.file);
|
|
3855
3983
|
let exists = true;
|
|
3856
3984
|
try {
|
|
3857
3985
|
await access(absPath);
|
|
@@ -3891,158 +4019,6 @@ function registerOwnerCommand(program2) {
|
|
|
3891
4019
|
});
|
|
3892
4020
|
}
|
|
3893
4021
|
|
|
3894
|
-
// src/utils/hash.ts
|
|
3895
|
-
import { readFile as readFile14, readdir as readdir6, stat as stat5 } from "fs/promises";
|
|
3896
|
-
import path15 from "path";
|
|
3897
|
-
import { createHash } from "crypto";
|
|
3898
|
-
import { createRequire } from "module";
|
|
3899
|
-
var require2 = createRequire(import.meta.url);
|
|
3900
|
-
var ignoreFactory = require2("ignore");
|
|
3901
|
-
async function hashFile(filePath) {
|
|
3902
|
-
const content = await readFile14(filePath);
|
|
3903
|
-
return createHash("sha256").update(content).digest("hex");
|
|
3904
|
-
}
|
|
3905
|
-
async function loadRootGitignoreStack(projectRoot) {
|
|
3906
|
-
if (!projectRoot) return [];
|
|
3907
|
-
try {
|
|
3908
|
-
const content = await readFile14(path15.join(projectRoot, ".gitignore"), "utf-8");
|
|
3909
|
-
const matcher = ignoreFactory();
|
|
3910
|
-
matcher.add(content);
|
|
3911
|
-
return [{ basePath: projectRoot, matcher }];
|
|
3912
|
-
} catch {
|
|
3913
|
-
return [];
|
|
3914
|
-
}
|
|
3915
|
-
}
|
|
3916
|
-
function isIgnoredByStack(candidatePath, stack) {
|
|
3917
|
-
for (const { basePath, matcher } of stack) {
|
|
3918
|
-
const relativePath = path15.relative(basePath, candidatePath);
|
|
3919
|
-
if (relativePath === "" || relativePath.startsWith("..")) continue;
|
|
3920
|
-
if (matcher.ignores(relativePath) || matcher.ignores(relativePath + "/")) return true;
|
|
3921
|
-
}
|
|
3922
|
-
return false;
|
|
3923
|
-
}
|
|
3924
|
-
function hashString(content) {
|
|
3925
|
-
return createHash("sha256").update(content).digest("hex");
|
|
3926
|
-
}
|
|
3927
|
-
async function hashTrackedFiles(projectRoot, trackedFiles, storedFileData, excludePrefixes) {
|
|
3928
|
-
const fileHashes = {};
|
|
3929
|
-
const fileMtimes = {};
|
|
3930
|
-
const gitignoreStack = await loadRootGitignoreStack(projectRoot);
|
|
3931
|
-
const allFiles = [];
|
|
3932
|
-
for (const tf of trackedFiles) {
|
|
3933
|
-
if (tf.syntheticHash) {
|
|
3934
|
-
fileHashes[tf.path] = tf.syntheticHash;
|
|
3935
|
-
continue;
|
|
3936
|
-
}
|
|
3937
|
-
const absPath = path15.join(projectRoot, tf.path);
|
|
3938
|
-
try {
|
|
3939
|
-
const st = await stat5(absPath);
|
|
3940
|
-
if (st.isDirectory()) {
|
|
3941
|
-
const dirEntries = await collectDirectoryFilePaths(absPath, absPath, {
|
|
3942
|
-
projectRoot,
|
|
3943
|
-
gitignoreStack
|
|
3944
|
-
});
|
|
3945
|
-
for (const entry of dirEntries) {
|
|
3946
|
-
allFiles.push({
|
|
3947
|
-
relPath: path15.join(tf.path, entry.relPath).replace(/\\/g, "/").replace(/\/+$/, ""),
|
|
3948
|
-
absPath: entry.absPath,
|
|
3949
|
-
mtimeMs: entry.mtimeMs
|
|
3950
|
-
});
|
|
3951
|
-
}
|
|
3952
|
-
} else {
|
|
3953
|
-
allFiles.push({ relPath: tf.path, absPath, mtimeMs: st.mtimeMs });
|
|
3954
|
-
}
|
|
3955
|
-
} catch {
|
|
3956
|
-
continue;
|
|
3957
|
-
}
|
|
3958
|
-
}
|
|
3959
|
-
const filtered = excludePrefixes?.length ? allFiles.filter((entry) => !excludePrefixes.some((prefix) => entry.relPath === prefix || entry.relPath.startsWith(prefix + "/"))) : allFiles;
|
|
3960
|
-
const dirty = [];
|
|
3961
|
-
for (const entry of filtered) {
|
|
3962
|
-
const storedMtime = storedFileData?.mtimes[entry.relPath];
|
|
3963
|
-
const storedHash = storedFileData?.hashes[entry.relPath];
|
|
3964
|
-
if (storedMtime !== void 0 && storedHash !== void 0 && entry.mtimeMs === storedMtime) {
|
|
3965
|
-
fileHashes[entry.relPath] = storedHash;
|
|
3966
|
-
} else {
|
|
3967
|
-
dirty.push(entry);
|
|
3968
|
-
}
|
|
3969
|
-
fileMtimes[entry.relPath] = entry.mtimeMs;
|
|
3970
|
-
}
|
|
3971
|
-
const BATCH_SIZE = 256;
|
|
3972
|
-
for (let i = 0; i < dirty.length; i += BATCH_SIZE) {
|
|
3973
|
-
const batch = dirty.slice(i, i + BATCH_SIZE);
|
|
3974
|
-
const hashes = await Promise.all(batch.map((e) => hashFile(e.absPath)));
|
|
3975
|
-
for (let j = 0; j < batch.length; j++) {
|
|
3976
|
-
fileHashes[batch[j].relPath] = hashes[j];
|
|
3977
|
-
}
|
|
3978
|
-
}
|
|
3979
|
-
const sorted = Object.entries(fileHashes).sort(([a], [b]) => a.localeCompare(b));
|
|
3980
|
-
const digest = sorted.map(([p2, h]) => `${p2}:${h}`).join("\n");
|
|
3981
|
-
const canonicalHash = hashString(digest);
|
|
3982
|
-
return { canonicalHash, fileHashes, fileMtimes };
|
|
3983
|
-
}
|
|
3984
|
-
async function collectDirectoryFilePaths(directoryPath, rootDirectoryPath, options) {
|
|
3985
|
-
let stack = options.gitignoreStack ?? [];
|
|
3986
|
-
try {
|
|
3987
|
-
const localContent = await readFile14(path15.join(directoryPath, ".gitignore"), "utf-8");
|
|
3988
|
-
const localMatcher = ignoreFactory();
|
|
3989
|
-
localMatcher.add(localContent);
|
|
3990
|
-
stack = [...stack, { basePath: directoryPath, matcher: localMatcher }];
|
|
3991
|
-
} catch {
|
|
3992
|
-
}
|
|
3993
|
-
const entries = await readdir6(directoryPath, { withFileTypes: true });
|
|
3994
|
-
const dirs = [];
|
|
3995
|
-
const files = [];
|
|
3996
|
-
for (const entry of entries) {
|
|
3997
|
-
const absoluteChildPath = path15.join(directoryPath, entry.name);
|
|
3998
|
-
if (isIgnoredByStack(absoluteChildPath, stack)) continue;
|
|
3999
|
-
if (entry.isDirectory()) dirs.push(absoluteChildPath);
|
|
4000
|
-
else if (entry.isFile()) files.push(absoluteChildPath);
|
|
4001
|
-
}
|
|
4002
|
-
const [dirResults, fileStats] = await Promise.all([
|
|
4003
|
-
Promise.all(dirs.map((d) => collectDirectoryFilePaths(d, rootDirectoryPath, {
|
|
4004
|
-
projectRoot: options.projectRoot,
|
|
4005
|
-
gitignoreStack: stack
|
|
4006
|
-
}))),
|
|
4007
|
-
Promise.all(files.map(async (f) => {
|
|
4008
|
-
const fileStat = await stat5(f);
|
|
4009
|
-
return {
|
|
4010
|
-
relPath: path15.relative(rootDirectoryPath, f).replace(/\\/g, "/").replace(/\/+$/, ""),
|
|
4011
|
-
absPath: f,
|
|
4012
|
-
mtimeMs: fileStat.mtimeMs
|
|
4013
|
-
};
|
|
4014
|
-
}))
|
|
4015
|
-
]);
|
|
4016
|
-
const result = [];
|
|
4017
|
-
for (const nested of dirResults) result.push(...nested);
|
|
4018
|
-
result.push(...fileStats);
|
|
4019
|
-
return result;
|
|
4020
|
-
}
|
|
4021
|
-
async function expandMappingPaths(projectRoot, mappingPaths) {
|
|
4022
|
-
const gitignoreStack = await loadRootGitignoreStack(projectRoot);
|
|
4023
|
-
const result = [];
|
|
4024
|
-
for (const mp of mappingPaths) {
|
|
4025
|
-
const absPath = path15.join(projectRoot, mp);
|
|
4026
|
-
try {
|
|
4027
|
-
const st = await stat5(absPath);
|
|
4028
|
-
if (st.isDirectory()) {
|
|
4029
|
-
const dirEntries = await collectDirectoryFilePaths(absPath, absPath, {
|
|
4030
|
-
projectRoot,
|
|
4031
|
-
gitignoreStack
|
|
4032
|
-
});
|
|
4033
|
-
for (const entry of dirEntries) {
|
|
4034
|
-
result.push(path15.join(mp, entry.relPath).replace(/\\/g, "/").replace(/\/+$/, ""));
|
|
4035
|
-
}
|
|
4036
|
-
} else {
|
|
4037
|
-
result.push(mp);
|
|
4038
|
-
}
|
|
4039
|
-
} catch {
|
|
4040
|
-
continue;
|
|
4041
|
-
}
|
|
4042
|
-
}
|
|
4043
|
-
return result;
|
|
4044
|
-
}
|
|
4045
|
-
|
|
4046
4022
|
// src/cli/build-context.ts
|
|
4047
4023
|
function findCandidateNodes(graph, unmappedFile) {
|
|
4048
4024
|
const dir = unmappedFile.replace(/\/[^/]+$/, "");
|
|
@@ -4108,7 +4084,8 @@ function registerBuildCommand(program2) {
|
|
|
4108
4084
|
let resolvedFilePath;
|
|
4109
4085
|
if (options.file) {
|
|
4110
4086
|
const repoRoot = projectRootFromGraph(graph.rootPath);
|
|
4111
|
-
const
|
|
4087
|
+
const repoRelative = resolveFileArg(process.cwd(), repoRoot, options.file);
|
|
4088
|
+
const result = findOwner(graph, repoRoot, repoRelative);
|
|
4112
4089
|
if (!result.nodePath) {
|
|
4113
4090
|
const candidates = findCandidateNodes(graph, result.file);
|
|
4114
4091
|
if (candidates.length > 0) {
|
|
@@ -4201,7 +4178,7 @@ import chalk4 from "chalk";
|
|
|
4201
4178
|
import path20 from "path";
|
|
4202
4179
|
|
|
4203
4180
|
// src/io/drift-state-store.ts
|
|
4204
|
-
import { readFile as readFile15, writeFile as writeFile5, stat as
|
|
4181
|
+
import { readFile as readFile15, writeFile as writeFile5, stat as stat5, readdir as readdir7, mkdir as mkdir3, rm as rm2 } from "fs/promises";
|
|
4205
4182
|
import path16 from "path";
|
|
4206
4183
|
var DRIFT_STATE_DIR = ".drift-state";
|
|
4207
4184
|
function nodeStatePath(yggRoot, nodePath) {
|
|
@@ -4281,7 +4258,7 @@ async function readDriftState(yggRoot) {
|
|
|
4281
4258
|
const driftPath = path16.join(yggRoot, DRIFT_STATE_DIR);
|
|
4282
4259
|
let driftStat;
|
|
4283
4260
|
try {
|
|
4284
|
-
driftStat = await
|
|
4261
|
+
driftStat = await stat5(driftPath);
|
|
4285
4262
|
} catch (err) {
|
|
4286
4263
|
debugWrite(`[drift-state-store] readDriftState stat: ${err.message}`);
|
|
4287
4264
|
return {};
|
|
@@ -6238,7 +6215,8 @@ function registerImpactCommand(program2) {
|
|
|
6238
6215
|
initDebugLog(graph.rootPath, graph.config.debug ?? false);
|
|
6239
6216
|
if (options.file) {
|
|
6240
6217
|
const repoRoot = projectRootFromGraph(graph.rootPath);
|
|
6241
|
-
const
|
|
6218
|
+
const repoRelative = resolveFileArg(process.cwd(), repoRoot, options.file);
|
|
6219
|
+
const result = findOwner(graph, repoRoot, repoRelative);
|
|
6242
6220
|
if (!result.nodePath) {
|
|
6243
6221
|
process.stderr.write(chalk6.red(`${result.file} -> no graph coverage
|
|
6244
6222
|
`));
|