@intentius/chant 0.98.0 → 0.100.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/commands/import.d.ts +20 -4
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/import/embedded.d.ts +184 -0
- package/dist/import/embedded.d.ts.map +1 -0
- package/dist/import/generator.d.ts +13 -0
- package/dist/import/generator.d.ts.map +1 -1
- package/dist/import/parser.d.ts +15 -1
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/lexicon.d.ts +19 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lint/engine.d.ts +6 -1
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/rule.d.ts +10 -0
- package/dist/lint/rule.d.ts.map +1 -1
- package/dist/lint/rules/file-declarable-limit.d.ts.map +1 -1
- package/dist/lint/rules/flat-declarations.d.ts.map +1 -1
- package/dist/lint/rules/no-unused-declarable.d.ts.map +1 -1
- package/dist/lint/rules/property-kind.d.ts +7 -0
- package/dist/lint/rules/property-kind.d.ts.map +1 -0
- package/dist/workspace/conformance/index.d.ts +9 -0
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/yaml.d.ts +28 -6
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/import-layout.test.ts +142 -0
- package/src/cli/commands/import-no-parser.test.ts +1 -1
- package/src/cli/commands/import.test.ts +25 -0
- package/src/cli/commands/import.ts +158 -36
- package/src/cli/commands/lint.ts +21 -8
- package/src/deep-observation.test.ts +21 -0
- package/src/deep-observation.ts +9 -0
- package/src/import/embedded.test.ts +153 -0
- package/src/import/embedded.ts +376 -0
- package/src/import/generator.ts +14 -0
- package/src/import/parser.ts +17 -1
- package/src/lexicon.ts +21 -0
- package/src/lint/engine.ts +7 -0
- package/src/lint/rule.ts +10 -0
- package/src/lint/rules/file-declarable-limit.ts +11 -4
- package/src/lint/rules/flat-declarations.ts +6 -1
- package/src/lint/rules/no-unused-declarable.ts +59 -2
- package/src/lint/rules/property-kind.test.ts +99 -0
- package/src/lint/rules/property-kind.ts +56 -0
- package/src/workspace/conformance/index.mjs +1 -0
- package/src/workspace/conformance/index.ts +23 -1
- package/src/yaml.test.ts +192 -1
- package/src/yaml.ts +404 -250
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/workspace/conformance/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/workspace/conformance/index.ts"],"names":[],"mappings":"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"}
|
package/dist/yaml.d.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Lightweight YAML emitter and parser.
|
|
3
3
|
*
|
|
4
4
|
* Covers the subset of YAML used by Chant lexicons (scalars, block arrays,
|
|
5
|
-
* nested objects, tagged values
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* nested objects, block scalars, tagged values, anchors, aliases and merge
|
|
6
|
+
* keys). Not a full YAML implementation: flow collections are read only when
|
|
7
|
+
* they are JSON. `splitYAMLDocuments` splits a multi-document stream. The
|
|
8
|
+
* parser throws a `YAMLParseError` on a line it cannot place instead of
|
|
9
|
+
* dropping it (#2991).
|
|
8
10
|
*/
|
|
9
11
|
export declare function emitYAML(value: unknown, indent: number): string;
|
|
10
12
|
/** Result of parsing a YAML block. */
|
|
@@ -12,11 +14,23 @@ export interface ParseResult {
|
|
|
12
14
|
value: unknown;
|
|
13
15
|
endIndex: number;
|
|
14
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Input the parser cannot place (#2991). The parser used to skip such a line,
|
|
19
|
+
* or read it as a key of whatever mapping was open at the top level, so a
|
|
20
|
+
* mis-indented or unsupported construct came back as a quietly different
|
|
21
|
+
* document. Now it is an error naming the line.
|
|
22
|
+
*/
|
|
23
|
+
export declare class YAMLParseError extends Error {
|
|
24
|
+
/** 1-based line number within the text handed to the parser. */
|
|
25
|
+
readonly line: number;
|
|
26
|
+
constructor(line: number, message: string);
|
|
27
|
+
}
|
|
15
28
|
/**
|
|
16
29
|
* Parse a YAML document (or JSON document) into a plain object.
|
|
17
30
|
*
|
|
18
31
|
* Tries `JSON.parse` first; falls back to a line-based YAML parser that
|
|
19
|
-
* handles the subset of YAML commonly found in CI configuration files.
|
|
32
|
+
* handles the subset of YAML commonly found in CI configuration files. Throws
|
|
33
|
+
* {@link YAMLParseError} on a line it cannot place rather than dropping it.
|
|
20
34
|
*/
|
|
21
35
|
export declare function parseYAML(content: string): Record<string, unknown>;
|
|
22
36
|
/**
|
|
@@ -36,11 +50,19 @@ export declare function parseYAMLDocument(content: string): unknown;
|
|
|
36
50
|
*/
|
|
37
51
|
export declare function splitYAMLDocuments(content: string): string[];
|
|
38
52
|
/**
|
|
39
|
-
* Parse indentation-based YAML lines into a key-value object
|
|
53
|
+
* Parse indentation-based YAML lines into a key-value object: the mapping
|
|
54
|
+
* whose keys sit at column `baseIndent`, starting at `startIndex`.
|
|
55
|
+
*
|
|
56
|
+
* It ends at the first line that is not one of its keys: a line at another
|
|
57
|
+
* column, a sequence entry, or text that is not `key:`. That line goes back
|
|
58
|
+
* to the caller in `endIndex`; if no caller owns it, `parseYAML` reports it
|
|
59
|
+
* (#2991). This function never skips a content line.
|
|
40
60
|
*/
|
|
41
61
|
export declare function parseYAMLLines(lines: string[], startIndex: number, baseIndent: number): ParseResult;
|
|
42
62
|
/**
|
|
43
|
-
* Parse a block array
|
|
63
|
+
* Parse a block array: the `- ` entries at column `baseIndent`, starting at
|
|
64
|
+
* `startIndex`. Like {@link parseYAMLLines} it ends at the first line that is
|
|
65
|
+
* not one of its entries and hands that line back in `endIndex`.
|
|
44
66
|
*/
|
|
45
67
|
export declare function parseYAMLArray(lines: string[], startIndex: number, baseIndent: number): ParseResult;
|
|
46
68
|
/**
|
package/dist/yaml.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgH/D;AAMD,sCAAsC;AACtC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAmJD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAalE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAc5D;AAiFD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CAqBb;AA+MD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CA4Db;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAwBlD"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { mkdir, rm, writeFile, readFile } from "node:fs/promises";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import type { LexiconPlugin } from "../../lexicon";
|
|
7
|
+
import type { GeneratedFile, TypeScriptGenerator } from "../../import/generator";
|
|
8
|
+
import type { TemplateIR } from "../../import/parser";
|
|
9
|
+
import { generateOrganizedFiles, importCommand } from "./import";
|
|
10
|
+
|
|
11
|
+
// A generator that writes one module per resource plus a barrel, the shape
|
|
12
|
+
// the otel, prometheus and grafana importers want (#2964).
|
|
13
|
+
function perResourceFiles(ir: TemplateIR): GeneratedFile[] {
|
|
14
|
+
return [
|
|
15
|
+
...ir.resources.map((r) => ({
|
|
16
|
+
path: `${r.logicalId}.ts`,
|
|
17
|
+
content: `export const ${r.logicalId} = ${JSON.stringify(r.properties)};\n`,
|
|
18
|
+
})),
|
|
19
|
+
{ path: "index.ts", content: ir.resources.map((r) => `export * from "./${r.logicalId}";`).join("\n") + "\n" },
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const placing: TypeScriptGenerator = { ownsLayout: true, generate: perResourceFiles };
|
|
24
|
+
const defaultLayout: TypeScriptGenerator = { generate: perResourceFiles };
|
|
25
|
+
|
|
26
|
+
// Five resources: over core's three-resource threshold, across two categories.
|
|
27
|
+
const fiveIR: TemplateIR = {
|
|
28
|
+
resources: [
|
|
29
|
+
{ logicalId: "receiver", type: "Fake::Receiver", properties: { port: 4317 } },
|
|
30
|
+
{ logicalId: "processor", type: "Fake::Processor", properties: {} },
|
|
31
|
+
{ logicalId: "exporter", type: "Fake::Exporter", properties: {} },
|
|
32
|
+
{ logicalId: "pipeline", type: "Fake::Pipeline", properties: {} },
|
|
33
|
+
{ logicalId: "bucket", type: "Fake::Bucket", properties: {} },
|
|
34
|
+
],
|
|
35
|
+
parameters: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
describe("generateOrganizedFiles (#2964)", () => {
|
|
39
|
+
test("a generator with ownsLayout gets one call and its files are written as returned", () => {
|
|
40
|
+
const generate = vi.fn(perResourceFiles);
|
|
41
|
+
const { files, warnings } = generateOrganizedFiles(fiveIR, { ownsLayout: true, generate });
|
|
42
|
+
expect(generate).toHaveBeenCalledTimes(1);
|
|
43
|
+
expect(generate).toHaveBeenCalledWith(fiveIR);
|
|
44
|
+
expect(files).toEqual(perResourceFiles(fiveIR));
|
|
45
|
+
expect(warnings).toEqual([]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("ownsLayout writes nothing extra when the generator returns nothing", () => {
|
|
49
|
+
const { files } = generateOrganizedFiles(fiveIR, { ownsLayout: true, generate: () => [] });
|
|
50
|
+
expect(files).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("the default layout splits by category and names every file it drops", () => {
|
|
54
|
+
const { files, warnings } = generateOrganizedFiles(fiveIR, defaultLayout);
|
|
55
|
+
expect(files.map((f) => f.path)).toEqual(["other.ts", "storage.ts", "index.ts"]);
|
|
56
|
+
expect(warnings).toEqual([
|
|
57
|
+
"The generator returned 5 files for other.ts; only the first was kept, and processor.ts, exporter.ts, " +
|
|
58
|
+
"pipeline.ts, index.ts were not written. A generator that places its own files sets ownsLayout (#2964).",
|
|
59
|
+
"The generator returned 2 files for storage.ts; only the first was kept, and index.ts was not written. " +
|
|
60
|
+
"A generator that places its own files sets ownsLayout (#2964).",
|
|
61
|
+
]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("the default layout skips a category the generator returns nothing for, with a warning", () => {
|
|
65
|
+
const { files, warnings } = generateOrganizedFiles(fiveIR, { generate: () => [] });
|
|
66
|
+
expect(files).toEqual([]);
|
|
67
|
+
expect(warnings).toEqual([
|
|
68
|
+
"The generator returned no file for other.ts; it was not written.",
|
|
69
|
+
"The generator returned no file for storage.ts; it was not written.",
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("the default layout stays silent for a generator that returns one file per call", () => {
|
|
74
|
+
const oneFile: TypeScriptGenerator = { generate: (ir) => [perResourceFiles(ir)[0]] };
|
|
75
|
+
const { files, warnings } = generateOrganizedFiles(fiveIR, oneFile);
|
|
76
|
+
expect(files.map((f) => f.path)).toEqual(["other.ts", "storage.ts", "index.ts"]);
|
|
77
|
+
expect(warnings).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("up to three resources are one call either way", () => {
|
|
81
|
+
const small: TemplateIR = { resources: fiveIR.resources.slice(0, 3), parameters: [] };
|
|
82
|
+
expect(generateOrganizedFiles(small, defaultLayout)).toEqual({ files: perResourceFiles(small), warnings: [] });
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Through the CLI: a fake lexicon whose parser yields fiveIR.
|
|
87
|
+
const { fakes } = vi.hoisted(() => ({ fakes: {} as Record<string, LexiconPlugin> }));
|
|
88
|
+
|
|
89
|
+
vi.mock("../plugins", () => ({
|
|
90
|
+
resolveProjectLexicons: async () => Object.keys(fakes),
|
|
91
|
+
loadPlugins: async (names: string[]) => names.map((n) => fakes[n]),
|
|
92
|
+
loadPlugin: async (n: string) => fakes[n],
|
|
93
|
+
listInstalledLexicons: () => [],
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
function fakeLexicon(name: string, generator: TypeScriptGenerator): LexiconPlugin {
|
|
97
|
+
return {
|
|
98
|
+
name,
|
|
99
|
+
detectTemplate: (data: unknown) => typeof data === "object" && data !== null && "fakeMarker" in data,
|
|
100
|
+
templateParser: () => ({ parse: () => fiveIR }),
|
|
101
|
+
templateGenerator: () => generator,
|
|
102
|
+
} as unknown as LexiconPlugin;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
describe("chant import writes a placing generator's files (#2964)", () => {
|
|
106
|
+
let testDir: string;
|
|
107
|
+
let templatePath: string;
|
|
108
|
+
let outputDir: string;
|
|
109
|
+
|
|
110
|
+
beforeEach(async () => {
|
|
111
|
+
testDir = join(tmpdir(), `chant-import-layout-${Date.now()}-${Math.random()}`);
|
|
112
|
+
await mkdir(testDir, { recursive: true });
|
|
113
|
+
templatePath = join(testDir, "config.json");
|
|
114
|
+
outputDir = join(testDir, "out");
|
|
115
|
+
await writeFile(templatePath, JSON.stringify({ fakeMarker: true }));
|
|
116
|
+
fakes.placing = fakeLexicon("placing", placing);
|
|
117
|
+
fakes.splitting = fakeLexicon("splitting", defaultLayout);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
afterEach(async () => {
|
|
121
|
+
await rm(testDir, { recursive: true, force: true });
|
|
122
|
+
for (const k of Object.keys(fakes)) delete fakes[k];
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("ownsLayout: every file the generator returns is written", async () => {
|
|
126
|
+
const result = await importCommand({ templatePath, output: outputDir, lexicon: "placing" });
|
|
127
|
+
expect(result.success).toBe(true);
|
|
128
|
+
expect(result.warnings).toEqual([]);
|
|
129
|
+
expect(result.generatedFiles).toEqual(perResourceFiles(fiveIR).map((f) => f.path));
|
|
130
|
+
expect(await readFile(join(outputDir, "pipeline.ts"), "utf-8")).toBe("export const pipeline = {};\n");
|
|
131
|
+
expect(existsSync(join(outputDir, "other.ts"))).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("default layout: the dropped files are named in the import warnings", async () => {
|
|
135
|
+
const result = await importCommand({ templatePath, output: outputDir, lexicon: "splitting" });
|
|
136
|
+
expect(result.success).toBe(true);
|
|
137
|
+
expect(result.generatedFiles).toEqual(["other.ts", "storage.ts", "index.ts"]);
|
|
138
|
+
expect(result.warnings.some((w) => w.includes("processor.ts, exporter.ts, pipeline.ts, index.ts were not written"))).toBe(
|
|
139
|
+
true,
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -7,7 +7,7 @@ import type { LexiconPlugin } from "../../lexicon";
|
|
|
7
7
|
import { importCommand, importFromContent } from "./import";
|
|
8
8
|
|
|
9
9
|
// A lexicon that recognizes a template but has no templateParser, as grafana
|
|
10
|
-
//
|
|
10
|
+
// did until #2945 (#2940). Import must say it cannot import, not throw
|
|
11
11
|
// "plugin.templateParser is not a function".
|
|
12
12
|
const { detectOnly } = vi.hoisted(() => ({
|
|
13
13
|
detectOnly: {
|
|
@@ -503,6 +503,31 @@ spec:
|
|
|
503
503
|
expect(result.error).toContain("no-such-lexicon");
|
|
504
504
|
});
|
|
505
505
|
|
|
506
|
+
// #2991 — a blank or whitespace-only line after `spec:` (Helm renders one
|
|
507
|
+
// for an empty conditional) no longer ends the pod template's spec.
|
|
508
|
+
test("a blank line after a key keeps its block nested", async () => {
|
|
509
|
+
const templatePath = join(testDir, "manifest.yaml");
|
|
510
|
+
await writeFile(templatePath, deployment.replace(" spec:\n", " spec:\n \n"));
|
|
511
|
+
|
|
512
|
+
const result = await importCommand({ templatePath, output: outputDir, lexicon: "k8s" });
|
|
513
|
+
|
|
514
|
+
expect(result.error).toBeUndefined();
|
|
515
|
+
// The containers stay under template.spec, not hoisted beside `replicas`.
|
|
516
|
+
expect(generated(result.generatedFiles)).toMatch(/template: \{[\s\S]*spec: \{\s*containers: \[/);
|
|
517
|
+
}, 30000);
|
|
518
|
+
|
|
519
|
+
// #2991 — a document the YAML reader cannot place fails the import with
|
|
520
|
+
// its line, where it used to import a different manifest.
|
|
521
|
+
test("a mis-indented document fails the import and names the line", async () => {
|
|
522
|
+
const templatePath = join(testDir, "manifests.yaml");
|
|
523
|
+
await writeFile(templatePath, `${configMap}---\n${deployment.replace(" replicas: 2\n", " replicas: 2\n paused: true\n")}`);
|
|
524
|
+
|
|
525
|
+
const result = await importCommand({ templatePath, output: outputDir });
|
|
526
|
+
|
|
527
|
+
expect(result.success).toBe(false);
|
|
528
|
+
expect(result.error).toContain("YAML line 7");
|
|
529
|
+
}, 30000);
|
|
530
|
+
|
|
506
531
|
test("content that is neither JSON nor YAML names both formats", async () => {
|
|
507
532
|
const templatePath = join(testDir, "notes.txt");
|
|
508
533
|
await writeFile(templatePath, "this is not a template\n{ nor is this");
|
|
@@ -6,6 +6,8 @@ import type { GeneratedFile, TypeScriptGenerator } from "../../import/generator"
|
|
|
6
6
|
import { listInstalledLexicons, loadPlugin, loadPlugins, resolveProjectLexicons } from "../plugins";
|
|
7
7
|
import type { LexiconPlugin, ResourceSelector } from "../../lexicon";
|
|
8
8
|
import { parseYAMLDocument, splitYAMLDocuments } from "../../yaml";
|
|
9
|
+
import { importLexiconPackage } from "../../lexicon-module";
|
|
10
|
+
import { EmbeddedImports, type EmbeddedContent, type RegisteredEmbeddedImporter } from "../../import/embedded";
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Import command options
|
|
@@ -57,9 +59,10 @@ type ResourceCategory = "storage" | "compute" | "network" | "other";
|
|
|
57
59
|
* detection sees the same per-document objects the plugin's parser will. A
|
|
58
60
|
* document whose top level is a list parses as that list (#2965). JSON yields
|
|
59
61
|
* one document, the parsed value, exactly as before. Returns undefined when
|
|
60
|
-
* the content is neither
|
|
61
|
-
*
|
|
62
|
-
*
|
|
62
|
+
* the content is neither. A document core's YAML reader cannot parse is
|
|
63
|
+
* skipped here, since detection only needs one it can read; the plugin's
|
|
64
|
+
* parser meets the same document and reports it (#2991). A file with no
|
|
65
|
+
* non-empty document is rejected.
|
|
63
66
|
*/
|
|
64
67
|
export function parseTemplateDocuments(content: string): unknown[] | undefined {
|
|
65
68
|
try {
|
|
@@ -204,22 +207,56 @@ function organizeByCategory(ir: TemplateIR): Map<ResourceCategory, ResourceIR[]>
|
|
|
204
207
|
return categories;
|
|
205
208
|
}
|
|
206
209
|
|
|
210
|
+
/** The files an import writes, and anything core could not keep. */
|
|
211
|
+
export interface OrganizedFiles {
|
|
212
|
+
files: GeneratedFile[];
|
|
213
|
+
warnings: string[];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The first file of one per-category `generate()` call, which core writes as
|
|
218
|
+
* `<name>.ts`. Any further files that call returned are named in a warning
|
|
219
|
+
* rather than dropped silently; a call that returned nothing is skipped with
|
|
220
|
+
* a warning rather than failing on `generated[0]`.
|
|
221
|
+
*/
|
|
222
|
+
function firstFileOf(generated: GeneratedFile[], fileName: string, warnings: string[]): string | undefined {
|
|
223
|
+
if (generated.length === 0) {
|
|
224
|
+
warnings.push(`The generator returned no file for ${fileName}; it was not written.`);
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
if (generated.length > 1) {
|
|
228
|
+
const dropped = generated.slice(1).map((f) => f.path).join(", ");
|
|
229
|
+
warnings.push(
|
|
230
|
+
`The generator returned ${generated.length} files for ${fileName}; only the first was kept, ` +
|
|
231
|
+
`and ${dropped} ${generated.length === 2 ? "was" : "were"} not written. ` +
|
|
232
|
+
"A generator that places its own files sets ownsLayout (#2964).",
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return generated[0].content;
|
|
236
|
+
}
|
|
237
|
+
|
|
207
238
|
/**
|
|
208
|
-
*
|
|
239
|
+
* Decide the files an import writes. A generator with `ownsLayout` is called
|
|
240
|
+
* once with the whole IR and its files are written exactly as returned
|
|
241
|
+
* (#2964). Otherwise an IR of up to three resources is generated in one call,
|
|
242
|
+
* and a larger one is split into one file per resource category plus an
|
|
243
|
+
* `index.ts` barrel.
|
|
209
244
|
*/
|
|
210
|
-
function generateOrganizedFiles(
|
|
245
|
+
export function generateOrganizedFiles(
|
|
211
246
|
ir: TemplateIR,
|
|
212
247
|
generator: TypeScriptGenerator,
|
|
213
|
-
):
|
|
248
|
+
): OrganizedFiles {
|
|
249
|
+
const warnings: string[] = [];
|
|
250
|
+
|
|
251
|
+
// The generator places its own files, or everything fits in one call.
|
|
252
|
+
if (generator.ownsLayout === true || ir.resources.length <= 3) {
|
|
253
|
+
return { files: generator.generate(ir), warnings };
|
|
254
|
+
}
|
|
255
|
+
|
|
214
256
|
const files: GeneratedFile[] = [];
|
|
215
257
|
const categories = organizeByCategory(ir);
|
|
216
258
|
const exports: string[] = [];
|
|
217
259
|
|
|
218
|
-
// If all resources fit in one file, just generate main.ts
|
|
219
|
-
if (ir.resources.length <= 3) {
|
|
220
|
-
return generator.generate(ir);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
260
|
// Generate files for each category
|
|
224
261
|
for (const [category, resources] of categories) {
|
|
225
262
|
if (resources.length === 0) continue;
|
|
@@ -229,13 +266,10 @@ function generateOrganizedFiles(
|
|
|
229
266
|
resources,
|
|
230
267
|
};
|
|
231
268
|
|
|
232
|
-
const generated = generator.generate(categoryIr);
|
|
233
269
|
const fileName = `${category}.ts`;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
content: generated[0].content,
|
|
238
|
-
});
|
|
270
|
+
const content = firstFileOf(generator.generate(categoryIr), fileName, warnings);
|
|
271
|
+
if (content === undefined) continue;
|
|
272
|
+
files.push({ path: fileName, content });
|
|
239
273
|
|
|
240
274
|
// Track exports
|
|
241
275
|
for (const resource of resources) {
|
|
@@ -250,15 +284,13 @@ function generateOrganizedFiles(
|
|
|
250
284
|
parameters: ir.parameters,
|
|
251
285
|
resources: [],
|
|
252
286
|
};
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
path: "parameters.ts",
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const varName = param.name.charAt(0).toLowerCase() + param.name.slice(1);
|
|
261
|
-
exports.push(`export { ${varName} } from "./parameters";`);
|
|
287
|
+
const content = firstFileOf(generator.generate(paramsIr), "parameters.ts", warnings);
|
|
288
|
+
if (content !== undefined) {
|
|
289
|
+
files.push({ path: "parameters.ts", content });
|
|
290
|
+
for (const param of ir.parameters) {
|
|
291
|
+
const varName = param.name.charAt(0).toLowerCase() + param.name.slice(1);
|
|
292
|
+
exports.push(`export { ${varName} } from "./parameters";`);
|
|
293
|
+
}
|
|
262
294
|
}
|
|
263
295
|
}
|
|
264
296
|
|
|
@@ -270,7 +302,7 @@ function generateOrganizedFiles(
|
|
|
270
302
|
});
|
|
271
303
|
}
|
|
272
304
|
|
|
273
|
-
return files;
|
|
305
|
+
return { files, warnings };
|
|
274
306
|
}
|
|
275
307
|
|
|
276
308
|
/**
|
|
@@ -351,7 +383,7 @@ export async function importCommand(options: ImportOptions): Promise<ImportResul
|
|
|
351
383
|
);
|
|
352
384
|
}
|
|
353
385
|
|
|
354
|
-
const result = parseAndWrite(plugin, content, outputDir, options.force, warnings, generatedFiles, plugin.name);
|
|
386
|
+
const result = await parseAndWrite(plugin, content, outputDir, options.force, warnings, generatedFiles, plugin.name, projectDir);
|
|
355
387
|
return { ...result, detected: true };
|
|
356
388
|
}
|
|
357
389
|
|
|
@@ -389,11 +421,93 @@ export async function importFromContent(options: ContentImportOptions): Promise<
|
|
|
389
421
|
if (!plugin) {
|
|
390
422
|
return { success: false, generatedFiles: [], warnings: [], error: `Lexicon "${options.lexicon}" not available.` };
|
|
391
423
|
}
|
|
392
|
-
|
|
424
|
+
const projectDir = resolve(options.output ? dirname(options.output) : ".");
|
|
425
|
+
return parseAndWrite(plugin, options.content, outputDir, options.force, [], [], plugin.name, projectDir);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* The importers for the content a host's parser offered (#2962): those of the
|
|
430
|
+
* project's lexicons, and of each installed lexicon whose `detectTemplate`
|
|
431
|
+
* recognizes one of the offered documents. The recognizing is done with the
|
|
432
|
+
* lexicon's light `/detect` module, so a lexicon is loaded in full only when
|
|
433
|
+
* it may own something; one that fails to load is skipped.
|
|
434
|
+
*/
|
|
435
|
+
async function embeddedImportersFor(
|
|
436
|
+
offered: readonly EmbeddedContent[],
|
|
437
|
+
host: LexiconPlugin,
|
|
438
|
+
projectDir: string,
|
|
439
|
+
): Promise<RegisteredEmbeddedImporter[]> {
|
|
440
|
+
const documents = offered.map((c) => c.document).filter((d) => typeof d === "object" && d !== null);
|
|
441
|
+
if (documents.length === 0) return [];
|
|
442
|
+
|
|
443
|
+
let projectNames: string[] = [];
|
|
444
|
+
try {
|
|
445
|
+
projectNames = await resolveProjectLexicons(projectDir);
|
|
446
|
+
} catch {
|
|
447
|
+
projectNames = [];
|
|
448
|
+
}
|
|
449
|
+
const candidates: string[] = [...projectNames];
|
|
450
|
+
for (const name of listInstalledLexicons(projectDir)) {
|
|
451
|
+
if (candidates.includes(name)) continue;
|
|
452
|
+
try {
|
|
453
|
+
const detect = await importLexiconPackage(`@intentius/chant-lexicon-${name}/detect`, projectDir);
|
|
454
|
+
const detectTemplate = detect.detectTemplate;
|
|
455
|
+
if (typeof detectTemplate !== "function") continue;
|
|
456
|
+
if (documents.some((d) => {
|
|
457
|
+
try {
|
|
458
|
+
return detectTemplate(d) === true;
|
|
459
|
+
} catch {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
})) {
|
|
463
|
+
candidates.push(name);
|
|
464
|
+
}
|
|
465
|
+
} catch {
|
|
466
|
+
// No /detect module, or it failed to load: not a candidate.
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const registered: RegisteredEmbeddedImporter[] = [];
|
|
471
|
+
for (const name of candidates) {
|
|
472
|
+
let plugin: LexiconPlugin;
|
|
473
|
+
try {
|
|
474
|
+
plugin = name === host.name ? host : await loadPlugin(name);
|
|
475
|
+
} catch {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
let importers;
|
|
479
|
+
try {
|
|
480
|
+
importers = plugin.embeddedImporters?.() ?? [];
|
|
481
|
+
} catch {
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
for (const importer of importers) registered.push({ lexicon: plugin.name, importer });
|
|
485
|
+
}
|
|
486
|
+
return registered;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Parse, resolving embedded content (#2962). A first parse with a probe
|
|
491
|
+
* collects what the parser offers; when it offers anything, the owners'
|
|
492
|
+
* importers are loaded and the content is parsed again with them. A parser
|
|
493
|
+
* that offers nothing is parsed once, and nothing is loaded.
|
|
494
|
+
*/
|
|
495
|
+
async function parseWithEmbedded(
|
|
496
|
+
plugin: LexiconPlugin,
|
|
497
|
+
parser: TemplateParser,
|
|
498
|
+
content: string,
|
|
499
|
+
projectDir: string,
|
|
500
|
+
): Promise<{ ir: TemplateIR; embedded?: EmbeddedImports }> {
|
|
501
|
+
const probe = new EmbeddedImports([], { quiet: true });
|
|
502
|
+
const ir = parser.parse(content, { embedded: probe });
|
|
503
|
+
if (probe.offered.length === 0) return { ir };
|
|
504
|
+
const importers = await embeddedImportersFor(probe.offered, plugin, projectDir);
|
|
505
|
+
const embedded = new EmbeddedImports(importers);
|
|
506
|
+
return { ir: parser.parse(content, { embedded }), embedded };
|
|
393
507
|
}
|
|
394
508
|
|
|
395
509
|
/** The shared tail of every template import: parse → generate → write. */
|
|
396
|
-
function parseAndWrite(
|
|
510
|
+
async function parseAndWrite(
|
|
397
511
|
plugin: LexiconPlugin,
|
|
398
512
|
content: string,
|
|
399
513
|
outputDir: string,
|
|
@@ -401,9 +515,10 @@ function parseAndWrite(
|
|
|
401
515
|
warnings: string[],
|
|
402
516
|
generatedFiles: string[],
|
|
403
517
|
lexicon: string,
|
|
404
|
-
|
|
518
|
+
projectDir: string,
|
|
519
|
+
): Promise<ImportResult> {
|
|
405
520
|
// A lexicon can recognize a template (detectTemplate) without being able to
|
|
406
|
-
// import it (grafana
|
|
521
|
+
// import it (grafana had no parser until #2945). Every path funnels through here, so
|
|
407
522
|
// this one check covers detection, --lexicon and content import (#2940).
|
|
408
523
|
if (!plugin.templateParser || !plugin.templateGenerator) {
|
|
409
524
|
return {
|
|
@@ -417,9 +532,10 @@ function parseAndWrite(
|
|
|
417
532
|
|
|
418
533
|
// Parse template
|
|
419
534
|
let ir: TemplateIR;
|
|
535
|
+
let embedded: EmbeddedImports | undefined;
|
|
420
536
|
try {
|
|
421
537
|
const parser = plugin.templateParser();
|
|
422
|
-
ir = parser
|
|
538
|
+
({ ir, embedded } = await parseWithEmbedded(plugin, parser, content, projectDir));
|
|
423
539
|
} catch (err) {
|
|
424
540
|
return {
|
|
425
541
|
success: false,
|
|
@@ -434,6 +550,8 @@ function parseAndWrite(
|
|
|
434
550
|
if (ir.warnings) {
|
|
435
551
|
warnings.push(...ir.warnings);
|
|
436
552
|
}
|
|
553
|
+
// Embedded content the owner could not carry, or that no installed lexicon imports.
|
|
554
|
+
if (embedded) warnings.push(...embedded.warnings);
|
|
437
555
|
|
|
438
556
|
const generator = plugin.templateGenerator();
|
|
439
557
|
|
|
@@ -451,7 +569,10 @@ function parseAndWrite(
|
|
|
451
569
|
}
|
|
452
570
|
|
|
453
571
|
// Generate files
|
|
454
|
-
const files = generateOrganizedFiles(ir, generator);
|
|
572
|
+
const { files: hostFiles, warnings: layoutWarnings } = generateOrganizedFiles(ir, generator);
|
|
573
|
+
warnings.push(...layoutWarnings);
|
|
574
|
+
// The owners' modules for embedded content, each in a directory of its own.
|
|
575
|
+
const files = [...hostFiles, ...(embedded?.files ?? [])];
|
|
455
576
|
|
|
456
577
|
// Write files
|
|
457
578
|
for (const file of files) {
|
|
@@ -525,7 +646,7 @@ function mergeIR(parts: TemplateIR[]): TemplateIR {
|
|
|
525
646
|
* for full-fidelity IR, then generate chant TypeScript from it.
|
|
526
647
|
*
|
|
527
648
|
* Unlike file import, the live config may contain secrets — the caller prints a
|
|
528
|
-
* warning.
|
|
649
|
+
* warning. Uses the same file layout as file import (`generateOrganizedFiles`).
|
|
529
650
|
*/
|
|
530
651
|
export async function importFromLive(options: LiveImportOptions): Promise<ImportResult> {
|
|
531
652
|
const projectDir = resolve(options.output ? dirname(options.output) : ".");
|
|
@@ -640,7 +761,8 @@ export async function liveImportFromPlugins(
|
|
|
640
761
|
mkdirSync(outputDir, { recursive: true });
|
|
641
762
|
}
|
|
642
763
|
|
|
643
|
-
const files = generateOrganizedFiles(ir, generator);
|
|
764
|
+
const { files, warnings: layoutWarnings } = generateOrganizedFiles(ir, generator);
|
|
765
|
+
warnings.push(...layoutWarnings);
|
|
644
766
|
const generatedFiles: string[] = [];
|
|
645
767
|
for (const file of files) {
|
|
646
768
|
const filePath = join(outputDir, file.path);
|