@visulima/vis 1.0.0-alpha.10 → 1.0.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -42
- package/LICENSE.md +213 -0
- package/README.md +8 -4
- package/dist/bin.js +9 -1
- package/dist/config/index.d.ts +1818 -0
- package/dist/config/index.js +2 -0
- package/dist/generate/index.d.ts +1 -1
- package/dist/generate/index.js +3 -1
- package/dist/packem_chunks/applyDefaults.js +336 -0
- package/dist/packem_chunks/bin.js +9554 -64
- package/dist/packem_chunks/doctor-probe.js +112 -0
- package/dist/packem_chunks/fix.js +229 -48
- package/dist/packem_chunks/handler.js +99 -1
- package/dist/packem_chunks/handler10.js +53 -1
- package/dist/packem_chunks/handler11.js +32 -1
- package/dist/packem_chunks/handler12.js +100 -2
- package/dist/packem_chunks/handler13.js +25 -1
- package/dist/packem_chunks/handler14.js +916 -5
- package/dist/packem_chunks/handler15.js +206 -1
- package/dist/packem_chunks/handler16.js +122 -18
- package/dist/packem_chunks/handler17.js +13 -1
- package/dist/packem_chunks/handler18.js +106 -1
- package/dist/packem_chunks/handler19.js +19 -1
- package/dist/packem_chunks/handler2.js +75 -1
- package/dist/packem_chunks/handler20.js +29 -1
- package/dist/packem_chunks/handler21.js +222 -1
- package/dist/packem_chunks/handler22.js +237 -5
- package/dist/packem_chunks/handler23.js +101 -1
- package/dist/packem_chunks/handler24.js +110 -1
- package/dist/packem_chunks/handler25.js +402 -5
- package/dist/packem_chunks/handler26.js +13 -1
- package/dist/packem_chunks/handler27.js +63 -3
- package/dist/packem_chunks/handler28.js +34 -1
- package/dist/packem_chunks/handler29.js +458 -7
- package/dist/packem_chunks/handler3.js +95 -2
- package/dist/packem_chunks/handler30.js +168 -21
- package/dist/packem_chunks/handler31.js +530 -3
- package/dist/packem_chunks/handler32.js +214 -2
- package/dist/packem_chunks/handler33.js +119 -24
- package/dist/packem_chunks/handler34.js +630 -2
- package/dist/packem_chunks/handler35.js +283 -19
- package/dist/packem_chunks/handler36.js +521 -407
- package/dist/packem_chunks/handler37.js +762 -22
- package/dist/packem_chunks/handler38.js +989 -22
- package/dist/packem_chunks/handler39.js +574 -22
- package/dist/packem_chunks/handler4.js +90 -4
- package/dist/packem_chunks/handler40.js +1685 -3
- package/dist/packem_chunks/handler41.js +1088 -10
- package/dist/packem_chunks/handler42.js +785 -141
- package/dist/packem_chunks/handler43.js +2658 -42
- package/dist/packem_chunks/handler44.js +3886 -3
- package/dist/packem_chunks/handler45.js +2568 -21
- package/dist/packem_chunks/handler46.js +3769 -0
- package/dist/packem_chunks/handler47.js +1491 -0
- package/dist/packem_chunks/handler5.js +174 -2
- package/dist/packem_chunks/handler6.js +95 -13
- package/dist/packem_chunks/handler7.js +115 -8
- package/dist/packem_chunks/handler8.js +12 -1
- package/dist/packem_chunks/handler9.js +29 -1
- package/dist/packem_chunks/heal-accept.js +522 -0
- package/dist/packem_chunks/heal.js +673 -0
- package/dist/packem_chunks/index.js +873 -7
- package/dist/packem_chunks/loader.js +23 -1
- package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +1316 -0
- package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +5 -0
- package/dist/packem_shared/ai-analysis-CHeB1joD.js +367 -0
- package/dist/packem_shared/ai-cache-Be_jexe4.js +142 -0
- package/dist/packem_shared/ai-fix-B9iQVcD2.js +379 -0
- package/dist/packem_shared/cache-directory-2qvs4goY.js +98 -0
- package/dist/packem_shared/catalog-BJTtyi-O.js +1371 -0
- package/dist/packem_shared/dependency-scan-A0KSklpG.js +188 -0
- package/dist/packem_shared/docker-2iZzc280.js +181 -0
- package/dist/packem_shared/failure-log-Cz3Z4SKL.js +100 -0
- package/dist/packem_shared/flakiness-goTxXuCX.js +180 -0
- package/dist/packem_shared/otel-DCvqCTz_.js +158 -0
- package/dist/packem_shared/otelPlugin-DFaLDvJf.js +3 -0
- package/dist/packem_shared/registry-CbqXI0rc.js +272 -0
- package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +130 -0
- package/dist/packem_shared/runtime-check-Cobi3p6l.js +127 -0
- package/dist/packem_shared/selectors-SM69TfqC.js +194 -0
- package/dist/packem_shared/symbols-Ta7g2nU-.js +14 -0
- package/dist/packem_shared/toolchain-BdZd9eBi.js +975 -0
- package/dist/packem_shared/typosquats-C-bCh3PX.js +1210 -0
- package/dist/packem_shared/use-measured-height-CNP0vT4M.js +20 -0
- package/dist/packem_shared/utils-CthVdBPS.js +40 -0
- package/dist/packem_shared/xxh3-Ck8mXNg1.js +239 -0
- package/index.js +727 -555
- package/package.json +35 -17
- package/schemas/project.schema.json +8 -10
- package/schemas/vis-config.schema.json +132 -8
- package/skills/vis/SKILL.md +96 -0
- package/templates/buildkite-ci/.buildkite/pipeline.yml.tera +85 -0
- package/templates/buildkite-ci/template.yml +20 -0
- package/dist/errors/index.d.ts +0 -26
- package/dist/errors/index.js +0 -1
- package/dist/packem_chunks/config.js +0 -2
- package/dist/packem_shared/VisConfigCycleError-CAYNC7d-.js +0 -1
- package/dist/packem_shared/VisConfigError-B5LP1zRf.js +0 -1
- package/dist/packem_shared/VisConfigLoadError-CeqBSd2Z.js +0 -2
- package/dist/packem_shared/VisConfigNotFoundError-DZ9KC527.js +0 -5
- package/dist/packem_shared/VisUpdateApp-D-L4_-Iu.js +0 -1
- package/dist/packem_shared/_commonjsHelpers-D6W6KoPK.js +0 -1
- package/dist/packem_shared/ai-analysis-CGuy7dfE.js +0 -67
- package/dist/packem_shared/ai-cache-Bynt6Y9x.js +0 -1
- package/dist/packem_shared/cache-directory-D72ZEag2.js +0 -1
- package/dist/packem_shared/catalog-BVPerCwG.js +0 -12
- package/dist/packem_shared/dependency-scan-Du0tBu64.js +0 -2
- package/dist/packem_shared/docker-BcfqH4Av.js +0 -2
- package/dist/packem_shared/failure-log-DqYen0LC.js +0 -2
- package/dist/packem_shared/flakiness-DSIHZGBT.js +0 -1
- package/dist/packem_shared/run-summary-utils-C24Aaf9E.js +0 -1
- package/dist/packem_shared/runtime-check-CGHal8SO.js +0 -1
- package/dist/packem_shared/selectors-CfH9ZY08.js +0 -3
- package/dist/packem_shared/symbols-CQmER5MT.js +0 -1
- package/dist/packem_shared/target-merge-DNa-6eWu.js +0 -1
- package/dist/packem_shared/toolchain-DQfTQY8E.js +0 -5
- package/dist/packem_shared/typosquats-DOR8izpX.js +0 -1
- package/dist/packem_shared/use-measured-height-DjYgUOKk.js +0 -1
- package/dist/packem_shared/utils-DrNg0XTR.js +0 -1
- package/dist/packem_shared/xxh3-DrAUNq4n.js +0 -1
|
@@ -1,2 +1,214 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
import { getAffectedProjects } from '@visulima/task-runner';
|
|
21
|
+
import { d as discoverWorkspace, b as buildProjectGraph } from './bin.js';
|
|
22
|
+
const {
|
|
23
|
+
execFile
|
|
24
|
+
} = __cjs_getBuiltinModule("node:child_process");
|
|
25
|
+
const {
|
|
26
|
+
promisify
|
|
27
|
+
} = __cjs_getBuiltinModule("node:util");
|
|
28
|
+
|
|
29
|
+
const execFileAsync = promisify(execFile);
|
|
30
|
+
const SKIP_TOKENS = ["[skip ci]", "[ci skip]", "[no ci]", "[vis skip]", "[nx skip]"];
|
|
31
|
+
const FORCE_TOKENS = ["[vis deploy]", "[nx deploy]"];
|
|
32
|
+
const PER_PROJECT_TOKEN_PREFIXES = ["vis", "nx"];
|
|
33
|
+
const CI_BASE_SHA_ENV_VARS = [
|
|
34
|
+
"CACHED_COMMIT_REF",
|
|
35
|
+
// Netlify
|
|
36
|
+
"VERCEL_GIT_PREVIOUS_SHA",
|
|
37
|
+
// Vercel
|
|
38
|
+
"GITHUB_BASE_REF",
|
|
39
|
+
// GitHub Actions (PRs; ref name, not SHA — validated below)
|
|
40
|
+
"CI_COMMIT_BEFORE_SHA"
|
|
41
|
+
// GitLab CI
|
|
42
|
+
];
|
|
43
|
+
const GIT_REF_RE = /^[\w./~^@{}][\w.\-/~^@{}]*$/;
|
|
44
|
+
const resolveCiBaseSha = (env = process.env) => {
|
|
45
|
+
for (const name of CI_BASE_SHA_ENV_VARS) {
|
|
46
|
+
const value = env[name];
|
|
47
|
+
if (value && value.trim().length > 0) {
|
|
48
|
+
return value.trim();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return void 0;
|
|
52
|
+
};
|
|
53
|
+
const validateGitRef = (ref) => {
|
|
54
|
+
if (!GIT_REF_RE.test(ref)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Invalid git ref: "${ref}". Refs must start with an alphanumeric character or one of _ . / ~ ^ @ { } and may only contain letters, digits, dots, dashes, underscores, slashes, tildes, carets, @, and braces.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const isRefReachable = async (cwd, ref) => {
|
|
61
|
+
try {
|
|
62
|
+
await execFileAsync("git", ["rev-parse", "--verify", `${ref}^{commit}`], { cwd });
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const readLastCommitMessage = async (cwd) => {
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = await execFileAsync("git", ["log", "-1", "--pretty=%B"], { cwd });
|
|
71
|
+
return stdout;
|
|
72
|
+
} catch {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const matchesPerProjectToken = (message, verb, project) => PER_PROJECT_TOKEN_PREFIXES.some((prefix) => message.includes(`[${prefix} ${verb} ${project}]`));
|
|
77
|
+
const commitHasSkipMessage = (message, project) => SKIP_TOKENS.some((token) => message.includes(token)) || matchesPerProjectToken(message, "skip", project);
|
|
78
|
+
const commitHasForceDeployMessage = (message, project) => FORCE_TOKENS.some((token) => message.includes(token)) || matchesPerProjectToken(message, "deploy", project);
|
|
79
|
+
const decideBuild = (project, reason, message, extra) => {
|
|
80
|
+
return {
|
|
81
|
+
action: "build",
|
|
82
|
+
message,
|
|
83
|
+
project,
|
|
84
|
+
reason,
|
|
85
|
+
...extra
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const decideSkip = (project, reason, message, extra) => {
|
|
89
|
+
return {
|
|
90
|
+
action: "skip",
|
|
91
|
+
message,
|
|
92
|
+
project,
|
|
93
|
+
reason,
|
|
94
|
+
...extra
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
const formatDecisionLine = (decision) => {
|
|
98
|
+
const prefix = decision.action === "skip" ? "🛑" : "✅";
|
|
99
|
+
return `${prefix} ${decision.message}`;
|
|
100
|
+
};
|
|
101
|
+
const exitCodeFor = (decision, exitZeroOnBuild) => {
|
|
102
|
+
if (decision.action === "skip") {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
return exitZeroOnBuild ? 0 : 1;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const VALID_SCOPES = /* @__PURE__ */ new Set(["deep", "direct", "none"]);
|
|
109
|
+
const execute = async ({ argument, logger, options, visConfig, workspaceRoot }) => {
|
|
110
|
+
const project = argument[0] ?? "";
|
|
111
|
+
const isJson = Boolean(options.json);
|
|
112
|
+
const isVerbose = Boolean(options.verbose);
|
|
113
|
+
const exitZeroOnBuild = Boolean(options["exit-zero-on-build"] ?? options.exitZeroOnBuild);
|
|
114
|
+
const debug = (message) => {
|
|
115
|
+
if (isVerbose && !isJson) {
|
|
116
|
+
logger.info(`❱ ${message}`);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const emit = (decision) => {
|
|
120
|
+
if (isJson) {
|
|
121
|
+
process.stdout.write(`${JSON.stringify(decision)}
|
|
122
|
+
`);
|
|
123
|
+
} else {
|
|
124
|
+
logger.info(formatDecisionLine(decision));
|
|
125
|
+
}
|
|
126
|
+
process.exit(exitCodeFor(decision, exitZeroOnBuild));
|
|
127
|
+
};
|
|
128
|
+
if (!project) {
|
|
129
|
+
return emit(decideBuild("", "missing-project-argument", "Missing project argument. Usage: vis ignore <project>"));
|
|
130
|
+
}
|
|
131
|
+
if (!workspaceRoot) {
|
|
132
|
+
return emit(decideBuild(project, "workspace-error", "Could not determine workspace root — building defensively"));
|
|
133
|
+
}
|
|
134
|
+
const commitMessage = await readLastCommitMessage(workspaceRoot);
|
|
135
|
+
const commitSubject = commitMessage.trim().split("\n")[0] ?? "";
|
|
136
|
+
debug(`commit: ${commitSubject}`);
|
|
137
|
+
if (commitMessage && commitHasForceDeployMessage(commitMessage, project)) {
|
|
138
|
+
return emit(decideBuild(project, "commit-force-deploy", `Force-deploy keyword in commit: "${commitSubject}"`));
|
|
139
|
+
}
|
|
140
|
+
if (commitMessage && commitHasSkipMessage(commitMessage, project)) {
|
|
141
|
+
return emit(decideSkip(project, "commit-skip", `Skip keyword in commit: "${commitSubject}"`));
|
|
142
|
+
}
|
|
143
|
+
let workspace;
|
|
144
|
+
let packageJsons;
|
|
145
|
+
try {
|
|
146
|
+
({ packageJsons, workspace } = discoverWorkspace(workspaceRoot, visConfig));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
149
|
+
return emit(decideBuild(project, "workspace-error", `Workspace discovery failed (${message}) — building defensively`));
|
|
150
|
+
}
|
|
151
|
+
if (!Object.hasOwn(workspace.projects, project)) {
|
|
152
|
+
return emit(decideBuild(project, "project-unknown", `Project "${project}" not found in workspace — building defensively`));
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const explicitBase = options.base?.trim();
|
|
156
|
+
const ciBase = resolveCiBaseSha();
|
|
157
|
+
let baseRef = explicitBase || ciBase || "HEAD~1";
|
|
158
|
+
const headRef = options.head?.trim() || "HEAD";
|
|
159
|
+
validateGitRef(baseRef);
|
|
160
|
+
validateGitRef(headRef);
|
|
161
|
+
debug(`resolved base ref: ${baseRef} (source: ${explicitBase ? "flag" : ciBase ? "ci-env" : "default"})`);
|
|
162
|
+
const reachablePromise = isRefReachable(workspaceRoot, baseRef);
|
|
163
|
+
const projectGraph = buildProjectGraph(workspaceRoot, workspace, packageJsons);
|
|
164
|
+
if (!await reachablePromise) {
|
|
165
|
+
debug(`base ref ${baseRef} not reachable — falling back to HEAD~1`);
|
|
166
|
+
baseRef = "HEAD~1";
|
|
167
|
+
}
|
|
168
|
+
debug(`comparing ${baseRef}...${headRef}`);
|
|
169
|
+
const downstream = options.downstream ?? "deep";
|
|
170
|
+
const upstream = options.upstream ?? "none";
|
|
171
|
+
if (!VALID_SCOPES.has(downstream)) {
|
|
172
|
+
throw new Error(`Invalid --downstream value: "${downstream}". Must be "none", "direct", or "deep".`);
|
|
173
|
+
}
|
|
174
|
+
if (!VALID_SCOPES.has(upstream)) {
|
|
175
|
+
throw new Error(`Invalid --upstream value: "${upstream}". Must be "none", "direct", or "deep".`);
|
|
176
|
+
}
|
|
177
|
+
const affectedOptions = {
|
|
178
|
+
base: baseRef,
|
|
179
|
+
downstream,
|
|
180
|
+
head: headRef,
|
|
181
|
+
projectGraph,
|
|
182
|
+
projects: workspace.projects,
|
|
183
|
+
upstream,
|
|
184
|
+
workspaceRoot
|
|
185
|
+
};
|
|
186
|
+
const result = await getAffectedProjects(affectedOptions);
|
|
187
|
+
debug(`changed files: ${result.changedFiles.length}`);
|
|
188
|
+
debug(`affected projects: ${result.affectedProjects.join(", ") || "(none)"}`);
|
|
189
|
+
const refs = { base: baseRef, head: headRef };
|
|
190
|
+
if (result.changedFiles.length === 0) {
|
|
191
|
+
return emit(decideSkip(project, "no-changes", `No files changed between ${baseRef}...${headRef}`, { ...refs, affectedProjects: [] }));
|
|
192
|
+
}
|
|
193
|
+
if (result.affectedProjects.includes(project)) {
|
|
194
|
+
return emit(
|
|
195
|
+
decideBuild(project, "project-affected", `Build ${project}: affected by ${result.changedFiles.length} changed file(s)`, {
|
|
196
|
+
...refs,
|
|
197
|
+
affectedProjects: result.affectedProjects
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return emit(
|
|
202
|
+
decideSkip(project, "project-not-affected", `Skip ${project}: not affected by changes between ${baseRef}...${headRef}`, {
|
|
203
|
+
...refs,
|
|
204
|
+
affectedProjects: result.affectedProjects
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
209
|
+
logger.error(`Affected detection failed: ${errorMessage}`);
|
|
210
|
+
return emit(decideBuild(project, "workspace-error", `Affected detection failed (${errorMessage}) — building defensively`));
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export { execute as default };
|
|
@@ -1,24 +1,119 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
+
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
readFileSync
|
|
22
|
+
} = __cjs_getBuiltinModule("node:fs");
|
|
23
|
+
import { writeFileSync } from '@visulima/fs';
|
|
24
|
+
import { join } from '@visulima/path';
|
|
25
|
+
import { d as discoverWorkspace } from './bin.js';
|
|
26
|
+
|
|
27
|
+
const toWorkspacePath = (projectRoot, entryPath) => {
|
|
28
|
+
const trimmed = entryPath.replace(/^\.?\//, "");
|
|
29
|
+
if (trimmed === "" || trimmed === ".") {
|
|
30
|
+
return `/${projectRoot}/`;
|
|
31
|
+
}
|
|
32
|
+
if (projectRoot === "" || projectRoot === ".") {
|
|
33
|
+
return `/${trimmed}`;
|
|
34
|
+
}
|
|
35
|
+
return `/${projectRoot}/${trimmed}`;
|
|
36
|
+
};
|
|
37
|
+
const buildCodeownersLines = (workspace, config) => {
|
|
38
|
+
const lines = [];
|
|
39
|
+
for (const [globalPath, owners] of Object.entries(config?.globalPaths ?? {})) {
|
|
40
|
+
lines.push({ owners, path: globalPath });
|
|
41
|
+
}
|
|
42
|
+
for (const [name, project] of Object.entries(workspace.projects)) {
|
|
43
|
+
const ownersList = project.owners ?? [];
|
|
44
|
+
for (const entry of ownersList) {
|
|
45
|
+
lines.push({
|
|
46
|
+
channel: entry.channel,
|
|
47
|
+
owners: entry.owners,
|
|
48
|
+
path: toWorkspacePath(project.root ?? name, entry.path),
|
|
49
|
+
projectId: name
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const orderBy = config?.orderBy ?? "file-source";
|
|
54
|
+
if (orderBy === "project-id") {
|
|
55
|
+
lines.sort((a, b) => (a.projectId ?? "").localeCompare(b.projectId ?? "") || a.path.localeCompare(b.path));
|
|
56
|
+
} else {
|
|
57
|
+
lines.sort((a, b) => a.path.localeCompare(b.path));
|
|
58
|
+
}
|
|
59
|
+
return lines;
|
|
60
|
+
};
|
|
61
|
+
const HEADER_LINES = [
|
|
62
|
+
"# CODEOWNERS — generated by `vis sync codeowners`. Do not edit by hand.",
|
|
63
|
+
"# Update each project's project.json `owners` field and re-run the command.",
|
|
64
|
+
""
|
|
65
|
+
];
|
|
66
|
+
const renderCodeowners = (lines, _provider = "github") => {
|
|
67
|
+
const out = [...HEADER_LINES];
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
const owners = line.owners.join(" ");
|
|
70
|
+
const channel = line.channel ? ` # notify: ${line.channel}` : "";
|
|
71
|
+
out.push(`${line.path} ${owners}${channel}`);
|
|
72
|
+
}
|
|
73
|
+
out.push("");
|
|
74
|
+
return out.join("\n");
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const execute = async ({ argument, logger, options, visConfig, workspaceRoot: wsRoot }) => {
|
|
78
|
+
const kind = argument[0];
|
|
79
|
+
if (!kind) {
|
|
80
|
+
throw new Error("Missing sync kind. Usage: vis sync <kind> (known kinds: codeowners)");
|
|
81
|
+
}
|
|
82
|
+
if (!wsRoot) {
|
|
83
|
+
throw new Error("Could not determine workspace root. Run inside a monorepo.");
|
|
84
|
+
}
|
|
85
|
+
if (kind !== "codeowners") {
|
|
86
|
+
throw new Error(`Unknown sync kind: "${kind}". Known kinds: codeowners.`);
|
|
87
|
+
}
|
|
88
|
+
const { workspace } = discoverWorkspace(wsRoot, visConfig);
|
|
89
|
+
const lines = buildCodeownersLines(workspace, visConfig?.codeowners);
|
|
90
|
+
if (lines.length === 0) {
|
|
91
|
+
logger.info("No `owners` entries found in any project. Nothing to sync.");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const rendered = renderCodeowners(lines, visConfig?.codeowners?.provider ?? "github");
|
|
95
|
+
const outPath = options.out ? join(wsRoot, options.out) : join(wsRoot, "CODEOWNERS");
|
|
96
|
+
if (options.check) {
|
|
97
|
+
let existing = "";
|
|
98
|
+
try {
|
|
99
|
+
existing = readFileSync(outPath, "utf8");
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error.code === "ENOENT") {
|
|
102
|
+
existing = "";
|
|
103
|
+
} else {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (existing.trim() !== rendered.trim()) {
|
|
108
|
+
logger.error(`${outPath} is out of date. Run \`vis sync codeowners\` to update it.`);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
logger.info(`${outPath} is up to date.`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
writeFileSync(outPath, rendered);
|
|
116
|
+
logger.info(`Wrote ${lines.length} entries to ${outPath}`);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export { execute as default };
|