@orkestrel/scaffold 0.0.81 → 0.0.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +14 -3
- package/dist/bin/main.js +295 -13
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +5 -3
- package/dist/host/agents/orchestration.md +1 -0
- package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +2 -2
- package/dist/host/agents/skills/orkestrel-journey/SKILL.md +43 -39
- package/dist/host/agents/skills/orkestrel-journey/references/captures.md +3 -3
- package/dist/host/agents/skills/orkestrel-journey/references/decide.md +10 -9
- package/dist/host/agents/skills/orkestrel-journey/references/layer.md +5 -5
- package/dist/host/agents/skills/orkestrel-journey/references/recorded.md +77 -0
- package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +3 -3
- package/dist/host/agents/skills/orkestrel-journey/references/styles.md +9 -9
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +4 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +22 -4
- package/dist/host/claude/agents/orkestrel.md +10 -10
- package/dist/host/claude/rules/application.md +20 -6
- package/dist/host/claude/rules/architecture.md +2 -2
- package/dist/host/claude/rules/browser.md +9 -0
- package/dist/host/claude/rules/documentation.md +2 -1
- package/dist/host/claude/rules/quality.md +5 -0
- package/dist/host/claude/rules/styles.md +35 -12
- package/dist/host/claude/rules/tests.md +15 -10
- package/dist/host/claude/rules/workspace.md +128 -98
- package/dist/host/claude/skills/orkestrel-journey/SKILL.md +1 -1
- package/dist/host/configs/helpers.ts +157 -9
- package/dist/host/configs/policy.ts +64 -61
- package/dist/host/dotfiles/oxlintrc.json +132 -16
- package/dist/host/dotfiles/prettierignore +1 -1
- package/dist/host/guides/README.md +9 -5
- package/dist/host/guides/guide.md +4 -1
- package/dist/host/guides/scaffold.md +482 -115
- package/dist/host/manifest.json +38 -32
- package/dist/host/scripts/codex.sh +0 -0
- package/dist/host/scripts/cursor.sh +0 -0
- package/dist/host/scripts/deps.sh +0 -0
- package/dist/host/scripts/ollama.sh +0 -0
- package/dist/host/tests/config.test.ts +996 -39
- package/dist/host/tests/policy.test.ts +11 -0
- package/dist/host/tests/setupPolicy.ts +396 -22
- package/dist/src/core/index.cjs +1289 -164
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +313 -35
- package/dist/src/core/index.d.ts +313 -35
- package/dist/src/core/index.js +1268 -165
- package/dist/src/core/index.js.map +1 -1
- package/package.json +7 -6
|
@@ -19,6 +19,9 @@ const RUNTIME_FIELDS = ['dependencies', 'peerDependencies'];
|
|
|
19
19
|
const RANGE_FIELDS = [...RUNTIME_FIELDS, 'devDependencies'];
|
|
20
20
|
const CATALOG = '.claude/agents/orkestrel.md';
|
|
21
21
|
const OFFLINE_REFUSAL = "'catalog' does not take --offline";
|
|
22
|
+
// The vendored host is staged from this package's checkout, so overwriting it from the host deletes
|
|
23
|
+
// and replaces its own canon.
|
|
24
|
+
const SCAFFOLD = '@orkestrel/scaffold';
|
|
22
25
|
const VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
|
23
26
|
// The checkout runs the `.ts` sources and a built twin under `dist/agents` runs `.js` siblings.
|
|
24
27
|
const EXTENSION = extname(fileURLToPath(import.meta.url));
|
|
@@ -223,10 +226,14 @@ function runOverwrite(runner, scaffoldFile, scaffoldPrefix, offline) {
|
|
|
223
226
|
runStep(runner, 'overwrite', scaffoldFile, [...scaffoldPrefix, 'audit', ...offline], runner.target);
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
|
-
function runVerify(runner, scaffoldFile, scaffoldPrefix, offline) {
|
|
229
|
+
function runVerify(runner, scaffoldFile, scaffoldPrefix, offline, own) {
|
|
227
230
|
const audit = runStep(runner, 'verify', scaffoldFile, [...scaffoldPrefix, 'audit', '--json', ...offline], runner.target);
|
|
228
231
|
if (audit === undefined)
|
|
229
232
|
return;
|
|
233
|
+
// The audit reads the canonical sources of scaffold's own checkout as foreign paths and exits 1.
|
|
234
|
+
if (own && audit.status === 1) {
|
|
235
|
+
replaceLastStep(runner, 0, 'foreign-path findings are expected on the scaffold checkout');
|
|
236
|
+
}
|
|
230
237
|
const releases = readReleases(audit.stdout);
|
|
231
238
|
if (releases === undefined) {
|
|
232
239
|
replaceLastStep(runner, audit.status === null || audit.status === 0 ? 2 : audit.status, 'the audit printed no releases envelope');
|
|
@@ -327,10 +334,14 @@ function runVisit(argv) {
|
|
|
327
334
|
if (selected.has('commit') && !failed(runner))
|
|
328
335
|
runCommit(runner);
|
|
329
336
|
if (selected.has('overwrite') && !failed(runner)) {
|
|
330
|
-
|
|
337
|
+
if (name === SCAFFOLD) {
|
|
338
|
+
recordStep(runner, 'overwrite', describeCommand(scaffoldFile, [...scaffoldPrefix, 'overwrite', '--json', ...offline]), `skipped with its audit: ${SCAFFOLD} is the package whose checkout the vendored host is staged from`);
|
|
339
|
+
}
|
|
340
|
+
else
|
|
341
|
+
runOverwrite(runner, scaffoldFile, scaffoldPrefix, offline);
|
|
331
342
|
}
|
|
332
343
|
if (selected.has('verify') && !failed(runner)) {
|
|
333
|
-
runVerify(runner, scaffoldFile, scaffoldPrefix, offline);
|
|
344
|
+
runVerify(runner, scaffoldFile, scaffoldPrefix, offline, name === SCAFFOLD);
|
|
334
345
|
}
|
|
335
346
|
if (selected.has('install') && !failed(runner))
|
|
336
347
|
runNpmStep(runner, 'install', ['install']);
|
package/dist/bin/main.js
CHANGED
|
@@ -1,11 +1,131 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { align, renderTable, strip, stripControls, width } from "@orkestrel/console";
|
|
3
3
|
import { attempt, isArray, isError, isInstance, isRecord, isString, parseJSON } from "@orkestrel/contract";
|
|
4
|
-
import { BIN_ENTRY_PATH, CATALOG_AGENT_PATH, CONFORMANCE_TEST_PATH, Compiler, DEPENDENCY_NAME_PATTERN, ENVIRONMENTS, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, JOURNEY_CONFIG_PATH, MAX_MANIFEST_BYTES, MAX_PATH_LENGTH, SERVICE_SETUP_PATH, SHOWCASE_CONFIG_PATH, SKILLS_CONFIG_PATH, ScaffoldError, blueprintToConfigArtifacts, blueprintToDevDependencies,
|
|
4
|
+
import { AXES, BIN_ENTRY_PATH, CATALOG_AGENT_PATH, CONFORMANCE_TEST_PATH, Compiler, DEPENDENCY_NAME_PATTERN, ENVIRONMENTS, FRAMEWORKS, GLOBAL_SETUP_PATH, GROUPS, GUIDES_TEST_PATH, HOST_PATHS, INTEGRATION_TEST_PATH, JOURNEY_CONFIG_PATH, MAX_MANIFEST_BYTES, MAX_PATH_LENGTH, RESERVED_SHEET_NAMES, SERVICE_SETUP_PATH, SHEET_ENTRY_NAME, SHOWCASE_CONFIG_PATH, SHOWCASE_PAGES_PATH, SKILLS_CONFIG_PATH, STYLES_ENTRY_PATH, ScaffoldError, THEMES_BARREL_PATH, THEMES_ENTRY_PATH, blueprintToConfigArtifacts, blueprintToDevDependencies, blueprintToFaces, blueprintToProjects, blueprintToScripts, blueprintToSheets, blueprintToTestArtifacts, blueprintToWritableScripts, compareVersions, createBlueprint, extractRangeMajor, extractVersion, isFloorPath, isScaffoldError, isSheetName, manifestToDependencies, manifestToName, nameToGuide, parseExtension, replaceManifestScripts, replacePlanRanges } from "../src/core/index.js";
|
|
5
5
|
import { MAX_INVENTORY_PATHS, Materializer, Upstream, filesToHost, isExactCaseFile, isPhysicalDirectory, isWorktree, listFiles, readFileText, readHostFloor, readHostManifest, readSnapshot, resolveContainedPath } from "../src/server/index.js";
|
|
6
6
|
import { createMarkdown, flattenText, isTableNode } from "@orkestrel/markdown";
|
|
7
7
|
import { createSession } from "@orkestrel/process/server";
|
|
8
|
+
import { readdirSync } from "node:fs";
|
|
9
|
+
import { normalize } from "node:path";
|
|
8
10
|
import { parseArgs } from "node:util";
|
|
11
|
+
var package_default = {
|
|
12
|
+
name: "@orkestrel/scaffold",
|
|
13
|
+
version: "0.0.83",
|
|
14
|
+
description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
|
|
15
|
+
keywords: [
|
|
16
|
+
"audit",
|
|
17
|
+
"catalog",
|
|
18
|
+
"new",
|
|
19
|
+
"overwrite",
|
|
20
|
+
"repair"
|
|
21
|
+
],
|
|
22
|
+
homepage: "https://github.com/orkestrel/scaffold#readme",
|
|
23
|
+
bugs: "https://github.com/orkestrel/scaffold/issues",
|
|
24
|
+
license: "MIT",
|
|
25
|
+
repository: {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/orkestrel/scaffold.git"
|
|
28
|
+
},
|
|
29
|
+
bin: { "scaffold": "dist/bin/main.js" },
|
|
30
|
+
files: [
|
|
31
|
+
"dist/src",
|
|
32
|
+
"dist/bin",
|
|
33
|
+
"dist/host",
|
|
34
|
+
"dist/agents",
|
|
35
|
+
"README.md"
|
|
36
|
+
],
|
|
37
|
+
type: "module",
|
|
38
|
+
sideEffects: ["./src/bin/main.ts", "./dist/bin/main.js"],
|
|
39
|
+
main: "./dist/src/core/index.cjs",
|
|
40
|
+
module: "./dist/src/core/index.js",
|
|
41
|
+
exports: {
|
|
42
|
+
".": {
|
|
43
|
+
"import": {
|
|
44
|
+
"types": "./dist/src/core/index.d.ts",
|
|
45
|
+
"default": "./dist/src/core/index.js"
|
|
46
|
+
},
|
|
47
|
+
"require": {
|
|
48
|
+
"types": "./dist/src/core/index.d.cts",
|
|
49
|
+
"default": "./dist/src/core/index.cjs"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"./server": {
|
|
53
|
+
"import": {
|
|
54
|
+
"types": "./dist/src/server/index.d.ts",
|
|
55
|
+
"default": "./dist/src/server/index.js"
|
|
56
|
+
},
|
|
57
|
+
"require": {
|
|
58
|
+
"types": "./dist/src/server/index.d.cts",
|
|
59
|
+
"default": "./dist/src/server/index.cjs"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"./package.json": "./package.json"
|
|
63
|
+
},
|
|
64
|
+
publishConfig: { "access": "public" },
|
|
65
|
+
scripts: {
|
|
66
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
67
|
+
"copy": "node -e \"const fs=require('node:fs'),p=require('node:path'),a=process.argv[1],b=process.argv[2];fs.mkdirSync(p.dirname(b),{recursive:true});fs.cpSync(a,b,{force:true});console.log('Copied: '+a+' to '+b)\"",
|
|
68
|
+
"scaffold": "node ./dist/bin/main.js",
|
|
69
|
+
"lint": "oxlint --config .oxlintrc.json --fix .",
|
|
70
|
+
"check": "tsc --noEmit --project tsconfig.json && npm run check:src && npm run check:skills",
|
|
71
|
+
"check:src": "npm run check:src:core && npm run check:src:server && npm run check:src:bin",
|
|
72
|
+
"check:src:core": "tsc --noEmit -p configs/src/tsconfig.core.json",
|
|
73
|
+
"check:src:server": "tsc --noEmit -p configs/src/tsconfig.server.json",
|
|
74
|
+
"check:src:bin": "tsc --noEmit -p configs/src/tsconfig.bin.json",
|
|
75
|
+
"check:skills": "tsc --noEmit -p configs/agents/tsconfig.skills.json",
|
|
76
|
+
"format": "oxfmt --config .oxfmtrc.json --write .",
|
|
77
|
+
"format:check": "oxfmt --config .oxfmtrc.json --check .",
|
|
78
|
+
"lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
|
|
79
|
+
"test": "npm run test:src:core && npm run test:src:server && npm run test:src:bin && npm run test:policy && npm run test:config && npm run test:setup && npm run test:skills && npm run test:guides",
|
|
80
|
+
"test:src:core": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
81
|
+
"test:src:server": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:server",
|
|
82
|
+
"test:src:bin": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:bin",
|
|
83
|
+
"test:policy": "vitest run --config vite.config.ts --no-cache --reporter=dot --project policy",
|
|
84
|
+
"test:config": "vitest run --config vite.config.ts --no-cache --reporter=dot --project config",
|
|
85
|
+
"test:guides": "node --experimental-strip-types tests/guides.test.ts",
|
|
86
|
+
"test:skills": "vitest run --config vite.config.ts --no-cache --reporter=dot --project skills",
|
|
87
|
+
"test:distribution": "vitest run --config vite.config.ts --no-cache --reporter=dot --project distribution",
|
|
88
|
+
"test:probe": "vitest run --config vite.config.ts --no-cache --reporter=verbose --project probe",
|
|
89
|
+
"test:bench": "vitest bench --config vite.config.ts --no-cache --project probe",
|
|
90
|
+
"test:setup": "vitest run --config vite.config.ts --no-cache --reporter=dot --project setup",
|
|
91
|
+
"build": "npm run clean && npm run build:src && npm run build:host && npm run build:skills && npm run build:inventory",
|
|
92
|
+
"build:src": "npm run build:src:core && npm run build:src:server && npm run build:src:bin",
|
|
93
|
+
"build:src:core": "vite build --config configs/src/vite.core.config.ts && npm run copy dist/src/core/index.d.ts dist/src/core/index.d.cts",
|
|
94
|
+
"build:src:server": "vite build --config configs/src/vite.server.config.ts && npm run copy dist/src/server/index.d.ts dist/src/server/index.d.cts",
|
|
95
|
+
"build:src:bin": "vite build --config configs/src/vite.bin.config.ts",
|
|
96
|
+
"build:host": "node -e \"import('./dist/src/server/index.js').then((m)=>{const n=m.stageHost(process.cwd(),'dist/host',{report: (message) => console.error(message)}).length;console.log('build-host: staged '+n+' file(s) into dist/host')})\"",
|
|
97
|
+
"build:inventory": "node -e \"import('./dist/src/server/index.js').then((m)=>{const p=process.argv[1]??'host.json',n=m.stageInventory(process.cwd(),p).entries.length;console.log('build-inventory: staged '+n+' file(s) into '+p)})\"",
|
|
98
|
+
"build:skills": "tsc -p configs/agents/tsconfig.skills.json --noEmit false --allowImportingTsExtensions false --rewriteRelativeImportExtensions --declaration false --sourceMap false --rootDir .agents/skills --outDir dist/agents/skills && npm run copy .agents/templates/brief.md dist/agents/templates/brief.md",
|
|
99
|
+
"prepack": "npm run build",
|
|
100
|
+
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test && npm run test:distribution -- --mode release"
|
|
101
|
+
},
|
|
102
|
+
dependencies: {
|
|
103
|
+
"@orkestrel/console": "^0.0.15",
|
|
104
|
+
"@orkestrel/contract": "^0.0.19",
|
|
105
|
+
"@orkestrel/emitter": "^0.0.11",
|
|
106
|
+
"@orkestrel/markdown": "^0.0.17",
|
|
107
|
+
"@orkestrel/process": "^0.0.14",
|
|
108
|
+
"@orkestrel/template": "^0.0.9"
|
|
109
|
+
},
|
|
110
|
+
devDependencies: {
|
|
111
|
+
"@microsoft/api-extractor": "^7.59.3",
|
|
112
|
+
"@orkestrel/browser": "^0.0.20",
|
|
113
|
+
"@orkestrel/guide": "^0.0.22",
|
|
114
|
+
"@orkestrel/html": "^0.0.12",
|
|
115
|
+
"@orkestrel/probe": "^0.0.19",
|
|
116
|
+
"@orkestrel/test": "^0.0.24",
|
|
117
|
+
"@types/node": "^26.6.3",
|
|
118
|
+
"@vitest/browser-playwright": "^4.1.11",
|
|
119
|
+
"oxfmt": "^0.71.0",
|
|
120
|
+
"oxlint": "^1.86.0",
|
|
121
|
+
"playwright": "^1.63.0",
|
|
122
|
+
"typescript": "^6.0.3",
|
|
123
|
+
"vite": "^8.3.2",
|
|
124
|
+
"vitest": "^4.1.11"
|
|
125
|
+
},
|
|
126
|
+
engines: { "node": ">=22.18.0" }
|
|
127
|
+
};
|
|
128
|
+
//#endregion
|
|
9
129
|
//#region src/bin/constants.ts
|
|
10
130
|
/**
|
|
11
131
|
* Names the command the executable installs as.
|
|
@@ -17,6 +137,15 @@ import { parseArgs } from "node:util";
|
|
|
17
137
|
*/
|
|
18
138
|
var EXECUTABLE_NAME = "scaffold";
|
|
19
139
|
/**
|
|
140
|
+
* Names the package this executable ships in, as its manifest declares it.
|
|
141
|
+
*
|
|
142
|
+
* @remarks
|
|
143
|
+
* A target declaring this name is the checkout the vendored host is staged
|
|
144
|
+
* from, so `repair` and `overwrite` refuse it: a write from the host would
|
|
145
|
+
* replace that checkout's canon with the copy staged from it.
|
|
146
|
+
*/
|
|
147
|
+
var PACKAGE_NAME = package_default.name;
|
|
148
|
+
/**
|
|
20
149
|
* Lists the {@link Verb} values in usage order, frozen.
|
|
21
150
|
*
|
|
22
151
|
* @remarks
|
|
@@ -82,6 +211,13 @@ var COMMAND_OPTIONS = Object.freeze({
|
|
|
82
211
|
src: Object.freeze({ type: "string" }),
|
|
83
212
|
app: Object.freeze({ type: "string" }),
|
|
84
213
|
bin: Object.freeze({ type: "boolean" }),
|
|
214
|
+
styles: Object.freeze({ type: "boolean" }),
|
|
215
|
+
themes: Object.freeze({ type: "boolean" }),
|
|
216
|
+
showcase: Object.freeze({ type: "boolean" }),
|
|
217
|
+
extend: Object.freeze({
|
|
218
|
+
type: "string",
|
|
219
|
+
multiple: true
|
|
220
|
+
}),
|
|
85
221
|
deps: Object.freeze({ type: "string" }),
|
|
86
222
|
groups: Object.freeze({ type: "string" }),
|
|
87
223
|
all: Object.freeze({ type: "boolean" }),
|
|
@@ -106,6 +242,10 @@ var OPTION_SUMMARY = Object.freeze({
|
|
|
106
242
|
"--src <list>": "the published library environments to build: core, browser, server",
|
|
107
243
|
"--app <list>": "the private application environments to build: core, browser, server",
|
|
108
244
|
"--bin": "scaffold a command-line executable at src/bin/main.ts",
|
|
245
|
+
"--styles": "select the base styles surface",
|
|
246
|
+
"--themes": "select themes; requires --styles",
|
|
247
|
+
"--showcase": "select the showcase; requires --app browser",
|
|
248
|
+
"--extend <list>": "select surface:name extensions on the selected surfaces",
|
|
109
249
|
"--deps <list>": "the @orkestrel/* packages the workspace depends on",
|
|
110
250
|
"--groups <list>": "the artifact groups to cover; every group when absent",
|
|
111
251
|
"--all": "fetch a guide for every package the organization publishes, not the declared ones alone",
|
|
@@ -131,6 +271,10 @@ var VERB_OPTIONS = Object.freeze({
|
|
|
131
271
|
"--src <list>",
|
|
132
272
|
"--app <list>",
|
|
133
273
|
"--bin",
|
|
274
|
+
"--styles",
|
|
275
|
+
"--themes",
|
|
276
|
+
"--showcase",
|
|
277
|
+
"--extend <list>",
|
|
134
278
|
"--deps <list>",
|
|
135
279
|
"--offline",
|
|
136
280
|
"--from <path>",
|
|
@@ -383,6 +527,9 @@ function argvToCommand(argv) {
|
|
|
383
527
|
const target = isString(values.target) ? values.target : void 0;
|
|
384
528
|
const json = values.json === true;
|
|
385
529
|
const [from] = paths;
|
|
530
|
+
const extensions = isArray(values.extend) ? values.extend.filter(isString) : [];
|
|
531
|
+
if (extensions.length > 1) throw new UsageError("'new' takes --extend once; combine entries in one list.");
|
|
532
|
+
const [extension] = extensions;
|
|
386
533
|
const location = target === void 0 ? {} : { target };
|
|
387
534
|
const source = from === void 0 ? {} : { from };
|
|
388
535
|
const selection = isString(values.groups) ? { groups: values.groups } : {};
|
|
@@ -398,6 +545,10 @@ function argvToCommand(argv) {
|
|
|
398
545
|
...src === void 0 ? {} : { src },
|
|
399
546
|
...app === void 0 ? {} : { app },
|
|
400
547
|
...bin ? { bin } : {},
|
|
548
|
+
...values.styles === true ? { styles: true } : {},
|
|
549
|
+
...values.themes === true ? { themes: true } : {},
|
|
550
|
+
...values.showcase === true ? { showcase: true } : {},
|
|
551
|
+
...extension === void 0 ? {} : { extensions: extension },
|
|
401
552
|
...offline ? { offline } : {},
|
|
402
553
|
...dependencies === void 0 ? {} : { dependencies }
|
|
403
554
|
};
|
|
@@ -888,6 +1039,7 @@ function releasesToPins(releases, declared) {
|
|
|
888
1039
|
* Reads the literal Vitest projects, configuration paths, and npm run scripts a shell command names.
|
|
889
1040
|
*
|
|
890
1041
|
* @param script - The manifest script text to read.
|
|
1042
|
+
* @param wrappers - Planned wrapper paths mapped to the face projects they run.
|
|
891
1043
|
* @returns The named projects, configurations, and scripts, or `undefined` when the command
|
|
892
1044
|
* cannot be read literally.
|
|
893
1045
|
*
|
|
@@ -906,7 +1058,7 @@ function releasesToPins(releases, declared) {
|
|
|
906
1058
|
* // { projects: ['src:core'], configs: [], scripts: [] }
|
|
907
1059
|
* ```
|
|
908
1060
|
*/
|
|
909
|
-
function scriptToInvocations(script) {
|
|
1061
|
+
function scriptToInvocations(script, wrappers = /* @__PURE__ */ new Map()) {
|
|
910
1062
|
const tokens = [];
|
|
911
1063
|
let value = "";
|
|
912
1064
|
let resolved = true;
|
|
@@ -983,9 +1135,19 @@ function scriptToInvocations(script) {
|
|
|
983
1135
|
const projects = [];
|
|
984
1136
|
const configs = [];
|
|
985
1137
|
const scripts = [];
|
|
1138
|
+
const configurations = new Map([...wrappers].map(([path, project]) => [normalize(path), project]));
|
|
1139
|
+
let testing = false;
|
|
986
1140
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
987
1141
|
const token = tokens[index];
|
|
988
1142
|
if (token === void 0) return void 0;
|
|
1143
|
+
if (index === 0 || [
|
|
1144
|
+
"&&",
|
|
1145
|
+
"||",
|
|
1146
|
+
";",
|
|
1147
|
+
"|",
|
|
1148
|
+
"&",
|
|
1149
|
+
"("
|
|
1150
|
+
].includes(tokens[index - 1]?.value ?? "")) testing = token.resolved && token.value === "vitest" && tokens[index + 1]?.value === "run";
|
|
989
1151
|
if (token.value === "npm" && tokens[index + 1]?.value === "run") {
|
|
990
1152
|
const name = tokens[index + 2];
|
|
991
1153
|
if (name === void 0 || !name.resolved || name.value.length === 0 || [
|
|
@@ -1012,14 +1174,20 @@ function scriptToInvocations(script) {
|
|
|
1012
1174
|
"(",
|
|
1013
1175
|
")"
|
|
1014
1176
|
].includes(project.value)) return void 0;
|
|
1015
|
-
|
|
1177
|
+
const names = token.value === "--project" ? projects : configs;
|
|
1178
|
+
names.push(project.value);
|
|
1179
|
+
const face = testing && names === configs ? configurations.get(normalize(project.value)) : void 0;
|
|
1180
|
+
if (face !== void 0) projects.push(face);
|
|
1016
1181
|
index += 1;
|
|
1017
1182
|
continue;
|
|
1018
1183
|
}
|
|
1019
1184
|
if (token.value.startsWith("--project=") || token.value.startsWith("--config=")) {
|
|
1020
1185
|
const project = token.value.slice(token.value.indexOf("=") + 1);
|
|
1021
1186
|
if (!token.resolved || project.length === 0) return void 0;
|
|
1022
|
-
|
|
1187
|
+
const names = token.value.startsWith("--project=") ? projects : configs;
|
|
1188
|
+
names.push(project);
|
|
1189
|
+
const face = testing && names === configs ? configurations.get(normalize(project)) : void 0;
|
|
1190
|
+
if (face !== void 0) projects.push(face);
|
|
1023
1191
|
continue;
|
|
1024
1192
|
}
|
|
1025
1193
|
if (!token.resolved && /--project|--config/.test(token.value)) return void 0;
|
|
@@ -1055,6 +1223,96 @@ function targetToEnvironments(target, axis) {
|
|
|
1055
1223
|
});
|
|
1056
1224
|
}
|
|
1057
1225
|
/**
|
|
1226
|
+
* Reads the styles, themes, and showcase markers a target physically holds.
|
|
1227
|
+
* @param target - The target directory.
|
|
1228
|
+
* @returns The independent structural facts, false when their markers are absent.
|
|
1229
|
+
*/
|
|
1230
|
+
function targetToFacts(target) {
|
|
1231
|
+
const styles = resolveContainedPath(target, STYLES_ENTRY_PATH);
|
|
1232
|
+
const themes = resolveContainedPath(target, THEMES_ENTRY_PATH);
|
|
1233
|
+
const barrel = resolveContainedPath(target, THEMES_BARREL_PATH);
|
|
1234
|
+
const wrapper = resolveContainedPath(target, SHOWCASE_CONFIG_PATH);
|
|
1235
|
+
const pages = resolveContainedPath(target, SHOWCASE_PAGES_PATH);
|
|
1236
|
+
return {
|
|
1237
|
+
styles: styles !== void 0 && isExactCaseFile(styles),
|
|
1238
|
+
themes: themes !== void 0 && isExactCaseFile(themes) && barrel !== void 0 && isExactCaseFile(barrel),
|
|
1239
|
+
showcase: wrapper !== void 0 && isExactCaseFile(wrapper) || pages !== void 0 && isPhysicalDirectory(pages)
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Reads browser faces and named sheet marker pairs from a target.
|
|
1244
|
+
* @param target - The target directory.
|
|
1245
|
+
* @returns Browser extensions in framework order followed by sheets in code-unit order.
|
|
1246
|
+
* @throws `ScaffoldError` when sheet markers name an invalid or case-colliding face, or the directory cannot be read.
|
|
1247
|
+
*/
|
|
1248
|
+
function targetToExtensions(target) {
|
|
1249
|
+
const extensions = [];
|
|
1250
|
+
for (const name of FRAMEWORKS) {
|
|
1251
|
+
const axes = AXES.filter((axis) => {
|
|
1252
|
+
const path = resolveContainedPath(target, `${axis}/${name}`);
|
|
1253
|
+
return path !== void 0 && isPhysicalDirectory(path);
|
|
1254
|
+
});
|
|
1255
|
+
if (axes.length > 0) extensions.push({
|
|
1256
|
+
surface: "browser",
|
|
1257
|
+
name,
|
|
1258
|
+
axes
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
const source = resolveContainedPath(target, "src");
|
|
1262
|
+
if (source === void 0 || !isPhysicalDirectory(source)) return extensions;
|
|
1263
|
+
const entries = readdirSync(source, { withFileTypes: true });
|
|
1264
|
+
if (entries.length > MAX_INVENTORY_PATHS) throw new ScaffoldError("TARGET", "The source directory exceeds the inventory limit.");
|
|
1265
|
+
const names = [];
|
|
1266
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1267
|
+
for (const entry of entries) {
|
|
1268
|
+
if (!entry.isDirectory() || RESERVED_SHEET_NAMES.includes(entry.name)) continue;
|
|
1269
|
+
const sheet = resolveContainedPath(target, `src/${entry.name}/index.scss`);
|
|
1270
|
+
const marker = resolveContainedPath(target, `src/${entry.name}/${SHEET_ENTRY_NAME}`);
|
|
1271
|
+
if (sheet === void 0 || marker === void 0 || !isExactCaseFile(sheet) || !isExactCaseFile(marker)) continue;
|
|
1272
|
+
const folded = entry.name.toLowerCase();
|
|
1273
|
+
if (seen.has(folded)) throw new ScaffoldError("TARGET", `Case-colliding sheet face '${entry.name}'.`);
|
|
1274
|
+
seen.add(folded);
|
|
1275
|
+
names.push(entry.name);
|
|
1276
|
+
}
|
|
1277
|
+
for (const name of names.sort()) {
|
|
1278
|
+
if (!isSheetName(name)) throw new ScaffoldError("TARGET", `Invalid sheet face '${name}'.`);
|
|
1279
|
+
extensions.push({
|
|
1280
|
+
surface: "styles",
|
|
1281
|
+
name
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
return extensions;
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Reads a creation extension list and assigns its selected browser axes.
|
|
1288
|
+
* @param selection - The comma-separated `surface:name` entries, or no selection.
|
|
1289
|
+
* @param axes - The axes selecting a browser environment.
|
|
1290
|
+
* @param styles - If `true`, admits stylesheet extensions; if `false`, refuses them.
|
|
1291
|
+
* @returns The validated extensions in selection order.
|
|
1292
|
+
* @throws `UsageError` when an entry is malformed, repeated, unsupported, or lacks its surface.
|
|
1293
|
+
*/
|
|
1294
|
+
function selectionToExtensions(selection, axes, styles) {
|
|
1295
|
+
if (selection === void 0) return [];
|
|
1296
|
+
const extensions = [];
|
|
1297
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1298
|
+
for (const entry of selection.split(",")) {
|
|
1299
|
+
const extension = parseExtension(entry);
|
|
1300
|
+
if (extension === void 0 || seen.has(entry)) throw new UsageError(`'--extend' refuses '${entry}': use distinct supported surface:name entries.`);
|
|
1301
|
+
seen.add(entry);
|
|
1302
|
+
if (extension.surface === "browser") {
|
|
1303
|
+
if (axes.length === 0) throw new UsageError("A browser extension requires browser in --src or --app.");
|
|
1304
|
+
extensions.push({
|
|
1305
|
+
...extension,
|
|
1306
|
+
axes
|
|
1307
|
+
});
|
|
1308
|
+
} else {
|
|
1309
|
+
if (!styles) throw new UsageError("A styles extension requires --styles.");
|
|
1310
|
+
extensions.push(extension);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
return extensions;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1058
1316
|
* Reads the packages a target's catalog table listed before this run.
|
|
1059
1317
|
*
|
|
1060
1318
|
* @param target - The target or raw checkout directory, or a verified value host.
|
|
@@ -1396,9 +1654,17 @@ var CLI = class {
|
|
|
1396
1654
|
async #create(command) {
|
|
1397
1655
|
const target = command.target ?? command.name;
|
|
1398
1656
|
const app = selectionToEnvironments(command.app, "app");
|
|
1657
|
+
const src = selectionToEnvironments(command.src, "src");
|
|
1658
|
+
if (command.themes === true && command.styles !== true) throw new UsageError("--themes requires --styles.");
|
|
1659
|
+
if (command.showcase === true && !app.includes("browser")) throw new UsageError("--showcase requires --app browser.");
|
|
1660
|
+
const axes = AXES.filter((axis) => (axis === "src" ? src : app).includes("browser"));
|
|
1399
1661
|
const blueprint = createBlueprint(command.name, {
|
|
1400
|
-
src
|
|
1662
|
+
src,
|
|
1401
1663
|
app,
|
|
1664
|
+
extensions: selectionToExtensions(command.extensions, axes, command.styles === true),
|
|
1665
|
+
styles: command.styles === true,
|
|
1666
|
+
themes: command.themes === true,
|
|
1667
|
+
showcase: command.showcase === true,
|
|
1402
1668
|
journey: app.includes("browser"),
|
|
1403
1669
|
bin: command.bin === true,
|
|
1404
1670
|
setup: [],
|
|
@@ -1900,16 +2166,17 @@ var CLI = class {
|
|
|
1900
2166
|
const conformance = resolveContainedPath(target, CONFORMANCE_TEST_PATH);
|
|
1901
2167
|
const service = resolveContainedPath(target, SERVICE_SETUP_PATH);
|
|
1902
2168
|
const global = resolveContainedPath(target, GLOBAL_SETUP_PATH);
|
|
1903
|
-
const showcase = resolveContainedPath(target, SHOWCASE_CONFIG_PATH);
|
|
1904
2169
|
const journey = resolveContainedPath(target, JOURNEY_CONFIG_PATH);
|
|
1905
2170
|
const skills = resolveContainedPath(target, SKILLS_CONFIG_PATH);
|
|
1906
2171
|
const setup = /* @__PURE__ */ new Set();
|
|
1907
2172
|
if (tests !== void 0) for (const path of listFiles(tests)) {
|
|
1908
2173
|
if (path.includes("/") || !path.startsWith("setup") || !path.endsWith(".test.ts")) continue;
|
|
1909
2174
|
const proof = resolveContainedPath(tests, path);
|
|
1910
|
-
if (proof !== void 0 && isExactCaseFile(proof)) setup.add(path === "setupBrowser.test.ts" ? "browser" : "node");
|
|
2175
|
+
if (proof !== void 0 && isExactCaseFile(proof)) setup.add(path === "setupBrowser.test.ts" || path === "setupStyles.test.ts" ? "browser" : "node");
|
|
1911
2176
|
}
|
|
1912
2177
|
return createBlueprint(declared.slice(declared.lastIndexOf("/") + 1), {
|
|
2178
|
+
...targetToFacts(target),
|
|
2179
|
+
extensions: targetToExtensions(target),
|
|
1913
2180
|
src: targetToEnvironments(target, "src"),
|
|
1914
2181
|
app: targetToEnvironments(target, "app"),
|
|
1915
2182
|
dependencies: manifestToDependencies(manifest).runtime,
|
|
@@ -1920,7 +2187,6 @@ var CLI = class {
|
|
|
1920
2187
|
conformance: conformance !== void 0 && isExactCaseFile(conformance),
|
|
1921
2188
|
service: service !== void 0 && isExactCaseFile(service),
|
|
1922
2189
|
global: global !== void 0 && isExactCaseFile(global),
|
|
1923
|
-
showcase: showcase !== void 0 && isExactCaseFile(showcase),
|
|
1924
2190
|
journey: journey !== void 0 && isExactCaseFile(journey),
|
|
1925
2191
|
skills: skills !== void 0 && isExactCaseFile(skills)
|
|
1926
2192
|
});
|
|
@@ -1929,7 +2195,7 @@ var CLI = class {
|
|
|
1929
2195
|
const text = this.#manifest(target);
|
|
1930
2196
|
const writable = writing ? manifestToWritableScripts(text, blueprint) : blueprintToWritableScripts(blueprint);
|
|
1931
2197
|
const manifest = replaceManifestScripts(text, writable) ?? text;
|
|
1932
|
-
const planned =
|
|
2198
|
+
const planned = new Set(blueprintToProjects(blueprint));
|
|
1933
2199
|
const parsed = parseJSON(manifest);
|
|
1934
2200
|
const scripts = isRecord(parsed) && isRecord(parsed.scripts) ? parsed.scripts : {};
|
|
1935
2201
|
const gates = !isRecord(parsed) || parsed.private !== true ? ["test", "prepublishOnly"] : ["test"];
|
|
@@ -1943,7 +2209,7 @@ var CLI = class {
|
|
|
1943
2209
|
unresolved = true;
|
|
1944
2210
|
continue;
|
|
1945
2211
|
}
|
|
1946
|
-
for (const project of invoked.projects) if (!planned.
|
|
2212
|
+
for (const project of invoked.projects) if (!planned.has(project)) absent.add(project);
|
|
1947
2213
|
}
|
|
1948
2214
|
const projects = [...absent].sort();
|
|
1949
2215
|
if (unresolved) return {
|
|
@@ -1991,6 +2257,14 @@ var CLI = class {
|
|
|
1991
2257
|
if (command !== void 0) expectedLines.set(project, `"${direct}": ${JSON.stringify(command)},`);
|
|
1992
2258
|
}
|
|
1993
2259
|
}
|
|
2260
|
+
const faces = /* @__PURE__ */ new Set([...blueprintToSheets(blueprint).map((name) => `src:${name}`), ...blueprintToFaces(blueprint).flatMap(({ name, axes }) => axes.map((axis) => `${axis}:${name}`))]);
|
|
2261
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
2262
|
+
for (const artifact of blueprintToConfigArtifacts(blueprint)) {
|
|
2263
|
+
const match = /^configs\/(src|app)\/vite\.([^.]+)\.config\.ts$/u.exec(artifact.path);
|
|
2264
|
+
if (match === null) continue;
|
|
2265
|
+
const project = `${match[1]}:${match[2]}`;
|
|
2266
|
+
if (faces.has(project)) wrappers.set(artifact.path, project);
|
|
2267
|
+
}
|
|
1994
2268
|
const reachable = /* @__PURE__ */ new Set();
|
|
1995
2269
|
const chains = /* @__PURE__ */ new Map();
|
|
1996
2270
|
for (const gate of gates) {
|
|
@@ -2003,7 +2277,7 @@ var CLI = class {
|
|
|
2003
2277
|
visited.add(name);
|
|
2004
2278
|
const script = scripts[name];
|
|
2005
2279
|
if (!isString(script)) continue;
|
|
2006
|
-
const invoked = scriptToInvocations(script);
|
|
2280
|
+
const invoked = scriptToInvocations(script, wrappers);
|
|
2007
2281
|
if (invoked === void 0) {
|
|
2008
2282
|
unresolved = true;
|
|
2009
2283
|
continue;
|
|
@@ -2102,7 +2376,7 @@ var CLI = class {
|
|
|
2102
2376
|
if (proofs.has(`${path.slice(0, -3)}.test.ts`)) return false;
|
|
2103
2377
|
if (resolveContainedPath(tests, path) === void 0) return false;
|
|
2104
2378
|
const content = (readFileText(tests, path) ?? "").trim();
|
|
2105
|
-
return content
|
|
2379
|
+
return /^export /m.test(content) && content !== (seeds.get(`tests/${path}`) ?? "");
|
|
2106
2380
|
}).sort();
|
|
2107
2381
|
if (modules.length === 0) return void 0;
|
|
2108
2382
|
const single = modules.length === 1;
|
|
@@ -2118,6 +2392,13 @@ var CLI = class {
|
|
|
2118
2392
|
}
|
|
2119
2393
|
#targetQuestions(target, blueprint, groups, writing = false) {
|
|
2120
2394
|
const questions = [];
|
|
2395
|
+
const browser = resolveContainedPath(target, "app/browser");
|
|
2396
|
+
if (browser !== void 0 && listFiles(browser).some((path) => path.endsWith(".vue"))) questions.push({
|
|
2397
|
+
field: "extensions",
|
|
2398
|
+
message: "Move Vue components from app/browser to app/vue before regenerating this workspace.",
|
|
2399
|
+
blocking: writing,
|
|
2400
|
+
groups: []
|
|
2401
|
+
});
|
|
2121
2402
|
if (!writing) {
|
|
2122
2403
|
const script = this.#scriptQuestion(target, blueprint);
|
|
2123
2404
|
if (script !== void 0) questions.push(script);
|
|
@@ -2130,7 +2411,7 @@ var CLI = class {
|
|
|
2130
2411
|
const setup = this.#setupQuestion(target, blueprint);
|
|
2131
2412
|
if (setup !== void 0) questions.push(setup);
|
|
2132
2413
|
}
|
|
2133
|
-
return questions.filter((question) => groups === void 0 || question.groups.some((group) => groups.includes(group))).map(({ field, message, blocking, candidates }) => ({
|
|
2414
|
+
return questions.filter((question) => question.blocking || groups === void 0 || question.groups.some((group) => groups.includes(group))).map(({ field, message, blocking, candidates }) => ({
|
|
2134
2415
|
field,
|
|
2135
2416
|
message,
|
|
2136
2417
|
blocking,
|
|
@@ -2145,6 +2426,7 @@ var CLI = class {
|
|
|
2145
2426
|
};
|
|
2146
2427
|
}
|
|
2147
2428
|
#assertTarget(target, blueprint, groups) {
|
|
2429
|
+
if (manifestToName(this.#manifest(target)) === PACKAGE_NAME) throw new ScaffoldError("TARGET", `The target at ${target} is the ${PACKAGE_NAME} package, whose canon the vendored host is staged from. Run repair and overwrite against a workspace that consumes it.`, { target });
|
|
2148
2430
|
const questions = this.#targetQuestions(target, blueprint, groups, true);
|
|
2149
2431
|
if (questions.length === 0) return;
|
|
2150
2432
|
throw new ScaffoldError("TARGET", questions.map((question) => question.message).join(" "), { target });
|