@modularcloud/cspec 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/dist/cli.d.ts +6 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +327 -4
- package/dist/editor/assets/index-BV-lxU2y.css +1 -0
- package/dist/editor/assets/index-DL-RbHCV.js +80 -0
- package/dist/editor/index.html +14 -0
- package/package.json +16 -14
- package/README.md +0 -226
- package/dist/config.d.ts +0 -8
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -47
- package/dist/errors.d.ts +0 -10
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -20
- package/dist/generate.d.ts +0 -11
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js +0 -57
- package/dist/glob.d.ts +0 -3
- package/dist/glob.d.ts.map +0 -1
- package/dist/glob.js +0 -49
- package/dist/ids.d.ts +0 -6
- package/dist/ids.d.ts.map +0 -1
- package/dist/ids.js +0 -38
- package/dist/parser.d.ts +0 -55
- package/dist/parser.d.ts.map +0 -1
- package/dist/parser.js +0 -210
- package/dist/render.d.ts +0 -9
- package/dist/render.d.ts.map +0 -1
- package/dist/render.js +0 -91
- package/dist/runtime.d.ts.map +0 -1
- package/dist/workspace.d.ts +0 -13
- package/dist/workspace.d.ts.map +0 -1
- package/dist/workspace.js +0 -31
package/dist/parser.js
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { CspecError } from "./errors.js";
|
|
3
|
-
import { assertStructuralId, idToSegments } from "./ids.js";
|
|
4
|
-
const TAG_RE = /<\/?(S|Spec)\b[^>]*>/g;
|
|
5
|
-
const IMPORT_RE = /^\s*import\s+([^'";]+?)\s+from\s+["']([^"']+)["']\s*;?\s*$/gm;
|
|
6
|
-
const EXTERNAL_REF_RE = /\{([A-Za-z_$][\w$]*(?:(?:\.[A-Za-z_$][\w$]*)|(?:\["(?:[^"\\]|\\.)*"\])|(?:\['(?:[^'\\]|\\.)*'\]))*)\.\$\}/g;
|
|
7
|
-
const LOCAL_REF_RE = /\{\s*ref\s*\(([^)]*)\)\s*\.\$\s*\}/g;
|
|
8
|
-
export function parseSource(file, source) {
|
|
9
|
-
const imports = parseImports(source);
|
|
10
|
-
const root = makeBlock("", null, 0, source.length, 0, source.length, "root");
|
|
11
|
-
const stack = [root];
|
|
12
|
-
const blocks = new Map();
|
|
13
|
-
const rangesToRemove = imports.map((item) => expandStandaloneRange(source, item.start, item.end));
|
|
14
|
-
for (const match of source.matchAll(TAG_RE)) {
|
|
15
|
-
const raw = match[0];
|
|
16
|
-
const index = match.index ?? 0;
|
|
17
|
-
const closing = raw.startsWith("</");
|
|
18
|
-
if (closing) {
|
|
19
|
-
if (stack.length === 1) {
|
|
20
|
-
throw new CspecError(`Unexpected closing ${raw}.`, { file, line: lineAt(source, index) });
|
|
21
|
-
}
|
|
22
|
-
const block = stack.pop();
|
|
23
|
-
if (!block)
|
|
24
|
-
throw new CspecError(`Unexpected closing ${raw}.`, { file, line: lineAt(source, index) });
|
|
25
|
-
const expected = `</${block.tag}>`;
|
|
26
|
-
if (raw !== expected) {
|
|
27
|
-
throw new CspecError(`Mismatched block tag. Expected ${expected}, received ${raw}.`, {
|
|
28
|
-
file,
|
|
29
|
-
line: lineAt(source, index)
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
block.closeStart = index;
|
|
33
|
-
block.closeEnd = index + raw.length;
|
|
34
|
-
block.contentEnd = index;
|
|
35
|
-
rangesToRemove.push(expandStandaloneRange(source, block.openStart, block.openEnd), expandStandaloneRange(source, block.closeStart, block.closeEnd));
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
const tag = match[1];
|
|
39
|
-
const parent = stack[stack.length - 1];
|
|
40
|
-
if (!parent)
|
|
41
|
-
throw new CspecError(`Unexpected <${tag}> block.`, { file, line: lineAt(source, index) });
|
|
42
|
-
const id = parseIdAttribute(raw);
|
|
43
|
-
if (!id) {
|
|
44
|
-
throw new CspecError(`Non-root <${tag}> block is missing required id.`, { file, line: lineAt(source, index) });
|
|
45
|
-
}
|
|
46
|
-
assertStructuralId(id, parent.id, { file, line: lineAt(source, index) });
|
|
47
|
-
if (blocks.has(id)) {
|
|
48
|
-
throw new CspecError(`Duplicate block id "${id}".`, { file, line: lineAt(source, index) });
|
|
49
|
-
}
|
|
50
|
-
const block = makeBlock(id, parent.id || null, index, index + raw.length, index + raw.length, index + raw.length, tag);
|
|
51
|
-
block.segments = idToSegments(id);
|
|
52
|
-
block.line = lineAt(source, index);
|
|
53
|
-
parent.children.push(block);
|
|
54
|
-
blocks.set(id, block);
|
|
55
|
-
stack.push(block);
|
|
56
|
-
}
|
|
57
|
-
if (stack.length > 1) {
|
|
58
|
-
const block = stack[stack.length - 1];
|
|
59
|
-
throw new CspecError(`Unclosed <${block.tag}> block "${block.id}".`, { file, line: block.line });
|
|
60
|
-
}
|
|
61
|
-
root.children = root.children.sort((a, b) => a.openStart - b.openStart);
|
|
62
|
-
return {
|
|
63
|
-
file,
|
|
64
|
-
source,
|
|
65
|
-
root,
|
|
66
|
-
blocks,
|
|
67
|
-
imports,
|
|
68
|
-
rangesToRemove,
|
|
69
|
-
references: parseReferences(source, imports)
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function makeBlock(id, parentId, openStart, openEnd, contentStart, contentEnd, tag) {
|
|
73
|
-
return {
|
|
74
|
-
id,
|
|
75
|
-
parentId,
|
|
76
|
-
openStart,
|
|
77
|
-
openEnd,
|
|
78
|
-
contentStart,
|
|
79
|
-
contentEnd,
|
|
80
|
-
closeStart: null,
|
|
81
|
-
closeEnd: null,
|
|
82
|
-
tag,
|
|
83
|
-
children: [],
|
|
84
|
-
segments: [],
|
|
85
|
-
line: 1,
|
|
86
|
-
compiled: null
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function parseIdAttribute(raw) {
|
|
90
|
-
const doubleMatch = raw.match(/\bid\s*=\s*"([^"]*)"/);
|
|
91
|
-
if (doubleMatch)
|
|
92
|
-
return doubleMatch[1];
|
|
93
|
-
const singleMatch = raw.match(/\bid\s*=\s*'([^']*)'/);
|
|
94
|
-
if (singleMatch)
|
|
95
|
-
return singleMatch[1];
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
function expandStandaloneRange(source, start, end) {
|
|
99
|
-
const lineStart = source.lastIndexOf("\n", start - 1) + 1;
|
|
100
|
-
const nextNewline = source.indexOf("\n", end);
|
|
101
|
-
const lineEnd = nextNewline === -1 ? source.length : nextNewline + 1;
|
|
102
|
-
const before = source.slice(lineStart, start);
|
|
103
|
-
const after = source.slice(end, nextNewline === -1 ? source.length : nextNewline);
|
|
104
|
-
if (/^[ \t]*$/.test(before) && /^[ \t]*$/.test(after)) {
|
|
105
|
-
return [lineStart, lineEnd];
|
|
106
|
-
}
|
|
107
|
-
return [start, end];
|
|
108
|
-
}
|
|
109
|
-
function parseImports(source) {
|
|
110
|
-
const imports = [];
|
|
111
|
-
for (const match of source.matchAll(IMPORT_RE)) {
|
|
112
|
-
const specifier = match[2] ?? "";
|
|
113
|
-
const clause = (match[1] ?? "").trim();
|
|
114
|
-
const start = match.index ?? 0;
|
|
115
|
-
const end = start + match[0].length;
|
|
116
|
-
if (specifier === "cspec") {
|
|
117
|
-
imports.push({ kind: "cspec", clause, specifier, start, end });
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
if (specifier.endsWith(".cspec")) {
|
|
121
|
-
const local = clause.match(/^([A-Za-z_$][\w$]*)$/)?.[1];
|
|
122
|
-
if (local)
|
|
123
|
-
imports.push({ kind: "module", clause, local, specifier, start, end });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return imports;
|
|
127
|
-
}
|
|
128
|
-
function parseReferences(source, imports) {
|
|
129
|
-
const importedNames = new Set(imports.filter((item) => item.kind === "module").map((item) => item.local));
|
|
130
|
-
const references = [];
|
|
131
|
-
for (const match of source.matchAll(LOCAL_REF_RE)) {
|
|
132
|
-
const arg = (match[1] ?? "").trim();
|
|
133
|
-
const literal = parseStringLiteral(arg);
|
|
134
|
-
references.push({
|
|
135
|
-
kind: "local",
|
|
136
|
-
raw: match[0],
|
|
137
|
-
start: match.index ?? 0,
|
|
138
|
-
end: (match.index ?? 0) + match[0].length,
|
|
139
|
-
id: literal,
|
|
140
|
-
validStatic: literal !== null,
|
|
141
|
-
expression: arg
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
for (const match of source.matchAll(EXTERNAL_REF_RE)) {
|
|
145
|
-
const expression = match[1] ?? "";
|
|
146
|
-
const [name] = expression.split(/[.\[]/, 1);
|
|
147
|
-
if (!importedNames.has(name))
|
|
148
|
-
continue;
|
|
149
|
-
references.push({
|
|
150
|
-
kind: "external",
|
|
151
|
-
raw: match[0],
|
|
152
|
-
start: match.index ?? 0,
|
|
153
|
-
end: (match.index ?? 0) + match[0].length,
|
|
154
|
-
local: name,
|
|
155
|
-
path: memberExpressionToPath(expression.slice(name.length)),
|
|
156
|
-
expression
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
return references.sort((a, b) => a.start - b.start);
|
|
160
|
-
}
|
|
161
|
-
function parseStringLiteral(value) {
|
|
162
|
-
if (!/^(['"])(?:\\.|(?!\1).)*\1$/s.test(value))
|
|
163
|
-
return null;
|
|
164
|
-
try {
|
|
165
|
-
return JSON.parse(value[0] === "'" ? `"${value.slice(1, -1).replace(/"/g, '\\"')}"` : value);
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
return value.slice(1, -1);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
function memberExpressionToPath(expression) {
|
|
172
|
-
const segments = [];
|
|
173
|
-
let rest = expression;
|
|
174
|
-
while (rest) {
|
|
175
|
-
const dot = rest.match(/^\.(?!\$)([A-Za-z_$][\w$]*)/);
|
|
176
|
-
if (dot) {
|
|
177
|
-
segments.push(dot[1]);
|
|
178
|
-
rest = rest.slice(dot[0].length);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
const bracket = rest.match(/^\[(["'])((?:\\.|(?!\1).)*)\]/s);
|
|
182
|
-
if (bracket) {
|
|
183
|
-
segments.push(unescapeQuoted(bracket[2]));
|
|
184
|
-
rest = rest.slice(bracket[0].length);
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
return segments.join(".");
|
|
190
|
-
}
|
|
191
|
-
function unescapeQuoted(value) {
|
|
192
|
-
try {
|
|
193
|
-
return JSON.parse(`"${value.replace(/"/g, '\\"')}"`);
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
return value.replace(/\\(['"\\])/g, "$1");
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
export function resolveImportPath(fromFile, specifier) {
|
|
200
|
-
const base = path.resolve(path.dirname(fromFile), specifier.replace(/\.cspec$/, ".mdx"));
|
|
201
|
-
return base;
|
|
202
|
-
}
|
|
203
|
-
export function lineAt(source, index) {
|
|
204
|
-
let line = 1;
|
|
205
|
-
for (let i = 0; i < index; i += 1) {
|
|
206
|
-
if (source.charCodeAt(i) === 10)
|
|
207
|
-
line += 1;
|
|
208
|
-
}
|
|
209
|
-
return line;
|
|
210
|
-
}
|
package/dist/render.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { CspecBlock, CspecDocument } from "./parser.js";
|
|
2
|
-
interface CompileContext {
|
|
3
|
-
byFile: Map<string, CspecDocument>;
|
|
4
|
-
stack: string[];
|
|
5
|
-
}
|
|
6
|
-
export declare function compileWorkspace(documents: CspecDocument[]): void;
|
|
7
|
-
export declare function compileBlock(doc: CspecDocument, block: CspecBlock, context: CompileContext): string;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=render.d.ts.map
|
package/dist/render.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAkB,MAAM,aAAa,CAAC;AAE7E,UAAU,cAAc;IACtB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAID,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,IAAI,CAOjE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAcnG"}
|
package/dist/render.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { CspecError } from "./errors.js";
|
|
2
|
-
import { lineAt, resolveImportPath } from "./parser.js";
|
|
3
|
-
export function compileWorkspace(documents) {
|
|
4
|
-
const byFile = new Map(documents.map((doc) => [doc.file, doc]));
|
|
5
|
-
const context = { byFile, stack: [] };
|
|
6
|
-
for (const doc of documents) {
|
|
7
|
-
compileBlock(doc, doc.root, context);
|
|
8
|
-
for (const block of doc.blocks.values())
|
|
9
|
-
compileBlock(doc, block, context);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
export function compileBlock(doc, block, context) {
|
|
13
|
-
const key = `${doc.file}#${block.id || "$"}`;
|
|
14
|
-
if (block.compiled !== null)
|
|
15
|
-
return block.compiled;
|
|
16
|
-
const cycleAt = context.stack.indexOf(key);
|
|
17
|
-
if (cycleAt !== -1) {
|
|
18
|
-
const cycle = context.stack.slice(cycleAt).concat(key).map((item) => item.split("#")[1]).join(" -> ");
|
|
19
|
-
throw new CspecError(`Embedding cycle detected:\n${cycle}`, { file: doc.file, line: block.line });
|
|
20
|
-
}
|
|
21
|
-
context.stack.push(key);
|
|
22
|
-
const start = block.id ? block.contentStart : 0;
|
|
23
|
-
const end = block.id ? block.contentEnd : doc.source.length;
|
|
24
|
-
block.compiled = renderRange(doc, start, end, context);
|
|
25
|
-
context.stack.pop();
|
|
26
|
-
return block.compiled;
|
|
27
|
-
}
|
|
28
|
-
function renderRange(doc, start, end, context) {
|
|
29
|
-
const removals = doc.rangesToRemove
|
|
30
|
-
.filter(([a, b]) => b > start && a < end)
|
|
31
|
-
.map(([a, b]) => [Math.max(a, start), Math.min(b, end), ""]);
|
|
32
|
-
const replacements = doc.references
|
|
33
|
-
.filter((ref) => ref.start >= start && ref.end <= end)
|
|
34
|
-
.map((ref) => [ref.start, ref.end, resolveReference(doc, ref, context)]);
|
|
35
|
-
const edits = removals.concat(replacements).sort((a, b) => a[0] - b[0] || b[1] - a[1]);
|
|
36
|
-
let out = "";
|
|
37
|
-
let cursor = start;
|
|
38
|
-
for (const [a, b, value] of edits) {
|
|
39
|
-
if (a < cursor)
|
|
40
|
-
continue;
|
|
41
|
-
out += doc.source.slice(cursor, a);
|
|
42
|
-
out += value;
|
|
43
|
-
cursor = b;
|
|
44
|
-
}
|
|
45
|
-
out += doc.source.slice(cursor, end);
|
|
46
|
-
return trimOuterBlankLines(out);
|
|
47
|
-
}
|
|
48
|
-
function resolveReference(doc, ref, context) {
|
|
49
|
-
if (ref.kind === "local") {
|
|
50
|
-
if (!ref.validStatic) {
|
|
51
|
-
throw new CspecError("ref(...) requires a static string literal.", {
|
|
52
|
-
file: doc.file,
|
|
53
|
-
line: lineAt(doc.source, ref.start)
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
const target = ref.id ? doc.blocks.get(ref.id) : undefined;
|
|
57
|
-
if (!target) {
|
|
58
|
-
throw new CspecError(`Unknown local block reference "${ref.id}".`, {
|
|
59
|
-
file: doc.file,
|
|
60
|
-
line: lineAt(doc.source, ref.start)
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
return compileBlock(doc, target, context);
|
|
64
|
-
}
|
|
65
|
-
const imported = doc.imports.find((item) => item.kind === "module" && item.local === ref.local);
|
|
66
|
-
if (!imported) {
|
|
67
|
-
throw new CspecError(`Unresolved import for ${ref.local}.`, {
|
|
68
|
-
file: doc.file,
|
|
69
|
-
line: lineAt(doc.source, ref.start)
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
const targetFile = resolveImportPath(doc.file, imported.specifier);
|
|
73
|
-
const targetDoc = context.byFile.get(targetFile);
|
|
74
|
-
if (!targetDoc) {
|
|
75
|
-
throw new CspecError(`Unresolved import ${imported.specifier}.`, {
|
|
76
|
-
file: doc.file,
|
|
77
|
-
line: lineAt(doc.source, ref.start)
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
const target = ref.path ? targetDoc.blocks.get(ref.path) : targetDoc.root;
|
|
81
|
-
if (!target) {
|
|
82
|
-
throw new CspecError(`Unknown block reference ${ref.expression}.`, {
|
|
83
|
-
file: doc.file,
|
|
84
|
-
line: lineAt(doc.source, ref.start)
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return compileBlock(targetDoc, target, context);
|
|
88
|
-
}
|
|
89
|
-
function trimOuterBlankLines(value) {
|
|
90
|
-
return value.replace(/^\s*\n/, "").replace(/\n\s*$/, "");
|
|
91
|
-
}
|
package/dist/runtime.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,CAAC,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,OAAO,CAEjD;AAED,eAAO,MAAM,IAAI,UAAI,CAAC;AAEtB,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;CACX;AAED,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAExC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAE5C"}
|
package/dist/workspace.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { CspecConfig } from "./config.js";
|
|
2
|
-
import type { CspecDocument } from "./parser.js";
|
|
3
|
-
import type { GeneratedFile } from "./generate.js";
|
|
4
|
-
export interface Workspace {
|
|
5
|
-
cwd: string;
|
|
6
|
-
config: CspecConfig;
|
|
7
|
-
documents: CspecDocument[];
|
|
8
|
-
}
|
|
9
|
-
export declare function loadWorkspace(cwd: string): Promise<Workspace>;
|
|
10
|
-
export declare function expectedOutputs(workspace: Workspace): GeneratedFile[];
|
|
11
|
-
export declare function writeOutputs(workspace: Workspace): void;
|
|
12
|
-
export declare function assertFresh(workspace: Workspace): void;
|
|
13
|
-
//# sourceMappingURL=workspace.d.ts.map
|
package/dist/workspace.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAMnE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,aAAa,EAAE,CAIrE;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAIvD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAOtD"}
|
package/dist/workspace.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { loadConfig } from "./config.js";
|
|
4
|
-
import { discoverFiles } from "./glob.js";
|
|
5
|
-
import { parseSource } from "./parser.js";
|
|
6
|
-
import { compileWorkspace } from "./render.js";
|
|
7
|
-
import { generatedFilesFor } from "./generate.js";
|
|
8
|
-
import { CspecError } from "./errors.js";
|
|
9
|
-
export async function loadWorkspace(cwd) {
|
|
10
|
-
const config = await loadConfig(cwd);
|
|
11
|
-
const files = discoverFiles(cwd, config.specs);
|
|
12
|
-
const documents = files.map((file) => parseSource(file, fs.readFileSync(file, "utf8")));
|
|
13
|
-
compileWorkspace(documents);
|
|
14
|
-
return { cwd, config, documents };
|
|
15
|
-
}
|
|
16
|
-
export function expectedOutputs(workspace) {
|
|
17
|
-
return workspace.documents.flatMap((doc) => generatedFilesFor(doc, { markdownEmit: workspace.config.markdown.emit }));
|
|
18
|
-
}
|
|
19
|
-
export function writeOutputs(workspace) {
|
|
20
|
-
for (const output of expectedOutputs(workspace)) {
|
|
21
|
-
fs.writeFileSync(output.file, output.content);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
export function assertFresh(workspace) {
|
|
25
|
-
for (const output of expectedOutputs(workspace)) {
|
|
26
|
-
const rel = path.relative(workspace.cwd, output.file);
|
|
27
|
-
if (!fs.existsSync(output.file) || fs.readFileSync(output.file, "utf8") !== output.content) {
|
|
28
|
-
throw new CspecError(`Generated file ${rel} is stale.\nRun cspec build.`, { file: output.file });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|