@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
package/README.md
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# @intentius/chant
|
|
2
|
+
|
|
3
|
+
> Part of the [chant](../../README.md) monorepo. Not yet published to npm.
|
|
4
|
+
|
|
5
|
+
Core functionality for chant - a lexicon-agnostic declarative infrastructure specification system.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
This package provides the foundational types, interfaces, and utilities that power chant's declarative infrastructure system. It includes:
|
|
10
|
+
|
|
11
|
+
- **Type system**: Declarables, Intrinsics, Parameters, and Outputs
|
|
12
|
+
- **Discovery system**: File scanning, module loading, and entity collection
|
|
13
|
+
- **Build pipeline**: Dependency resolution, topological sorting, and serialization
|
|
14
|
+
- **Error handling**: Structured errors for discovery, build, and lint phases
|
|
15
|
+
- **Lint system**: Infrastructure code validation framework
|
|
16
|
+
- **Template import**: Convert external templates to TypeScript
|
|
17
|
+
- **Codegen infrastructure**: Reusable pipelines for generating, naming, packaging, and fetching schemas
|
|
18
|
+
- **LSP providers**: Generic lexicon-based completion and hover helpers
|
|
19
|
+
- **Runtime factories**: `createResource` and `createProperty` for Declarable-marked constructors
|
|
20
|
+
|
|
21
|
+
## Key Concepts
|
|
22
|
+
|
|
23
|
+
### Declarables
|
|
24
|
+
|
|
25
|
+
A `Declarable` is any entity that can be declared in an infrastructure specification:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { isDeclarable, DECLARABLE_MARKER } from "@intentius/chant";
|
|
29
|
+
|
|
30
|
+
if (isDeclarable(value)) {
|
|
31
|
+
console.log(value.entityType);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Intrinsics
|
|
36
|
+
|
|
37
|
+
An `Intrinsic` represents a lexicon-provided function resolved at build time:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { isIntrinsic, INTRINSIC_MARKER } from "@intentius/chant";
|
|
41
|
+
|
|
42
|
+
if (isIntrinsic(value)) {
|
|
43
|
+
const serialized = value.toJSON();
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### AttrRef
|
|
48
|
+
|
|
49
|
+
Reference attributes of other entities with deferred resolution:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { AttrRef } from "@intentius/chant";
|
|
53
|
+
|
|
54
|
+
const bucket = {};
|
|
55
|
+
const arnRef = new AttrRef(bucket, "arn");
|
|
56
|
+
|
|
57
|
+
// Later, during discovery
|
|
58
|
+
arnRef._setLogicalName("MyBucket");
|
|
59
|
+
arnRef.toJSON(); // { "Fn::GetAttr": ["MyBucket", "arn"] }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Discovery System
|
|
63
|
+
|
|
64
|
+
Discover and collect infrastructure entities from TypeScript files:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { discover } from "@intentius/chant";
|
|
68
|
+
|
|
69
|
+
const result = await discover("./src/infra");
|
|
70
|
+
// result.entities: Map of entity name to Declarable
|
|
71
|
+
// result.dependencies: Dependency graph
|
|
72
|
+
// result.sourceFiles: Discovered files
|
|
73
|
+
// result.errors: Any errors encountered
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Individual Discovery Functions
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import {
|
|
80
|
+
findInfraFiles,
|
|
81
|
+
importModule,
|
|
82
|
+
collectEntities,
|
|
83
|
+
resolveAttrRefs,
|
|
84
|
+
buildDependencyGraph,
|
|
85
|
+
detectCycles,
|
|
86
|
+
topologicalSort,
|
|
87
|
+
} from "@intentius/chant";
|
|
88
|
+
|
|
89
|
+
// Find all .ts files (excluding tests and node_modules)
|
|
90
|
+
const files = await findInfraFiles("./src");
|
|
91
|
+
|
|
92
|
+
// Import a module
|
|
93
|
+
const exports = await importModule("./src/resources.ts");
|
|
94
|
+
|
|
95
|
+
// Collect declarables from modules
|
|
96
|
+
const entities = collectEntities([
|
|
97
|
+
{ file: "resources.ts", exports }
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
// Resolve attribute references
|
|
101
|
+
resolveAttrRefs(entities);
|
|
102
|
+
|
|
103
|
+
// Build dependency graph
|
|
104
|
+
const deps = buildDependencyGraph(entities);
|
|
105
|
+
|
|
106
|
+
// Check for cycles
|
|
107
|
+
const cycles = detectCycles(deps);
|
|
108
|
+
|
|
109
|
+
// Topological sort
|
|
110
|
+
const order = topologicalSort(deps);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Build Pipeline
|
|
114
|
+
|
|
115
|
+
Build infrastructure specifications with lexicon-specific serialization:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { build } from "@intentius/chant";
|
|
119
|
+
import { myLexicon } from "@intentius/chant-lexicon-myplatform";
|
|
120
|
+
|
|
121
|
+
const result = await build("./src/infra", [myLexicon]);
|
|
122
|
+
// result.outputs: Map of lexicon name to serialized output
|
|
123
|
+
// result.entities: Discovered entities
|
|
124
|
+
// result.warnings: Build warnings
|
|
125
|
+
// result.errors: Any errors
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Error Handling
|
|
129
|
+
|
|
130
|
+
Structured error types for different phases:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { DiscoveryError, BuildError, LintError } from "@intentius/chant";
|
|
134
|
+
|
|
135
|
+
// Discovery phase errors
|
|
136
|
+
throw new DiscoveryError("config.ts", "Module not found", "import");
|
|
137
|
+
|
|
138
|
+
// Build phase errors
|
|
139
|
+
throw new BuildError("MyResource", "Invalid configuration");
|
|
140
|
+
|
|
141
|
+
// Lint errors with location
|
|
142
|
+
const error = new LintError(
|
|
143
|
+
"config.ts",
|
|
144
|
+
10,
|
|
145
|
+
5,
|
|
146
|
+
"no-unused",
|
|
147
|
+
"Variable is declared but never used"
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// All errors support JSON serialization
|
|
151
|
+
const json = error.toJSON();
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Lint System
|
|
155
|
+
|
|
156
|
+
Validate infrastructure code with custom rules:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { parseFile } from "@intentius/chant";
|
|
160
|
+
import type { LintRule, LintContext } from "@intentius/chant";
|
|
161
|
+
|
|
162
|
+
// Parse a file for linting
|
|
163
|
+
const sourceFile = parseFile("./src/infra/main.ts");
|
|
164
|
+
|
|
165
|
+
// Define a lint rule
|
|
166
|
+
const myRule: LintRule = {
|
|
167
|
+
id: "my-rule",
|
|
168
|
+
severity: "error",
|
|
169
|
+
category: "correctness",
|
|
170
|
+
check(context: LintContext): LintDiagnostic[] {
|
|
171
|
+
// Analyze context.sourceFile
|
|
172
|
+
return [];
|
|
173
|
+
},
|
|
174
|
+
fix(context: LintContext): LintFix[] {
|
|
175
|
+
// Optional: provide auto-fixes
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Serializer Interface
|
|
182
|
+
|
|
183
|
+
Implement lexicon-specific serialization:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import type { Serializer, Declarable } from "@intentius/chant";
|
|
187
|
+
|
|
188
|
+
const mySerializer: Serializer = {
|
|
189
|
+
name: "my-lexicon",
|
|
190
|
+
rulePrefix: "ML",
|
|
191
|
+
serialize(entities: Map<string, Declarable>): string {
|
|
192
|
+
// Serialize entities to lexicon-specific format
|
|
193
|
+
return JSON.stringify({ entities: Array.from(entities.keys()) });
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Utilities
|
|
199
|
+
|
|
200
|
+
Helper functions for working with declarables:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import {
|
|
204
|
+
getAttributes,
|
|
205
|
+
getLogicalName,
|
|
206
|
+
LOGICAL_NAME_SYMBOL
|
|
207
|
+
} from "@intentius/chant";
|
|
208
|
+
|
|
209
|
+
// Get attribute names that have AttrRef values
|
|
210
|
+
const attrs = getAttributes(myEntity);
|
|
211
|
+
// ["arn", "endpoint"]
|
|
212
|
+
|
|
213
|
+
// Get the logical name assigned during discovery
|
|
214
|
+
(entity as any)[LOGICAL_NAME_SYMBOL] = "MyResource";
|
|
215
|
+
const name = getLogicalName(entity);
|
|
216
|
+
// "MyResource"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Lexicon Detection
|
|
220
|
+
|
|
221
|
+
Automatically detect which lexicon is being used:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import { detectLexicon } from "@intentius/chant";
|
|
225
|
+
|
|
226
|
+
const lexicon = await detectLexicon(["./src/infra/main.ts"]);
|
|
227
|
+
// "aws" (currently the only supported lexicon)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Template Import System
|
|
231
|
+
|
|
232
|
+
Convert external templates to TypeScript:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import type {
|
|
236
|
+
TemplateIR,
|
|
237
|
+
TemplateParser,
|
|
238
|
+
TypeScriptGenerator
|
|
239
|
+
} from "@intentius/chant";
|
|
240
|
+
|
|
241
|
+
// Parser converts external format to IR
|
|
242
|
+
const parser: TemplateParser = {
|
|
243
|
+
parse(content: string): TemplateIR {
|
|
244
|
+
return { resources: [], parameters: [] };
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Generator converts IR to TypeScript
|
|
249
|
+
const generator: TypeScriptGenerator = {
|
|
250
|
+
generate(ir: TemplateIR): GeneratedFile[] {
|
|
251
|
+
return [{ path: "resources.ts", content: "..." }];
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Codegen Infrastructure
|
|
257
|
+
|
|
258
|
+
Core provides reusable infrastructure for lexicon code generation pipelines. Lexicons supply provider-specific callbacks; core handles orchestration.
|
|
259
|
+
|
|
260
|
+
### Runtime Factories
|
|
261
|
+
|
|
262
|
+
Create Declarable-marked constructors for generated resource and property types:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { createResource, createProperty } from "@intentius/chant/runtime";
|
|
266
|
+
|
|
267
|
+
// Creates a constructor that stamps out Declarable objects
|
|
268
|
+
const MyResource = createResource("Provider::Service::Type", "my-lexicon", { arn: "Arn" });
|
|
269
|
+
const MyProperty = createProperty("Provider::Service::Type.PropType", "my-lexicon");
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Naming Strategy
|
|
273
|
+
|
|
274
|
+
Collision-free TypeScript class name generation, parameterized by data tables:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { NamingStrategy, type NamingConfig } from "@intentius/chant/codegen/naming";
|
|
278
|
+
|
|
279
|
+
const config: NamingConfig = {
|
|
280
|
+
priorityNames: { "Provider::S3::Bucket": "Bucket" },
|
|
281
|
+
priorityAliases: {},
|
|
282
|
+
priorityPropertyAliases: {},
|
|
283
|
+
serviceAbbreviations: { "ElasticLoadBalancingV2": "Elbv2" },
|
|
284
|
+
shortName: (t) => t.split("::").pop()!,
|
|
285
|
+
serviceName: (t) => t.split("::")[1],
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const naming = new NamingStrategy(inputs, config);
|
|
289
|
+
naming.resolve("Provider::S3::Bucket"); // "Bucket"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Generation Pipeline
|
|
293
|
+
|
|
294
|
+
Generic pipeline orchestration — step sequencing, logging, warning collection, stats counting:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import { generatePipeline, type GeneratePipelineConfig } from "@intentius/chant/codegen/generate";
|
|
298
|
+
|
|
299
|
+
const config: GeneratePipelineConfig<MyParsedResult> = {
|
|
300
|
+
fetchSchemas: async (opts) => { /* ... */ },
|
|
301
|
+
parseSchema: (name, data) => { /* ... */ },
|
|
302
|
+
createNaming: (results) => new MyNamingStrategy(results),
|
|
303
|
+
generateRegistry: (results, naming) => { /* ... */ },
|
|
304
|
+
generateTypes: (results, naming) => { /* ... */ },
|
|
305
|
+
generateRuntimeIndex: (results, naming) => { /* ... */ },
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const result = await generatePipeline(config, { verbose: true });
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Packaging Pipeline
|
|
312
|
+
|
|
313
|
+
Bundles generation output into a distributable `BundleSpec`:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { packagePipeline, type PackagePipelineConfig } from "@intentius/chant/codegen/package";
|
|
317
|
+
|
|
318
|
+
const result = await packagePipeline({
|
|
319
|
+
generate: (opts) => myGenerate(opts),
|
|
320
|
+
buildManifest: (genResult) => ({ name: "my-lexicon", version: "1.0.0", /* ... */ }),
|
|
321
|
+
srcDir: __dirname,
|
|
322
|
+
collectSkills: () => new Map(),
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Fetch Utilities
|
|
327
|
+
|
|
328
|
+
HTTP fetch with local file caching and zip extraction:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { fetchWithCache, extractFromZip } from "@intentius/chant/codegen/fetch";
|
|
332
|
+
|
|
333
|
+
const data = await fetchWithCache({ url: "https://...", cacheFile: "/tmp/cache.zip" });
|
|
334
|
+
const files = await extractFromZip(data, (name) => name.endsWith(".json"));
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### LSP Providers
|
|
338
|
+
|
|
339
|
+
Generic lexicon-based completion and hover:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { LexiconIndex, lexiconCompletions, lexiconHover } from "@intentius/chant/lsp/lexicon-providers";
|
|
343
|
+
|
|
344
|
+
const index = new LexiconIndex(lexiconData);
|
|
345
|
+
const completions = lexiconCompletions(ctx, index, "My resource");
|
|
346
|
+
const hover = lexiconHover(ctx, index);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## TypeScript Support
|
|
350
|
+
|
|
351
|
+
This package is written in TypeScript and provides full type definitions.
|
|
352
|
+
|
|
353
|
+
## Documentation
|
|
354
|
+
|
|
355
|
+
- [Core Concepts](../../docs/src/content/docs/guides/core-concepts.md) - Detailed guide on declarables, intrinsics, and the type system
|
|
356
|
+
- [Testing Guide](../../TESTING.md) - Testing patterns and utilities
|
|
357
|
+
|
|
358
|
+
## Related Packages
|
|
359
|
+
|
|
360
|
+
- `@intentius/chant-lexicon-aws` - AWS CloudFormation lexicon
|
|
361
|
+
- `@intentius/chant-test-utils` - Testing utilities
|
|
362
|
+
|
|
363
|
+
## License
|
|
364
|
+
|
|
365
|
+
See the main project LICENSE file.
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intentius/chant",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": ["src/"],
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"chant": "./src/cli/main.ts"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./src/index.ts",
|
|
14
|
+
"./cli": "./src/cli/index.ts",
|
|
15
|
+
"./cli/*": "./src/cli/*",
|
|
16
|
+
"./*": "./src/*"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"fflate": "^0.8.2",
|
|
20
|
+
"zod": "^4.3.6"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { AttrRef } from "./attrref";
|
|
3
|
+
import { INTRINSIC_MARKER, isIntrinsic } from "./intrinsic";
|
|
4
|
+
|
|
5
|
+
describe("AttrRef", () => {
|
|
6
|
+
test("creates AttrRef with parent and attribute", () => {
|
|
7
|
+
const parent = { id: "test" };
|
|
8
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
9
|
+
|
|
10
|
+
expect(attrRef).toBeInstanceOf(AttrRef);
|
|
11
|
+
expect(attrRef.attribute).toBe("Arn");
|
|
12
|
+
expect(attrRef.parent.deref()).toBe(parent);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("implements Intrinsic interface", () => {
|
|
16
|
+
const parent = {};
|
|
17
|
+
const attrRef = new AttrRef(parent, "Name");
|
|
18
|
+
|
|
19
|
+
expect(attrRef[INTRINSIC_MARKER]).toBe(true);
|
|
20
|
+
expect(isIntrinsic(attrRef)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("stores parent as WeakRef", () => {
|
|
24
|
+
const parent = { id: "test" };
|
|
25
|
+
const attrRef = new AttrRef(parent, "Id");
|
|
26
|
+
|
|
27
|
+
expect(attrRef.parent).toBeInstanceOf(WeakRef);
|
|
28
|
+
expect(attrRef.parent.deref()).toBe(parent);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("getLogicalName returns undefined before set", () => {
|
|
32
|
+
const parent = {};
|
|
33
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
34
|
+
|
|
35
|
+
expect(attrRef.getLogicalName()).toBeUndefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("getLogicalName returns the logical name after set", () => {
|
|
39
|
+
const parent = {};
|
|
40
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
41
|
+
|
|
42
|
+
attrRef._setLogicalName("MyResource");
|
|
43
|
+
expect(attrRef.getLogicalName()).toBe("MyResource");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("_setLogicalName sets the logical name", () => {
|
|
47
|
+
const parent = {};
|
|
48
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
49
|
+
|
|
50
|
+
attrRef._setLogicalName("MyResource");
|
|
51
|
+
|
|
52
|
+
const json = attrRef.toJSON();
|
|
53
|
+
expect(json).toEqual({
|
|
54
|
+
__attrRef: { entity: "MyResource", attribute: "Arn" },
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("toJSON returns __attrRef when logical name is set", () => {
|
|
59
|
+
const parent = {};
|
|
60
|
+
const attrRef = new AttrRef(parent, "DomainName");
|
|
61
|
+
|
|
62
|
+
attrRef._setLogicalName("MyBucket");
|
|
63
|
+
|
|
64
|
+
const json = attrRef.toJSON();
|
|
65
|
+
expect(json).toEqual({
|
|
66
|
+
__attrRef: { entity: "MyBucket", attribute: "DomainName" },
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("toJSON throws when logical name is not set", () => {
|
|
71
|
+
const parent = {};
|
|
72
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
73
|
+
|
|
74
|
+
expect(() => attrRef.toJSON()).toThrow(
|
|
75
|
+
'Cannot serialize AttrRef for attribute "Arn": logical name not set'
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("toJSON throws with correct attribute name in error message", () => {
|
|
80
|
+
const parent = {};
|
|
81
|
+
const attrRef = new AttrRef(parent, "CustomAttribute");
|
|
82
|
+
|
|
83
|
+
expect(() => attrRef.toJSON()).toThrow(
|
|
84
|
+
'Cannot serialize AttrRef for attribute "CustomAttribute": logical name not set'
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("handles different attribute names", () => {
|
|
89
|
+
const parent = {};
|
|
90
|
+
const attributes = ["Arn", "Name", "Id", "DomainName", "PhysicalResourceId"];
|
|
91
|
+
|
|
92
|
+
for (const attr of attributes) {
|
|
93
|
+
const attrRef = new AttrRef(parent, attr);
|
|
94
|
+
attrRef._setLogicalName("Resource");
|
|
95
|
+
|
|
96
|
+
const json = attrRef.toJSON();
|
|
97
|
+
expect(json).toEqual({
|
|
98
|
+
__attrRef: { entity: "Resource", attribute: attr },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("handles different logical names", () => {
|
|
104
|
+
const parent = {};
|
|
105
|
+
const logicalNames = ["MyBucket", "MyTable", "MyFunction", "My-Resource-123"];
|
|
106
|
+
|
|
107
|
+
for (const name of logicalNames) {
|
|
108
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
109
|
+
attrRef._setLogicalName(name);
|
|
110
|
+
|
|
111
|
+
const json = attrRef.toJSON();
|
|
112
|
+
expect(json).toEqual({
|
|
113
|
+
__attrRef: { entity: name, attribute: "Arn" },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("_setLogicalName can be called multiple times", () => {
|
|
119
|
+
const parent = {};
|
|
120
|
+
const attrRef = new AttrRef(parent, "Arn");
|
|
121
|
+
|
|
122
|
+
attrRef._setLogicalName("FirstName");
|
|
123
|
+
expect(attrRef.toJSON()).toEqual({
|
|
124
|
+
__attrRef: { entity: "FirstName", attribute: "Arn" },
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
attrRef._setLogicalName("SecondName");
|
|
128
|
+
expect(attrRef.toJSON()).toEqual({
|
|
129
|
+
__attrRef: { entity: "SecondName", attribute: "Arn" },
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("maintains separate logical names for different instances", () => {
|
|
134
|
+
const parent = {};
|
|
135
|
+
const attrRef1 = new AttrRef(parent, "Arn");
|
|
136
|
+
const attrRef2 = new AttrRef(parent, "Name");
|
|
137
|
+
|
|
138
|
+
attrRef1._setLogicalName("Resource1");
|
|
139
|
+
attrRef2._setLogicalName("Resource2");
|
|
140
|
+
|
|
141
|
+
expect(attrRef1.toJSON()).toEqual({
|
|
142
|
+
__attrRef: { entity: "Resource1", attribute: "Arn" },
|
|
143
|
+
});
|
|
144
|
+
expect(attrRef2.toJSON()).toEqual({
|
|
145
|
+
__attrRef: { entity: "Resource2", attribute: "Name" },
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
package/src/attrref.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { INTRINSIC_MARKER, type Intrinsic } from "./intrinsic";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reference to an attribute of a parent entity.
|
|
5
|
+
* Lexicon serializers read `getLogicalName()` and `attribute` to produce
|
|
6
|
+
* their own output format (e.g. CloudFormation `Fn::GetAttr`).
|
|
7
|
+
*/
|
|
8
|
+
export class AttrRef implements Intrinsic {
|
|
9
|
+
readonly [INTRINSIC_MARKER] = true as const;
|
|
10
|
+
readonly parent: WeakRef<object>;
|
|
11
|
+
readonly attribute: string;
|
|
12
|
+
private logicalName?: string;
|
|
13
|
+
|
|
14
|
+
constructor(parent: object, attribute: string) {
|
|
15
|
+
this.parent = new WeakRef(parent);
|
|
16
|
+
this.attribute = attribute;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Set the logical name for this attribute reference
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
_setLogicalName(name: string): void {
|
|
24
|
+
this.logicalName = name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the logical name assigned to this attribute reference.
|
|
29
|
+
*/
|
|
30
|
+
getLogicalName(): string | undefined {
|
|
31
|
+
return this.logicalName;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Serialize to a generic envelope. Lexicon-specific serializers should
|
|
36
|
+
* read `getLogicalName()` and `attribute` directly instead of relying
|
|
37
|
+
* on this format.
|
|
38
|
+
* @throws {Error} If logical name has not been set
|
|
39
|
+
*/
|
|
40
|
+
toJSON(): { __attrRef: { entity: string; attribute: string } } {
|
|
41
|
+
if (!this.logicalName) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Cannot serialize AttrRef for attribute "${this.attribute}": logical name not set`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
__attrRef: { entity: this.logicalName, attribute: this.attribute },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|