@intentius/chant 0.0.1
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 +365 -0
- package/package.json +22 -0
- package/src/attrref.test.ts +148 -0
- package/src/attrref.ts +50 -0
- package/src/barrel.test.ts +157 -0
- package/src/barrel.ts +101 -0
- package/src/bench.test.ts +227 -0
- package/src/build.test.ts +437 -0
- package/src/build.ts +425 -0
- package/src/builder.test.ts +312 -0
- package/src/builder.ts +56 -0
- package/src/child-project.ts +44 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/README.md +26 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +16 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +8 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +7 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/justfile +26 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +29 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +8 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +74 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/naming.ts +33 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/rollback.ts +45 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/coverage.ts +11 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/generated/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/generator.ts +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/parser.ts +10 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/index.ts +9 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/index.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/sample.ts +18 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/completions.ts +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/hover.ts +14 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +110 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/serializer.ts +24 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/fetch.ts +21 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/parse.ts +25 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate.ts +24 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/tsconfig.json +10 -0
- package/src/cli/commands/__fixtures__/sample-rule.ts +11 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +222 -0
- package/src/cli/commands/build.test.ts +149 -0
- package/src/cli/commands/build.ts +344 -0
- package/src/cli/commands/diff.test.ts +148 -0
- package/src/cli/commands/diff.ts +221 -0
- package/src/cli/commands/doctor.test.ts +239 -0
- package/src/cli/commands/doctor.ts +224 -0
- package/src/cli/commands/import.test.ts +379 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/init-lexicon.test.ts +297 -0
- package/src/cli/commands/init-lexicon.ts +993 -0
- package/src/cli/commands/init.test.ts +317 -0
- package/src/cli/commands/init.ts +505 -0
- package/src/cli/commands/licenses.ts +165 -0
- package/src/cli/commands/lint.test.ts +332 -0
- package/src/cli/commands/lint.ts +408 -0
- package/src/cli/commands/list.test.ts +100 -0
- package/src/cli/commands/list.ts +108 -0
- package/src/cli/commands/update.test.ts +38 -0
- package/src/cli/commands/update.ts +207 -0
- package/src/cli/conflict-check.test.ts +255 -0
- package/src/cli/conflict-check.ts +89 -0
- package/src/cli/debug.ts +8 -0
- package/src/cli/format.test.ts +140 -0
- package/src/cli/format.ts +133 -0
- package/src/cli/handlers/build.ts +58 -0
- package/src/cli/handlers/dev.ts +38 -0
- package/src/cli/handlers/init.ts +46 -0
- package/src/cli/handlers/lint.ts +36 -0
- package/src/cli/handlers/misc.ts +57 -0
- package/src/cli/handlers/serve.ts +26 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/lsp/capabilities.ts +46 -0
- package/src/cli/lsp/diagnostics.ts +52 -0
- package/src/cli/lsp/server.test.ts +618 -0
- package/src/cli/lsp/server.ts +393 -0
- package/src/cli/main.test.ts +257 -0
- package/src/cli/main.ts +224 -0
- package/src/cli/mcp/resources/context.ts +59 -0
- package/src/cli/mcp/server.test.ts +747 -0
- package/src/cli/mcp/server.ts +402 -0
- package/src/cli/mcp/tools/build.ts +117 -0
- package/src/cli/mcp/tools/import.ts +48 -0
- package/src/cli/mcp/tools/lint.ts +45 -0
- package/src/cli/plugins.test.ts +31 -0
- package/src/cli/plugins.ts +94 -0
- package/src/cli/registry.ts +73 -0
- package/src/cli/reporters/stylish.test.ts +282 -0
- package/src/cli/reporters/stylish.ts +186 -0
- package/src/cli/watch.test.ts +81 -0
- package/src/cli/watch.ts +101 -0
- package/src/codegen/case.test.ts +30 -0
- package/src/codegen/case.ts +11 -0
- package/src/codegen/coverage.ts +167 -0
- package/src/codegen/docs.ts +634 -0
- package/src/codegen/fetch.test.ts +119 -0
- package/src/codegen/fetch.ts +261 -0
- package/src/codegen/generate-registry.test.ts +118 -0
- package/src/codegen/generate-registry.ts +107 -0
- package/src/codegen/generate-runtime-index.test.ts +81 -0
- package/src/codegen/generate-runtime-index.ts +99 -0
- package/src/codegen/generate-typescript.test.ts +146 -0
- package/src/codegen/generate-typescript.ts +161 -0
- package/src/codegen/generate.ts +206 -0
- package/src/codegen/json-patch.test.ts +113 -0
- package/src/codegen/json-patch.ts +151 -0
- package/src/codegen/json-schema.test.ts +196 -0
- package/src/codegen/json-schema.ts +209 -0
- package/src/codegen/naming.ts +201 -0
- package/src/codegen/package.ts +161 -0
- package/src/codegen/rollback.test.ts +92 -0
- package/src/codegen/rollback.ts +115 -0
- package/src/codegen/topo-sort.test.ts +69 -0
- package/src/codegen/topo-sort.ts +46 -0
- package/src/codegen/typecheck.test.ts +37 -0
- package/src/codegen/typecheck.ts +74 -0
- package/src/codegen/validate.test.ts +86 -0
- package/src/codegen/validate.ts +143 -0
- package/src/composite.test.ts +426 -0
- package/src/composite.ts +243 -0
- package/src/config.test.ts +91 -0
- package/src/config.ts +87 -0
- package/src/declarable.test.ts +160 -0
- package/src/declarable.ts +47 -0
- package/src/detectLexicon.test.ts +236 -0
- package/src/detectLexicon.ts +37 -0
- package/src/discovery/cache.test.ts +78 -0
- package/src/discovery/cache.ts +86 -0
- package/src/discovery/collect.test.ts +269 -0
- package/src/discovery/collect.ts +51 -0
- package/src/discovery/cycles.test.ts +238 -0
- package/src/discovery/cycles.ts +107 -0
- package/src/discovery/files.test.ts +154 -0
- package/src/discovery/files.ts +61 -0
- package/src/discovery/graph.test.ts +476 -0
- package/src/discovery/graph.ts +150 -0
- package/src/discovery/import.test.ts +199 -0
- package/src/discovery/import.ts +20 -0
- package/src/discovery/index.test.ts +272 -0
- package/src/discovery/index.ts +132 -0
- package/src/discovery/resolve.test.ts +267 -0
- package/src/discovery/resolve.ts +54 -0
- package/src/errors.test.ts +138 -0
- package/src/errors.ts +86 -0
- package/src/import/base-parser.test.ts +67 -0
- package/src/import/base-parser.ts +48 -0
- package/src/import/generator.ts +21 -0
- package/src/import/ir-utils.test.ts +103 -0
- package/src/import/ir-utils.ts +87 -0
- package/src/import/parser.ts +41 -0
- package/src/index.ts +60 -0
- package/src/intrinsic-interpolation.test.ts +91 -0
- package/src/intrinsic-interpolation.ts +89 -0
- package/src/intrinsic.test.ts +69 -0
- package/src/intrinsic.ts +43 -0
- package/src/lexicon-integrity.test.ts +94 -0
- package/src/lexicon-integrity.ts +69 -0
- package/src/lexicon-manifest.test.ts +101 -0
- package/src/lexicon-manifest.ts +71 -0
- package/src/lexicon-output.test.ts +182 -0
- package/src/lexicon-output.ts +82 -0
- package/src/lexicon-schema.test.ts +239 -0
- package/src/lexicon-schema.ts +144 -0
- package/src/lexicon.ts +212 -0
- package/src/lint/config-overrides.test.ts +254 -0
- package/src/lint/config.test.ts +644 -0
- package/src/lint/config.ts +375 -0
- package/src/lint/declarative.test.ts +256 -0
- package/src/lint/declarative.ts +187 -0
- package/src/lint/engine.test.ts +465 -0
- package/src/lint/engine.ts +172 -0
- package/src/lint/named-checks.test.ts +37 -0
- package/src/lint/named-checks.ts +33 -0
- package/src/lint/parser.test.ts +129 -0
- package/src/lint/parser.ts +42 -0
- package/src/lint/post-synth.test.ts +113 -0
- package/src/lint/post-synth.ts +76 -0
- package/src/lint/presets/relaxed.json +19 -0
- package/src/lint/presets/strict.json +19 -0
- package/src/lint/rule-loader.test.ts +67 -0
- package/src/lint/rule-loader.ts +67 -0
- package/src/lint/rule-options.test.ts +141 -0
- package/src/lint/rule.test.ts +196 -0
- package/src/lint/rule.ts +98 -0
- package/src/lint/rules/barrel-import-style.test.ts +80 -0
- package/src/lint/rules/barrel-import-style.ts +59 -0
- package/src/lint/rules/composite-scope.ts +55 -0
- package/src/lint/rules/cor017-composite-name-match.test.ts +107 -0
- package/src/lint/rules/cor017-composite-name-match.ts +108 -0
- package/src/lint/rules/cor018-composite-prefer-lexicon-type.test.ts +172 -0
- package/src/lint/rules/cor018-composite-prefer-lexicon-type.ts +167 -0
- package/src/lint/rules/declarable-naming-convention.test.ts +69 -0
- package/src/lint/rules/declarable-naming-convention.ts +70 -0
- package/src/lint/rules/enforce-barrel-import.test.ts +169 -0
- package/src/lint/rules/enforce-barrel-import.ts +81 -0
- package/src/lint/rules/enforce-barrel-ref.test.ts +114 -0
- package/src/lint/rules/enforce-barrel-ref.ts +75 -0
- package/src/lint/rules/evl001-non-literal-expression.test.ts +158 -0
- package/src/lint/rules/evl001-non-literal-expression.ts +149 -0
- package/src/lint/rules/evl002-control-flow-resource.test.ts +110 -0
- package/src/lint/rules/evl002-control-flow-resource.ts +61 -0
- package/src/lint/rules/evl003-dynamic-property-access.test.ts +63 -0
- package/src/lint/rules/evl003-dynamic-property-access.ts +41 -0
- package/src/lint/rules/evl004-spread-non-const.test.ts +130 -0
- package/src/lint/rules/evl004-spread-non-const.ts +111 -0
- package/src/lint/rules/evl005-resource-block-body.test.ts +59 -0
- package/src/lint/rules/evl005-resource-block-body.ts +49 -0
- package/src/lint/rules/evl006-barrel-usage.test.ts +63 -0
- package/src/lint/rules/evl006-barrel-usage.ts +95 -0
- package/src/lint/rules/evl007-invalid-siblings.test.ts +87 -0
- package/src/lint/rules/evl007-invalid-siblings.ts +139 -0
- package/src/lint/rules/evl008-unresolvable-barrel-ref.test.ts +118 -0
- package/src/lint/rules/evl008-unresolvable-barrel-ref.ts +140 -0
- package/src/lint/rules/evl009-composite-no-constant.test.ts +162 -0
- package/src/lint/rules/evl009-composite-no-constant.ts +171 -0
- package/src/lint/rules/evl010-composite-no-transform.test.ts +121 -0
- package/src/lint/rules/evl010-composite-no-transform.ts +69 -0
- package/src/lint/rules/export-required.test.ts +213 -0
- package/src/lint/rules/export-required.ts +158 -0
- package/src/lint/rules/file-declarable-limit.test.ts +148 -0
- package/src/lint/rules/file-declarable-limit.ts +96 -0
- package/src/lint/rules/flat-declarations.test.ts +210 -0
- package/src/lint/rules/flat-declarations.ts +70 -0
- package/src/lint/rules/index.ts +99 -0
- package/src/lint/rules/no-cyclic-declarable-ref.test.ts +135 -0
- package/src/lint/rules/no-cyclic-declarable-ref.ts +178 -0
- package/src/lint/rules/no-redundant-type-import.test.ts +129 -0
- package/src/lint/rules/no-redundant-type-import.ts +85 -0
- package/src/lint/rules/no-redundant-value-cast.test.ts +51 -0
- package/src/lint/rules/no-redundant-value-cast.ts +46 -0
- package/src/lint/rules/no-string-ref.test.ts +100 -0
- package/src/lint/rules/no-string-ref.ts +66 -0
- package/src/lint/rules/no-unused-declarable-import.test.ts +74 -0
- package/src/lint/rules/no-unused-declarable-import.ts +103 -0
- package/src/lint/rules/no-unused-declarable.test.ts +134 -0
- package/src/lint/rules/no-unused-declarable.ts +118 -0
- package/src/lint/rules/prefer-namespace-import.test.ts +102 -0
- package/src/lint/rules/prefer-namespace-import.ts +63 -0
- package/src/lint/rules/single-concern-file.test.ts +156 -0
- package/src/lint/rules/single-concern-file.ts +98 -0
- package/src/lint/rules/stale-barrel-types.ts +60 -0
- package/src/lint/selectors.test.ts +113 -0
- package/src/lint/selectors.ts +188 -0
- package/src/lsp/lexicon-providers.ts +191 -0
- package/src/lsp/types.ts +79 -0
- package/src/mcp/types.ts +22 -0
- package/src/project/scan.test.ts +178 -0
- package/src/project/scan.ts +182 -0
- package/src/project/sync.test.ts +87 -0
- package/src/project/sync.ts +46 -0
- package/src/project-validation.test.ts +64 -0
- package/src/project-validation.ts +79 -0
- package/src/pseudo-parameter.test.ts +39 -0
- package/src/pseudo-parameter.ts +47 -0
- package/src/runtime.ts +68 -0
- package/src/serializer-walker.test.ts +124 -0
- package/src/serializer-walker.ts +83 -0
- package/src/serializer.ts +42 -0
- package/src/sort.test.ts +290 -0
- package/src/sort.ts +58 -0
- package/src/stack-output.ts +82 -0
- package/src/types.test.ts +307 -0
- package/src/types.ts +46 -0
- package/src/utils.test.ts +195 -0
- package/src/utils.ts +46 -0
- package/src/validation.test.ts +308 -0
- package/src/validation.ts +50 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import { importCommand, type ImportOptions } from "./import";
|
|
3
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
|
|
8
|
+
describe("importCommand", () => {
|
|
9
|
+
let testDir: string;
|
|
10
|
+
let templateDir: string;
|
|
11
|
+
let outputDir: string;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
testDir = join(tmpdir(), `chant-import-test-${Date.now()}-${Math.random()}`);
|
|
15
|
+
templateDir = join(testDir, "templates");
|
|
16
|
+
outputDir = join(testDir, "output");
|
|
17
|
+
await mkdir(templateDir, { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
await rm(testDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("imports CloudFormation template", async () => {
|
|
25
|
+
const template = {
|
|
26
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
27
|
+
Description: "Test CloudFormation template",
|
|
28
|
+
Resources: {
|
|
29
|
+
MyBucket: {
|
|
30
|
+
Type: "AWS::S3::Bucket",
|
|
31
|
+
Properties: {
|
|
32
|
+
BucketName: "my-bucket",
|
|
33
|
+
VersioningConfiguration: {
|
|
34
|
+
Status: "Enabled",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const templatePath = join(templateDir, "template.json");
|
|
42
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
43
|
+
|
|
44
|
+
const options: ImportOptions = {
|
|
45
|
+
templatePath,
|
|
46
|
+
output: outputDir,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const result = await importCommand(options);
|
|
50
|
+
|
|
51
|
+
expect(result.success).toBe(true);
|
|
52
|
+
expect(result.lexicon).toBe("aws");
|
|
53
|
+
expect(result.generatedFiles.length).toBeGreaterThan(0);
|
|
54
|
+
expect(existsSync(outputDir)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("imports CloudFormation template with multiple resources", async () => {
|
|
58
|
+
const template = {
|
|
59
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
60
|
+
Resources: {
|
|
61
|
+
MyQueue: {
|
|
62
|
+
Type: "AWS::SQS::Queue",
|
|
63
|
+
Properties: {
|
|
64
|
+
QueueName: "my-queue",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const templatePath = join(templateDir, "template.json");
|
|
71
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
72
|
+
|
|
73
|
+
const options: ImportOptions = {
|
|
74
|
+
templatePath,
|
|
75
|
+
output: outputDir,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const result = await importCommand(options);
|
|
79
|
+
|
|
80
|
+
expect(result.success).toBe(true);
|
|
81
|
+
expect(result.lexicon).toBe("aws");
|
|
82
|
+
expect(result.generatedFiles.length).toBeGreaterThan(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("auto-detects AWS lexicon with AWSTemplateFormatVersion", async () => {
|
|
86
|
+
const template = {
|
|
87
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
88
|
+
Parameters: { Env: { Type: "String" } },
|
|
89
|
+
Resources: {},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const templatePath = join(templateDir, "template.json");
|
|
93
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
94
|
+
|
|
95
|
+
const result = await importCommand({
|
|
96
|
+
templatePath,
|
|
97
|
+
output: outputDir,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(result.lexicon).toBe("aws");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("auto-detects AWS lexicon with AWS:: resource types", async () => {
|
|
104
|
+
const template = {
|
|
105
|
+
Resources: {
|
|
106
|
+
Bucket: {
|
|
107
|
+
Type: "AWS::S3::Bucket",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const templatePath = join(templateDir, "template.json");
|
|
113
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
114
|
+
|
|
115
|
+
const result = await importCommand({
|
|
116
|
+
templatePath,
|
|
117
|
+
output: outputDir,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(result.lexicon).toBe("aws");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("fails for unknown lexicon", async () => {
|
|
124
|
+
const template = {
|
|
125
|
+
version: "1.0",
|
|
126
|
+
unknownField: {},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const templatePath = join(templateDir, "template.json");
|
|
130
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
131
|
+
|
|
132
|
+
const result = await importCommand({
|
|
133
|
+
templatePath,
|
|
134
|
+
output: outputDir,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(result.success).toBe(false);
|
|
138
|
+
expect(result.error).toContain("Could not detect");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("fails for non-existent template", async () => {
|
|
142
|
+
const result = await importCommand({
|
|
143
|
+
templatePath: "/nonexistent/template.json",
|
|
144
|
+
output: outputDir,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(result.success).toBe(false);
|
|
148
|
+
expect(result.error).toContain("not found");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("uses default output directory", async () => {
|
|
152
|
+
const template = {
|
|
153
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
154
|
+
Resources: {
|
|
155
|
+
Bucket: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const templatePath = join(templateDir, "template.json");
|
|
160
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
161
|
+
|
|
162
|
+
// Change to test dir so default ./infra/ is relative to it
|
|
163
|
+
const originalCwd = process.cwd();
|
|
164
|
+
process.chdir(testDir);
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const result = await importCommand({ templatePath });
|
|
168
|
+
|
|
169
|
+
expect(result.success).toBe(true);
|
|
170
|
+
expect(existsSync(join(testDir, "infra"))).toBe(true);
|
|
171
|
+
} finally {
|
|
172
|
+
process.chdir(originalCwd);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("generates TypeScript with correct syntax", async () => {
|
|
177
|
+
const template = {
|
|
178
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
179
|
+
Resources: {
|
|
180
|
+
DataBucket: {
|
|
181
|
+
Type: "AWS::S3::Bucket",
|
|
182
|
+
Properties: {
|
|
183
|
+
BucketName: "data-bucket",
|
|
184
|
+
VersioningConfiguration: {
|
|
185
|
+
Status: "Enabled",
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const templatePath = join(templateDir, "template.json");
|
|
193
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
194
|
+
|
|
195
|
+
const result = await importCommand({
|
|
196
|
+
templatePath,
|
|
197
|
+
output: outputDir,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(result.success).toBe(true);
|
|
201
|
+
|
|
202
|
+
// Find a generated file and check content
|
|
203
|
+
const mainFile = result.generatedFiles.find((f) => f.endsWith(".ts"));
|
|
204
|
+
expect(mainFile).toBeDefined();
|
|
205
|
+
|
|
206
|
+
const content = readFileSync(join(outputDir, mainFile!), "utf-8");
|
|
207
|
+
expect(content).toContain("import {");
|
|
208
|
+
expect(content).toContain("export const");
|
|
209
|
+
expect(content).toContain("Bucket");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("warns about non-empty output directory", async () => {
|
|
213
|
+
const template = {
|
|
214
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
215
|
+
Resources: {
|
|
216
|
+
Bucket: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const templatePath = join(templateDir, "template.json");
|
|
221
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
222
|
+
|
|
223
|
+
// Create output dir with existing file
|
|
224
|
+
await mkdir(outputDir, { recursive: true });
|
|
225
|
+
await writeFile(join(outputDir, "existing.ts"), "// existing");
|
|
226
|
+
|
|
227
|
+
const result = await importCommand({
|
|
228
|
+
templatePath,
|
|
229
|
+
output: outputDir,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(result.success).toBe(true);
|
|
233
|
+
expect(result.warnings.some((w) => w.includes("not empty"))).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("force overwrites existing files", async () => {
|
|
237
|
+
const template = {
|
|
238
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
239
|
+
Resources: {
|
|
240
|
+
Bucket: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const templatePath = join(templateDir, "template.json");
|
|
245
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
246
|
+
|
|
247
|
+
// Create output dir with existing file
|
|
248
|
+
await mkdir(outputDir, { recursive: true });
|
|
249
|
+
await writeFile(join(outputDir, "main.ts"), "// old content");
|
|
250
|
+
|
|
251
|
+
const result = await importCommand({
|
|
252
|
+
templatePath,
|
|
253
|
+
output: outputDir,
|
|
254
|
+
force: true,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(result.success).toBe(true);
|
|
258
|
+
|
|
259
|
+
// Check that main.ts was overwritten
|
|
260
|
+
const content = readFileSync(join(outputDir, "main.ts"), "utf-8");
|
|
261
|
+
expect(content).not.toContain("old content");
|
|
262
|
+
expect(content).toContain("Bucket");
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("organizes resources by category for large templates", async () => {
|
|
266
|
+
const template = {
|
|
267
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
268
|
+
Resources: {
|
|
269
|
+
Bucket1: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
270
|
+
Bucket2: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
271
|
+
Queue1: { Type: "AWS::SQS::Queue", Properties: {} },
|
|
272
|
+
LB1: { Type: "AWS::ElasticLoadBalancingV2::LoadBalancer", Properties: {} },
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const templatePath = join(templateDir, "template.json");
|
|
277
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
278
|
+
|
|
279
|
+
const result = await importCommand({
|
|
280
|
+
templatePath,
|
|
281
|
+
output: outputDir,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(result.success).toBe(true);
|
|
285
|
+
// With 4 resources, should create separate files
|
|
286
|
+
expect(result.generatedFiles.length).toBeGreaterThan(1);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("creates index.ts for organized imports", async () => {
|
|
290
|
+
const template = {
|
|
291
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
292
|
+
Resources: {
|
|
293
|
+
Bucket1: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
294
|
+
Bucket2: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
295
|
+
Lambda1: { Type: "AWS::Lambda::Function", Properties: {} },
|
|
296
|
+
LB1: { Type: "AWS::ElasticLoadBalancingV2::LoadBalancer", Properties: {} },
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const templatePath = join(templateDir, "template.json");
|
|
301
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
302
|
+
|
|
303
|
+
const result = await importCommand({
|
|
304
|
+
templatePath,
|
|
305
|
+
output: outputDir,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(result.success).toBe(true);
|
|
309
|
+
|
|
310
|
+
if (result.generatedFiles.includes("index.ts")) {
|
|
311
|
+
const indexContent = readFileSync(join(outputDir, "index.ts"), "utf-8");
|
|
312
|
+
expect(indexContent).toContain("export {");
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("handles template with parameters", async () => {
|
|
317
|
+
const template = {
|
|
318
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
319
|
+
Parameters: {
|
|
320
|
+
Environment: { Type: "String" },
|
|
321
|
+
BucketName: { Type: "String" },
|
|
322
|
+
},
|
|
323
|
+
Resources: {
|
|
324
|
+
MyBucket: {
|
|
325
|
+
Type: "AWS::S3::Bucket",
|
|
326
|
+
Properties: {
|
|
327
|
+
BucketName: { Ref: "BucketName" },
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const templatePath = join(templateDir, "template.json");
|
|
334
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
335
|
+
|
|
336
|
+
const result = await importCommand({
|
|
337
|
+
templatePath,
|
|
338
|
+
output: outputDir,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result.success).toBe(true);
|
|
342
|
+
|
|
343
|
+
// Find main file and check for Parameter imports
|
|
344
|
+
const files = result.generatedFiles.filter((f) => f.endsWith(".ts"));
|
|
345
|
+
let hasParameter = false;
|
|
346
|
+
for (const file of files) {
|
|
347
|
+
const content = readFileSync(join(outputDir, file), "utf-8");
|
|
348
|
+
if (content.includes("Parameter")) {
|
|
349
|
+
hasParameter = true;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
expect(hasParameter).toBe(true);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("prints generated files on success", async () => {
|
|
357
|
+
const template = {
|
|
358
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
359
|
+
Resources: {
|
|
360
|
+
Bucket: { Type: "AWS::S3::Bucket", Properties: {} },
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const templatePath = join(templateDir, "template.json");
|
|
365
|
+
await writeFile(templatePath, JSON.stringify(template));
|
|
366
|
+
|
|
367
|
+
const result = await importCommand({
|
|
368
|
+
templatePath,
|
|
369
|
+
output: outputDir,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
expect(result.success).toBe(true);
|
|
373
|
+
expect(result.generatedFiles.length).toBeGreaterThan(0);
|
|
374
|
+
// Each file should be a .ts file
|
|
375
|
+
for (const file of result.generatedFiles) {
|
|
376
|
+
expect(file.endsWith(".ts")).toBe(true);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
});
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
2
|
+
import { join, resolve, basename } from "path";
|
|
3
|
+
import { formatSuccess, formatWarning, formatError } from "../format";
|
|
4
|
+
import type { TemplateIR, ResourceIR, TemplateParser } from "../../import/parser";
|
|
5
|
+
import type { GeneratedFile, TypeScriptGenerator } from "../../import/generator";
|
|
6
|
+
import { loadPlugins, resolveProjectLexicons } from "../plugins";
|
|
7
|
+
import type { LexiconPlugin } from "../../lexicon";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Import command options
|
|
11
|
+
*/
|
|
12
|
+
export interface ImportOptions {
|
|
13
|
+
/** Path to template file */
|
|
14
|
+
templatePath: string;
|
|
15
|
+
/** Output directory (defaults to ./infra/) */
|
|
16
|
+
output?: string;
|
|
17
|
+
/** Force overwrite existing files */
|
|
18
|
+
force?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Import command result
|
|
23
|
+
*/
|
|
24
|
+
export interface ImportResult {
|
|
25
|
+
/** Whether import succeeded */
|
|
26
|
+
success: boolean;
|
|
27
|
+
/** Generated files */
|
|
28
|
+
generatedFiles: string[];
|
|
29
|
+
/** Warning messages */
|
|
30
|
+
warnings: string[];
|
|
31
|
+
/** Error message if failed */
|
|
32
|
+
error?: string;
|
|
33
|
+
/** Detected lexicon */
|
|
34
|
+
lexicon?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resource category for organizing files
|
|
39
|
+
*/
|
|
40
|
+
type ResourceCategory = "storage" | "compute" | "network" | "other";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Detect which plugin handles a template by asking each plugin.
|
|
44
|
+
* @param data - Parsed JSON object
|
|
45
|
+
* @param plugins - Loaded lexicon plugins
|
|
46
|
+
* @returns The matching plugin, or undefined if none match
|
|
47
|
+
*/
|
|
48
|
+
function detectPlugin(data: unknown, plugins: LexiconPlugin[]): LexiconPlugin | undefined {
|
|
49
|
+
for (const plugin of plugins) {
|
|
50
|
+
if (plugin.detectTemplate?.(data)) {
|
|
51
|
+
return plugin;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the category for a resource type
|
|
59
|
+
*/
|
|
60
|
+
function getResourceCategory(type: string): ResourceCategory {
|
|
61
|
+
const typeLower = type.toLowerCase();
|
|
62
|
+
|
|
63
|
+
// Storage resources
|
|
64
|
+
if (typeLower.includes("bucket") || typeLower.includes("storage") || typeLower.includes("queue")) {
|
|
65
|
+
return "storage";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Compute resources
|
|
69
|
+
if (typeLower.includes("container") || typeLower.includes("service") || typeLower.includes("function")) {
|
|
70
|
+
return "compute";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Network resources
|
|
74
|
+
if (typeLower.includes("loadbalancer") || typeLower.includes("lb") || typeLower.includes("network")) {
|
|
75
|
+
return "network";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return "other";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Organize resources into categories
|
|
83
|
+
*/
|
|
84
|
+
function organizeByCategory(ir: TemplateIR): Map<ResourceCategory, ResourceIR[]> {
|
|
85
|
+
const categories = new Map<ResourceCategory, ResourceIR[]>();
|
|
86
|
+
|
|
87
|
+
for (const resource of ir.resources) {
|
|
88
|
+
const category = getResourceCategory(resource.type);
|
|
89
|
+
const existing = categories.get(category) ?? [];
|
|
90
|
+
existing.push(resource);
|
|
91
|
+
categories.set(category, existing);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return categories;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate organized files with separate modules
|
|
99
|
+
*/
|
|
100
|
+
function generateOrganizedFiles(
|
|
101
|
+
ir: TemplateIR,
|
|
102
|
+
generator: TypeScriptGenerator,
|
|
103
|
+
): GeneratedFile[] {
|
|
104
|
+
const files: GeneratedFile[] = [];
|
|
105
|
+
const categories = organizeByCategory(ir);
|
|
106
|
+
const exports: string[] = [];
|
|
107
|
+
|
|
108
|
+
// If all resources fit in one file, just generate main.ts
|
|
109
|
+
if (ir.resources.length <= 3) {
|
|
110
|
+
return generator.generate(ir);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Generate files for each category
|
|
114
|
+
for (const [category, resources] of categories) {
|
|
115
|
+
if (resources.length === 0) continue;
|
|
116
|
+
|
|
117
|
+
const categoryIr: TemplateIR = {
|
|
118
|
+
parameters: category === "other" ? ir.parameters : [],
|
|
119
|
+
resources,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const generated = generator.generate(categoryIr);
|
|
123
|
+
const fileName = `${category}.ts`;
|
|
124
|
+
|
|
125
|
+
files.push({
|
|
126
|
+
path: fileName,
|
|
127
|
+
content: generated[0].content,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Track exports
|
|
131
|
+
for (const resource of resources) {
|
|
132
|
+
const varName = resource.logicalId.charAt(0).toLowerCase() + resource.logicalId.slice(1);
|
|
133
|
+
exports.push(`export { ${varName} } from "./${category}";`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Handle parameters separately if not included in other category
|
|
138
|
+
if (ir.parameters.length > 0 && !categories.has("other")) {
|
|
139
|
+
const paramsIr: TemplateIR = {
|
|
140
|
+
parameters: ir.parameters,
|
|
141
|
+
resources: [],
|
|
142
|
+
};
|
|
143
|
+
const generated = generator.generate(paramsIr);
|
|
144
|
+
files.push({
|
|
145
|
+
path: "parameters.ts",
|
|
146
|
+
content: generated[0].content,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
for (const param of ir.parameters) {
|
|
150
|
+
const varName = param.name.charAt(0).toLowerCase() + param.name.slice(1);
|
|
151
|
+
exports.push(`export { ${varName} } from "./parameters";`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Generate index.ts
|
|
156
|
+
if (exports.length > 0) {
|
|
157
|
+
files.push({
|
|
158
|
+
path: "index.ts",
|
|
159
|
+
content: exports.join("\n") + "\n",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return files;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Execute the import command
|
|
168
|
+
*/
|
|
169
|
+
export async function importCommand(options: ImportOptions): Promise<ImportResult> {
|
|
170
|
+
const templatePath = resolve(options.templatePath);
|
|
171
|
+
const outputDir = resolve(options.output ?? "./infra/");
|
|
172
|
+
const generatedFiles: string[] = [];
|
|
173
|
+
const warnings: string[] = [];
|
|
174
|
+
|
|
175
|
+
// Check if template exists
|
|
176
|
+
if (!existsSync(templatePath)) {
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
generatedFiles: [],
|
|
180
|
+
warnings: [],
|
|
181
|
+
error: `Template file not found: ${templatePath}`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Read template content
|
|
186
|
+
let content: string;
|
|
187
|
+
try {
|
|
188
|
+
content = readFileSync(templatePath, "utf-8");
|
|
189
|
+
} catch (err) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
generatedFiles: [],
|
|
193
|
+
warnings: [],
|
|
194
|
+
error: `Failed to read template: ${err}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Load plugins and detect lexicon
|
|
199
|
+
let data: unknown;
|
|
200
|
+
try {
|
|
201
|
+
data = JSON.parse(content);
|
|
202
|
+
} catch {
|
|
203
|
+
return {
|
|
204
|
+
success: false,
|
|
205
|
+
generatedFiles: [],
|
|
206
|
+
warnings: [],
|
|
207
|
+
error: "Template is not valid JSON.",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Load plugins from project config, falling back to all installed lexicons
|
|
212
|
+
let plugins: LexiconPlugin[];
|
|
213
|
+
try {
|
|
214
|
+
const lexiconNames = await resolveProjectLexicons(resolve("."));
|
|
215
|
+
plugins = await loadPlugins(lexiconNames);
|
|
216
|
+
} catch {
|
|
217
|
+
plugins = [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// If no plugins resolved (no config, no source files), try common lexicons
|
|
221
|
+
if (plugins.length === 0) {
|
|
222
|
+
try {
|
|
223
|
+
plugins = await loadPlugins(["aws"]);
|
|
224
|
+
} catch {
|
|
225
|
+
// No lexicons available at all
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const plugin = detectPlugin(data, plugins);
|
|
230
|
+
if (!plugin) {
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
generatedFiles: [],
|
|
234
|
+
warnings: [],
|
|
235
|
+
error: "Could not detect template lexicon. No installed lexicon recognizes this template.",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const lexicon = plugin.name;
|
|
240
|
+
|
|
241
|
+
if (!plugin.templateParser || !plugin.templateGenerator) {
|
|
242
|
+
return {
|
|
243
|
+
success: false,
|
|
244
|
+
generatedFiles: [],
|
|
245
|
+
warnings: [],
|
|
246
|
+
error: `Lexicon "${plugin.name}" does not support template import.`,
|
|
247
|
+
lexicon,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Parse template
|
|
252
|
+
let ir: TemplateIR;
|
|
253
|
+
try {
|
|
254
|
+
const parser = plugin.templateParser();
|
|
255
|
+
ir = parser.parse(content);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
return {
|
|
258
|
+
success: false,
|
|
259
|
+
generatedFiles: [],
|
|
260
|
+
warnings: [],
|
|
261
|
+
error: `Failed to parse template: ${err}`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const generator = plugin.templateGenerator();
|
|
266
|
+
|
|
267
|
+
// Check output directory
|
|
268
|
+
if (existsSync(outputDir) && !options.force) {
|
|
269
|
+
const files = require("fs").readdirSync(outputDir);
|
|
270
|
+
if (files.length > 0) {
|
|
271
|
+
warnings.push(`Output directory ${outputDir} is not empty. Use --force to overwrite.`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Create output directory
|
|
276
|
+
if (!existsSync(outputDir)) {
|
|
277
|
+
mkdirSync(outputDir, { recursive: true });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Generate files
|
|
281
|
+
const files = generateOrganizedFiles(ir, generator);
|
|
282
|
+
|
|
283
|
+
// Write files
|
|
284
|
+
for (const file of files) {
|
|
285
|
+
const filePath = join(outputDir, file.path);
|
|
286
|
+
const dirPath = join(outputDir, file.path.split("/").slice(0, -1).join("/"));
|
|
287
|
+
|
|
288
|
+
if (dirPath && !existsSync(dirPath)) {
|
|
289
|
+
mkdirSync(dirPath, { recursive: true });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Check for existing file
|
|
293
|
+
if (existsSync(filePath) && !options.force) {
|
|
294
|
+
warnings.push(`File ${file.path} already exists, skipping`);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
writeFileSync(filePath, file.content);
|
|
299
|
+
generatedFiles.push(file.path);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
success: true,
|
|
304
|
+
generatedFiles,
|
|
305
|
+
warnings,
|
|
306
|
+
lexicon,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Print import result
|
|
312
|
+
*/
|
|
313
|
+
export function printImportResult(result: ImportResult): void {
|
|
314
|
+
if (!result.success) {
|
|
315
|
+
console.error(formatError({ message: result.error ?? "Import failed" }));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (const warning of result.warnings) {
|
|
320
|
+
console.error(formatWarning({ message: warning }));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (result.lexicon) {
|
|
324
|
+
console.log(`Detected lexicon: ${result.lexicon}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (result.generatedFiles.length > 0) {
|
|
328
|
+
console.log(formatSuccess("Generated files:"));
|
|
329
|
+
for (const file of result.generatedFiles) {
|
|
330
|
+
console.log(` ${file}`);
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
console.log("No files generated.");
|
|
334
|
+
}
|
|
335
|
+
}
|