@intentius/chant 0.0.12 → 0.0.14
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/package.json +1 -1
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/getting-started.mdx +6 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/lint-rules.mdx +6 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/serialization.mdx +6 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/package.json +9 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/src/infra.ts +12 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/composites/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/post-synth/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +37 -42
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +37 -42
- package/src/cli/commands/build.ts +12 -7
- package/src/cli/commands/check-lexicon.ts +385 -0
- package/src/cli/commands/import.ts +6 -3
- package/src/cli/commands/init-lexicon.test.ts +22 -1
- package/src/cli/commands/init-lexicon.ts +194 -43
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/onboard.test.ts +295 -0
- package/src/cli/commands/onboard.ts +313 -0
- package/src/cli/handlers/dev.ts +26 -1
- package/src/cli/main.ts +5 -1
- package/src/codegen/docs.ts +11 -5
- package/src/codegen/generate-registry.ts +3 -2
- package/src/codegen/typecheck.ts +44 -2
- package/src/detectLexicon.test.ts +24 -0
- package/src/detectLexicon.ts +4 -2
- package/src/runtime.ts +4 -0
- package/src/serializer-walker.test.ts +8 -0
- package/src/toml.test.ts +388 -0
- package/src/toml.ts +606 -0
- /package/src/cli/commands/__fixtures__/init-lexicon-output/{examples/getting-started → src/actions}/.gitkeep +0 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
2
|
+
import { join, basename } from "path";
|
|
3
|
+
|
|
4
|
+
// ── Types ────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface CheckItem {
|
|
7
|
+
name: string;
|
|
8
|
+
tier: 1 | 2 | 3;
|
|
9
|
+
pass: boolean;
|
|
10
|
+
detail?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CheckResult {
|
|
14
|
+
items: CheckItem[];
|
|
15
|
+
tier1Pass: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
/** List .ts files in a directory, optionally excluding some basenames. */
|
|
21
|
+
function listTsFiles(dir: string, exclude: string[] = []): string[] {
|
|
22
|
+
if (!existsSync(dir)) return [];
|
|
23
|
+
return readdirSync(dir)
|
|
24
|
+
.filter((f) => f.endsWith(".ts") && !exclude.includes(f));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Recursively find files matching a predicate. */
|
|
28
|
+
function findFiles(dir: string, predicate: (name: string) => boolean): string[] {
|
|
29
|
+
if (!existsSync(dir)) return [];
|
|
30
|
+
const results: string[] = [];
|
|
31
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
results.push(...findFiles(join(dir, entry.name), predicate));
|
|
34
|
+
} else if (predicate(entry.name)) {
|
|
35
|
+
results.push(join(dir, entry.name));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Read a file's content, returning empty string if missing. */
|
|
42
|
+
function readOr(path: string): string {
|
|
43
|
+
try {
|
|
44
|
+
return readFileSync(path, "utf-8");
|
|
45
|
+
} catch {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Count subdirectories in a directory, ignoring .gitkeep-only dirs. */
|
|
51
|
+
function countSubdirs(dir: string): number {
|
|
52
|
+
if (!existsSync(dir)) return 0;
|
|
53
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
54
|
+
.filter((e) => e.isDirectory())
|
|
55
|
+
.filter((e) => {
|
|
56
|
+
const contents = readdirSync(join(dir, e.name));
|
|
57
|
+
// Ignore directories that only contain .gitkeep
|
|
58
|
+
return contents.length > 0 && !(contents.length === 1 && contents[0] === ".gitkeep");
|
|
59
|
+
})
|
|
60
|
+
.length;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Check runner ─────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Run all completeness checks against a lexicon directory.
|
|
67
|
+
*/
|
|
68
|
+
export function checkLexicon(dir: string): CheckResult {
|
|
69
|
+
const items: CheckItem[] = [];
|
|
70
|
+
|
|
71
|
+
// ── Tier 1: Required ───────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
items.push({
|
|
74
|
+
name: "src/plugin.ts exists",
|
|
75
|
+
tier: 1,
|
|
76
|
+
pass: existsSync(join(dir, "src/plugin.ts")),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
items.push({
|
|
80
|
+
name: "src/serializer.ts exists",
|
|
81
|
+
tier: 1,
|
|
82
|
+
pass: existsSync(join(dir, "src/serializer.ts")),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const ruleFiles = listTsFiles(join(dir, "src/lint/rules"), ["index.ts"]);
|
|
86
|
+
items.push({
|
|
87
|
+
name: "At least 1 lint rule in src/lint/rules/",
|
|
88
|
+
tier: 1,
|
|
89
|
+
pass: ruleFiles.length > 0,
|
|
90
|
+
detail: ruleFiles.length > 0 ? `${ruleFiles.length} rule(s)` : undefined,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const postSynthFiles = listTsFiles(join(dir, "src/lint/post-synth"), ["index.ts"])
|
|
94
|
+
.filter((f) => !f.endsWith("-helpers.ts") && f !== "helpers.ts");
|
|
95
|
+
items.push({
|
|
96
|
+
name: "At least 1 post-synth check",
|
|
97
|
+
tier: 1,
|
|
98
|
+
pass: postSynthFiles.length > 0,
|
|
99
|
+
detail: postSynthFiles.length > 0 ? `${postSynthFiles.length} check(s)` : undefined,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
items.push({
|
|
103
|
+
name: "src/lsp/completions.ts exists",
|
|
104
|
+
tier: 1,
|
|
105
|
+
pass: existsSync(join(dir, "src/lsp/completions.ts")),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
items.push({
|
|
109
|
+
name: "src/lsp/hover.ts exists",
|
|
110
|
+
tier: 1,
|
|
111
|
+
pass: existsSync(join(dir, "src/lsp/hover.ts")),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
items.push({
|
|
115
|
+
name: "dist/manifest.json exists",
|
|
116
|
+
tier: 1,
|
|
117
|
+
pass: existsSync(join(dir, "dist/manifest.json")),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const exampleCount = countSubdirs(join(dir, "examples"));
|
|
121
|
+
items.push({
|
|
122
|
+
name: "At least 1 example in examples/",
|
|
123
|
+
tier: 1,
|
|
124
|
+
pass: exampleCount > 0,
|
|
125
|
+
detail: exampleCount > 0 ? `${exampleCount} example(s)` : undefined,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const hasPluginTest = findFiles(join(dir, "src"), (n) => n === "plugin.test.ts").length > 0;
|
|
129
|
+
items.push({
|
|
130
|
+
name: "plugin.test.ts exists",
|
|
131
|
+
tier: 1,
|
|
132
|
+
pass: hasPluginTest,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const hasSerializerTest = findFiles(join(dir, "src"), (n) => n === "serializer.test.ts").length > 0;
|
|
136
|
+
items.push({
|
|
137
|
+
name: "serializer.test.ts exists",
|
|
138
|
+
tier: 1,
|
|
139
|
+
pass: hasSerializerTest,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const mdxFiles = findFiles(join(dir, "docs"), (n) => n.endsWith(".mdx"));
|
|
143
|
+
items.push({
|
|
144
|
+
name: "At least 1 .mdx doc page",
|
|
145
|
+
tier: 1,
|
|
146
|
+
pass: mdxFiles.length > 0,
|
|
147
|
+
detail: mdxFiles.length > 0 ? `${mdxFiles.length} page(s)` : undefined,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ── Tier 2: Recommended ────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
const pluginContent = readOr(join(dir, "src/plugin.ts"));
|
|
153
|
+
|
|
154
|
+
for (const method of ["mcpTools", "mcpResources", "skills", "detectTemplate", "initTemplates"] as const) {
|
|
155
|
+
// Check for uncommented method: line starts with optional whitespace, then the method name
|
|
156
|
+
// Exclude lines that start with // or * (comment blocks)
|
|
157
|
+
const lines = pluginContent.split("\n");
|
|
158
|
+
const hasUncommented = lines.some((line) => {
|
|
159
|
+
const trimmed = line.trim();
|
|
160
|
+
return trimmed.startsWith(`${method}(`) || trimmed.startsWith(`${method} (`);
|
|
161
|
+
});
|
|
162
|
+
items.push({
|
|
163
|
+
name: `plugin.ts has uncommented ${method}`,
|
|
164
|
+
tier: 2,
|
|
165
|
+
pass: hasUncommented,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const compositeFiles = listTsFiles(join(dir, "src/composites"), ["index.ts"]);
|
|
170
|
+
items.push({
|
|
171
|
+
name: "At least 1 composite in src/composites/",
|
|
172
|
+
tier: 2,
|
|
173
|
+
pass: compositeFiles.length > 0,
|
|
174
|
+
detail: compositeFiles.length > 0 ? `${compositeFiles.length} composite(s)` : undefined,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
items.push({
|
|
178
|
+
name: "At least 3 examples",
|
|
179
|
+
tier: 2,
|
|
180
|
+
pass: exampleCount >= 3,
|
|
181
|
+
detail: `${exampleCount} example(s)`,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
items.push({
|
|
185
|
+
name: "src/lsp/completions.test.ts exists",
|
|
186
|
+
tier: 2,
|
|
187
|
+
pass: existsSync(join(dir, "src/lsp/completions.test.ts")),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
items.push({
|
|
191
|
+
name: "src/lsp/hover.test.ts exists",
|
|
192
|
+
tier: 2,
|
|
193
|
+
pass: existsSync(join(dir, "src/lsp/hover.test.ts")),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const coverageContent = readOr(join(dir, "src/coverage.ts"));
|
|
197
|
+
items.push({
|
|
198
|
+
name: "coverage.ts is implemented",
|
|
199
|
+
tier: 2,
|
|
200
|
+
pass: !coverageContent.includes("not yet implemented"),
|
|
201
|
+
detail: coverageContent.includes("not yet implemented") ? "contains 'not yet implemented'" : undefined,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
items.push({
|
|
205
|
+
name: "At least 8 doc pages",
|
|
206
|
+
tier: 2,
|
|
207
|
+
pass: mdxFiles.length >= 8,
|
|
208
|
+
detail: `${mdxFiles.length} page(s)`,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ── Tier 3: Thoroughness ───────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
// Each lint rule has a test (per-file or consolidated)
|
|
214
|
+
const ruleDir = join(dir, "src/lint/rules");
|
|
215
|
+
const ruleSourceFiles = listTsFiles(ruleDir, ["index.ts"]).filter((f) => !f.endsWith(".test.ts"));
|
|
216
|
+
const ruleTestFiles = listTsFiles(ruleDir).filter((f) => f.endsWith(".test.ts"));
|
|
217
|
+
// A consolidated test file (e.g. rules.test.ts) covers all rules in the directory
|
|
218
|
+
const hasConsolidatedRuleTest = ruleTestFiles.length > 0;
|
|
219
|
+
const untestedRules = hasConsolidatedRuleTest
|
|
220
|
+
? []
|
|
221
|
+
: ruleSourceFiles.filter(
|
|
222
|
+
(f) => !ruleTestFiles.includes(f.replace(".ts", ".test.ts")),
|
|
223
|
+
);
|
|
224
|
+
items.push({
|
|
225
|
+
name: "Each lint rule has a .test.ts",
|
|
226
|
+
tier: 3,
|
|
227
|
+
pass: ruleSourceFiles.length > 0 && untestedRules.length === 0,
|
|
228
|
+
detail: untestedRules.length > 0 ? `missing: ${untestedRules.join(", ")}` : undefined,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Each post-synth has a test (per-file or consolidated)
|
|
232
|
+
const postSynthDir = join(dir, "src/lint/post-synth");
|
|
233
|
+
const postSynthSourceFiles = listTsFiles(postSynthDir, ["index.ts"])
|
|
234
|
+
.filter((f) => !f.endsWith(".test.ts") && !f.endsWith("-helpers.ts") && f !== "helpers.ts");
|
|
235
|
+
const postSynthTestFiles = listTsFiles(postSynthDir).filter((f) => f.endsWith(".test.ts"));
|
|
236
|
+
// A consolidated test file (e.g. post-synth.test.ts) covers all checks in the directory
|
|
237
|
+
const hasConsolidatedPostSynthTest = postSynthTestFiles.length > 0;
|
|
238
|
+
const untestedPostSynth = hasConsolidatedPostSynthTest
|
|
239
|
+
? []
|
|
240
|
+
: postSynthSourceFiles.filter(
|
|
241
|
+
(f) => !postSynthTestFiles.includes(f.replace(".ts", ".test.ts")),
|
|
242
|
+
);
|
|
243
|
+
items.push({
|
|
244
|
+
name: "Each post-synth check has a .test.ts",
|
|
245
|
+
tier: 3,
|
|
246
|
+
pass: postSynthSourceFiles.length > 0 && untestedPostSynth.length === 0,
|
|
247
|
+
detail: untestedPostSynth.length > 0 ? `missing: ${untestedPostSynth.join(", ")}` : undefined,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const hasTypecheckTest = findFiles(join(dir, "src"), (n) => n === "typecheck.test.ts").length > 0;
|
|
251
|
+
items.push({
|
|
252
|
+
name: "typecheck.test.ts exists",
|
|
253
|
+
tier: 3,
|
|
254
|
+
pass: hasTypecheckTest,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const hasRoundtripTest = findFiles(join(dir, "src"), (n) => n === "roundtrip.test.ts").length > 0;
|
|
258
|
+
items.push({
|
|
259
|
+
name: "roundtrip.test.ts exists",
|
|
260
|
+
tier: 3,
|
|
261
|
+
pass: hasRoundtripTest,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
items.push({
|
|
265
|
+
name: "At least 5 composites",
|
|
266
|
+
tier: 3,
|
|
267
|
+
pass: compositeFiles.length >= 5,
|
|
268
|
+
detail: `${compositeFiles.length} composite(s)`,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const hasActions = existsSync(join(dir, "src/actions")) &&
|
|
272
|
+
listTsFiles(join(dir, "src/actions"), ["index.ts"]).length > 0;
|
|
273
|
+
items.push({
|
|
274
|
+
name: "src/actions/ with at least 1 action",
|
|
275
|
+
tier: 3,
|
|
276
|
+
pass: hasActions,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Examples with tests (per-example or consolidated root test file)
|
|
280
|
+
const examplesDir = join(dir, "examples");
|
|
281
|
+
let examplesWithTests = 0;
|
|
282
|
+
if (existsSync(examplesDir)) {
|
|
283
|
+
// A .test.ts in the examples root directory covers all examples
|
|
284
|
+
const rootTestFiles = readdirSync(examplesDir).filter((f) => f.endsWith(".test.ts"));
|
|
285
|
+
const hasConsolidatedExampleTest = rootTestFiles.length > 0;
|
|
286
|
+
const exampleDirs = readdirSync(examplesDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
287
|
+
for (const entry of exampleDirs) {
|
|
288
|
+
if (hasConsolidatedExampleTest) {
|
|
289
|
+
// Consolidated test covers all non-empty example dirs
|
|
290
|
+
const contents = readdirSync(join(examplesDir, entry.name));
|
|
291
|
+
if (contents.length > 0 && !(contents.length === 1 && contents[0] === ".gitkeep")) {
|
|
292
|
+
examplesWithTests++;
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
const exampleTests = findFiles(join(examplesDir, entry.name), (n) => n.endsWith(".test.ts"));
|
|
296
|
+
if (exampleTests.length > 0) examplesWithTests++;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
items.push({
|
|
301
|
+
name: "At least 5 examples with tests",
|
|
302
|
+
tier: 3,
|
|
303
|
+
pass: examplesWithTests >= 5,
|
|
304
|
+
detail: `${examplesWithTests} example(s) with tests`,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const tier1Pass = items.filter((i) => i.tier === 1).every((i) => i.pass);
|
|
308
|
+
|
|
309
|
+
return { items, tier1Pass };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ── Output formatting ────────────────────────────────────────────────
|
|
313
|
+
|
|
314
|
+
const COLORS = {
|
|
315
|
+
green: "\x1b[32m",
|
|
316
|
+
yellow: "\x1b[33m",
|
|
317
|
+
gray: "\x1b[90m",
|
|
318
|
+
red: "\x1b[31m",
|
|
319
|
+
reset: "\x1b[0m",
|
|
320
|
+
bold: "\x1b[1m",
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
function useColors(): boolean {
|
|
324
|
+
return !process.env.NO_COLOR && process.stdout.isTTY !== false;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function c(text: string, code: string): string {
|
|
328
|
+
return useColors() ? `${code}${text}${COLORS.reset}` : text;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Print the check result as a colored table or JSON.
|
|
333
|
+
*/
|
|
334
|
+
export function printCheckResult(result: CheckResult, json: boolean): void {
|
|
335
|
+
if (json) {
|
|
336
|
+
console.log(JSON.stringify(result, null, 2));
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const tierLabels: Record<number, string> = {
|
|
341
|
+
1: "required",
|
|
342
|
+
2: "recommended",
|
|
343
|
+
3: "thoroughness",
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
for (const tier of [1, 2, 3] as const) {
|
|
347
|
+
const tierItems = result.items.filter((i) => i.tier === tier);
|
|
348
|
+
if (tierItems.length === 0) continue;
|
|
349
|
+
|
|
350
|
+
const passCount = tierItems.filter((i) => i.pass).length;
|
|
351
|
+
const label = tierLabels[tier];
|
|
352
|
+
console.log("");
|
|
353
|
+
console.log(c(`Tier ${tier} — ${label} (${passCount}/${tierItems.length})`, COLORS.bold));
|
|
354
|
+
|
|
355
|
+
for (const item of tierItems) {
|
|
356
|
+
let icon: string;
|
|
357
|
+
let nameColor: string;
|
|
358
|
+
|
|
359
|
+
if (item.pass) {
|
|
360
|
+
icon = c("PASS", COLORS.green);
|
|
361
|
+
nameColor = COLORS.green;
|
|
362
|
+
} else if (tier === 1) {
|
|
363
|
+
icon = c("FAIL", COLORS.red);
|
|
364
|
+
nameColor = COLORS.red;
|
|
365
|
+
} else if (tier === 2) {
|
|
366
|
+
icon = c("WARN", COLORS.yellow);
|
|
367
|
+
nameColor = COLORS.yellow;
|
|
368
|
+
} else {
|
|
369
|
+
icon = c("INFO", COLORS.gray);
|
|
370
|
+
nameColor = COLORS.gray;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const detail = item.detail ? c(` (${item.detail})`, COLORS.gray) : "";
|
|
374
|
+
console.log(` ${icon} ${c(item.name, nameColor)}${detail}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
console.log("");
|
|
379
|
+
if (result.tier1Pass) {
|
|
380
|
+
console.log(c("All tier-1 checks passed.", COLORS.green));
|
|
381
|
+
} else {
|
|
382
|
+
const failures = result.items.filter((i) => i.tier === 1 && !i.pass);
|
|
383
|
+
console.log(c(`${failures.length} tier-1 check(s) failed.`, COLORS.red));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs";
|
|
2
|
-
import { join, resolve, basename } from "path";
|
|
2
|
+
import { join, resolve, basename, dirname } from "path";
|
|
3
3
|
import { formatSuccess, formatWarning, formatError } from "../format";
|
|
4
4
|
import type { TemplateIR, ResourceIR, TemplateParser } from "../../import/parser";
|
|
5
5
|
import type { GeneratedFile, TypeScriptGenerator } from "../../import/generator";
|
|
@@ -208,10 +208,13 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
|
|
|
208
208
|
};
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
// Load plugins from
|
|
211
|
+
// Load plugins — resolve from the output directory (or CWD) so that
|
|
212
|
+
// project config is found relative to where the user is working, not
|
|
213
|
+
// an arbitrary monorepo root.
|
|
214
|
+
const projectDir = resolve(options.output ? dirname(options.output) : ".");
|
|
212
215
|
let plugins: LexiconPlugin[];
|
|
213
216
|
try {
|
|
214
|
-
const lexiconNames = await resolveProjectLexicons(
|
|
217
|
+
const lexiconNames = await resolveProjectLexicons(projectDir);
|
|
215
218
|
plugins = await loadPlugins(lexiconNames);
|
|
216
219
|
} catch {
|
|
217
220
|
plugins = [];
|
|
@@ -34,8 +34,10 @@ describe("initLexiconCommand", () => {
|
|
|
34
34
|
|
|
35
35
|
const expectedFiles = [
|
|
36
36
|
"src/plugin.ts",
|
|
37
|
+
"src/plugin.test.ts",
|
|
37
38
|
"src/index.ts",
|
|
38
39
|
"src/serializer.ts",
|
|
40
|
+
"src/serializer.test.ts",
|
|
39
41
|
"src/codegen/generate.ts",
|
|
40
42
|
"src/codegen/generate-cli.ts",
|
|
41
43
|
"src/codegen/naming.ts",
|
|
@@ -47,6 +49,8 @@ describe("initLexiconCommand", () => {
|
|
|
47
49
|
"src/lint/rules/index.ts",
|
|
48
50
|
"src/lsp/completions.ts",
|
|
49
51
|
"src/lsp/hover.ts",
|
|
52
|
+
"src/lsp/completions.test.ts",
|
|
53
|
+
"src/lsp/hover.test.ts",
|
|
50
54
|
"src/import/parser.ts",
|
|
51
55
|
"src/import/generator.ts",
|
|
52
56
|
"src/coverage.ts",
|
|
@@ -62,8 +66,15 @@ describe("initLexiconCommand", () => {
|
|
|
62
66
|
"docs/astro.config.mjs",
|
|
63
67
|
"docs/src/content.config.ts",
|
|
64
68
|
"docs/src/content/docs/index.mdx",
|
|
69
|
+
"docs/src/content/docs/getting-started.mdx",
|
|
70
|
+
"docs/src/content/docs/serialization.mdx",
|
|
71
|
+
"docs/src/content/docs/lint-rules.mdx",
|
|
72
|
+
"examples/getting-started/package.json",
|
|
73
|
+
"examples/getting-started/src/infra.ts",
|
|
65
74
|
"src/generated/.gitkeep",
|
|
66
|
-
"
|
|
75
|
+
"src/composites/.gitkeep",
|
|
76
|
+
"src/actions/.gitkeep",
|
|
77
|
+
"src/lint/post-synth/.gitkeep",
|
|
67
78
|
];
|
|
68
79
|
|
|
69
80
|
for (const file of expectedFiles) {
|
|
@@ -248,6 +259,16 @@ describe("init-lexicon fixture snapshot", () => {
|
|
|
248
259
|
path: FIXTURE_DIR,
|
|
249
260
|
});
|
|
250
261
|
|
|
262
|
+
// Remove generated .test.ts files so bun test won't try to run them as tests
|
|
263
|
+
for (const f of [
|
|
264
|
+
"src/plugin.test.ts",
|
|
265
|
+
"src/serializer.test.ts",
|
|
266
|
+
"src/lsp/completions.test.ts",
|
|
267
|
+
"src/lsp/hover.test.ts",
|
|
268
|
+
]) {
|
|
269
|
+
rmSync(join(FIXTURE_DIR, f), { force: true });
|
|
270
|
+
}
|
|
271
|
+
|
|
251
272
|
expect(result.success).toBe(true);
|
|
252
273
|
|
|
253
274
|
// Key files that must exist
|