speckeeper 0.2.0 → 0.3.0
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 +13 -5
- package/dist/cli.js +85 -145
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +82 -69
- package/dist/index.js +60 -129
- package/dist/index.js.map +1 -1
- package/dist/templates/init/design/index.ts +4 -5
- package/dist/templates/init/design/requirements.ts +5 -4
- package/dist/templates/init/speckeeper.config.ts +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,21 +69,23 @@ npx speckeeper scaffold --source requirements.md
|
|
|
69
69
|
|
|
70
70
|
This generates:
|
|
71
71
|
- `design/_models/` — Model classes with Zod schemas, lint rules, and exporters derived from your flowchart
|
|
72
|
+
- `design/*.ts` — Spec data files using `defineSpecs()` for each model
|
|
73
|
+
- `design/index.ts` — Entry point that aggregates all spec modules via `mergeSpecs()`
|
|
72
74
|
- `design/_checkers/` — External checker skeletons for `implements` edges (e.g. OpenAPI, DDL)
|
|
73
|
-
- `design/_models/index.ts` — Re-exports and `allModels` array
|
|
74
|
-
- `speckeeper.config.ts` — Configuration wired to the generated models
|
|
75
75
|
|
|
76
76
|
See [Scaffold Mermaid Specification](./docs/scaffold-mermaid-spec.md) for the full input format and built-in node mappings.
|
|
77
77
|
|
|
78
78
|
### 3. Fill in your specifications
|
|
79
79
|
|
|
80
|
-
Edit files in `design/` to add your actual specification data:
|
|
80
|
+
Edit spec data files in `design/` to add your actual specification data. Each file uses `defineSpecs()` to pair Model instances with data:
|
|
81
81
|
|
|
82
82
|
```typescript
|
|
83
83
|
// design/requirements.ts
|
|
84
|
-
import
|
|
84
|
+
import { defineSpecs } from 'speckeeper';
|
|
85
|
+
import type { Requirement } from './_models/requirement';
|
|
86
|
+
import { FunctionalRequirementModel } from './_models/requirement';
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
const requirements: Requirement[] = [
|
|
87
89
|
{
|
|
88
90
|
id: 'FR-001',
|
|
89
91
|
name: 'User Authentication',
|
|
@@ -96,8 +98,14 @@ export const requirements: Requirement[] = [
|
|
|
96
98
|
],
|
|
97
99
|
},
|
|
98
100
|
];
|
|
101
|
+
|
|
102
|
+
export default defineSpecs(
|
|
103
|
+
[FunctionalRequirementModel.instance, requirements],
|
|
104
|
+
);
|
|
99
105
|
```
|
|
100
106
|
|
|
107
|
+
`design/index.ts` aggregates all spec files, and `speckeeper.config.ts` imports the result — no manual wiring needed beyond adding your spec file to `design/index.ts`.
|
|
108
|
+
|
|
101
109
|
### 4. Run validation
|
|
102
110
|
|
|
103
111
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import chalk4 from 'chalk';
|
|
4
|
-
import { dirname,
|
|
4
|
+
import { dirname, join, resolve } from 'path';
|
|
5
5
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
6
6
|
import { parse } from 'yaml';
|
|
7
7
|
import 'crypto';
|
|
@@ -127,9 +127,6 @@ var specStore = /* @__PURE__ */ new Map();
|
|
|
127
127
|
function getSpecStore() {
|
|
128
128
|
return specStore;
|
|
129
129
|
}
|
|
130
|
-
function resetSpecStore() {
|
|
131
|
-
specStore.clear();
|
|
132
|
-
}
|
|
133
130
|
var modelRegistry = /* @__PURE__ */ new Map();
|
|
134
131
|
function registerModel(model) {
|
|
135
132
|
modelRegistry.set(model.id, model);
|
|
@@ -137,103 +134,30 @@ function registerModel(model) {
|
|
|
137
134
|
function getAllModels() {
|
|
138
135
|
return Array.from(modelRegistry.values());
|
|
139
136
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (!value || typeof value !== "object") return false;
|
|
145
|
-
const obj = value;
|
|
146
|
-
return typeof obj.id === "string" && typeof obj.name === "string" && typeof obj.idPrefix === "string" && obj.schema !== void 0 && typeof obj.validate === "function" && typeof obj.lint === "function";
|
|
147
|
-
}
|
|
148
|
-
function registerExportedModelInstances(module) {
|
|
149
|
-
for (const [, exportValue] of Object.entries(module)) {
|
|
150
|
-
if (!exportValue || typeof exportValue !== "object") continue;
|
|
151
|
-
if (isModelInstance(exportValue)) {
|
|
152
|
-
registerModel(exportValue);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
async function loadModelsFromDirectory(dirPath, options = {}) {
|
|
157
|
-
const loadedFiles = [];
|
|
158
|
-
if (!existsSync(dirPath)) {
|
|
159
|
-
return loadedFiles;
|
|
160
|
-
}
|
|
161
|
-
const entries = readdirSync(dirPath);
|
|
162
|
-
for (const entry of entries) {
|
|
163
|
-
const fullPath = join(dirPath, entry);
|
|
164
|
-
const stat = statSync(fullPath);
|
|
165
|
-
if (stat.isDirectory() && options.recursive) {
|
|
166
|
-
const subFiles = await loadModelsFromDirectory(fullPath, options);
|
|
167
|
-
loadedFiles.push(...subFiles);
|
|
168
|
-
} else if (stat.isFile()) {
|
|
169
|
-
const ext = extname(entry);
|
|
170
|
-
if ([".ts", ".js", ".mts", ".mjs"].includes(ext) && !entry.endsWith(".d.ts")) {
|
|
171
|
-
if (basename(entry, ext) === "index") {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
const module = await import(fullPath);
|
|
176
|
-
loadedFiles.push(fullPath);
|
|
177
|
-
registerExportedModelInstances(module);
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error(`Failed to load from ${fullPath}:`, error);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return loadedFiles;
|
|
185
|
-
}
|
|
186
|
-
async function loadAllModels(config, cwd = process.cwd()) {
|
|
187
|
-
resetSpecStore();
|
|
188
|
-
clearModelRegistry();
|
|
189
|
-
const loadedFiles = [];
|
|
190
|
-
const errors = [];
|
|
191
|
-
if (config.models && Array.isArray(config.models)) {
|
|
192
|
-
for (const model of config.models) {
|
|
193
|
-
if (isModelInstance(model)) {
|
|
194
|
-
registerModel(model);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
const designDir = resolve(cwd, config.designDir || "design");
|
|
199
|
-
const modelDirs = [
|
|
200
|
-
{ path: join(designDir, "_models") },
|
|
201
|
-
{ path: join(designDir, "requirements") },
|
|
202
|
-
{ path: join(designDir, "usecases") },
|
|
203
|
-
{ path: join(designDir, "architecture") },
|
|
204
|
-
{ path: join(designDir, "data-model") },
|
|
205
|
-
{ path: join(designDir, "screens") },
|
|
206
|
-
{ path: join(designDir, "flows") },
|
|
207
|
-
{ path: join(designDir, "glossary") },
|
|
208
|
-
{ path: designDir }
|
|
209
|
-
];
|
|
210
|
-
for (const { path: dirPath } of modelDirs) {
|
|
211
|
-
if (!existsSync(dirPath)) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
try {
|
|
215
|
-
const files = await loadModelsFromDirectory(dirPath, { recursive: true });
|
|
216
|
-
loadedFiles.push(...files);
|
|
217
|
-
} catch (error) {
|
|
218
|
-
errors.push({ file: dirPath, error });
|
|
137
|
+
function registerModelsFromConfig(models) {
|
|
138
|
+
for (const model of models) {
|
|
139
|
+
if (model && typeof model === "object" && "id" in model && "schema" in model) {
|
|
140
|
+
registerModel(model);
|
|
219
141
|
}
|
|
220
142
|
}
|
|
221
|
-
const store = getSpecStore();
|
|
222
|
-
const registry = {};
|
|
223
|
-
for (const [modelId, specMap] of store) {
|
|
224
|
-
registry[modelId] = specMap;
|
|
225
|
-
}
|
|
226
|
-
return {
|
|
227
|
-
registry,
|
|
228
|
-
loadedFiles,
|
|
229
|
-
errors
|
|
230
|
-
};
|
|
231
143
|
}
|
|
232
|
-
function
|
|
233
|
-
const map =
|
|
144
|
+
function getSpecs(modelId) {
|
|
145
|
+
const map = specStore.get(modelId);
|
|
234
146
|
if (!map) return [];
|
|
235
147
|
return Array.from(map.values());
|
|
236
148
|
}
|
|
149
|
+
function findModelTypeBySpecId(specId) {
|
|
150
|
+
for (const [modelId, map] of specStore) {
|
|
151
|
+
if (map.has(specId)) return modelId;
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function registerSpecsFromConfig(specs) {
|
|
156
|
+
if (!specs) return;
|
|
157
|
+
for (const entry of specs) {
|
|
158
|
+
entry.model.register(entry.data);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
237
161
|
|
|
238
162
|
// src/cli/build.ts
|
|
239
163
|
async function buildCommand(options) {
|
|
@@ -247,14 +171,8 @@ async function buildCommand(options) {
|
|
|
247
171
|
console.log("");
|
|
248
172
|
try {
|
|
249
173
|
console.log(chalk4.blue(" Loading models..."));
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
console.log(chalk4.yellow(` \u26A0 ${loadErrors.length} file(s) failed to load`));
|
|
253
|
-
for (const { file, error } of loadErrors) {
|
|
254
|
-
console.log(chalk4.yellow(` - ${relative(cwd, file)}: ${error}`));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
console.log(chalk4.gray(` Loaded: ${loadedFiles.length} files`));
|
|
174
|
+
registerModelsFromConfig(config.models || []);
|
|
175
|
+
registerSpecsFromConfig(config.specs);
|
|
258
176
|
console.log("");
|
|
259
177
|
ensureDir(join(cwd, config.docsDir));
|
|
260
178
|
ensureDir(join(cwd, config.specsDir));
|
|
@@ -273,7 +191,7 @@ async function buildCommand(options) {
|
|
|
273
191
|
}
|
|
274
192
|
continue;
|
|
275
193
|
}
|
|
276
|
-
const specs =
|
|
194
|
+
const specs = getSpecs(model.id);
|
|
277
195
|
if (specs.length === 0) {
|
|
278
196
|
if (options.verbose) {
|
|
279
197
|
console.log(chalk4.gray(` ${model.name}: no specs found`));
|
|
@@ -323,7 +241,6 @@ async function buildCommand(options) {
|
|
|
323
241
|
async function lintCommand(options) {
|
|
324
242
|
console.log(chalk4.blue("speckeeper lint"));
|
|
325
243
|
console.log("");
|
|
326
|
-
const cwd = process.cwd();
|
|
327
244
|
const config = await loadConfig(options.config);
|
|
328
245
|
console.log(chalk4.gray(` Design: ${config.designDir || "design"}/`));
|
|
329
246
|
if (options.phase) {
|
|
@@ -332,17 +249,13 @@ async function lintCommand(options) {
|
|
|
332
249
|
console.log("");
|
|
333
250
|
try {
|
|
334
251
|
console.log(chalk4.blue(" Loading models..."));
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
console.log(chalk4.yellow(` - ${relative(cwd, file)}: ${error}`));
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
console.log(chalk4.gray(` Loaded: ${loadedFiles.length} files`));
|
|
252
|
+
registerModelsFromConfig(config.models || []);
|
|
253
|
+
registerSpecsFromConfig(config.specs);
|
|
254
|
+
const models = getAllModels();
|
|
255
|
+
console.log(chalk4.gray(` Loaded: ${models.length} models`));
|
|
343
256
|
console.log("");
|
|
344
257
|
console.log(chalk4.blue(" Running lint checks..."));
|
|
345
|
-
const result = runModelLint(
|
|
258
|
+
const result = runModelLint(options);
|
|
346
259
|
console.log("");
|
|
347
260
|
outputLintResults(result, options);
|
|
348
261
|
if (result.errors > 0) {
|
|
@@ -353,11 +266,11 @@ async function lintCommand(options) {
|
|
|
353
266
|
process.exit(1);
|
|
354
267
|
}
|
|
355
268
|
}
|
|
356
|
-
function runModelLint(
|
|
269
|
+
function runModelLint(options) {
|
|
357
270
|
const issues = [];
|
|
358
271
|
const models = getAllModels();
|
|
359
272
|
for (const model of models) {
|
|
360
|
-
const specs =
|
|
273
|
+
const specs = getSpecs(model.id);
|
|
361
274
|
if (specs.length === 0) continue;
|
|
362
275
|
const lintResults = model.lintAll(specs);
|
|
363
276
|
for (const result of lintResults) {
|
|
@@ -419,11 +332,12 @@ async function driftCommand(options) {
|
|
|
419
332
|
console.log("");
|
|
420
333
|
try {
|
|
421
334
|
console.log(chalk4.blue(" Loading models..."));
|
|
422
|
-
|
|
335
|
+
registerModelsFromConfig(config.models || []);
|
|
336
|
+
registerSpecsFromConfig(config.specs);
|
|
423
337
|
const results = [];
|
|
424
338
|
const models = getAllModels();
|
|
425
339
|
for (const model of models) {
|
|
426
|
-
const specs =
|
|
340
|
+
const specs = getSpecs(model.id);
|
|
427
341
|
if (specs.length === 0) continue;
|
|
428
342
|
for (const exporter of model.getExporters()) {
|
|
429
343
|
if (exporter.format !== "markdown") continue;
|
|
@@ -510,14 +424,15 @@ async function checkCommand(type, options) {
|
|
|
510
424
|
console.log("");
|
|
511
425
|
try {
|
|
512
426
|
console.log(chalk4.blue(" Loading models..."));
|
|
513
|
-
|
|
427
|
+
registerModelsFromConfig(config.models || []);
|
|
428
|
+
registerSpecsFromConfig(config.specs);
|
|
514
429
|
const results = [];
|
|
515
430
|
const models = getAllModels();
|
|
516
431
|
if (options.verbose) {
|
|
517
432
|
console.log(chalk4.gray(` Registered models: ${models.map((m) => m.id).join(", ")}`));
|
|
518
433
|
}
|
|
519
434
|
for (const model of models) {
|
|
520
|
-
const specs =
|
|
435
|
+
const specs = getSpecs(model.id);
|
|
521
436
|
if (specs.length === 0) continue;
|
|
522
437
|
for (const spec of specs) {
|
|
523
438
|
const sourcePath = model.getExternalSourcePath(spec);
|
|
@@ -553,7 +468,7 @@ async function checkCommand(type, options) {
|
|
|
553
468
|
if (options.coverage) {
|
|
554
469
|
console.log("");
|
|
555
470
|
console.log(chalk4.blue(" Coverage checks..."));
|
|
556
|
-
const coverageResults = runAllCoverageChecks(
|
|
471
|
+
const coverageResults = runAllCoverageChecks();
|
|
557
472
|
if (coverageResults.length > 0) {
|
|
558
473
|
outputAllCoverageResults(coverageResults);
|
|
559
474
|
for (const check of coverageResults) {
|
|
@@ -623,13 +538,14 @@ function outputCheckResults(results) {
|
|
|
623
538
|
console.log("");
|
|
624
539
|
console.log(chalk4.gray(` Summary: ${totalErrors} errors, ${totalWarnings} warnings`));
|
|
625
540
|
}
|
|
626
|
-
function runAllCoverageChecks(
|
|
541
|
+
function runAllCoverageChecks() {
|
|
627
542
|
const models = getAllModels();
|
|
628
543
|
const results = [];
|
|
544
|
+
const registry = Object.fromEntries(getSpecStore());
|
|
629
545
|
for (const model of models) {
|
|
630
546
|
const checker = model.getCoverageChecker();
|
|
631
547
|
if (checker) {
|
|
632
|
-
const specs =
|
|
548
|
+
const specs = getSpecs(model.id);
|
|
633
549
|
const result = model.checkCoverage(specs, registry);
|
|
634
550
|
if (result) {
|
|
635
551
|
results.push({
|
|
@@ -740,7 +656,6 @@ async function impactCommand(targetId, options) {
|
|
|
740
656
|
console.log(chalk4.gray(" Usage: speckeeper impact <id>"));
|
|
741
657
|
process.exit(1);
|
|
742
658
|
}
|
|
743
|
-
const cwd = process.cwd();
|
|
744
659
|
const config = await loadConfig(options.config);
|
|
745
660
|
const maxDepth = options.depth ? parseInt(options.depth, 10) : 3;
|
|
746
661
|
console.log(chalk4.gray(` Target: ${targetId}`));
|
|
@@ -748,35 +663,28 @@ async function impactCommand(targetId, options) {
|
|
|
748
663
|
console.log("");
|
|
749
664
|
try {
|
|
750
665
|
console.log(chalk4.blue(" Loading models..."));
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
666
|
+
registerModelsFromConfig(config.models || []);
|
|
667
|
+
registerSpecsFromConfig(config.specs);
|
|
668
|
+
const store = getSpecStore();
|
|
669
|
+
const targetType = findModelTypeBySpecId(targetId);
|
|
670
|
+
if (!targetType) {
|
|
754
671
|
console.error(chalk4.red(` Error: Target '${targetId}' not found`));
|
|
755
672
|
process.exit(1);
|
|
756
673
|
}
|
|
757
674
|
console.log(chalk4.blue(" Analyzing impact..."));
|
|
758
|
-
const result = analyzeImpact(
|
|
675
|
+
const result = analyzeImpact(store, targetId, targetType, maxDepth);
|
|
759
676
|
outputImpactResults(result, options);
|
|
760
677
|
} catch (error) {
|
|
761
678
|
console.error(chalk4.red("Impact analysis failed:"), error);
|
|
762
679
|
process.exit(1);
|
|
763
680
|
}
|
|
764
681
|
}
|
|
765
|
-
function
|
|
766
|
-
for (const [type, map] of Object.entries(registry)) {
|
|
767
|
-
if (map instanceof Map && map.has(id)) {
|
|
768
|
-
return { type, data: map.get(id) };
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
return null;
|
|
772
|
-
}
|
|
773
|
-
function analyzeImpact(registry, targetId, targetType, maxDepth) {
|
|
682
|
+
function analyzeImpact(store, targetId, targetType, maxDepth) {
|
|
774
683
|
const impactedNodes = [];
|
|
775
684
|
const visited = /* @__PURE__ */ new Set([targetId]);
|
|
776
685
|
function findReferences(id, depth) {
|
|
777
686
|
if (depth > maxDepth) return;
|
|
778
|
-
for (const [type, map] of
|
|
779
|
-
if (!(map instanceof Map)) continue;
|
|
687
|
+
for (const [type, map] of store) {
|
|
780
688
|
for (const [itemId, item] of map) {
|
|
781
689
|
if (visited.has(itemId)) continue;
|
|
782
690
|
const itemStr = JSON.stringify(item);
|
|
@@ -875,14 +783,14 @@ async function runInit(options = {}) {
|
|
|
875
783
|
console.log(" Next steps:");
|
|
876
784
|
if (packageJsonCreated) {
|
|
877
785
|
console.log(chalk4.gray(" 1. Run `npm install` to install dependencies"));
|
|
878
|
-
console.log(chalk4.gray(" 2.
|
|
879
|
-
console.log(chalk4.gray(" 3.
|
|
786
|
+
console.log(chalk4.gray(" 2. Add spec data in design/*.ts using defineSpecs()"));
|
|
787
|
+
console.log(chalk4.gray(" 3. Import new spec files in design/index.ts"));
|
|
880
788
|
console.log(chalk4.gray(" 4. Run `npx speckeeper lint` to validate"));
|
|
881
789
|
} else {
|
|
882
790
|
console.log(chalk4.gray(" 1. Add speckeeper and zod to your package.json dependencies"));
|
|
883
791
|
console.log(chalk4.gray(' 2. Ensure "type": "module" is set in package.json'));
|
|
884
|
-
console.log(chalk4.gray(" 3.
|
|
885
|
-
console.log(chalk4.gray(" 4.
|
|
792
|
+
console.log(chalk4.gray(" 3. Add spec data in design/*.ts using defineSpecs()"));
|
|
793
|
+
console.log(chalk4.gray(" 4. Import new spec files in design/index.ts"));
|
|
886
794
|
console.log(chalk4.gray(" 5. Run `npx speckeeper lint` to validate"));
|
|
887
795
|
}
|
|
888
796
|
}
|
|
@@ -2374,14 +2282,17 @@ function generateSpecDataFile(node) {
|
|
|
2374
2282
|
*
|
|
2375
2283
|
* Generated by speckeeper scaffold \u2014 add your specs below.
|
|
2376
2284
|
*/
|
|
2285
|
+
import { defineSpecs } from 'speckeeper';
|
|
2377
2286
|
import type { ${typeName} } from './_models/${fileName}';
|
|
2378
2287
|
import { ${className} } from './_models/${fileName}';
|
|
2379
2288
|
|
|
2380
|
-
|
|
2289
|
+
const ${varName}: ${typeName}[] = [
|
|
2381
2290
|
// { id: '${templateInfo.defaultIdPrefix}-001', name: '...', description: '...' },
|
|
2382
2291
|
];
|
|
2383
2292
|
|
|
2384
|
-
|
|
2293
|
+
export default defineSpecs(
|
|
2294
|
+
[${className}.instance, ${varName}],
|
|
2295
|
+
);
|
|
2385
2296
|
`;
|
|
2386
2297
|
return {
|
|
2387
2298
|
relativePath: `${fileName}.ts`,
|
|
@@ -2391,6 +2302,34 @@ ${className}.instance.register(${varName});
|
|
|
2391
2302
|
function toCamelCase3(pascalCase) {
|
|
2392
2303
|
return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
|
|
2393
2304
|
}
|
|
2305
|
+
function generateDesignIndex(speckeeperNodes) {
|
|
2306
|
+
const generated = /* @__PURE__ */ new Set();
|
|
2307
|
+
const specFiles = [];
|
|
2308
|
+
for (const node of speckeeperNodes) {
|
|
2309
|
+
const templateInfo = resolveModelTemplate(node.id);
|
|
2310
|
+
const key = templateInfo.templateName === "base" ? `base:${node.id}` : templateInfo.templateName;
|
|
2311
|
+
if (generated.has(key)) continue;
|
|
2312
|
+
generated.add(key);
|
|
2313
|
+
specFiles.push({
|
|
2314
|
+
varName: toCamelCase3(templateInfo.modelName),
|
|
2315
|
+
fileName: templateInfo.fileName
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
const imports = specFiles.map((f) => `import ${f.varName} from './${f.fileName}';`).join("\n");
|
|
2319
|
+
const args = specFiles.map((f) => f.varName).join(", ");
|
|
2320
|
+
const content = `/**
|
|
2321
|
+
* Design entry point \u2014 generated by speckeeper scaffold
|
|
2322
|
+
*/
|
|
2323
|
+
import { mergeSpecs } from 'speckeeper';
|
|
2324
|
+
${imports}
|
|
2325
|
+
|
|
2326
|
+
export default mergeSpecs(${args});
|
|
2327
|
+
`;
|
|
2328
|
+
return {
|
|
2329
|
+
relativePath: "index.ts",
|
|
2330
|
+
content
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2394
2333
|
function generateAllModelFiles(speckeeperNodes, resolvedEdges) {
|
|
2395
2334
|
const files = [];
|
|
2396
2335
|
const generated = /* @__PURE__ */ new Set();
|
|
@@ -2404,6 +2343,7 @@ function generateAllModelFiles(speckeeperNodes, resolvedEdges) {
|
|
|
2404
2343
|
files.push(generateModelFile(node));
|
|
2405
2344
|
files.push(generateSpecDataFile(node));
|
|
2406
2345
|
}
|
|
2346
|
+
files.push(generateDesignIndex(speckeeperNodes));
|
|
2407
2347
|
return files;
|
|
2408
2348
|
}
|
|
2409
2349
|
|