@outcomeeng/spx 0.6.4 → 0.6.6
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/README.md +4 -3
- package/dist/cli.js +648 -220
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -4036,7 +4036,14 @@ function sessionStoreRunner(probe) {
|
|
|
4036
4036
|
// src/domains/hooks/session-start.ts
|
|
4037
4037
|
var HOOK_SESSION_START_PAYLOAD = {
|
|
4038
4038
|
CWD: "cwd",
|
|
4039
|
-
SESSION_ID: "session_id"
|
|
4039
|
+
SESSION_ID: "session_id",
|
|
4040
|
+
SOURCE: "source"
|
|
4041
|
+
};
|
|
4042
|
+
var HOOK_SESSION_START_SOURCE = {
|
|
4043
|
+
CLEAR: "clear",
|
|
4044
|
+
COMPACT: "compact",
|
|
4045
|
+
RESUME: "resume",
|
|
4046
|
+
STARTUP: "startup"
|
|
4040
4047
|
};
|
|
4041
4048
|
var HOOK_SESSION_START_ENV = {
|
|
4042
4049
|
CLAUDE_ENV_FILE: "CLAUDE_ENV_FILE",
|
|
@@ -4080,7 +4087,8 @@ function parseHookSessionStartPayload(content) {
|
|
|
4080
4087
|
ok: true,
|
|
4081
4088
|
value: {
|
|
4082
4089
|
cwd: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.CWD]),
|
|
4083
|
-
sessionId: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SESSION_ID])
|
|
4090
|
+
sessionId: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SESSION_ID]),
|
|
4091
|
+
source: nonEmptyString(record6[HOOK_SESSION_START_PAYLOAD.SOURCE])
|
|
4084
4092
|
}
|
|
4085
4093
|
};
|
|
4086
4094
|
}
|
|
@@ -4090,6 +4098,15 @@ function resolveHookSessionStartSessionId(payload, env) {
|
|
|
4090
4098
|
function resolveHookSessionStartProductDir(payload, cwd) {
|
|
4091
4099
|
return payload.cwd ?? cwd;
|
|
4092
4100
|
}
|
|
4101
|
+
var NO_STARTUP_DIRECTIVE = "";
|
|
4102
|
+
var HOOK_COMPACT_FOUNDATION_DIRECTIVE = [
|
|
4103
|
+
"Spec-tree foundation was reset by this compaction.",
|
|
4104
|
+
"Before any spec-governed action, including resuming an in-flight PR, /apply, or /handoff, re-invoke /understand then /contextualize on every spec node still in scope (not just the next one) before any gh/git archaeology or reading spec-governed source.",
|
|
4105
|
+
"The pre-compaction skill text in this summary is a historical record, not a live tool."
|
|
4106
|
+
].join("\n");
|
|
4107
|
+
function renderSessionStartStdout(source) {
|
|
4108
|
+
return source === HOOK_SESSION_START_SOURCE.COMPACT ? HOOK_COMPACT_FOUNDATION_DIRECTIVE : NO_STARTUP_DIRECTIVE;
|
|
4109
|
+
}
|
|
4093
4110
|
function resolveHookSessionStartEnvFile(env, explicitEnvFile) {
|
|
4094
4111
|
return nonEmptyString(explicitEnvFile) ?? nonEmptyString(env[HOOK_SESSION_START_ENV.CLAUDE_ENV_FILE]);
|
|
4095
4112
|
}
|
|
@@ -4954,7 +4971,8 @@ function isValidPid(pid) {
|
|
|
4954
4971
|
// src/domains/worktree/resolve.ts
|
|
4955
4972
|
import { dirname as dirname4, resolve as resolve4 } from "path";
|
|
4956
4973
|
var WORKTREE_RESOLVE_ERROR = {
|
|
4957
|
-
NOT_A_WORKTREE: "path resolves to no worktree"
|
|
4974
|
+
NOT_A_WORKTREE: "path resolves to no worktree",
|
|
4975
|
+
WORKTREE_LIST_UNAVAILABLE: "git worktree list is unavailable"
|
|
4958
4976
|
};
|
|
4959
4977
|
async function resolveWorktreesDir(options) {
|
|
4960
4978
|
if (options.worktreesDir !== void 0) return options.worktreesDir;
|
|
@@ -4966,6 +4984,22 @@ async function resolveCurrentWorktreeName(options) {
|
|
|
4966
4984
|
const worktree = await detectWorktreeProductRoot(options.cwd, options.gitDeps);
|
|
4967
4985
|
return worktreeClaimName(worktree.productDir);
|
|
4968
4986
|
}
|
|
4987
|
+
async function resolveAllTargetWorktrees(options) {
|
|
4988
|
+
const facts = await gatherGitFacts(options.cwd, options.gitDeps);
|
|
4989
|
+
if (facts === null) {
|
|
4990
|
+
return { ok: false, error: `${WORKTREE_RESOLVE_ERROR.NOT_A_WORKTREE}: ${options.cwd}` };
|
|
4991
|
+
}
|
|
4992
|
+
if (!facts.worktreeListRead) {
|
|
4993
|
+
return { ok: false, error: WORKTREE_RESOLVE_ERROR.WORKTREE_LIST_UNAVAILABLE };
|
|
4994
|
+
}
|
|
4995
|
+
return {
|
|
4996
|
+
ok: true,
|
|
4997
|
+
value: facts.worktreeRoots.map((worktreeRoot) => ({
|
|
4998
|
+
name: worktreeClaimName(worktreeRoot),
|
|
4999
|
+
worktreeRoot
|
|
5000
|
+
}))
|
|
5001
|
+
};
|
|
5002
|
+
}
|
|
4969
5003
|
async function resolveTargetWorktree(options) {
|
|
4970
5004
|
const base = options.cwd;
|
|
4971
5005
|
const targetPath = options.worktree === void 0 ? base : resolve4(base, options.worktree);
|
|
@@ -5003,7 +5037,6 @@ var defaultHookEnvFileSystem = {
|
|
|
5003
5037
|
}
|
|
5004
5038
|
};
|
|
5005
5039
|
var ERROR_DETAIL_SEPARATOR3 = ": ";
|
|
5006
|
-
var EMPTY_HOOK_STDOUT = "";
|
|
5007
5040
|
async function runSessionStartHook(options) {
|
|
5008
5041
|
const diagnostics = [];
|
|
5009
5042
|
const payloadResult = parseHookSessionStartPayload(options.content);
|
|
@@ -5042,7 +5075,7 @@ async function runSessionStartHook(options) {
|
|
|
5042
5075
|
diagnostics,
|
|
5043
5076
|
envFileWritten,
|
|
5044
5077
|
productDir,
|
|
5045
|
-
stdout:
|
|
5078
|
+
stdout: renderSessionStartStdout(payload.source),
|
|
5046
5079
|
...sessionId === void 0 ? {} : { sessionId }
|
|
5047
5080
|
}
|
|
5048
5081
|
};
|
|
@@ -5135,17 +5168,17 @@ function createProcessHookIo(streams) {
|
|
|
5135
5168
|
return {
|
|
5136
5169
|
readStdin: async () => {
|
|
5137
5170
|
if (streams.stdin.isTTY) return { ok: true, value: void 0 };
|
|
5138
|
-
return new Promise((
|
|
5171
|
+
return new Promise((resolve11) => {
|
|
5139
5172
|
let data = "";
|
|
5140
5173
|
streams.stdin.setEncoding("utf-8");
|
|
5141
5174
|
streams.stdin.on(HOOK_PROCESS_IO_EVENT.DATA, (chunk) => {
|
|
5142
5175
|
data += chunk;
|
|
5143
5176
|
});
|
|
5144
5177
|
streams.stdin.on(HOOK_PROCESS_IO_EVENT.END, () => {
|
|
5145
|
-
|
|
5178
|
+
resolve11({ ok: true, value: data.length === 0 ? void 0 : data });
|
|
5146
5179
|
});
|
|
5147
5180
|
streams.stdin.on(HOOK_PROCESS_IO_EVENT.ERROR, (error) => {
|
|
5148
|
-
|
|
5181
|
+
resolve11({ ok: false, error: formatStdinReadError(error) });
|
|
5149
5182
|
});
|
|
5150
5183
|
});
|
|
5151
5184
|
},
|
|
@@ -6255,16 +6288,16 @@ ${output}`;
|
|
|
6255
6288
|
// src/domains/session/handoff-base-checklist.ts
|
|
6256
6289
|
var SESSION_HANDOFF_BASE_ERROR_NAME = "SessionHandoffBaseError";
|
|
6257
6290
|
var HANDOFF_BASE_MARK = {
|
|
6258
|
-
MET: "
|
|
6259
|
-
UNMET: "
|
|
6291
|
+
MET: "[PASS]:",
|
|
6292
|
+
UNMET: "[FAIL]:"
|
|
6260
6293
|
};
|
|
6261
6294
|
var HANDOFF_BASE_PREREQUISITE_LABEL = {
|
|
6262
6295
|
CLEAN_WORKING_TREE: "working tree is clean",
|
|
6263
6296
|
DETACHED_AT_DEFAULT_TIP: "HEAD is detached at the default-branch tip"
|
|
6264
6297
|
};
|
|
6265
6298
|
var HANDOFF_BASE_REMEDY = {
|
|
6266
|
-
/** Unclean working tree: commit
|
|
6267
|
-
|
|
6299
|
+
/** Unclean working tree: commit before any handoff proceeds. */
|
|
6300
|
+
COMMIT_BEFORE_HANDOFF: "commit the changes before handoff. DO NOT PROCEED while this checkout is dirty",
|
|
6268
6301
|
/** Off the default-branch tip: detach to it, or hand off from the main checkout. */
|
|
6269
6302
|
DETACH_TO_TIP_OR_MAIN_CHECKOUT: "detach HEAD to the default-branch tip, or run handoff from the main checkout",
|
|
6270
6303
|
/** Default branch unresolved: only the main checkout can anchor the base. */
|
|
@@ -6281,23 +6314,34 @@ var HANDOFF_BASE_UNRESOLVED = "unresolved";
|
|
|
6281
6314
|
var CHECKLIST_INDENT = " ";
|
|
6282
6315
|
var REMEDY_SEPARATOR = " \u2014 ";
|
|
6283
6316
|
var CHECKLIST_HEADER = `${SESSION_HANDOFF_BASE_ERROR_NAME}: cannot create a handoff session from this worktree \u2014 it is not the main checkout.`;
|
|
6317
|
+
var HANDOFF_BASE_DIRTY_HEADER = `${SESSION_HANDOFF_BASE_ERROR_NAME}: YOUR CHECKOUT IS DIRTY. YOU CANNOT HANDOFF.`;
|
|
6318
|
+
var HANDOFF_BASE_FACTS_HEADER = "GIT FACTS:";
|
|
6319
|
+
var HANDOFF_BASE_CHECKS_HEADER = "HERE ARE THE CHECKS AND THEIR STATUS:";
|
|
6284
6320
|
function renderFactLine(label, value) {
|
|
6285
6321
|
return `${CHECKLIST_INDENT}${label}: ${value ?? HANDOFF_BASE_UNRESOLVED}`;
|
|
6286
6322
|
}
|
|
6287
|
-
function renderPrerequisiteLine(prerequisite) {
|
|
6323
|
+
function renderPrerequisiteLine(prerequisite, index) {
|
|
6288
6324
|
const mark = prerequisite.met ? HANDOFF_BASE_MARK.MET : HANDOFF_BASE_MARK.UNMET;
|
|
6289
|
-
const
|
|
6325
|
+
const lineNumber = index + 1;
|
|
6326
|
+
const base = `${lineNumber}. ${mark} ${prerequisite.label}`;
|
|
6290
6327
|
return prerequisite.met ? base : `${base}${REMEDY_SEPARATOR}${prerequisite.remedy}`;
|
|
6291
6328
|
}
|
|
6329
|
+
function isDirtyCheckoutRefusal(checklist) {
|
|
6330
|
+
return checklist.prerequisites.some(
|
|
6331
|
+
(prerequisite) => prerequisite.label === HANDOFF_BASE_PREREQUISITE_LABEL.CLEAN_WORKING_TREE && !prerequisite.met
|
|
6332
|
+
);
|
|
6333
|
+
}
|
|
6292
6334
|
function renderHandoffBaseChecklist(checklist) {
|
|
6293
6335
|
return [
|
|
6294
|
-
CHECKLIST_HEADER,
|
|
6336
|
+
isDirtyCheckoutRefusal(checklist) ? HANDOFF_BASE_DIRTY_HEADER : CHECKLIST_HEADER,
|
|
6337
|
+
HANDOFF_BASE_FACTS_HEADER,
|
|
6295
6338
|
renderFactLine(HANDOFF_BASE_FACT_LABEL.DEFAULT_BRANCH, checklist.defaultBranch),
|
|
6296
6339
|
renderFactLine(HANDOFF_BASE_FACT_LABEL.DEFAULT_TIP, checklist.defaultTipSha),
|
|
6297
6340
|
renderFactLine(HANDOFF_BASE_FACT_LABEL.HEAD, checklist.headSha),
|
|
6298
6341
|
renderFactLine(HANDOFF_BASE_FACT_LABEL.CURRENT_WORKTREE, checklist.currentWorktreePath),
|
|
6299
6342
|
renderFactLine(HANDOFF_BASE_FACT_LABEL.MAIN_CHECKOUT, checklist.mainCheckoutPath),
|
|
6300
|
-
|
|
6343
|
+
HANDOFF_BASE_CHECKS_HEADER,
|
|
6344
|
+
...checklist.prerequisites.map((prerequisite, index) => renderPrerequisiteLine(prerequisite, index))
|
|
6301
6345
|
].join("\n");
|
|
6302
6346
|
}
|
|
6303
6347
|
|
|
@@ -6404,6 +6448,17 @@ var SessionWorkBranchNotOnOriginError = class extends SessionError {
|
|
|
6404
6448
|
this.workBranch = workBranch;
|
|
6405
6449
|
}
|
|
6406
6450
|
};
|
|
6451
|
+
var SessionInjectionDirectoryError = class extends SessionError {
|
|
6452
|
+
/** The listed `specs`/`files` entry that resolves to an existing directory. */
|
|
6453
|
+
entry;
|
|
6454
|
+
constructor(entry) {
|
|
6455
|
+
super(
|
|
6456
|
+
`Session injection entry resolves to a directory: ${entry}. The specs and files arrays hold file paths; list the file inside the directory rather than the directory itself.`
|
|
6457
|
+
);
|
|
6458
|
+
this.name = "SessionInjectionDirectoryError";
|
|
6459
|
+
this.entry = entry;
|
|
6460
|
+
}
|
|
6461
|
+
};
|
|
6407
6462
|
var SessionInvalidJsonHeaderError = class extends SessionError {
|
|
6408
6463
|
constructor(reason) {
|
|
6409
6464
|
super(`Invalid JSON header for handoff: ${reason}`);
|
|
@@ -6985,7 +7040,7 @@ async function deleteCommand(options) {
|
|
|
6985
7040
|
}
|
|
6986
7041
|
|
|
6987
7042
|
// src/commands/session/handoff.ts
|
|
6988
|
-
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
7043
|
+
import { mkdir as mkdir2, stat as stat3, writeFile } from "fs/promises";
|
|
6989
7044
|
import { join as join11, resolve as resolve5 } from "path";
|
|
6990
7045
|
import { stringify as stringifyYaml3 } from "yaml";
|
|
6991
7046
|
|
|
@@ -7007,7 +7062,7 @@ function cleanPrerequisite(facts) {
|
|
|
7007
7062
|
return {
|
|
7008
7063
|
label: HANDOFF_BASE_PREREQUISITE_LABEL.CLEAN_WORKING_TREE,
|
|
7009
7064
|
met: facts.isClean,
|
|
7010
|
-
remedy: facts.isClean ? "" : HANDOFF_BASE_REMEDY.
|
|
7065
|
+
remedy: facts.isClean ? "" : HANDOFF_BASE_REMEDY.COMMIT_BEFORE_HANDOFF
|
|
7011
7066
|
};
|
|
7012
7067
|
}
|
|
7013
7068
|
function detachedAtTipPrerequisite(facts, met) {
|
|
@@ -7231,6 +7286,20 @@ async function resolveRecordedGitRef(suppliedRef, gateRef, cwd, deps) {
|
|
|
7231
7286
|
const existsOnOrigin = await originBranchExists(suppliedRef, cwd, deps);
|
|
7232
7287
|
return resolveWorkBranchGitRef(suppliedRef, existsOnOrigin);
|
|
7233
7288
|
}
|
|
7289
|
+
async function rejectDirectoryInjectionEntries(entries, cwd) {
|
|
7290
|
+
for (const entry of entries) {
|
|
7291
|
+
if (entry.length === 0) continue;
|
|
7292
|
+
let entryIsDirectory = false;
|
|
7293
|
+
try {
|
|
7294
|
+
entryIsDirectory = (await stat3(resolve5(cwd, entry))).isDirectory();
|
|
7295
|
+
} catch {
|
|
7296
|
+
continue;
|
|
7297
|
+
}
|
|
7298
|
+
if (entryIsDirectory) {
|
|
7299
|
+
throw new SessionInjectionDirectoryError(entry);
|
|
7300
|
+
}
|
|
7301
|
+
}
|
|
7302
|
+
}
|
|
7234
7303
|
async function handoffCommand(options) {
|
|
7235
7304
|
const { config } = await resolveSessionConfig({
|
|
7236
7305
|
sessionsDir: options.sessionsDir,
|
|
@@ -7250,6 +7319,8 @@ async function handoffCommand(options) {
|
|
|
7250
7319
|
}
|
|
7251
7320
|
const gateRef = await resolveSessionGitRef(options.cwd, options.deps);
|
|
7252
7321
|
const gitRef = await resolveRecordedGitRef(header.git_ref, gateRef, options.cwd, options.deps);
|
|
7322
|
+
const injectionCwd = options.cwd ?? CONFIG_PROCESS_CWD.read();
|
|
7323
|
+
await rejectDirectoryInjectionEntries([...header.specs, ...header.files], injectionCwd);
|
|
7253
7324
|
const sessionId = generateSessionId();
|
|
7254
7325
|
const agentSessionId = resolveAgentSessionId(options.env ?? process.env);
|
|
7255
7326
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7430,6 +7501,7 @@ async function loadTodoSessionsWithDeps(config, deps) {
|
|
|
7430
7501
|
}
|
|
7431
7502
|
var SESSION_INJECTION_SECTION_PREFIX = "Injected file";
|
|
7432
7503
|
var SESSION_INJECTION_MISSING_WARNING_PREFIX = "Warning: missing session injection file";
|
|
7504
|
+
var SESSION_INJECTION_UNREADABLE_WARNING_PREFIX = "Warning: unreadable session injection path";
|
|
7433
7505
|
function injectionPath(cwd, filePath) {
|
|
7434
7506
|
return resolve6(cwd, filePath);
|
|
7435
7507
|
}
|
|
@@ -7437,6 +7509,11 @@ function formatInjectedFile(listedPath, content) {
|
|
|
7437
7509
|
return `${SESSION_INJECTION_SECTION_PREFIX}: ${listedPath}
|
|
7438
7510
|
${content}`;
|
|
7439
7511
|
}
|
|
7512
|
+
function formatInjectionWarning(error, listedPath) {
|
|
7513
|
+
const isAbsent = error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND;
|
|
7514
|
+
const prefix = isAbsent ? SESSION_INJECTION_MISSING_WARNING_PREFIX : SESSION_INJECTION_UNREADABLE_WARNING_PREFIX;
|
|
7515
|
+
return `${prefix}: ${listedPath}`;
|
|
7516
|
+
}
|
|
7440
7517
|
async function readInjectedFiles(metadata, cwd, deps, onWarning) {
|
|
7441
7518
|
const sections = [];
|
|
7442
7519
|
for (const listedPath of [...metadata.specs, ...metadata.files]) {
|
|
@@ -7444,11 +7521,7 @@ async function readInjectedFiles(metadata, cwd, deps, onWarning) {
|
|
|
7444
7521
|
const content = await deps.readFile(injectionPath(cwd, listedPath), SESSION_FILE_ENCODING);
|
|
7445
7522
|
sections.push(formatInjectedFile(listedPath, content));
|
|
7446
7523
|
} catch (error) {
|
|
7447
|
-
|
|
7448
|
-
onWarning?.(`${SESSION_INJECTION_MISSING_WARNING_PREFIX}: ${listedPath}`);
|
|
7449
|
-
continue;
|
|
7450
|
-
}
|
|
7451
|
-
throw error;
|
|
7524
|
+
onWarning?.(formatInjectionWarning(error, listedPath));
|
|
7452
7525
|
}
|
|
7453
7526
|
}
|
|
7454
7527
|
return sections;
|
|
@@ -7672,12 +7745,12 @@ async function releaseCommand(options) {
|
|
|
7672
7745
|
}
|
|
7673
7746
|
|
|
7674
7747
|
// src/commands/session/show.ts
|
|
7675
|
-
import { readFile as readFile7, stat as
|
|
7748
|
+
import { readFile as readFile7, stat as stat4 } from "fs/promises";
|
|
7676
7749
|
async function findExistingPath(paths, _config) {
|
|
7677
7750
|
for (let i = 0; i < paths.length; i++) {
|
|
7678
7751
|
const filePath = paths[i];
|
|
7679
7752
|
try {
|
|
7680
|
-
const stats = await
|
|
7753
|
+
const stats = await stat4(filePath);
|
|
7681
7754
|
if (stats.isFile()) {
|
|
7682
7755
|
return { path: filePath, status: SEARCH_ORDER[i] };
|
|
7683
7756
|
}
|
|
@@ -8119,13 +8192,13 @@ function spawnManagedSubprocess(runner, command, args, options) {
|
|
|
8119
8192
|
var LAUNCH_FAILURE_STATUS = 1;
|
|
8120
8193
|
function launchAgent(runner, suspender, command) {
|
|
8121
8194
|
const restoreSignals = suspender.suspend();
|
|
8122
|
-
return new Promise((
|
|
8195
|
+
return new Promise((resolve11) => {
|
|
8123
8196
|
let settled = false;
|
|
8124
8197
|
const settle = (status) => {
|
|
8125
8198
|
if (settled) return;
|
|
8126
8199
|
settled = true;
|
|
8127
8200
|
restoreSignals();
|
|
8128
|
-
|
|
8201
|
+
resolve11(status);
|
|
8129
8202
|
};
|
|
8130
8203
|
const child = runner.spawn(command.command, command.args, { stdio: "inherit" });
|
|
8131
8204
|
child.once("exit", (code) => settle(code ?? LAUNCH_FAILURE_STATUS));
|
|
@@ -8260,17 +8333,17 @@ async function readStdin() {
|
|
|
8260
8333
|
if (process.stdin.isTTY) {
|
|
8261
8334
|
return void 0;
|
|
8262
8335
|
}
|
|
8263
|
-
return new Promise((
|
|
8336
|
+
return new Promise((resolve11) => {
|
|
8264
8337
|
let data = "";
|
|
8265
8338
|
process.stdin.setEncoding(SESSION_FILE_ENCODING);
|
|
8266
8339
|
process.stdin.on("data", (chunk) => {
|
|
8267
8340
|
data += chunk;
|
|
8268
8341
|
});
|
|
8269
8342
|
process.stdin.on("end", () => {
|
|
8270
|
-
|
|
8343
|
+
resolve11(data.length === 0 ? void 0 : data);
|
|
8271
8344
|
});
|
|
8272
8345
|
process.stdin.on("error", () => {
|
|
8273
|
-
|
|
8346
|
+
resolve11(void 0);
|
|
8274
8347
|
});
|
|
8275
8348
|
});
|
|
8276
8349
|
}
|
|
@@ -9953,17 +10026,56 @@ function createNodeStatusProvider(productDir) {
|
|
|
9953
10026
|
// src/lib/node-status/update.ts
|
|
9954
10027
|
import { mkdir as mkdir4, readdir as readdir7, rm, writeFile as writeFile2 } from "fs/promises";
|
|
9955
10028
|
import { dirname as dirname7, join as join21 } from "path";
|
|
10029
|
+
|
|
10030
|
+
// src/git/tracked-paths.ts
|
|
10031
|
+
var GIT_LS_FILES_SUBCOMMAND = "ls-files";
|
|
10032
|
+
var GIT_NUL_TERMINATED_FLAG = "-z";
|
|
10033
|
+
var TRACKED_PATH_NUL_SEPARATOR = "\0";
|
|
10034
|
+
var TRACKED_PATH_DIRECTORY_SEPARATOR = "/";
|
|
10035
|
+
var GIT_SUCCESS_EXIT_CODE = 0;
|
|
10036
|
+
async function listTrackedPaths(productDir, deps) {
|
|
10037
|
+
let result;
|
|
10038
|
+
try {
|
|
10039
|
+
result = await deps.execa(
|
|
10040
|
+
GIT_ROOT_COMMAND.EXECUTABLE,
|
|
10041
|
+
[GIT_LS_FILES_SUBCOMMAND, GIT_NUL_TERMINATED_FLAG],
|
|
10042
|
+
{ cwd: productDir, reject: false }
|
|
10043
|
+
);
|
|
10044
|
+
} catch {
|
|
10045
|
+
return void 0;
|
|
10046
|
+
}
|
|
10047
|
+
if (result.exitCode !== GIT_SUCCESS_EXIT_CODE) return void 0;
|
|
10048
|
+
return new Set(result.stdout.split(TRACKED_PATH_NUL_SEPARATOR).filter((entry) => entry.length > 0));
|
|
10049
|
+
}
|
|
10050
|
+
function createTrackedPathInclusion(trackedPaths) {
|
|
10051
|
+
if (trackedPaths === void 0) return () => true;
|
|
10052
|
+
const admitted = new Set(trackedPaths);
|
|
10053
|
+
for (const trackedPath of trackedPaths) {
|
|
10054
|
+
let separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR);
|
|
10055
|
+
while (separatorIndex !== -1) {
|
|
10056
|
+
admitted.add(trackedPath.slice(0, separatorIndex));
|
|
10057
|
+
separatorIndex = trackedPath.indexOf(TRACKED_PATH_DIRECTORY_SEPARATOR, separatorIndex + 1);
|
|
10058
|
+
}
|
|
10059
|
+
}
|
|
10060
|
+
return (path7) => admitted.has(path7);
|
|
10061
|
+
}
|
|
10062
|
+
|
|
10063
|
+
// src/lib/node-status/update.ts
|
|
9956
10064
|
var NODE_STATUS_TEXT_ENCODING = "utf8";
|
|
9957
10065
|
async function updateNodeStatus(options) {
|
|
9958
|
-
const { productDir, resolveOutcome } = options;
|
|
10066
|
+
const { productDir, resolveOutcome, gitDependencies = defaultGitDependencies } = options;
|
|
9959
10067
|
const snapshot = await readSpecTree({ source: createFilesystemSpecTreeSource({ productDir }) });
|
|
9960
10068
|
const ignoreReader = createIgnoreSourceReader(productDir, {
|
|
9961
10069
|
ignoreSourceFilename: IGNORE_SOURCE_FILENAME_DEFAULT,
|
|
9962
10070
|
specTreeRootSegment: SPEC_TREE_CONFIG.ROOT_DIRECTORY
|
|
9963
10071
|
});
|
|
10072
|
+
const isTrackedNodeDirectory = createTrackedPathInclusion(await listTrackedPaths(productDir, gitDependencies));
|
|
9964
10073
|
const evidenceByNode = collectEvidenceByNode(snapshot);
|
|
9965
10074
|
const liveStatusPaths = /* @__PURE__ */ new Set();
|
|
9966
10075
|
for (const node of snapshot.allNodes) {
|
|
10076
|
+
if (!isTrackedNodeDirectory(`${SPEC_TREE_CONFIG.ROOT_DIRECTORY}${TRACKED_PATH_DIRECTORY_SEPARATOR}${node.id}`)) {
|
|
10077
|
+
continue;
|
|
10078
|
+
}
|
|
9967
10079
|
const evidence = evidenceByNode.get(node.id) ?? [];
|
|
9968
10080
|
const verification = await resolveVerification(node, {
|
|
9969
10081
|
evidence,
|
|
@@ -10163,13 +10275,14 @@ async function statusCommand(options = {}) {
|
|
|
10163
10275
|
}
|
|
10164
10276
|
return renderSpecStatus(projectSpecTree(await readSpecTree({ source: options.source })), options.format);
|
|
10165
10277
|
}
|
|
10278
|
+
const gitDependencies = options.gitDependencies ?? defaultGitDependencies;
|
|
10166
10279
|
const productDir = await resolveSpecProductDir(
|
|
10167
10280
|
options.cwd ?? CONFIG_PROCESS_CWD.read(),
|
|
10168
|
-
|
|
10281
|
+
gitDependencies,
|
|
10169
10282
|
options.onWarning
|
|
10170
10283
|
);
|
|
10171
10284
|
if (options.update === true) {
|
|
10172
|
-
await updateNodeStatus({ productDir, resolveOutcome: options.resolveOutcomeFor(productDir) });
|
|
10285
|
+
await updateNodeStatus({ productDir, resolveOutcome: options.resolveOutcomeFor(productDir), gitDependencies });
|
|
10173
10286
|
}
|
|
10174
10287
|
const snapshot = await readSpecTree({
|
|
10175
10288
|
source: createFilesystemSpecTreeSource({ productDir }),
|
|
@@ -10859,8 +10972,12 @@ function createTestingDomain(deps) {
|
|
|
10859
10972
|
}
|
|
10860
10973
|
var testingDomain = createTestingDomain();
|
|
10861
10974
|
|
|
10975
|
+
// src/interfaces/cli/validation.ts
|
|
10976
|
+
import { existsSync as existsSync8, realpathSync } from "fs";
|
|
10977
|
+
import { isAbsolute as isAbsolute9, relative as relative9, resolve as resolve10, sep as sep3 } from "path";
|
|
10978
|
+
|
|
10862
10979
|
// src/commands/validation/formatting.ts
|
|
10863
|
-
import { existsSync } from "fs";
|
|
10980
|
+
import { existsSync, statSync } from "fs";
|
|
10864
10981
|
import { isAbsolute as isAbsolute3, join as join24, relative as relative3 } from "path";
|
|
10865
10982
|
|
|
10866
10983
|
// src/validation/config/path-filter.ts
|
|
@@ -10870,11 +10987,24 @@ function hasEffectiveValidationPathMetadata(filter) {
|
|
|
10870
10987
|
return "hasIncludeFilter" in filter && typeof filter.hasIncludeFilter === "boolean" && "noMatchingIncludes" in filter && typeof filter.noMatchingIncludes === "boolean";
|
|
10871
10988
|
}
|
|
10872
10989
|
function normalizePathPrefix2(prefix) {
|
|
10873
|
-
|
|
10990
|
+
const normalizedSeparators = prefix.split("\\").join(PATH_PREFIX_SEPARATOR);
|
|
10991
|
+
const withoutLeadingDot = normalizedSeparators.startsWith(`.${PATH_PREFIX_SEPARATOR}`) ? normalizedSeparators.slice(2) : normalizedSeparators;
|
|
10992
|
+
const normalized = trimTrailingPathSeparators(withoutLeadingDot);
|
|
10993
|
+
return normalized === "." ? "" : normalized;
|
|
10994
|
+
}
|
|
10995
|
+
function trimTrailingPathSeparators(path7) {
|
|
10996
|
+
let end = path7.length;
|
|
10997
|
+
while (end > 0 && path7[end - 1] === PATH_PREFIX_SEPARATOR) {
|
|
10998
|
+
end -= 1;
|
|
10999
|
+
}
|
|
11000
|
+
return path7.slice(0, end);
|
|
10874
11001
|
}
|
|
10875
11002
|
function pathMatchesPrefix2(path7, prefix) {
|
|
10876
11003
|
const normalizedPath = normalizePathPrefix2(path7);
|
|
10877
11004
|
const normalizedPrefix = normalizePathPrefix2(prefix);
|
|
11005
|
+
if (normalizedPrefix.length === 0) {
|
|
11006
|
+
return true;
|
|
11007
|
+
}
|
|
10878
11008
|
return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}${PATH_PREFIX_SEPARATOR}`);
|
|
10879
11009
|
}
|
|
10880
11010
|
function unique(values) {
|
|
@@ -10930,6 +11060,27 @@ function pathPassesValidationFilter(path7, filter) {
|
|
|
10930
11060
|
}
|
|
10931
11061
|
return !exclude.some((prefix) => pathMatchesPrefix2(path7, prefix));
|
|
10932
11062
|
}
|
|
11063
|
+
function pathIntersectsValidationFilter(path7, filter) {
|
|
11064
|
+
return validationPathFilterIntersections(path7, filter).length > 0;
|
|
11065
|
+
}
|
|
11066
|
+
function validationPathFilterExcludes(filter) {
|
|
11067
|
+
return unique(nonEmpty(filter.exclude).map(normalizePathPrefix2));
|
|
11068
|
+
}
|
|
11069
|
+
function validationPathFilterIntersections(path7, filter) {
|
|
11070
|
+
if (hasEffectiveValidationPathMetadata(filter) && filter.noMatchingIncludes) {
|
|
11071
|
+
return [];
|
|
11072
|
+
}
|
|
11073
|
+
const normalizedPath = normalizePathPrefix2(path7);
|
|
11074
|
+
const include = nonEmpty(filter.include);
|
|
11075
|
+
const exclude = nonEmpty(filter.exclude);
|
|
11076
|
+
const candidates = include.length > 0 ? include.flatMap((prefix) => {
|
|
11077
|
+
const normalizedPrefix = normalizePathPrefix2(prefix);
|
|
11078
|
+
if (pathMatchesPrefix2(normalizedPath, normalizedPrefix)) return [normalizedPath];
|
|
11079
|
+
if (pathMatchesPrefix2(normalizedPrefix, normalizedPath)) return [normalizedPrefix];
|
|
11080
|
+
return [];
|
|
11081
|
+
}) : [normalizedPath];
|
|
11082
|
+
return unique(candidates).filter((candidate) => !exclude.some((prefix) => pathMatchesPrefix2(candidate, prefix)));
|
|
11083
|
+
}
|
|
10933
11084
|
function applyValidationPathFilterToScope(scopeConfig, filter) {
|
|
10934
11085
|
if (!hasValidationPathFilter(filter)) {
|
|
10935
11086
|
return scopeConfig;
|
|
@@ -10945,7 +11096,14 @@ function applyValidationPathFilterToScope(scopeConfig, filter) {
|
|
|
10945
11096
|
(include) => scopeConfig.directories.some((directory) => pathMatchesPrefix2(include, directory))
|
|
10946
11097
|
);
|
|
10947
11098
|
const noMatchingIncludes = hasEffectiveMetadata && filter.noMatchingIncludes || hasIncludeFilter && scopedDirectories.length === 0 && scopedFilePatterns.length === 0 && includeFallbacksWithinScope.length === 0;
|
|
10948
|
-
|
|
11099
|
+
let directories;
|
|
11100
|
+
if (noMatchingIncludes) {
|
|
11101
|
+
directories = [];
|
|
11102
|
+
} else if (scopedDirectories.length > 0) {
|
|
11103
|
+
directories = scopedDirectories;
|
|
11104
|
+
} else {
|
|
11105
|
+
directories = includeFallbacksWithinScope;
|
|
11106
|
+
}
|
|
10949
11107
|
const filePatterns = scopedFilePatterns.length > 0 ? scopedFilePatterns : [...directories];
|
|
10950
11108
|
return {
|
|
10951
11109
|
...scopeConfig,
|
|
@@ -10991,14 +11149,23 @@ function forwardChunkWithBackpressure(source, stream, chunk) {
|
|
|
10991
11149
|
// src/validation/steps/formatting.ts
|
|
10992
11150
|
var DPRINT_COMMAND = "dprint";
|
|
10993
11151
|
var DPRINT_CHECK_SUBCOMMAND = "check";
|
|
11152
|
+
var DPRINT_EXCLUDES_OPTION = "--excludes";
|
|
11153
|
+
var DPRINT_OPTIONS_TERMINATOR = "--";
|
|
10994
11154
|
var DPRINT_CONFIG_FILENAME = "dprint.jsonc";
|
|
10995
11155
|
var defaultFormattingProcessRunner = lifecycleProcessRunner;
|
|
10996
11156
|
function buildDprintCheckArgs(options) {
|
|
10997
|
-
|
|
11157
|
+
const excludes = options.excludes ?? [];
|
|
11158
|
+
const files = options.files ?? [];
|
|
11159
|
+
return [
|
|
11160
|
+
DPRINT_CHECK_SUBCOMMAND,
|
|
11161
|
+
...excludes.length > 0 ? [DPRINT_EXCLUDES_OPTION, ...excludes] : [],
|
|
11162
|
+
...files.length > 0 ? [DPRINT_OPTIONS_TERMINATOR] : [],
|
|
11163
|
+
...files
|
|
11164
|
+
];
|
|
10998
11165
|
}
|
|
10999
11166
|
async function validateFormatting(context, runner = defaultFormattingProcessRunner) {
|
|
11000
|
-
const args = buildDprintCheckArgs({ files: context.files });
|
|
11001
|
-
return new Promise((
|
|
11167
|
+
const args = buildDprintCheckArgs({ files: context.files, excludes: context.excludes });
|
|
11168
|
+
return new Promise((resolve11) => {
|
|
11002
11169
|
const child = spawnManagedSubprocess(runner, DPRINT_COMMAND, args, { cwd: context.projectRoot });
|
|
11003
11170
|
const chunks = [];
|
|
11004
11171
|
const capture = (chunk) => {
|
|
@@ -11007,10 +11174,10 @@ async function validateFormatting(context, runner = defaultFormattingProcessRunn
|
|
|
11007
11174
|
child.stdout?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
|
|
11008
11175
|
child.stderr?.on(VALIDATION_SUBPROCESS_EVENTS.DATA, capture);
|
|
11009
11176
|
child.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
|
|
11010
|
-
|
|
11177
|
+
resolve11({ success: code === 0, output: chunks.join("") });
|
|
11011
11178
|
});
|
|
11012
11179
|
child.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
|
|
11013
|
-
|
|
11180
|
+
resolve11({ success: false, output: chunks.join(""), error: error.message });
|
|
11014
11181
|
});
|
|
11015
11182
|
});
|
|
11016
11183
|
}
|
|
@@ -11032,7 +11199,7 @@ var VALIDATION_SKIP_LABELS = {
|
|
|
11032
11199
|
DISABLED_BY_PREFIX: "disabled by",
|
|
11033
11200
|
TYPESCRIPT_ABSENT_REASON: "TypeScript not detected in project",
|
|
11034
11201
|
VALIDATION_PATHS_NO_TARGETS_REASON: "validation paths matched no files",
|
|
11035
|
-
MARKDOWN_NO_SCOPE_REASON: "no markdown files in
|
|
11202
|
+
MARKDOWN_NO_SCOPE_REASON: "no markdown files in explicit path scope",
|
|
11036
11203
|
MARKDOWN_NO_DEFAULT_DIRECTORIES_REASON: "no spx/ or docs/ directories found"
|
|
11037
11204
|
};
|
|
11038
11205
|
var VALIDATION_COMMAND_OUTPUT = {
|
|
@@ -11077,6 +11244,7 @@ var FORMATTING_COMMAND_OUTPUT = {
|
|
|
11077
11244
|
NO_CONFIG_SKIP_REASON: `no ${DPRINT_CONFIG_FILENAME} at product root`
|
|
11078
11245
|
};
|
|
11079
11246
|
var FORMATTING_CONFIG_ERROR_MESSAGE = `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: \u2717 config error`;
|
|
11247
|
+
var DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX = "/**/*";
|
|
11080
11248
|
async function formattingCommand(options) {
|
|
11081
11249
|
const { cwd, files, quiet } = options;
|
|
11082
11250
|
const startTime = Date.now();
|
|
@@ -11098,12 +11266,22 @@ async function formattingCommand(options) {
|
|
|
11098
11266
|
VALIDATION_PATH_TOOL_SUBSECTIONS.FORMATTING
|
|
11099
11267
|
);
|
|
11100
11268
|
const hasExplicitScope = files !== void 0 && files.length > 0;
|
|
11101
|
-
const scopedFiles = hasExplicitScope ? files.
|
|
11269
|
+
const scopedFiles = hasExplicitScope ? files.flatMap(
|
|
11270
|
+
(filePath) => formattingPathOperandsForValidationPathFilter(
|
|
11271
|
+
cwd,
|
|
11272
|
+
isAbsolute3(filePath) ? relative3(cwd, filePath) : filePath,
|
|
11273
|
+
pathFilter
|
|
11274
|
+
)
|
|
11275
|
+
) : void 0;
|
|
11102
11276
|
if (hasExplicitScope && (scopedFiles === void 0 || scopedFiles.length === 0)) {
|
|
11103
11277
|
const output2 = quiet ? "" : `${VALIDATION_STAGE_DISPLAY_NAMES.FORMATTING}: skipped (${FORMATTING_COMMAND_OUTPUT.EMPTY_SCOPE_REASON})`;
|
|
11104
11278
|
return { exitCode: 0, output: output2, durationMs: Date.now() - startTime };
|
|
11105
11279
|
}
|
|
11106
|
-
const result = await validateFormatting({
|
|
11280
|
+
const result = await validateFormatting({
|
|
11281
|
+
projectRoot: cwd,
|
|
11282
|
+
files: scopedFiles,
|
|
11283
|
+
excludes: validationPathFilterExcludes(pathFilter)
|
|
11284
|
+
});
|
|
11107
11285
|
const durationMs = Date.now() - startTime;
|
|
11108
11286
|
if (result.success) {
|
|
11109
11287
|
return { exitCode: 0, output: quiet ? "" : FORMATTING_COMMAND_OUTPUT.NO_ISSUES, durationMs };
|
|
@@ -11112,6 +11290,24 @@ async function formattingCommand(options) {
|
|
|
11112
11290
|
const output = [FORMATTING_COMMAND_OUTPUT.FAILURE_SUMMARY, detail].filter((line) => line.length > 0).join("\n");
|
|
11113
11291
|
return { exitCode: 1, output, durationMs };
|
|
11114
11292
|
}
|
|
11293
|
+
function normalizeFormattingPathOperand(productDir, relativePath) {
|
|
11294
|
+
const absolutePath = join24(productDir, relativePath);
|
|
11295
|
+
if (!existsSync(absolutePath) || !statSync(absolutePath).isDirectory()) {
|
|
11296
|
+
return relativePath;
|
|
11297
|
+
}
|
|
11298
|
+
const normalizedDirectory = trimTrailingPathSeparators(relativePath);
|
|
11299
|
+
if (normalizedDirectory.length === 0 || normalizedDirectory === ".") {
|
|
11300
|
+
return DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX.slice(1);
|
|
11301
|
+
}
|
|
11302
|
+
return `${normalizedDirectory}${DPRINT_RECURSIVE_DIRECTORY_GLOB_SUFFIX}`;
|
|
11303
|
+
}
|
|
11304
|
+
function formattingPathOperandsForValidationPathFilter(productDir, relativePath, pathFilter) {
|
|
11305
|
+
const absolutePath = join24(productDir, relativePath);
|
|
11306
|
+
if (!existsSync(absolutePath) || !statSync(absolutePath).isDirectory()) {
|
|
11307
|
+
return pathPassesValidationFilter(relativePath, pathFilter) ? [relativePath] : [];
|
|
11308
|
+
}
|
|
11309
|
+
return validationPathFilterIntersections(relativePath, pathFilter).map((path7) => normalizeFormattingPathOperand(productDir, path7));
|
|
11310
|
+
}
|
|
11115
11311
|
|
|
11116
11312
|
// src/validation/languages/formatting.ts
|
|
11117
11313
|
var FORMATTING_LANGUAGE_NAME = "formatting";
|
|
@@ -11127,10 +11323,10 @@ var formattingValidationLanguage = {
|
|
|
11127
11323
|
};
|
|
11128
11324
|
|
|
11129
11325
|
// src/commands/validation/markdown.ts
|
|
11130
|
-
import { relative as relative4 } from "path";
|
|
11326
|
+
import { isAbsolute as isAbsolute4, join as join26, relative as relative4 } from "path";
|
|
11131
11327
|
|
|
11132
11328
|
// src/validation/steps/markdown.ts
|
|
11133
|
-
import { existsSync as existsSync2, statSync } from "fs";
|
|
11329
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
11134
11330
|
import { basename as basename4, dirname as dirname9, join as join25, relative as pathRelative } from "path";
|
|
11135
11331
|
import { main as markdownlintMain } from "markdownlint-cli2";
|
|
11136
11332
|
import relativeLinksRule from "markdownlint-rule-relative-links";
|
|
@@ -11154,9 +11350,8 @@ var MARKDOWN_VALIDATION_TARGET_KIND = {
|
|
|
11154
11350
|
var MARKDOWN_VALIDATION_TARGET_DIAGNOSTICS = {
|
|
11155
11351
|
MISSING_OR_UNRELATED_SCOPE: "not an existing directory or markdown file"
|
|
11156
11352
|
};
|
|
11157
|
-
var ERROR_LINE_PATTERN = /^(.+?):(\d+)(?::\d+)?\s+(.+)$/;
|
|
11158
11353
|
var defaultMarkdownValidationTargetDeps = {
|
|
11159
|
-
statSync
|
|
11354
|
+
statSync: statSync2
|
|
11160
11355
|
};
|
|
11161
11356
|
function buildMarkdownlintConfig(directoryName) {
|
|
11162
11357
|
const md024Disabled = MD024_DISABLED_DIRECTORIES.includes(
|
|
@@ -11199,25 +11394,71 @@ function parseErrorLine(line) {
|
|
|
11199
11394
|
if (DATA_URI_PATTERN.test(line)) {
|
|
11200
11395
|
return null;
|
|
11201
11396
|
}
|
|
11202
|
-
const
|
|
11203
|
-
if (
|
|
11204
|
-
|
|
11397
|
+
const parsed = parseMarkdownlintErrorLine(line);
|
|
11398
|
+
if (parsed === null) return null;
|
|
11399
|
+
return {
|
|
11400
|
+
file: parsed.file,
|
|
11401
|
+
line: Number.parseInt(parsed.line, 10),
|
|
11402
|
+
detail: parsed.detail
|
|
11403
|
+
};
|
|
11404
|
+
}
|
|
11405
|
+
function parseMarkdownlintErrorLine(line) {
|
|
11406
|
+
for (let fileSeparator = line.indexOf(":"); fileSeparator > 0; fileSeparator = line.indexOf(":", fileSeparator + 1)) {
|
|
11407
|
+
const parsed = parseMarkdownlintErrorLineAtSeparator(line, fileSeparator);
|
|
11408
|
+
if (parsed !== null) return parsed;
|
|
11205
11409
|
}
|
|
11206
|
-
|
|
11410
|
+
return null;
|
|
11411
|
+
}
|
|
11412
|
+
function parseMarkdownlintErrorLineAtSeparator(line, fileSeparator) {
|
|
11413
|
+
const lineStart = fileSeparator + 1;
|
|
11414
|
+
const lineEnd = scanDigits(line, lineStart);
|
|
11415
|
+
if (lineEnd === lineStart) return null;
|
|
11416
|
+
const detailStart = scanMarkdownlintColumnAndWhitespace(line, lineEnd);
|
|
11417
|
+
if (detailStart === null || detailStart >= line.length) return null;
|
|
11207
11418
|
return {
|
|
11208
|
-
file,
|
|
11209
|
-
line:
|
|
11210
|
-
detail
|
|
11419
|
+
file: line.slice(0, fileSeparator),
|
|
11420
|
+
line: line.slice(lineStart, lineEnd),
|
|
11421
|
+
detail: line.slice(detailStart)
|
|
11211
11422
|
};
|
|
11212
11423
|
}
|
|
11424
|
+
function scanDigits(value, start) {
|
|
11425
|
+
let index = start;
|
|
11426
|
+
while (index < value.length && isAsciiDigit(value[index] ?? "")) {
|
|
11427
|
+
index += 1;
|
|
11428
|
+
}
|
|
11429
|
+
return index;
|
|
11430
|
+
}
|
|
11431
|
+
function scanMarkdownlintColumnAndWhitespace(value, start) {
|
|
11432
|
+
let index = start;
|
|
11433
|
+
if (value[index] === ":") {
|
|
11434
|
+
const columnStart = index + 1;
|
|
11435
|
+
index = scanDigits(value, columnStart);
|
|
11436
|
+
if (index === columnStart) return null;
|
|
11437
|
+
}
|
|
11438
|
+
if (!isAsciiWhitespace(value[index] ?? "")) return null;
|
|
11439
|
+
while (index < value.length && isAsciiWhitespace(value[index] ?? "")) {
|
|
11440
|
+
index += 1;
|
|
11441
|
+
}
|
|
11442
|
+
return index;
|
|
11443
|
+
}
|
|
11444
|
+
function isAsciiDigit(value) {
|
|
11445
|
+
return value >= "0" && value <= "9";
|
|
11446
|
+
}
|
|
11447
|
+
function isAsciiWhitespace(value) {
|
|
11448
|
+
return value === " " || value === " ";
|
|
11449
|
+
}
|
|
11213
11450
|
async function validateMarkdown(options) {
|
|
11214
|
-
const { targets, projectRoot } = options;
|
|
11451
|
+
const { targets, projectRoot, validationPathExcludes = [] } = options;
|
|
11215
11452
|
const errors = [];
|
|
11216
|
-
const
|
|
11453
|
+
const specTreeExcludeGlobs = getExcludeGlobs(projectRoot);
|
|
11217
11454
|
for (const target of targets) {
|
|
11218
11455
|
const directory = targetDirectory(target);
|
|
11219
11456
|
const dirName = markdownlintConfigDirectoryName(directory, projectRoot);
|
|
11220
11457
|
const config = buildMarkdownlintConfig(dirName);
|
|
11458
|
+
const excludeGlobs = [
|
|
11459
|
+
...specTreeExcludeGlobs,
|
|
11460
|
+
...validationPathExcludeGlobsForTarget(target, projectRoot, validationPathExcludes)
|
|
11461
|
+
];
|
|
11221
11462
|
const dirErrors = await validateTarget(target, config, projectRoot, excludeGlobs);
|
|
11222
11463
|
errors.push(...dirErrors);
|
|
11223
11464
|
}
|
|
@@ -11263,6 +11504,32 @@ async function validateTarget(target, config, projectRoot, ignoreGlobs = []) {
|
|
|
11263
11504
|
function targetDirectory(target) {
|
|
11264
11505
|
return target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE ? dirname9(target.path) : target.path;
|
|
11265
11506
|
}
|
|
11507
|
+
function validationPathExcludeGlobsForTarget(target, projectRoot, excludes) {
|
|
11508
|
+
if (projectRoot === void 0 || excludes.length === 0) return [];
|
|
11509
|
+
const directory = targetDirectory(target);
|
|
11510
|
+
const targetPath = normalizePathPrefix2(pathRelative(projectRoot, directory));
|
|
11511
|
+
return excludes.flatMap((exclude) => {
|
|
11512
|
+
const excludedPath = normalizePathPrefix2(exclude);
|
|
11513
|
+
if (pathContainsValidationPath(excludedPath, targetPath)) {
|
|
11514
|
+
return [MARKDOWN_DIRECTORY_GLOB];
|
|
11515
|
+
}
|
|
11516
|
+
if (!pathContainsValidationPath(targetPath, excludedPath)) {
|
|
11517
|
+
return [];
|
|
11518
|
+
}
|
|
11519
|
+
const relativeExclude = normalizePathPrefix2(pathRelative(directory, join25(projectRoot, excludedPath)));
|
|
11520
|
+
if (relativeExclude.length === 0) {
|
|
11521
|
+
return [MARKDOWN_DIRECTORY_GLOB];
|
|
11522
|
+
}
|
|
11523
|
+
const absoluteExclude = join25(projectRoot, excludedPath);
|
|
11524
|
+
return [
|
|
11525
|
+
isExistingFile(absoluteExclude, defaultMarkdownValidationTargetDeps) ? relativeExclude : `${relativeExclude}/**`
|
|
11526
|
+
];
|
|
11527
|
+
});
|
|
11528
|
+
}
|
|
11529
|
+
function pathContainsValidationPath(prefix, path7) {
|
|
11530
|
+
if (prefix.length === 0) return true;
|
|
11531
|
+
return path7 === prefix || path7.startsWith(`${prefix}/`);
|
|
11532
|
+
}
|
|
11266
11533
|
function hasMarkdownExtension(path7) {
|
|
11267
11534
|
const lastDot = path7.lastIndexOf(".");
|
|
11268
11535
|
if (lastDot < 0) return false;
|
|
@@ -11315,11 +11582,12 @@ async function markdownCommand(options) {
|
|
|
11315
11582
|
validationConfig.paths,
|
|
11316
11583
|
VALIDATION_PATH_TOOL_SUBSECTIONS.MARKDOWN
|
|
11317
11584
|
);
|
|
11318
|
-
const targetResolutions = files && files.length > 0 ? files.map((filePath) => resolveMarkdownValidationTarget(filePath)) : void 0;
|
|
11585
|
+
const targetResolutions = files && files.length > 0 ? files.map((filePath) => resolveMarkdownValidationTarget(markdownValidationOperandPath(cwd, filePath))) : void 0;
|
|
11586
|
+
const explicitTargets = targetResolutions === void 0 ? void 0 : targetResolutions.map((resolution) => resolution.target).filter((target) => target !== void 0).flatMap((target) => markdownTargetsForValidationPathFilter(cwd, target, pathFilter));
|
|
11319
11587
|
const unfilteredTargets = targetResolutions === void 0 ? getDefaultDirectories(cwd).map((path7) => ({
|
|
11320
11588
|
kind: MARKDOWN_VALIDATION_TARGET_KIND.DIRECTORY,
|
|
11321
11589
|
path: path7
|
|
11322
|
-
})) :
|
|
11590
|
+
})) : explicitTargets ?? [];
|
|
11323
11591
|
const targets = unfilteredTargets.filter(
|
|
11324
11592
|
(target) => pathPassesValidationFilter(relative4(cwd, target.path), pathFilter)
|
|
11325
11593
|
);
|
|
@@ -11335,11 +11603,22 @@ async function markdownCommand(options) {
|
|
|
11335
11603
|
}
|
|
11336
11604
|
const result = await validateMarkdown({
|
|
11337
11605
|
targets,
|
|
11338
|
-
projectRoot: cwd
|
|
11606
|
+
projectRoot: cwd,
|
|
11607
|
+
validationPathExcludes: validationPathFilterExcludes(pathFilter)
|
|
11339
11608
|
});
|
|
11340
11609
|
const durationMs = Date.now() - startTime;
|
|
11341
11610
|
return formatMarkdownResult(result, skippedOutput, quiet, durationMs);
|
|
11342
11611
|
}
|
|
11612
|
+
function markdownValidationOperandPath(productDir, filePath) {
|
|
11613
|
+
return isAbsolute4(filePath) ? filePath : join26(productDir, filePath);
|
|
11614
|
+
}
|
|
11615
|
+
function markdownTargetsForValidationPathFilter(productDir, target, pathFilter) {
|
|
11616
|
+
const relativePath = relative4(productDir, target.path);
|
|
11617
|
+
if (target.kind === MARKDOWN_VALIDATION_TARGET_KIND.FILE) {
|
|
11618
|
+
return pathPassesValidationFilter(relativePath, pathFilter) ? [target] : [];
|
|
11619
|
+
}
|
|
11620
|
+
return validationPathFilterIntersections(relativePath, pathFilter).map((path7) => resolveMarkdownValidationTarget(markdownValidationOperandPath(productDir, path7)).target).filter((filteredTarget) => filteredTarget !== void 0);
|
|
11621
|
+
}
|
|
11343
11622
|
function formatSkippedFileScope(target) {
|
|
11344
11623
|
return `${MARKDOWN_COMMAND_OUTPUT.SKIPPED_FILE_SCOPE_PREFIX}: ${target.path} (${target.reason})`;
|
|
11345
11624
|
}
|
|
@@ -12233,7 +12512,7 @@ var ParseErrorCode;
|
|
|
12233
12512
|
|
|
12234
12513
|
// src/validation/config/scope.ts
|
|
12235
12514
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
12236
|
-
import { isAbsolute as
|
|
12515
|
+
import { isAbsolute as isAbsolute5, join as join27, relative as relative5, resolve as resolve8 } from "path";
|
|
12237
12516
|
var TSCONFIG_FILES = {
|
|
12238
12517
|
full: "tsconfig.json",
|
|
12239
12518
|
production: "tsconfig.production.json"
|
|
@@ -12270,7 +12549,7 @@ var EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND = {
|
|
|
12270
12549
|
FILE: "file"
|
|
12271
12550
|
};
|
|
12272
12551
|
function resolveProjectPath(projectRoot, path7) {
|
|
12273
|
-
return
|
|
12552
|
+
return isAbsolute5(path7) ? path7 : join27(projectRoot, path7);
|
|
12274
12553
|
}
|
|
12275
12554
|
function parseTypeScriptConfig(configPath, deps = defaultScopeDeps) {
|
|
12276
12555
|
try {
|
|
@@ -12309,12 +12588,12 @@ function hasTypeScriptFilesRecursive(dirPath, maxDepth = 2, deps = defaultScopeD
|
|
|
12309
12588
|
try {
|
|
12310
12589
|
const items = deps.readdirSync(dirPath, { withFileTypes: true });
|
|
12311
12590
|
const hasDirectTsFiles = items.some(
|
|
12312
|
-
(item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(
|
|
12591
|
+
(item) => item.isFile() && pathHasTypeScriptSourceExtension(item.name) && pathPassesTypeScriptFileDiscoveryExcludes(join27(dirPath, item.name), options)
|
|
12313
12592
|
);
|
|
12314
12593
|
if (hasDirectTsFiles) return true;
|
|
12315
12594
|
const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith("."));
|
|
12316
12595
|
for (const subdir of subdirs.slice(0, 5)) {
|
|
12317
|
-
if (hasTypeScriptFilesRecursive(
|
|
12596
|
+
if (hasTypeScriptFilesRecursive(join27(dirPath, subdir.name), maxDepth - 1, deps, options)) {
|
|
12318
12597
|
return true;
|
|
12319
12598
|
}
|
|
12320
12599
|
}
|
|
@@ -12351,7 +12630,7 @@ function directoryContributesTypeScriptScope(dir, config, projectRoot, deps) {
|
|
|
12351
12630
|
return false;
|
|
12352
12631
|
}
|
|
12353
12632
|
try {
|
|
12354
|
-
return hasTypeScriptFilesRecursive(
|
|
12633
|
+
return hasTypeScriptFilesRecursive(join27(projectRoot, dir), 2, deps, {
|
|
12355
12634
|
excludePatterns: config.exclude,
|
|
12356
12635
|
projectRoot
|
|
12357
12636
|
});
|
|
@@ -12580,13 +12859,13 @@ function normalizeActiveIncludePattern(pattern, projectRoot, deps) {
|
|
|
12580
12859
|
function filterActiveIncludePatterns(patterns, excludePatterns, projectRoot, deps) {
|
|
12581
12860
|
return patterns.map((pattern) => normalizeActiveIncludePattern(pattern, projectRoot, deps)).filter((pattern) => typeScriptScopePatternTargetsTypeScriptSource(pattern)).filter((pattern) => {
|
|
12582
12861
|
const topLevelDir = getLiteralTopLevelPatternDirectory(pattern);
|
|
12583
|
-
return topLevelDir === null || deps.existsSync(
|
|
12862
|
+
return topLevelDir === null || deps.existsSync(join27(projectRoot, topLevelDir));
|
|
12584
12863
|
}).filter((pattern) => pathPassesValidationFilter(pattern, { exclude: excludePatterns }));
|
|
12585
12864
|
}
|
|
12586
12865
|
function getValidationDirectories(scope2, projectRoot, deps = defaultScopeDeps) {
|
|
12587
12866
|
const config = resolveTypeScriptConfig(scope2, projectRoot, deps);
|
|
12588
12867
|
const configDirectories = getTopLevelDirectoriesWithTypeScript(config, projectRoot, deps);
|
|
12589
|
-
const existingDirectories = configDirectories.filter((dir) => deps.existsSync(
|
|
12868
|
+
const existingDirectories = configDirectories.filter((dir) => deps.existsSync(join27(projectRoot, dir)));
|
|
12590
12869
|
return existingDirectories;
|
|
12591
12870
|
}
|
|
12592
12871
|
function getTypeScriptScope(scope2, projectRoot, deps = defaultScopeDeps) {
|
|
@@ -12607,13 +12886,13 @@ function pathPassesTypeScriptScope(path7, scopeConfig) {
|
|
|
12607
12886
|
return included && !excluded;
|
|
12608
12887
|
}
|
|
12609
12888
|
function pathStaysInsideTypeScriptScopeRoot(projectRoot, path7) {
|
|
12610
|
-
const resolvedPath =
|
|
12889
|
+
const resolvedPath = isAbsolute5(path7) ? resolve8(path7) : resolve8(projectRoot, path7);
|
|
12611
12890
|
const relativePath = relative5(projectRoot, resolvedPath);
|
|
12612
12891
|
const segments = normalizeTypeScriptScopePath(relativePath).split(PATH_SEGMENT_SEPARATOR3);
|
|
12613
|
-
return relativePath.length === 0 || !segments.includes("..") && !
|
|
12892
|
+
return relativePath.length === 0 || !segments.includes("..") && !isAbsolute5(relativePath);
|
|
12614
12893
|
}
|
|
12615
12894
|
function toProjectRelativeTypeScriptScopePath(projectRoot, path7) {
|
|
12616
|
-
const resolvedPath =
|
|
12895
|
+
const resolvedPath = isAbsolute5(path7) ? resolve8(path7) : resolve8(projectRoot, path7);
|
|
12617
12896
|
const relativePath = relative5(projectRoot, resolvedPath);
|
|
12618
12897
|
return relativePath.length === 0 ? TYPESCRIPT_SCOPE_PROJECT_ROOT : normalizeTypeScriptScopePath(relativePath);
|
|
12619
12898
|
}
|
|
@@ -12632,7 +12911,10 @@ function filterExplicitTypeScriptScopeTargets(filter, deps = defaultScopeDeps) {
|
|
|
12632
12911
|
if (paths === void 0) {
|
|
12633
12912
|
return void 0;
|
|
12634
12913
|
}
|
|
12635
|
-
return paths.filter((path7) => pathStaysInsideTypeScriptScopeRoot(projectRoot, path7)).map((path7) => toExplicitTypeScriptScopeTarget(projectRoot, path7, deps)).filter((target) => !requireExistingPaths || explicitTypeScriptScopeTargetExists(projectRoot, target, deps)).filter((target) => explicitTypeScriptScopeTargetPassesSourceKind(target)).filter((target) =>
|
|
12914
|
+
return paths.filter((path7) => pathStaysInsideTypeScriptScopeRoot(projectRoot, path7)).map((path7) => toExplicitTypeScriptScopeTarget(projectRoot, path7, deps)).filter((target) => !requireExistingPaths || explicitTypeScriptScopeTargetExists(projectRoot, target, deps)).filter((target) => explicitTypeScriptScopeTargetPassesSourceKind(target)).filter((target) => explicitTypeScriptScopeTargetIntersectsValidationPathFilter(target, validationPathFilter)).filter((target) => explicitTypeScriptScopeTargetPassesScope(target, scopeConfig));
|
|
12915
|
+
}
|
|
12916
|
+
function explicitTypeScriptScopeTargetIntersectsValidationPathFilter(target, validationPathFilter) {
|
|
12917
|
+
return target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.DIRECTORY ? pathIntersectsValidationFilter(target.path, validationPathFilter) : pathPassesValidationFilter(target.path, validationPathFilter);
|
|
12636
12918
|
}
|
|
12637
12919
|
function explicitTypeScriptScopeTargetPassesSourceKind(target) {
|
|
12638
12920
|
return target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.DIRECTORY || pathHasTypeScriptSourceExtension(target.path);
|
|
@@ -12653,10 +12935,12 @@ function explicitTypeScriptScopeTargetPassesScope(target, scopeConfig) {
|
|
|
12653
12935
|
if (typeScriptSourcePatterns.length > 0) {
|
|
12654
12936
|
return typeScriptSourcePatterns.some((pattern) => typeScriptScopePatternIntersectsDirectory(pattern, target.path));
|
|
12655
12937
|
}
|
|
12656
|
-
return
|
|
12938
|
+
return scopeConfig.directories.some(
|
|
12939
|
+
(directory) => pathMatchesLiteralPrefix(directory, target.path) || pathMatchesLiteralPrefix(target.path, directory)
|
|
12940
|
+
) || pathPassesTypeScriptScope(join27(target.path, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME), scopeConfig);
|
|
12657
12941
|
}
|
|
12658
12942
|
function directoryPassesTypeScriptExcludes(directory, scopeConfig) {
|
|
12659
|
-
const probePath =
|
|
12943
|
+
const probePath = join27(directory, TYPESCRIPT_SCOPE_DIRECTORY_PROBE_FILENAME);
|
|
12660
12944
|
return !scopeConfig.excludePatterns.some(
|
|
12661
12945
|
(pattern) => typeScriptScopePatternCoversDirectorySourceSet(pattern, directory) || pathMatchesTypeScriptPattern(probePath, pattern)
|
|
12662
12946
|
);
|
|
@@ -12683,6 +12967,15 @@ function constrainTypeScriptScopeToExplicitTargets(scopeConfig, targets) {
|
|
|
12683
12967
|
const retainedDirectoryFilePatterns = scopeConfig.filePatterns.filter(
|
|
12684
12968
|
(pattern) => !typeScriptScopePatternHasGlob(pattern) && pathHasTypeScriptSourceExtension(pattern) && retainedDirectories.some((directory) => pathMatchesLiteralPrefix(pattern, directory))
|
|
12685
12969
|
);
|
|
12970
|
+
const retainedDirectoryPatterns = scopeConfig.filePatterns.filter(
|
|
12971
|
+
(pattern) => !typeScriptScopePatternHasGlob(pattern) && !pathHasTypeScriptSourceExtension(pattern) && retainedDirectories.some((directory) => pathMatchesLiteralPrefix(pattern, directory))
|
|
12972
|
+
).map((pattern) => typeScriptLiteralDirectoryPattern(pattern));
|
|
12973
|
+
const retainedDirectoryScopePatterns = retainedDirectories.filter(
|
|
12974
|
+
(directory) => scopeConfig.filePatterns.some(
|
|
12975
|
+
(pattern) => !typeScriptScopePatternHasGlob(pattern) && !pathHasTypeScriptSourceExtension(pattern) && pathMatchesLiteralPrefix(directory, pattern)
|
|
12976
|
+
)
|
|
12977
|
+
).map((directory) => typeScriptLiteralDirectoryPattern(directory));
|
|
12978
|
+
const retainedDirectoryOperandPatterns = scopeConfig.filePatterns.length === 0 ? retainedDirectories.map(typeScriptLiteralDirectoryPattern) : [];
|
|
12686
12979
|
const explicitFileTargets = targets.filter((target) => target.kind === EXPLICIT_TYPESCRIPT_SCOPE_TARGET_KIND.FILE).map((target) => target.path).filter(
|
|
12687
12980
|
(path7) => !retainedDirectories.some(
|
|
12688
12981
|
(directory) => path7 === directory || path7.startsWith(`${directory}${PATH_SEGMENT_SEPARATOR3}`)
|
|
@@ -12695,12 +12988,20 @@ function constrainTypeScriptScopeToExplicitTargets(scopeConfig, targets) {
|
|
|
12695
12988
|
...scopeConfig,
|
|
12696
12989
|
directories: retainedDirectories,
|
|
12697
12990
|
filePatterns: [
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12991
|
+
.../* @__PURE__ */ new Set([
|
|
12992
|
+
...scopedFilePatternsForDirectoryTargets,
|
|
12993
|
+
...retainedDirectoryFilePatterns,
|
|
12994
|
+
...retainedDirectoryPatterns,
|
|
12995
|
+
...retainedDirectoryScopePatterns,
|
|
12996
|
+
...retainedDirectoryOperandPatterns,
|
|
12997
|
+
...uncoveredExplicitFileTargets
|
|
12998
|
+
])
|
|
12701
12999
|
]
|
|
12702
13000
|
};
|
|
12703
13001
|
}
|
|
13002
|
+
function typeScriptLiteralDirectoryPattern(pattern) {
|
|
13003
|
+
return pattern === TYPESCRIPT_SCOPE_PROJECT_ROOT ? TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX.slice(1) : `${pattern}${TYPESCRIPT_SCOPE_DIRECTORY_PATTERN_SUFFIX}`;
|
|
13004
|
+
}
|
|
12704
13005
|
function resolveTypeScriptValidationScope(filter, deps = defaultScopeDeps) {
|
|
12705
13006
|
const scopeConfig = applyValidationPathFilterToScope(
|
|
12706
13007
|
getTypeScriptScope(filter.scope, filter.projectRoot, deps),
|
|
@@ -12847,10 +13148,10 @@ function resolvedModulePath(resolvedPath) {
|
|
|
12847
13148
|
return resolvedPath;
|
|
12848
13149
|
}
|
|
12849
13150
|
}
|
|
12850
|
-
function nearestPackageRoot(filePath,
|
|
13151
|
+
function nearestPackageRoot(filePath, existsSync9) {
|
|
12851
13152
|
let currentDirectory = path6.dirname(filePath);
|
|
12852
13153
|
for (; ; ) {
|
|
12853
|
-
if (
|
|
13154
|
+
if (existsSync9(path6.join(currentDirectory, PACKAGE_MANIFEST_FILENAME))) {
|
|
12854
13155
|
return currentDirectory;
|
|
12855
13156
|
}
|
|
12856
13157
|
const parentDirectory = path6.dirname(currentDirectory);
|
|
@@ -12860,9 +13161,9 @@ function nearestPackageRoot(filePath, existsSync8) {
|
|
|
12860
13161
|
currentDirectory = parentDirectory;
|
|
12861
13162
|
}
|
|
12862
13163
|
}
|
|
12863
|
-
function bundledToolPath(resolvedPath,
|
|
13164
|
+
function bundledToolPath(resolvedPath, existsSync9) {
|
|
12864
13165
|
const bundledFilePath = resolvedModulePath(resolvedPath);
|
|
12865
|
-
return nearestPackageRoot(bundledFilePath,
|
|
13166
|
+
return nearestPackageRoot(bundledFilePath, existsSync9) ?? path6.dirname(bundledFilePath);
|
|
12866
13167
|
}
|
|
12867
13168
|
async function discoverTool(tool, options = {}) {
|
|
12868
13169
|
const { projectRoot = CONFIG_PROCESS_CWD.read(), deps = defaultToolDiscoveryDeps } = options;
|
|
@@ -12919,7 +13220,7 @@ import {
|
|
|
12919
13220
|
cruise as dependencyCruiser
|
|
12920
13221
|
} from "dependency-cruiser";
|
|
12921
13222
|
import extractTypeScriptConfig from "dependency-cruiser/config-utl/extract-ts-config";
|
|
12922
|
-
import { join as
|
|
13223
|
+
import { join as join28 } from "path";
|
|
12923
13224
|
var DEPENDENCY_CRUISER_MODULE_SYSTEMS = ["es6", "cjs"];
|
|
12924
13225
|
var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_GLOB_SUFFIXES = [...TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS];
|
|
12925
13226
|
var DEPENDENCY_CRUISER_TYPESCRIPT_SOURCE_PATTERN = String.raw`(?<!\.d)\.(?:[cm]?ts|tsx)$`;
|
|
@@ -13159,7 +13460,7 @@ async function validateCircularDependencies(scope2, typescriptScope, projectRoot
|
|
|
13159
13460
|
if (analyzeSourcePatterns.length === 0) {
|
|
13160
13461
|
return { success: true };
|
|
13161
13462
|
}
|
|
13162
|
-
const tsConfigFile =
|
|
13463
|
+
const tsConfigFile = join28(projectRoot, TSCONFIG_FILES[scope2]);
|
|
13163
13464
|
const result = await deps.dependencyCruiser(
|
|
13164
13465
|
analyzeSourcePatterns,
|
|
13165
13466
|
buildDependencyCruiserOptions(typescriptScope, projectRoot, tsConfigFile),
|
|
@@ -13281,7 +13582,7 @@ async function circularCommand(options, deps = defaultCircularCommandDeps) {
|
|
|
13281
13582
|
// src/validation/steps/knip.ts
|
|
13282
13583
|
import { existsSync as existsSync4 } from "fs";
|
|
13283
13584
|
import { mkdir as mkdir5, mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
13284
|
-
import { isAbsolute as
|
|
13585
|
+
import { isAbsolute as isAbsolute6, join as join29 } from "path";
|
|
13285
13586
|
var defaultKnipProcessRunner = lifecycleProcessRunner;
|
|
13286
13587
|
var KNIP_COMMAND_TOKENS = {
|
|
13287
13588
|
COMMAND: "knip",
|
|
@@ -13314,7 +13615,7 @@ async function validateKnip2(context, runner = defaultKnipProcessRunner, deps =
|
|
|
13314
13615
|
}
|
|
13315
13616
|
async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
|
|
13316
13617
|
const scopedTsconfig = typescriptScope.filteredByValidationPaths ? await createScopedKnipTsconfig(projectRoot, typescriptScope, deps) : void 0;
|
|
13317
|
-
const localBin =
|
|
13618
|
+
const localBin = join29(projectRoot, "node_modules", ".bin", "knip");
|
|
13318
13619
|
const binary = deps.existsSync(localBin) ? localBin : KNIP_COMMAND_TOKENS.NPX_COMMAND;
|
|
13319
13620
|
const baseArgs = scopedTsconfig === void 0 ? [] : [
|
|
13320
13621
|
KNIP_COMMAND_TOKENS.USE_TSCONFIG_FILES_FLAG,
|
|
@@ -13344,13 +13645,13 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
|
|
|
13344
13645
|
knipProcess.stderr?.on("data", (data) => {
|
|
13345
13646
|
knipError += data.toString();
|
|
13346
13647
|
});
|
|
13347
|
-
return new Promise((
|
|
13648
|
+
return new Promise((resolve11) => {
|
|
13348
13649
|
const resolveAfterCleanup = (result) => {
|
|
13349
13650
|
if (resultResolved) {
|
|
13350
13651
|
return;
|
|
13351
13652
|
}
|
|
13352
13653
|
resultResolved = true;
|
|
13353
|
-
void cleanupOnce().finally(() =>
|
|
13654
|
+
void cleanupOnce().finally(() => resolve11(result));
|
|
13354
13655
|
};
|
|
13355
13656
|
knipProcess.on("close", (code) => {
|
|
13356
13657
|
if (code === 0) {
|
|
@@ -13369,11 +13670,11 @@ async function runKnipSubprocess(projectRoot, typescriptScope, runner, deps) {
|
|
|
13369
13670
|
});
|
|
13370
13671
|
}
|
|
13371
13672
|
async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
|
|
13372
|
-
const tempParentDir =
|
|
13673
|
+
const tempParentDir = join29(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
|
|
13373
13674
|
await deps.mkdir(tempParentDir, { recursive: true });
|
|
13374
|
-
const tempDir = await deps.mkdtemp(
|
|
13375
|
-
const configPath =
|
|
13376
|
-
const toProjectPathPattern = (pattern) =>
|
|
13675
|
+
const tempDir = await deps.mkdtemp(join29(tempParentDir, "validate-knip-"));
|
|
13676
|
+
const configPath = join29(tempDir, TSCONFIG_FILES.full);
|
|
13677
|
+
const toProjectPathPattern = (pattern) => isAbsolute6(pattern) ? pattern : join29(projectRoot, pattern);
|
|
13377
13678
|
const project = [
|
|
13378
13679
|
...typescriptScope.directories.flatMap(
|
|
13379
13680
|
(directory) => TYPESCRIPT_FALLBACK_INCLUDE_PATTERNS.map((pattern) => `${directory}/${pattern}`)
|
|
@@ -13381,7 +13682,7 @@ async function createScopedKnipTsconfig(projectRoot, typescriptScope, deps) {
|
|
|
13381
13682
|
...typescriptScope.filePatterns
|
|
13382
13683
|
];
|
|
13383
13684
|
const config = {
|
|
13384
|
-
extends:
|
|
13685
|
+
extends: join29(projectRoot, TSCONFIG_FILES.full),
|
|
13385
13686
|
include: project.map(toProjectPathPattern),
|
|
13386
13687
|
exclude: typescriptScope.excludePatterns.map(toProjectPathPattern)
|
|
13387
13688
|
};
|
|
@@ -13448,12 +13749,12 @@ async function knipCommand(options, deps = defaultKnipCommandDeps) {
|
|
|
13448
13749
|
|
|
13449
13750
|
// src/validation/steps/eslint.ts
|
|
13450
13751
|
import { existsSync as existsSync6 } from "fs";
|
|
13451
|
-
import { join as
|
|
13752
|
+
import { join as join31 } from "path";
|
|
13452
13753
|
|
|
13453
13754
|
// src/validation/lint-policy.ts
|
|
13454
13755
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
13455
|
-
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as
|
|
13456
|
-
import { join as
|
|
13756
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
13757
|
+
import { join as join30 } from "path";
|
|
13457
13758
|
|
|
13458
13759
|
// src/validation/lint-policy-constants.ts
|
|
13459
13760
|
var LINT_POLICY_MANIFESTS = {
|
|
@@ -13501,17 +13802,17 @@ function isSpecTreeNodePath(entry) {
|
|
|
13501
13802
|
}
|
|
13502
13803
|
function readManifest2(productDir, file, key) {
|
|
13503
13804
|
return parseLintPolicyManifest(
|
|
13504
|
-
readFileSync4(
|
|
13805
|
+
readFileSync4(join30(productDir, file), "utf-8"),
|
|
13505
13806
|
file,
|
|
13506
13807
|
key
|
|
13507
13808
|
);
|
|
13508
13809
|
}
|
|
13509
13810
|
function manifestExists(productDir, file) {
|
|
13510
|
-
return existsSync5(
|
|
13811
|
+
return existsSync5(join30(productDir, file));
|
|
13511
13812
|
}
|
|
13512
13813
|
function findDeprecatedSpecNodePath(productDir) {
|
|
13513
13814
|
function visit2(relativeDirectory) {
|
|
13514
|
-
const absoluteDirectory =
|
|
13815
|
+
const absoluteDirectory = join30(productDir, relativeDirectory);
|
|
13515
13816
|
for (const entry of readdirSync2(absoluteDirectory, { withFileTypes: true })) {
|
|
13516
13817
|
if (!entry.isDirectory()) {
|
|
13517
13818
|
continue;
|
|
@@ -13527,7 +13828,7 @@ function findDeprecatedSpecNodePath(productDir) {
|
|
|
13527
13828
|
}
|
|
13528
13829
|
return void 0;
|
|
13529
13830
|
}
|
|
13530
|
-
const specTreeRootPath =
|
|
13831
|
+
const specTreeRootPath = join30(productDir, SPEC_TREE_ROOT);
|
|
13531
13832
|
if (!existsSync5(specTreeRootPath)) {
|
|
13532
13833
|
return void 0;
|
|
13533
13834
|
}
|
|
@@ -13553,8 +13854,8 @@ function assertManifestEntries(productDir, file, entries, suffixPredicate, suffi
|
|
|
13553
13854
|
if (!suffixPredicate(entry)) {
|
|
13554
13855
|
throw new Error(`${file} entry must ${suffixDescription}: ${entry}`);
|
|
13555
13856
|
}
|
|
13556
|
-
const absoluteEntry =
|
|
13557
|
-
if (!existsSync5(absoluteEntry) || !
|
|
13857
|
+
const absoluteEntry = join30(productDir, entry);
|
|
13858
|
+
if (!existsSync5(absoluteEntry) || !statSync3(absoluteEntry).isDirectory()) {
|
|
13558
13859
|
throw new Error(`${file} entry does not exist as a directory: ${entry}`);
|
|
13559
13860
|
}
|
|
13560
13861
|
}
|
|
@@ -13715,7 +14016,14 @@ var ESLINT_COMMAND_TOKENS = {
|
|
|
13715
14016
|
};
|
|
13716
14017
|
var ESLINT_LOCAL_BIN_SEGMENTS = ["node_modules", ".bin", ESLINT_COMMAND_TOKENS.COMMAND];
|
|
13717
14018
|
function buildEslintArgs(context) {
|
|
13718
|
-
const {
|
|
14019
|
+
const {
|
|
14020
|
+
validatedFiles,
|
|
14021
|
+
validatedFileIgnorePatterns = [],
|
|
14022
|
+
mode,
|
|
14023
|
+
configFile = DEFAULT_ESLINT_CONFIG_FILE,
|
|
14024
|
+
scope: scope2,
|
|
14025
|
+
scopeConfig
|
|
14026
|
+
} = context;
|
|
13719
14027
|
const fixArg = mode === EXECUTION_MODES.WRITE ? [ESLINT_COMMAND_TOKENS.FIX_FLAG] : [];
|
|
13720
14028
|
if (validatedFiles && validatedFiles.length > 0) {
|
|
13721
14029
|
return [
|
|
@@ -13723,6 +14031,7 @@ function buildEslintArgs(context) {
|
|
|
13723
14031
|
ESLINT_COMMAND_TOKENS.CONFIG_FLAG,
|
|
13724
14032
|
configFile,
|
|
13725
14033
|
...fixArg,
|
|
14034
|
+
...buildIgnorePatternArgs(validatedFileIgnorePatterns),
|
|
13726
14035
|
ESLINT_COMMAND_TOKENS.FILE_SEPARATOR,
|
|
13727
14036
|
...validatedFiles
|
|
13728
14037
|
];
|
|
@@ -13763,13 +14072,14 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
|
|
|
13763
14072
|
}
|
|
13764
14073
|
const eslintArgs = buildEslintArgs({
|
|
13765
14074
|
validatedFiles,
|
|
14075
|
+
validatedFileIgnorePatterns: context.validatedFileIgnorePatterns,
|
|
13766
14076
|
mode,
|
|
13767
14077
|
configFile: eslintConfigFile,
|
|
13768
14078
|
scope: scope2,
|
|
13769
14079
|
scopeConfig: context.scopeConfig
|
|
13770
14080
|
});
|
|
13771
|
-
return new Promise((
|
|
13772
|
-
const localBin =
|
|
14081
|
+
return new Promise((resolve11) => {
|
|
14082
|
+
const localBin = join31(projectRoot, ...ESLINT_LOCAL_BIN_SEGMENTS);
|
|
13773
14083
|
const binary = existsSync6(localBin) ? localBin : "npx";
|
|
13774
14084
|
const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
|
|
13775
14085
|
const eslintProcess = spawnManagedSubprocess(runner, binary, spawnArgs, {
|
|
@@ -13778,13 +14088,13 @@ async function validateESLint(context, runner = defaultEslintProcessRunner, outp
|
|
|
13778
14088
|
forwardValidationSubprocessOutput(eslintProcess, outputStreams);
|
|
13779
14089
|
eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
|
|
13780
14090
|
if (code === 0) {
|
|
13781
|
-
|
|
14091
|
+
resolve11({ success: true });
|
|
13782
14092
|
} else {
|
|
13783
|
-
|
|
14093
|
+
resolve11({ success: false, error: `ESLint exited with code ${code}` });
|
|
13784
14094
|
}
|
|
13785
14095
|
});
|
|
13786
14096
|
eslintProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
|
|
13787
|
-
|
|
14097
|
+
resolve11({ success: false, error: error.message });
|
|
13788
14098
|
});
|
|
13789
14099
|
});
|
|
13790
14100
|
}
|
|
@@ -13832,7 +14142,12 @@ async function lintCommand(options) {
|
|
|
13832
14142
|
getTypeScriptScope(scope2, cwd),
|
|
13833
14143
|
validationPathFilter
|
|
13834
14144
|
);
|
|
13835
|
-
const validatedFiles = files?.
|
|
14145
|
+
const validatedFiles = files?.flatMap(
|
|
14146
|
+
(file) => validationPathFilterIntersections(
|
|
14147
|
+
toProjectRelativeValidationPath(cwd, file),
|
|
14148
|
+
validationPathFilter
|
|
14149
|
+
).map(formatLintValidationOperand)
|
|
14150
|
+
);
|
|
13836
14151
|
if (scopeConfig.filteredByValidationPathNoMatches || files !== void 0 && files.length > 0 && validatedFiles?.length === 0) {
|
|
13837
14152
|
return {
|
|
13838
14153
|
exitCode: 0,
|
|
@@ -13852,6 +14167,7 @@ async function lintCommand(options) {
|
|
|
13852
14167
|
mode: fix ? "write" : "read",
|
|
13853
14168
|
enabledValidations: { ESLINT: true },
|
|
13854
14169
|
validatedFiles,
|
|
14170
|
+
validatedFileIgnorePatterns: files === void 0 ? void 0 : validationPathFilterExcludes(validationPathFilter),
|
|
13855
14171
|
isFileSpecificMode: Boolean(validatedFiles && validatedFiles.length > 0),
|
|
13856
14172
|
eslintConfigFile
|
|
13857
14173
|
};
|
|
@@ -13859,6 +14175,9 @@ async function lintCommand(options) {
|
|
|
13859
14175
|
const durationMs = Date.now() - startTime;
|
|
13860
14176
|
return formatLintResult(result, quiet, durationMs);
|
|
13861
14177
|
}
|
|
14178
|
+
function formatLintValidationOperand(path7) {
|
|
14179
|
+
return path7.length === 0 ? "." : path7;
|
|
14180
|
+
}
|
|
13862
14181
|
function formatLintResult(result, quiet, durationMs) {
|
|
13863
14182
|
if (result.skipped) {
|
|
13864
14183
|
const output2 = quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE;
|
|
@@ -13874,7 +14193,7 @@ function formatLintResult(result, quiet, durationMs) {
|
|
|
13874
14193
|
|
|
13875
14194
|
// src/validation/literal/index.ts
|
|
13876
14195
|
import { readFile as readFile9 } from "fs/promises";
|
|
13877
|
-
import { isAbsolute as
|
|
14196
|
+
import { isAbsolute as isAbsolute7, relative as relative7, resolve as resolve9 } from "path";
|
|
13878
14197
|
|
|
13879
14198
|
// src/lib/file-inclusion/predicates/ignore-source.ts
|
|
13880
14199
|
var IGNORE_SOURCE_LAYER = "ignore-source";
|
|
@@ -13909,7 +14228,7 @@ var ignoreSourceLayer = makeLayer(
|
|
|
13909
14228
|
|
|
13910
14229
|
// src/lib/file-inclusion/pipeline.ts
|
|
13911
14230
|
import { readdir as readdir8 } from "fs/promises";
|
|
13912
|
-
import { join as
|
|
14231
|
+
import { join as join32, relative as relative6, sep as sep2 } from "path";
|
|
13913
14232
|
var EXPLICIT_OVERRIDE_LAYER = "explicit-override";
|
|
13914
14233
|
function isNodeError4(err) {
|
|
13915
14234
|
return err instanceof Error && "code" in err;
|
|
@@ -13927,10 +14246,10 @@ async function collectPaths(absoluteDir, projectRoot, result, artifactDirs) {
|
|
|
13927
14246
|
for (const entry of dirEntries) {
|
|
13928
14247
|
if (entry.isDirectory()) {
|
|
13929
14248
|
if (artifactDirs.has(entry.name)) continue;
|
|
13930
|
-
const absolutePath =
|
|
14249
|
+
const absolutePath = join32(absoluteDir, entry.name);
|
|
13931
14250
|
await collectPaths(absolutePath, projectRoot, result, artifactDirs);
|
|
13932
14251
|
} else if (entry.isFile()) {
|
|
13933
|
-
const absolutePath =
|
|
14252
|
+
const absolutePath = join32(absoluteDir, entry.name);
|
|
13934
14253
|
const rel = relative6(projectRoot, absolutePath);
|
|
13935
14254
|
result.push(sep2 === "/" ? rel : rel.split(sep2).join("/"));
|
|
13936
14255
|
}
|
|
@@ -14442,9 +14761,9 @@ function applyPathFilter2(entries, pathConfig) {
|
|
|
14442
14761
|
}
|
|
14443
14762
|
async function validateLiteralReuse(input) {
|
|
14444
14763
|
const config = input.config ?? literalConfigDescriptor.defaults;
|
|
14445
|
-
const request = input.files ? {
|
|
14764
|
+
const request = input.scopeConfig === void 0 && input.files ? {
|
|
14446
14765
|
explicit: input.files.map((f) => {
|
|
14447
|
-
const abs =
|
|
14766
|
+
const abs = isAbsolute7(f) ? f : resolve9(input.productDir, f);
|
|
14448
14767
|
return relative7(input.productDir, abs).split(/[\\/]/g).join("/");
|
|
14449
14768
|
})
|
|
14450
14769
|
} : { walkRoot: input.productDir };
|
|
@@ -14455,7 +14774,9 @@ async function validateLiteralReuse(input) {
|
|
|
14455
14774
|
DEFAULT_SCOPE_CONFIG,
|
|
14456
14775
|
EMPTY_IGNORE_READER
|
|
14457
14776
|
);
|
|
14458
|
-
const
|
|
14777
|
+
const pathFiltered = applyPathFilter2(scope2.included, input.pathConfig);
|
|
14778
|
+
const literalScopeConfig = input.scopeConfig;
|
|
14779
|
+
const filtered = literalScopeConfig === void 0 ? pathFiltered : pathFiltered.filter((entry) => pathPassesTypeScriptScope(entry.path, literalScopeConfig));
|
|
14459
14780
|
const candidateFiles = filtered.filter((entry) => isTypescriptSource(entry.path)).map((entry) => resolve9(input.productDir, entry.path));
|
|
14460
14781
|
const collectOptions = {
|
|
14461
14782
|
visitorKeys: defaultVisitorKeys,
|
|
@@ -14483,7 +14804,11 @@ async function validateLiteralReuse(input) {
|
|
|
14483
14804
|
testOccurrencesByFile,
|
|
14484
14805
|
allowlist: resolveAllowlist(config)
|
|
14485
14806
|
});
|
|
14486
|
-
return {
|
|
14807
|
+
return {
|
|
14808
|
+
findings,
|
|
14809
|
+
indexedOccurrencesByFile,
|
|
14810
|
+
filteredByValidationPathNoMatches: input.scopeConfig?.filteredByValidationPathNoMatches
|
|
14811
|
+
};
|
|
14487
14812
|
}
|
|
14488
14813
|
async function readSafe(path7) {
|
|
14489
14814
|
try {
|
|
@@ -14518,6 +14843,9 @@ var LITERAL_EXIT_CODES = {
|
|
|
14518
14843
|
var TYPESCRIPT_ABSENT_MESSAGE3 = formatTypeScriptAbsentSkipMessage(
|
|
14519
14844
|
VALIDATION_STAGE_DISPLAY_NAMES.LITERAL
|
|
14520
14845
|
);
|
|
14846
|
+
var VALIDATION_PATHS_NO_TARGETS_MESSAGE2 = formatValidationPathsNoTargetsSkipMessage(
|
|
14847
|
+
VALIDATION_STAGE_DISPLAY_NAMES.LITERAL
|
|
14848
|
+
);
|
|
14521
14849
|
var LITERAL_DISABLED_MESSAGE = `\u23ED ${VALIDATION_SKIP_LABELS.VERB} ${VALIDATION_STAGE_DISPLAY_NAMES.LITERAL} (${VALIDATION_SKIP_LABELS.DISABLED_BY_PREFIX} validation.literal.enabled)`;
|
|
14522
14850
|
var NO_PROBLEMS_MESSAGE = "Literal: \u2713 No problems";
|
|
14523
14851
|
function formatNoProblemsOfKind(kind) {
|
|
@@ -14533,31 +14861,15 @@ async function literalCommand(options) {
|
|
|
14533
14861
|
durationMs: Date.now() - start
|
|
14534
14862
|
};
|
|
14535
14863
|
}
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
exitCode: LITERAL_EXIT_CODES.CONFIG_ERROR,
|
|
14544
|
-
output: `Literal: \u2717 config error \u2014 ${loaded.error}`,
|
|
14545
|
-
durationMs: Date.now() - start
|
|
14546
|
-
};
|
|
14547
|
-
}
|
|
14548
|
-
const validationConfig = loaded.value[validationConfigDescriptor.section];
|
|
14549
|
-
resolvedEnabled = validationConfig.literal.enabled;
|
|
14550
|
-
resolvedLiteralConfig = validationConfig.literal.values;
|
|
14551
|
-
resolvedPathConfig = validationPathFilterForTool(
|
|
14552
|
-
validationConfig.paths,
|
|
14553
|
-
VALIDATION_PATH_TOOL_SUBSECTIONS.LITERAL
|
|
14554
|
-
);
|
|
14555
|
-
} else {
|
|
14556
|
-
resolvedEnabled = options.enabled ?? validationConfigDescriptor.defaults.literal.enabled;
|
|
14557
|
-
resolvedLiteralConfig = options.config;
|
|
14558
|
-
resolvedPathConfig = options.pathConfig ?? validationConfigDescriptor.defaults.paths;
|
|
14864
|
+
const resolved = await resolveLiteralCommandConfig(options);
|
|
14865
|
+
if (typeof resolved === "string") {
|
|
14866
|
+
return {
|
|
14867
|
+
exitCode: LITERAL_EXIT_CODES.CONFIG_ERROR,
|
|
14868
|
+
output: `Literal: \u2717 config error \u2014 ${resolved}`,
|
|
14869
|
+
durationMs: Date.now() - start
|
|
14870
|
+
};
|
|
14559
14871
|
}
|
|
14560
|
-
if (!
|
|
14872
|
+
if (!resolved.enabled) {
|
|
14561
14873
|
return {
|
|
14562
14874
|
exitCode: LITERAL_EXIT_CODES.OK,
|
|
14563
14875
|
output: options.quiet ? "" : LITERAL_DISABLED_MESSAGE,
|
|
@@ -14566,10 +14878,17 @@ async function literalCommand(options) {
|
|
|
14566
14878
|
}
|
|
14567
14879
|
const result = await validateLiteralReuse({
|
|
14568
14880
|
productDir: options.cwd,
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14881
|
+
config: resolved.literalConfig,
|
|
14882
|
+
pathConfig: resolved.pathConfig,
|
|
14883
|
+
scopeConfig: resolveExplicitLiteralTypeScriptScope(options, resolved.pathConfig)
|
|
14572
14884
|
});
|
|
14885
|
+
if (options.files !== void 0 && options.files.length > 0 && result.filteredByValidationPathNoMatches) {
|
|
14886
|
+
return {
|
|
14887
|
+
exitCode: LITERAL_EXIT_CODES.OK,
|
|
14888
|
+
output: options.quiet ? "" : VALIDATION_PATHS_NO_TARGETS_MESSAGE2,
|
|
14889
|
+
durationMs: Date.now() - start
|
|
14890
|
+
};
|
|
14891
|
+
}
|
|
14573
14892
|
const filteredFindings = filterLiteralFindings(result.findings, options.kind);
|
|
14574
14893
|
const totalProblems = countLiteralProblems(filteredFindings);
|
|
14575
14894
|
const exitCode = totalProblems === 0 ? LITERAL_EXIT_CODES.OK : LITERAL_EXIT_CODES.FINDINGS;
|
|
@@ -14583,6 +14902,38 @@ async function literalCommand(options) {
|
|
|
14583
14902
|
}
|
|
14584
14903
|
return { exitCode, output, durationMs: Date.now() - start };
|
|
14585
14904
|
}
|
|
14905
|
+
async function resolveLiteralCommandConfig(options) {
|
|
14906
|
+
if (options.config !== void 0) {
|
|
14907
|
+
return {
|
|
14908
|
+
enabled: options.enabled ?? validationConfigDescriptor.defaults.literal.enabled,
|
|
14909
|
+
literalConfig: options.config,
|
|
14910
|
+
pathConfig: options.pathConfig ?? validationConfigDescriptor.defaults.paths
|
|
14911
|
+
};
|
|
14912
|
+
}
|
|
14913
|
+
const loaded = await resolveConfig(options.cwd, [validationConfigDescriptor]);
|
|
14914
|
+
if (!loaded.ok) return loaded.error;
|
|
14915
|
+
const validationConfig = loaded.value[validationConfigDescriptor.section];
|
|
14916
|
+
return {
|
|
14917
|
+
enabled: validationConfig.literal.enabled,
|
|
14918
|
+
literalConfig: validationConfig.literal.values,
|
|
14919
|
+
pathConfig: validationPathFilterForTool(
|
|
14920
|
+
validationConfig.paths,
|
|
14921
|
+
VALIDATION_PATH_TOOL_SUBSECTIONS.LITERAL
|
|
14922
|
+
)
|
|
14923
|
+
};
|
|
14924
|
+
}
|
|
14925
|
+
function resolveExplicitLiteralTypeScriptScope(options, pathConfig) {
|
|
14926
|
+
if (options.files === void 0 || options.files.length === 0) {
|
|
14927
|
+
return void 0;
|
|
14928
|
+
}
|
|
14929
|
+
return resolveTypeScriptValidationScope({
|
|
14930
|
+
projectRoot: options.cwd,
|
|
14931
|
+
scope: options.scope ?? VALIDATION_SCOPES.FULL,
|
|
14932
|
+
paths: options.files,
|
|
14933
|
+
validationPathFilter: pathConfig,
|
|
14934
|
+
markExplicitPathsAsValidationFilter: true
|
|
14935
|
+
});
|
|
14936
|
+
}
|
|
14586
14937
|
function filterLiteralFindings(findings, kind) {
|
|
14587
14938
|
return {
|
|
14588
14939
|
srcReuse: kind === LITERAL_PROBLEM_KIND.DUPE ? [] : sortReuseFindings(findings.srcReuse),
|
|
@@ -14706,7 +15057,7 @@ function formatLoc(loc) {
|
|
|
14706
15057
|
// src/validation/steps/typescript.ts
|
|
14707
15058
|
import { existsSync as existsSync7, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
14708
15059
|
import { mkdtemp as mkdtemp3 } from "fs/promises";
|
|
14709
|
-
import { isAbsolute as
|
|
15060
|
+
import { isAbsolute as isAbsolute8, join as join33, relative as relative8 } from "path";
|
|
14710
15061
|
var defaultTypeScriptProcessRunner = lifecycleProcessRunner;
|
|
14711
15062
|
var defaultTypeScriptDeps = {
|
|
14712
15063
|
mkdtemp: mkdtemp3,
|
|
@@ -14718,9 +15069,9 @@ var defaultTypeScriptDeps = {
|
|
|
14718
15069
|
var TEMPORARY_TSCONFIG_COMPILER_OPTIONS = { noEmit: true };
|
|
14719
15070
|
var TEMPORARY_TSCONFIG_DIR_PREFIX = "validate-ts-";
|
|
14720
15071
|
async function createTemporaryTsconfigDir(projectRoot, deps) {
|
|
14721
|
-
const parent =
|
|
15072
|
+
const parent = join33(projectRoot, ...TEMPORARY_TSCONFIG_PARENT_SEGMENTS);
|
|
14722
15073
|
deps.mkdirSync(parent, { recursive: true });
|
|
14723
|
-
return deps.mkdtemp(
|
|
15074
|
+
return deps.mkdtemp(join33(parent, TEMPORARY_TSCONFIG_DIR_PREFIX));
|
|
14724
15075
|
}
|
|
14725
15076
|
function buildTypeScriptArgs(context) {
|
|
14726
15077
|
const { scope: scope2, configFile } = context;
|
|
@@ -14728,11 +15079,11 @@ function buildTypeScriptArgs(context) {
|
|
|
14728
15079
|
}
|
|
14729
15080
|
async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = defaultTypeScriptDeps) {
|
|
14730
15081
|
const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
|
|
14731
|
-
const configPath =
|
|
15082
|
+
const configPath = join33(tempDir, "tsconfig.json");
|
|
14732
15083
|
const baseConfigFile = TSCONFIG_FILES[scope2];
|
|
14733
|
-
const absoluteFiles = files.map((file) =>
|
|
15084
|
+
const absoluteFiles = files.map((file) => isAbsolute8(file) ? file : join33(projectRoot, file));
|
|
14734
15085
|
const tempConfig = {
|
|
14735
|
-
extends:
|
|
15086
|
+
extends: join33(projectRoot, baseConfigFile),
|
|
14736
15087
|
files: absoluteFiles,
|
|
14737
15088
|
include: [],
|
|
14738
15089
|
exclude: [],
|
|
@@ -14744,13 +15095,16 @@ async function createFileSpecificTsconfig(scope2, files, projectRoot, deps = def
|
|
|
14744
15095
|
}
|
|
14745
15096
|
async function createScopeFilteredTsconfig(scope2, projectRoot, scopeConfig, deps = defaultTypeScriptDeps) {
|
|
14746
15097
|
const tempDir = await createTemporaryTsconfigDir(projectRoot, deps);
|
|
14747
|
-
const configPath =
|
|
15098
|
+
const configPath = join33(tempDir, "tsconfig.json");
|
|
14748
15099
|
const baseConfigFile = TSCONFIG_FILES[scope2];
|
|
14749
|
-
const
|
|
15100
|
+
const toTemporaryConfigPathPattern = (pattern) => {
|
|
15101
|
+
const absolutePattern = isAbsolute8(pattern) ? pattern : join33(projectRoot, pattern);
|
|
15102
|
+
return relative8(tempDir, absolutePattern);
|
|
15103
|
+
};
|
|
14750
15104
|
const tempConfig = {
|
|
14751
|
-
extends:
|
|
14752
|
-
include: scopeConfig.filePatterns.map(
|
|
14753
|
-
exclude: scopeConfig.excludePatterns.map(
|
|
15105
|
+
extends: join33(projectRoot, baseConfigFile),
|
|
15106
|
+
include: scopeConfig.filePatterns.map(toTemporaryConfigPathPattern),
|
|
15107
|
+
exclude: scopeConfig.excludePatterns.map(toTemporaryConfigPathPattern),
|
|
14754
15108
|
compilerOptions: TEMPORARY_TSCONFIG_COMPILER_OPTIONS
|
|
14755
15109
|
};
|
|
14756
15110
|
deps.writeFileSync(configPath, JSON.stringify(tempConfig, null, 2));
|
|
@@ -14812,7 +15166,7 @@ async function validateTypeScript(context, options = {}) {
|
|
|
14812
15166
|
);
|
|
14813
15167
|
}
|
|
14814
15168
|
function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
|
|
14815
|
-
const tscBin =
|
|
15169
|
+
const tscBin = join33(projectRoot, "node_modules", ".bin", "tsc");
|
|
14816
15170
|
const tool = deps.existsSync(tscBin) ? tscBin : "npx";
|
|
14817
15171
|
return {
|
|
14818
15172
|
tool,
|
|
@@ -14821,7 +15175,7 @@ function resolveProjectTscInvocation(projectRoot, deps, tscArgs) {
|
|
|
14821
15175
|
}
|
|
14822
15176
|
function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams, cleanup = () => {
|
|
14823
15177
|
}) {
|
|
14824
|
-
return new Promise((
|
|
15178
|
+
return new Promise((resolve11) => {
|
|
14825
15179
|
const tscProcess = spawnManagedSubprocess(runner, invocation.tool, invocation.args, {
|
|
14826
15180
|
cwd: projectRoot
|
|
14827
15181
|
});
|
|
@@ -14829,14 +15183,14 @@ function runTypeScriptInvocation(projectRoot, invocation, runner, outputStreams,
|
|
|
14829
15183
|
tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.CLOSE, (code) => {
|
|
14830
15184
|
cleanup();
|
|
14831
15185
|
if (code === 0) {
|
|
14832
|
-
|
|
15186
|
+
resolve11({ success: true, skipped: false });
|
|
14833
15187
|
} else {
|
|
14834
|
-
|
|
15188
|
+
resolve11({ success: false, error: `TypeScript exited with code ${code}` });
|
|
14835
15189
|
}
|
|
14836
15190
|
});
|
|
14837
15191
|
tscProcess.on(VALIDATION_SUBPROCESS_EVENTS.ERROR, (error) => {
|
|
14838
15192
|
cleanup();
|
|
14839
|
-
|
|
15193
|
+
resolve11({ success: false, error: error.message });
|
|
14840
15194
|
});
|
|
14841
15195
|
});
|
|
14842
15196
|
}
|
|
@@ -14869,16 +15223,17 @@ async function typescriptCommand(options) {
|
|
|
14869
15223
|
};
|
|
14870
15224
|
}
|
|
14871
15225
|
const validationConfig = loaded.value[validationConfigDescriptor.section];
|
|
14872
|
-
const
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
15226
|
+
const scopeConfig = resolveTypeScriptValidationScope({
|
|
15227
|
+
projectRoot: cwd,
|
|
15228
|
+
scope: scope2,
|
|
15229
|
+
paths: files,
|
|
15230
|
+
validationPathFilter: validationPathFilterForTool(
|
|
15231
|
+
validationConfig.paths,
|
|
15232
|
+
VALIDATION_PATH_TOOL_SUBSECTIONS.TYPESCRIPT
|
|
15233
|
+
),
|
|
15234
|
+
markExplicitPathsAsValidationFilter: true
|
|
15235
|
+
});
|
|
15236
|
+
if (scopeConfig.filteredByValidationPathNoMatches) {
|
|
14882
15237
|
return {
|
|
14883
15238
|
exitCode: 0,
|
|
14884
15239
|
output: quiet ? "" : TYPESCRIPT_VALIDATION_MESSAGES.NO_VALIDATION_PATH_TARGETS,
|
|
@@ -14893,7 +15248,6 @@ async function typescriptCommand(options) {
|
|
|
14893
15248
|
const result = await validateTypeScript({
|
|
14894
15249
|
scope: scope2,
|
|
14895
15250
|
projectRoot: cwd,
|
|
14896
|
-
files: filteredFiles,
|
|
14897
15251
|
scopeConfig
|
|
14898
15252
|
});
|
|
14899
15253
|
const durationMs = Date.now() - startTime;
|
|
@@ -14937,6 +15291,7 @@ async function runLiteralStage(context) {
|
|
|
14937
15291
|
}
|
|
14938
15292
|
return literalCommand({
|
|
14939
15293
|
cwd: context.cwd,
|
|
15294
|
+
scope: context.scope,
|
|
14940
15295
|
files: context.files,
|
|
14941
15296
|
quiet: context.quiet,
|
|
14942
15297
|
json: context.json
|
|
@@ -15066,7 +15421,7 @@ async function allCommand(options) {
|
|
|
15066
15421
|
// src/validation/literal/allowlist-existing.ts
|
|
15067
15422
|
import { randomBytes } from "crypto";
|
|
15068
15423
|
import { rename as rename4, writeFile as writeFile4 } from "fs/promises";
|
|
15069
|
-
import { dirname as dirname10, join as
|
|
15424
|
+
import { dirname as dirname10, join as join34 } from "path";
|
|
15070
15425
|
var EXIT_OK = 0;
|
|
15071
15426
|
var EXIT_ERROR = 1;
|
|
15072
15427
|
var INCLUDE_FIELD = "include";
|
|
@@ -15086,7 +15441,7 @@ var productionWriter = {
|
|
|
15086
15441
|
async write(filePath, content) {
|
|
15087
15442
|
const dir = dirname10(filePath);
|
|
15088
15443
|
const random = randomBytes(RANDOM_TOKEN_BYTES).toString("hex");
|
|
15089
|
-
const tmpPath =
|
|
15444
|
+
const tmpPath = join34(dir, `${TEMP_FILE_PREFIX}${random}${TEMP_FILE_SUFFIX}`);
|
|
15090
15445
|
await writeFile4(tmpPath, content, "utf8");
|
|
15091
15446
|
await rename4(tmpPath, filePath);
|
|
15092
15447
|
}
|
|
@@ -15206,6 +15561,10 @@ var validationCliDefinition = {
|
|
|
15206
15561
|
longFlag: "--help",
|
|
15207
15562
|
shortFlag: "-h"
|
|
15208
15563
|
},
|
|
15564
|
+
pathOperands: {
|
|
15565
|
+
optionalVariadic: "[paths...]",
|
|
15566
|
+
description: "Specific files/directories to validate"
|
|
15567
|
+
},
|
|
15209
15568
|
diagnostics: {
|
|
15210
15569
|
unknownSubcommand: {
|
|
15211
15570
|
messageLabel: "unknown subcommand",
|
|
@@ -15214,6 +15573,11 @@ var validationCliDefinition = {
|
|
|
15214
15573
|
unknownLiteralProblemKind: {
|
|
15215
15574
|
messageLabel: "unknown problem kind",
|
|
15216
15575
|
exitCode: 1
|
|
15576
|
+
},
|
|
15577
|
+
invalidPathOperand: {
|
|
15578
|
+
messageLabel: "invalid path operand",
|
|
15579
|
+
reason: "escapes product directory",
|
|
15580
|
+
exitCode: 1
|
|
15217
15581
|
}
|
|
15218
15582
|
}
|
|
15219
15583
|
};
|
|
@@ -15269,7 +15633,50 @@ function emitValidationResult(result, io) {
|
|
|
15269
15633
|
return io.exit(result.exitCode);
|
|
15270
15634
|
}
|
|
15271
15635
|
function addCommonOptions(cmd) {
|
|
15272
|
-
|
|
15636
|
+
const { pathOperands } = validationCliDefinition;
|
|
15637
|
+
return cmd.argument(pathOperands.optionalVariadic, pathOperands.description).option("--scope <scope>", "Validation scope (full|production)", "full").option("--quiet", "Suppress progress output").option("--json", "Output results as JSON");
|
|
15638
|
+
}
|
|
15639
|
+
function normalizeProductPathOperand(productDir, effectiveInvocationDir, operand) {
|
|
15640
|
+
const resolvedProductDir = canonicalExistingPath(resolve10(productDir));
|
|
15641
|
+
const resolvedInvocationDir = canonicalExistingPath(resolve10(effectiveInvocationDir));
|
|
15642
|
+
const absoluteOperand = canonicalExistingPath(resolve10(resolvedInvocationDir, operand));
|
|
15643
|
+
const relativeOperand = relative9(resolvedProductDir, absoluteOperand);
|
|
15644
|
+
if (relativeOperand === ".." || relativeOperand.startsWith(`..${sep3}`) || isAbsolute9(relativeOperand)) {
|
|
15645
|
+
return void 0;
|
|
15646
|
+
}
|
|
15647
|
+
return relativeOperand.length > 0 ? relativeOperand.replaceAll("\\", "/") : ".";
|
|
15648
|
+
}
|
|
15649
|
+
function canonicalExistingPath(path7) {
|
|
15650
|
+
return existsSync8(path7) ? realpathSync.native(path7) : path7;
|
|
15651
|
+
}
|
|
15652
|
+
function normalizePathOperands(productDir, effectiveInvocationDir, pathOperands) {
|
|
15653
|
+
if (pathOperands.length === 0) return void 0;
|
|
15654
|
+
const normalized = [];
|
|
15655
|
+
for (const operand of pathOperands) {
|
|
15656
|
+
const path7 = normalizeProductPathOperand(productDir, effectiveInvocationDir, operand);
|
|
15657
|
+
if (path7 === void 0) return void 0;
|
|
15658
|
+
normalized.push(path7);
|
|
15659
|
+
}
|
|
15660
|
+
return normalized;
|
|
15661
|
+
}
|
|
15662
|
+
function resolveValidationPaths(invocation, pathOperands) {
|
|
15663
|
+
const context = invocation.resolveProductContext();
|
|
15664
|
+
const files = normalizePathOperands(context.productDir, context.effectiveInvocationDir, pathOperands);
|
|
15665
|
+
if (pathOperands.length > 0 && files === void 0) {
|
|
15666
|
+
const { invalidPathOperand } = validationCliDefinition.diagnostics;
|
|
15667
|
+
const invalidOperand = pathOperands.find(
|
|
15668
|
+
(operand) => normalizeProductPathOperand(context.productDir, context.effectiveInvocationDir, operand) === void 0
|
|
15669
|
+
);
|
|
15670
|
+
invocation.io.writeStderr(
|
|
15671
|
+
`spx ${validationCliDefinition.domain.commandName}: ${invalidPathOperand.messageLabel}: ${sanitizeCliArgument(invalidOperand)} (${invalidPathOperand.reason})
|
|
15672
|
+
`
|
|
15673
|
+
);
|
|
15674
|
+
invocation.io.exit(invalidPathOperand.exitCode);
|
|
15675
|
+
}
|
|
15676
|
+
return {
|
|
15677
|
+
productDir: context.productDir,
|
|
15678
|
+
files
|
|
15679
|
+
};
|
|
15273
15680
|
}
|
|
15274
15681
|
function addValidationSubcommand(validationCmd, definition) {
|
|
15275
15682
|
let subcommand = validationCmd.command(definition.commandName).description(definition.description);
|
|
@@ -15280,23 +15687,24 @@ function addValidationSubcommand(validationCmd, definition) {
|
|
|
15280
15687
|
}
|
|
15281
15688
|
function registerValidationCommands(validationCmd, invocation) {
|
|
15282
15689
|
const { subcommands } = validationCliDefinition;
|
|
15283
|
-
const
|
|
15284
|
-
|
|
15690
|
+
const tsCmd = addValidationSubcommand(validationCmd, subcommands.typescript).action(async (pathOperands, options) => {
|
|
15691
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15285
15692
|
const result = await typescriptCommand({
|
|
15286
|
-
cwd: productDir
|
|
15693
|
+
cwd: paths.productDir,
|
|
15287
15694
|
scope: options.scope,
|
|
15288
|
-
files:
|
|
15695
|
+
files: paths.files,
|
|
15289
15696
|
quiet: options.quiet,
|
|
15290
15697
|
json: options.json
|
|
15291
15698
|
});
|
|
15292
15699
|
emitValidationResult(result, invocation.io);
|
|
15293
15700
|
});
|
|
15294
15701
|
addCommonOptions(tsCmd);
|
|
15295
|
-
const lintCmd = addValidationSubcommand(validationCmd, subcommands.lint).option("--fix", "Auto-fix issues").action(async (options) => {
|
|
15702
|
+
const lintCmd = addValidationSubcommand(validationCmd, subcommands.lint).option("--fix", "Auto-fix issues").action(async (pathOperands, options) => {
|
|
15703
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15296
15704
|
const result = await lintCommand({
|
|
15297
|
-
cwd: productDir
|
|
15705
|
+
cwd: paths.productDir,
|
|
15298
15706
|
scope: options.scope,
|
|
15299
|
-
files:
|
|
15707
|
+
files: paths.files,
|
|
15300
15708
|
fix: options.fix,
|
|
15301
15709
|
quiet: options.quiet,
|
|
15302
15710
|
json: options.json
|
|
@@ -15304,22 +15712,24 @@ function registerValidationCommands(validationCmd, invocation) {
|
|
|
15304
15712
|
emitValidationResult(result, invocation.io);
|
|
15305
15713
|
});
|
|
15306
15714
|
addCommonOptions(lintCmd);
|
|
15307
|
-
const circularCmd = addValidationSubcommand(validationCmd, subcommands.circular).action(async (options) => {
|
|
15715
|
+
const circularCmd = addValidationSubcommand(validationCmd, subcommands.circular).action(async (pathOperands, options) => {
|
|
15716
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15308
15717
|
const result = await circularCommand({
|
|
15309
|
-
cwd: productDir
|
|
15718
|
+
cwd: paths.productDir,
|
|
15310
15719
|
scope: options.scope,
|
|
15311
|
-
files:
|
|
15720
|
+
files: paths.files,
|
|
15312
15721
|
quiet: options.quiet,
|
|
15313
15722
|
json: options.json
|
|
15314
15723
|
});
|
|
15315
15724
|
emitValidationResult(result, invocation.io);
|
|
15316
15725
|
});
|
|
15317
15726
|
addCommonOptions(circularCmd);
|
|
15318
|
-
const knipCmd = addValidationSubcommand(validationCmd, subcommands.knip).action(async (options) => {
|
|
15727
|
+
const knipCmd = addValidationSubcommand(validationCmd, subcommands.knip).action(async (pathOperands, options) => {
|
|
15728
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15319
15729
|
const result = await knipCommand({
|
|
15320
|
-
cwd: productDir
|
|
15730
|
+
cwd: paths.productDir,
|
|
15321
15731
|
scope: options.scope,
|
|
15322
|
-
files:
|
|
15732
|
+
files: paths.files,
|
|
15323
15733
|
quiet: options.quiet,
|
|
15324
15734
|
json: options.json
|
|
15325
15735
|
});
|
|
@@ -15335,9 +15745,10 @@ function registerValidationCommands(validationCmd, invocation) {
|
|
|
15335
15745
|
).option(literalValidationCliOptions.literals.flag, literalValidationCliOptions.literals.description).option(literalValidationCliOptions.verbose.flag, literalValidationCliOptions.verbose.description).addHelpText(
|
|
15336
15746
|
"after",
|
|
15337
15747
|
"\nEnabled for TypeScript projects by default. Set validation.literal.enabled=false\nin spx.config.* to skip during migration."
|
|
15338
|
-
).action(async (options) => {
|
|
15748
|
+
).action(async (pathOperands, options) => {
|
|
15749
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15339
15750
|
if (options.allowlistExisting) {
|
|
15340
|
-
const result2 = await allowlistExisting({ productDir: productDir
|
|
15751
|
+
const result2 = await allowlistExisting({ productDir: paths.productDir });
|
|
15341
15752
|
emitValidationResult(result2, invocation.io);
|
|
15342
15753
|
}
|
|
15343
15754
|
let kind;
|
|
@@ -15353,8 +15764,9 @@ function registerValidationCommands(validationCmd, invocation) {
|
|
|
15353
15764
|
}
|
|
15354
15765
|
}
|
|
15355
15766
|
const result = await literalCommand({
|
|
15356
|
-
cwd: productDir
|
|
15357
|
-
|
|
15767
|
+
cwd: paths.productDir,
|
|
15768
|
+
scope: options.scope,
|
|
15769
|
+
files: paths.files,
|
|
15358
15770
|
kind,
|
|
15359
15771
|
filesWithProblems: options.filesWithProblems,
|
|
15360
15772
|
literals: options.literals,
|
|
@@ -15368,29 +15780,32 @@ function registerValidationCommands(validationCmd, invocation) {
|
|
|
15368
15780
|
const markdownCmd = addValidationSubcommand(validationCmd, subcommands.markdown).addHelpText(
|
|
15369
15781
|
"after",
|
|
15370
15782
|
"\nValidates spx/ and docs/ by default. Nodes listed in spx/EXCLUDE are\nskipped \u2014 use this for declared-state nodes whose [test] links point\nto files that do not exist yet."
|
|
15371
|
-
).action(async (options) => {
|
|
15783
|
+
).action(async (pathOperands, options) => {
|
|
15784
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15372
15785
|
const result = await markdownCommand({
|
|
15373
|
-
cwd: productDir
|
|
15374
|
-
files:
|
|
15786
|
+
cwd: paths.productDir,
|
|
15787
|
+
files: paths.files,
|
|
15375
15788
|
quiet: options.quiet
|
|
15376
15789
|
});
|
|
15377
15790
|
emitValidationResult(result, invocation.io);
|
|
15378
15791
|
});
|
|
15379
15792
|
addCommonOptions(markdownCmd);
|
|
15380
|
-
const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (options) => {
|
|
15793
|
+
const formatCmd = addValidationSubcommand(validationCmd, subcommands.format).action(async (pathOperands, options) => {
|
|
15794
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15381
15795
|
const result = await formattingCommand({
|
|
15382
|
-
cwd: productDir
|
|
15383
|
-
files:
|
|
15796
|
+
cwd: paths.productDir,
|
|
15797
|
+
files: paths.files,
|
|
15384
15798
|
quiet: options.quiet
|
|
15385
15799
|
});
|
|
15386
15800
|
emitValidationResult(result, invocation.io);
|
|
15387
15801
|
});
|
|
15388
15802
|
addCommonOptions(formatCmd);
|
|
15389
|
-
const allCmd = addValidationSubcommand(validationCmd, subcommands.all).option("--fix", "Auto-fix ESLint issues").option(allValidationCliOptions.skipCircular.flag, allValidationCliOptions.skipCircular.description).option(allValidationCliOptions.skipLiteral.flag, allValidationCliOptions.skipLiteral.description).action(async (options) => {
|
|
15803
|
+
const allCmd = addValidationSubcommand(validationCmd, subcommands.all).option("--fix", "Auto-fix ESLint issues").option(allValidationCliOptions.skipCircular.flag, allValidationCliOptions.skipCircular.description).option(allValidationCliOptions.skipLiteral.flag, allValidationCliOptions.skipLiteral.description).action(async (pathOperands, options) => {
|
|
15804
|
+
const paths = resolveValidationPaths(invocation, pathOperands);
|
|
15390
15805
|
const result = await allCommand({
|
|
15391
|
-
cwd: productDir
|
|
15806
|
+
cwd: paths.productDir,
|
|
15392
15807
|
scope: options.scope,
|
|
15393
|
-
files:
|
|
15808
|
+
files: paths.files,
|
|
15394
15809
|
fix: options.fix,
|
|
15395
15810
|
skipCircular: options.skipCircular,
|
|
15396
15811
|
skipLiteral: options.skipLiteral,
|
|
@@ -15430,7 +15845,7 @@ var validationDomain = {
|
|
|
15430
15845
|
};
|
|
15431
15846
|
|
|
15432
15847
|
// src/commands/verification-context/cli.ts
|
|
15433
|
-
import { isAbsolute as
|
|
15848
|
+
import { isAbsolute as isAbsolute10, win32 } from "path";
|
|
15434
15849
|
|
|
15435
15850
|
// src/domains/verification-context/context.ts
|
|
15436
15851
|
var VERIFICATION_CONTEXT_SCHEMA_VERSION = "verification-context.v1";
|
|
@@ -15467,7 +15882,7 @@ function createVerificationContextDocument(payload) {
|
|
|
15467
15882
|
import { dirname as dirname11 } from "path";
|
|
15468
15883
|
|
|
15469
15884
|
// src/domains/verification-context/path.ts
|
|
15470
|
-
import { join as
|
|
15885
|
+
import { join as join35 } from "path";
|
|
15471
15886
|
var VERIFICATION_CONTEXT_STATE_DOMAIN = VERIFICATION_CONTEXT_PERSISTENCE.domain;
|
|
15472
15887
|
var VERIFICATION_CONTEXT_STATE_PATH = {
|
|
15473
15888
|
CONTEXTS_DIR: "contexts",
|
|
@@ -15488,7 +15903,7 @@ function verificationContextFilePath(scope2) {
|
|
|
15488
15903
|
if (!contextsDir.ok) return contextsDir;
|
|
15489
15904
|
const digest = validateScopeToken(scope2.digest);
|
|
15490
15905
|
if (!digest.ok) return digest;
|
|
15491
|
-
return { ok: true, value:
|
|
15906
|
+
return { ok: true, value: join35(contextsDir.value, verificationContextFileName(digest.value)) };
|
|
15492
15907
|
}
|
|
15493
15908
|
|
|
15494
15909
|
// src/commands/verification-context/runtime.ts
|
|
@@ -15581,7 +15996,7 @@ function normalizeFileSubjectPath(path7) {
|
|
|
15581
15996
|
VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.SEPARATOR.CANONICAL
|
|
15582
15997
|
);
|
|
15583
15998
|
const segments = normalized.split(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.SEPARATOR.CANONICAL);
|
|
15584
|
-
if (
|
|
15999
|
+
if (isAbsolute10(path7) || win32.isAbsolute(path7) || windowsRoot.length > 0 || segments.includes(VERIFICATION_CONTEXT_FILE_SUBJECT_PATH.PARENT_DIRECTORY.SEGMENT)) {
|
|
15585
16000
|
return void 0;
|
|
15586
16001
|
}
|
|
15587
16002
|
return normalized;
|
|
@@ -15689,8 +16104,11 @@ var WORKTREE_STATUS_RENDER = {
|
|
|
15689
16104
|
FREE: "-",
|
|
15690
16105
|
RUNNING_PID_PREFIX: "PID "
|
|
15691
16106
|
};
|
|
16107
|
+
var WORKTREE_STATUS_ERROR = {
|
|
16108
|
+
ALL_WITH_EXPLICIT_TARGETS: "worktree status --all cannot be combined with explicit worktree operands"
|
|
16109
|
+
};
|
|
15692
16110
|
async function statusCommand2(options) {
|
|
15693
|
-
const multiTargetRequest = options.worktrees !== void 0 && options.worktrees.length > 1;
|
|
16111
|
+
const multiTargetRequest = options.all === true || options.worktrees !== void 0 && options.worktrees.length > 1;
|
|
15694
16112
|
const targets = await resolveStatusTargets(options);
|
|
15695
16113
|
if (!targets.ok) return targets;
|
|
15696
16114
|
const records = [];
|
|
@@ -15708,6 +16126,12 @@ async function statusCommand2(options) {
|
|
|
15708
16126
|
}
|
|
15709
16127
|
async function resolveStatusTargets(options) {
|
|
15710
16128
|
const requested = options.worktrees;
|
|
16129
|
+
if (options.all === true) {
|
|
16130
|
+
if (requested !== void 0 && requested.length > 0) {
|
|
16131
|
+
return { ok: false, error: WORKTREE_STATUS_ERROR.ALL_WITH_EXPLICIT_TARGETS };
|
|
16132
|
+
}
|
|
16133
|
+
return resolveAllTargetWorktrees(options);
|
|
16134
|
+
}
|
|
15711
16135
|
if (requested === void 0 || requested.length === 0) {
|
|
15712
16136
|
const target = await resolveTargetWorktree(options);
|
|
15713
16137
|
if (!target.ok) return target;
|
|
@@ -15745,11 +16169,11 @@ function renderTextStatus(record6) {
|
|
|
15745
16169
|
}
|
|
15746
16170
|
|
|
15747
16171
|
// src/lib/worktree-path-info.ts
|
|
15748
|
-
import { stat as
|
|
16172
|
+
import { stat as stat5 } from "fs/promises";
|
|
15749
16173
|
var defaultWorktreePathInfo = {
|
|
15750
16174
|
isExistingNonDirectory: async (path7) => {
|
|
15751
16175
|
try {
|
|
15752
|
-
const pathStats = await
|
|
16176
|
+
const pathStats = await stat5(path7);
|
|
15753
16177
|
return !pathStats.isDirectory();
|
|
15754
16178
|
} catch {
|
|
15755
16179
|
return false;
|
|
@@ -15765,6 +16189,7 @@ var WORKTREE_CLI = {
|
|
|
15765
16189
|
RELEASE: "release",
|
|
15766
16190
|
WORKTREE_ARGUMENT: "[worktrees...]",
|
|
15767
16191
|
SESSION_ID_FLAG: "--session-id",
|
|
16192
|
+
ALL_FLAG: "--all",
|
|
15768
16193
|
FORMAT_FLAG: "--format",
|
|
15769
16194
|
WORKTREES_DIR_FLAG: "--worktrees-dir"
|
|
15770
16195
|
};
|
|
@@ -15803,21 +16228,24 @@ function registerWorktreeCommands(worktreeCmd, invocation) {
|
|
|
15803
16228
|
});
|
|
15804
16229
|
if (!result.ok) handleError3(invocation, result.error);
|
|
15805
16230
|
});
|
|
15806
|
-
worktreeCmd.command(`${WORKTREE_CLI.STATUS} ${WORKTREE_CLI.WORKTREE_ARGUMENT}`).description("Report a worktree's occupancy (running | free)").option(`${WORKTREE_CLI.FORMAT_FLAG} <format>`, "Output format (text|json)", WORKTREE_STATUS_FORMAT.TEXT).option(`${WORKTREE_CLI.WORKTREES_DIR_FLAG} <path>`, "Explicit .spx/worktrees directory").action(
|
|
15807
|
-
|
|
15808
|
-
|
|
15809
|
-
|
|
15810
|
-
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
16231
|
+
worktreeCmd.command(`${WORKTREE_CLI.STATUS} ${WORKTREE_CLI.WORKTREE_ARGUMENT}`).description("Report a worktree's occupancy (running | free)").option(WORKTREE_CLI.ALL_FLAG, "Report every git-observed worktree").option(`${WORKTREE_CLI.FORMAT_FLAG} <format>`, "Output format (text|json)", WORKTREE_STATUS_FORMAT.TEXT).option(`${WORKTREE_CLI.WORKTREES_DIR_FLAG} <path>`, "Explicit .spx/worktrees directory").action(
|
|
16232
|
+
async (worktrees, options) => {
|
|
16233
|
+
const result = await statusCommand2({
|
|
16234
|
+
cwd: effectiveInvocationDir(),
|
|
16235
|
+
fs: defaultOccupancyFileSystem,
|
|
16236
|
+
gitDeps: defaultGitDependencies,
|
|
16237
|
+
worktrees,
|
|
16238
|
+
all: options.all,
|
|
16239
|
+
format: options.format,
|
|
16240
|
+
pathInfo: defaultWorktreePathInfo,
|
|
16241
|
+
processTable: defaultProcessTable,
|
|
16242
|
+
worktreesDir: options.worktreesDir,
|
|
16243
|
+
onWarning: (warning) => writeInvocationWarning4(invocation, warning)
|
|
16244
|
+
});
|
|
16245
|
+
if (!result.ok) handleError3(invocation, result.error);
|
|
16246
|
+
writeOutput4(invocation, result.value);
|
|
16247
|
+
}
|
|
16248
|
+
);
|
|
15821
16249
|
worktreeCmd.command(WORKTREE_CLI.RELEASE).description("Release the running worktree's occupancy claim").option(`${WORKTREE_CLI.WORKTREES_DIR_FLAG} <path>`, "Explicit .spx/worktrees directory").action(async (options) => {
|
|
15822
16250
|
const result = await releaseCommand2({
|
|
15823
16251
|
cwd: effectiveInvocationDir(),
|