@intentius/chant 0.44.14 → 0.46.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/audit/discover.d.ts +26 -2
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +10 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/cli/build-params-cli.d.ts +16 -7
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +20 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts +9 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +13 -0
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +24 -14
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +14 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +5 -0
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/lexicon-surface-diff.d.ts +6 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +28 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts +13 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/search.d.ts +27 -0
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +7 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/docs-pages.d.ts +39 -0
- package/dist/codegen/docs-pages.d.ts.map +1 -0
- package/dist/codegen/docs-sections.d.ts.map +1 -1
- package/dist/codegen/docs-sidebar.d.ts +13 -2
- package/dist/codegen/docs-sidebar.d.ts.map +1 -1
- package/dist/codegen/docs-types.d.ts +30 -10
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +14 -2
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.d.ts +11 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -1
- package/dist/codegen/surface-snapshot.d.ts +27 -3
- package/dist/codegen/surface-snapshot.d.ts.map +1 -1
- package/dist/codegen/validate.d.ts +21 -3
- package/dist/codegen/validate.d.ts.map +1 -1
- package/dist/components/capability-plugin.d.ts +13 -2
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/driver.d.ts +38 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/config.d.ts +85 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/env.d.ts +12 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +100 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-ir.d.ts +12 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ops.d.ts +17 -0
- package/dist/graph-ops.d.ts.map +1 -0
- package/dist/lexicon.d.ts +182 -2
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +23 -2
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts +8 -0
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/teardown.d.ts +130 -0
- package/dist/lifecycle/teardown.d.ts.map +1 -0
- package/dist/lifecycle/types.d.ts +7 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/lint/engine.d.ts +6 -2
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/policy.d.ts.map +1 -1
- package/dist/lint/rule.d.ts +31 -0
- package/dist/lint/rule.d.ts.map +1 -1
- package/dist/lint/rules/cor021-env-literal-name.d.ts +3 -0
- package/dist/lint/rules/cor021-env-literal-name.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +2 -1
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/managed-fields.d.ts +33 -21
- package/dist/managed-fields.d.ts.map +1 -1
- package/dist/observation.d.ts +23 -1
- package/dist/observation.d.ts.map +1 -1
- package/dist/op/builders.d.ts +36 -7
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/index.d.ts +1 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/testing.d.ts +136 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/yaml.d.ts +0 -8
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/__snapshots__/okf.test.ts.snap +20 -0
- package/src/audit/discover.test.ts +55 -1
- package/src/audit/discover.ts +70 -2
- package/src/audit/edge-init-safety.test.ts +149 -18
- package/src/audit/report-model.ts +12 -1
- package/src/audit/rules-doc.ts +1 -0
- package/src/build-params.test.ts +52 -0
- package/src/cli/build-params-cli.test.ts +25 -8
- package/src/cli/build-params-cli.ts +37 -23
- package/src/cli/commands/__fixtures__/audit-coverage/.github/workflows/ci.yml +5 -0
- package/src/cli/commands/__fixtures__/audit-coverage/Dockerfile +2 -0
- package/src/cli/commands/__fixtures__/audit-coverage/infra/main.tf +3 -0
- package/src/cli/commands/__fixtures__/audit-coverage/infra/stack.json +6 -0
- package/src/cli/commands/__fixtures__/audit-coverage/k8s/deploy.yaml +12 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/pages/getting-started.mdx +16 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +10 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +3 -1
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +2 -1
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +2 -1
- package/src/cli/commands/audit.test.ts +83 -1
- package/src/cli/commands/audit.ts +121 -28
- package/src/cli/commands/build.test.ts +275 -5
- package/src/cli/commands/build.ts +86 -14
- package/src/cli/commands/check-lexicon-docs.ts +30 -0
- package/src/cli/commands/check-lexicon-examples.test.ts +73 -7
- package/src/cli/commands/check-lexicon-examples.ts +66 -17
- package/src/cli/commands/check-lexicon.test.ts +46 -2
- package/src/cli/commands/check-lexicon.ts +64 -4
- package/src/cli/commands/init-lexicon/templates/codegen.ts +5 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +35 -2
- package/src/cli/commands/init-lexicon.test.ts +42 -1
- package/src/cli/commands/init-lexicon.ts +3 -1
- package/src/cli/commands/lexicon-rolling-upgrade.ts +1 -1
- package/src/cli/commands/lexicon-surface-diff.test.ts +1 -1
- package/src/cli/commands/lexicon-surface-diff.ts +10 -1
- package/src/cli/commands/lexicon-surface-diff.update.test.ts +112 -0
- package/src/cli/commands/lint.ts +19 -6
- package/src/cli/commands/onboard.test.ts +319 -225
- package/src/cli/commands/onboard.ts +144 -58
- package/src/cli/handlers/build.test.ts +3 -3
- package/src/cli/handlers/build.ts +2 -0
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/graph.test.ts +44 -0
- package/src/cli/handlers/graph.ts +24 -4
- package/src/cli/handlers/lifecycle.test.ts +348 -1
- package/src/cli/handlers/lifecycle.ts +235 -3
- package/src/cli/handlers/run.test.ts +5 -5
- package/src/cli/handlers/run.ts +1 -0
- package/src/cli/handlers/search-live.test.ts +217 -0
- package/src/cli/handlers/search.test.ts +41 -1
- package/src/cli/handlers/search.ts +60 -8
- package/src/cli/main.ts +17 -4
- package/src/cli/registry.ts +7 -0
- package/src/cli/security-boundary.test.ts +2 -1
- package/src/codegen/docs-pages.test.ts +194 -0
- package/src/codegen/docs-pages.ts +138 -0
- package/src/codegen/docs-sections.ts +0 -8
- package/src/codegen/docs-sidebar.ts +50 -37
- package/src/codegen/docs-types.ts +32 -4
- package/src/codegen/docs.ts +78 -40
- package/src/codegen/lexicon-regen.ts +22 -2
- package/src/codegen/rolling-upgrade.test.ts +1 -1
- package/src/codegen/rolling-upgrade.ts +1 -1
- package/src/codegen/surface-snapshot.test.ts +139 -0
- package/src/codegen/surface-snapshot.ts +297 -122
- package/src/codegen/validate.test.ts +49 -3
- package/src/codegen/validate.ts +36 -7
- package/src/components/auto-release.ts +1 -1
- package/src/components/capability-plugin.ts +23 -2
- package/src/components/driver.test.ts +45 -0
- package/src/components/driver.ts +64 -27
- package/src/components/index.ts +2 -0
- package/src/components/starter-plugin.ts +5 -2
- package/src/config.test.ts +40 -0
- package/src/config.ts +159 -8
- package/src/discovery/fold-import.test.ts +427 -2
- package/src/discovery/fold-import.ts +163 -7
- package/src/discovery/sandbox/driver.test.ts +11 -0
- package/src/env.test.ts +35 -1
- package/src/env.ts +17 -3
- package/src/fold/fold.test.ts +41 -0
- package/src/fold/fold.ts +372 -2
- package/src/graph-ir-live.test.ts +42 -0
- package/src/graph-ir.ts +42 -2
- package/src/graph-ops.test.ts +69 -0
- package/src/graph-ops.ts +42 -0
- package/src/lexicon.ts +182 -2
- package/src/lifecycle/change-set.test.ts +41 -0
- package/src/lifecycle/change-set.ts +30 -2
- package/src/lifecycle/deep-observe.ts +4 -0
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/observe.ts +29 -2
- package/src/lifecycle/replay.ts +3 -0
- package/src/lifecycle/snapshot.ts +3 -0
- package/src/lifecycle/teardown.test.ts +537 -0
- package/src/lifecycle/teardown.ts +357 -0
- package/src/lifecycle/types.ts +7 -0
- package/src/lint/engine.ts +7 -1
- package/src/lint/policy.ts +14 -3
- package/src/lint/rule.ts +23 -0
- package/src/lint/rules/cor021-env-literal-name.test.ts +128 -0
- package/src/lint/rules/cor021-env-literal-name.ts +114 -0
- package/src/lint/rules/index.ts +4 -1
- package/src/managed-fields.test.ts +15 -42
- package/src/managed-fields.ts +43 -26
- package/src/observation.test.ts +24 -8
- package/src/observation.ts +48 -4
- package/src/op/builders.ts +40 -7
- package/src/op/index.ts +1 -1
- package/src/ownership.test.ts +63 -1
- package/src/testing.test.ts +261 -0
- package/src/testing.ts +338 -0
- package/src/yaml.test.ts +15 -0
- package/src/yaml.ts +19 -2
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sidebar generation for Starlight docs sites.
|
|
3
|
+
*
|
|
4
|
+
* Grouped by Diátaxis quadrant (https://diataxis.fr), chant #1731. Every page
|
|
5
|
+
* the pipeline knows about — authored under docs/pages/ and generated reference
|
|
6
|
+
* tables — arrives as a {@link SidebarPage} with a quadrant; this module only sorts and nests. Empty quadrants are omitted.
|
|
3
7
|
*/
|
|
4
8
|
|
|
5
|
-
import
|
|
9
|
+
import { QUADRANTS, QUADRANT_LABELS } from "./docs-pages";
|
|
10
|
+
import type { DocsConfig, DocsResult, Quadrant, SidebarPage } from "./docs-types";
|
|
11
|
+
|
|
12
|
+
type Entry = Record<string, unknown>;
|
|
13
|
+
|
|
14
|
+
function byOrderThenLabel(a: SidebarPage, b: SidebarPage): number {
|
|
15
|
+
const ao = a.order ?? Number.POSITIVE_INFINITY;
|
|
16
|
+
const bo = b.order ?? Number.POSITIVE_INFINITY;
|
|
17
|
+
if (ao !== bo) return ao - bo;
|
|
18
|
+
return a.label.localeCompare(b.label);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* One quadrant's entries: ungrouped pages first (by order, then label), then
|
|
23
|
+
* each nested `group` in order of its lowest-ordered member.
|
|
24
|
+
*/
|
|
25
|
+
export function quadrantItems(pages: SidebarPage[]): Entry[] {
|
|
26
|
+
const loose = pages.filter((p) => !p.group).sort(byOrderThenLabel);
|
|
27
|
+
const groups = new Map<string, SidebarPage[]>();
|
|
28
|
+
for (const p of pages) {
|
|
29
|
+
if (!p.group) continue;
|
|
30
|
+
const list = groups.get(p.group) ?? [];
|
|
31
|
+
list.push(p);
|
|
32
|
+
groups.set(p.group, list);
|
|
33
|
+
}
|
|
34
|
+
const items: Entry[] = loose.map((p) => ({ label: p.label, slug: p.slug }));
|
|
35
|
+
const grouped = [...groups.entries()]
|
|
36
|
+
.map(([label, members]) => ({ label, members: members.sort(byOrderThenLabel) }))
|
|
37
|
+
.sort((a, b) => byOrderThenLabel(a.members[0], b.members[0]));
|
|
38
|
+
for (const g of grouped) {
|
|
39
|
+
items.push({ label: g.label, items: g.members.map((p) => ({ label: p.label, slug: p.slug })) });
|
|
40
|
+
}
|
|
41
|
+
return items;
|
|
42
|
+
}
|
|
6
43
|
|
|
7
44
|
export function buildSidebar(
|
|
8
45
|
config: DocsConfig,
|
|
9
46
|
result: DocsResult,
|
|
10
|
-
):
|
|
47
|
+
): Entry[] {
|
|
11
48
|
// Starlight prepends basePath to every sidebar `link`, so a site-root-relative
|
|
12
49
|
// path like "/chant/" becomes "/chant/lexicons/aws/chant/" — a 404. Instead
|
|
13
50
|
// we use relative traversal: "../../" is prepended to become
|
|
@@ -15,47 +52,23 @@ export function buildSidebar(
|
|
|
15
52
|
const segments = (config.basePath ?? "/").replace(/^\/|\/$/g, "").split("/");
|
|
16
53
|
const backLink = segments.length > 1 ? "../".repeat(segments.length - 1) : "/";
|
|
17
54
|
|
|
18
|
-
const items:
|
|
55
|
+
const items: Entry[] = [
|
|
19
56
|
{ label: "← chant docs", link: backLink },
|
|
20
57
|
{ label: "Overview", slug: "index" },
|
|
21
58
|
];
|
|
22
59
|
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (page.sidebar === false) continue;
|
|
30
|
-
items.push({ label: page.title, slug: page.slug });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!suppress.has("intrinsics") && !extraSlugs.has("intrinsics") && result.pages.has("intrinsics.mdx")) {
|
|
35
|
-
items.push({ label: "Intrinsics", slug: "intrinsics" });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!suppress.has("pseudo-parameters") && !extraSlugs.has("pseudo-parameters") && result.pages.has("pseudo-parameters.mdx")) {
|
|
39
|
-
items.push({ label: "Pseudo-Parameters", slug: "pseudo-parameters" });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Every lexicon links its generated rules table, whether or not it also
|
|
43
|
-
// ships a prose `lint-rules` page. Skipping it when one existed was how gcp
|
|
44
|
-
// ended up emitting a page nothing pointed at (#1312), and it left readers
|
|
45
|
-
// with no complete list on the lexicons whose prose covers only part of the
|
|
46
|
-
// set — aws documented 26 of 50 that way. The label distinguishes the
|
|
47
|
-
// generated table from a prose page rather than competing with it.
|
|
48
|
-
if (!suppress.has("rules") && !extraSlugs.has("rules") && result.pages.has("rules.mdx")) {
|
|
49
|
-
items.push({ label: "All Rules", slug: "rules" });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!suppress.has("serialization") && !extraSlugs.has("serialization") && result.pages.has("serialization.mdx")) {
|
|
53
|
-
items.push({ label: "Serialization", slug: "serialization" });
|
|
60
|
+
const byQuadrant = new Map<Quadrant, SidebarPage[]>();
|
|
61
|
+
for (const page of result.sidebarPages) {
|
|
62
|
+
if (page.hidden) continue;
|
|
63
|
+
const list = byQuadrant.get(page.quadrant) ?? [];
|
|
64
|
+
list.push(page);
|
|
65
|
+
byQuadrant.set(page.quadrant, list);
|
|
54
66
|
}
|
|
55
67
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
for (const q of QUADRANTS) {
|
|
69
|
+
const pages = byQuadrant.get(q);
|
|
70
|
+
if (!pages || pages.length === 0) continue;
|
|
71
|
+
items.push({ label: QUADRANT_LABELS[q], items: quadrantItems(pages) });
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
return items;
|
|
@@ -13,6 +13,12 @@ export interface DocsConfig {
|
|
|
13
13
|
distDir: string;
|
|
14
14
|
/** Output directory for generated .mdx files */
|
|
15
15
|
outDir: string;
|
|
16
|
+
/**
|
|
17
|
+
* The lexicon's package.json, read for the version rendered into the docs
|
|
18
|
+
* (chant #1377). Defaults to `<distDir>/../package.json`. The manifest's
|
|
19
|
+
* version is only a fallback; it goes stale between bundles.
|
|
20
|
+
*/
|
|
21
|
+
packageJsonPath?: string;
|
|
16
22
|
/** Lexicon-specific overview content (markdown) */
|
|
17
23
|
overview?: string;
|
|
18
24
|
/** Output format description (e.g. "CloudFormation JSON template") */
|
|
@@ -21,8 +27,6 @@ export interface DocsConfig {
|
|
|
21
27
|
serviceFromType?: (resourceType: string) => string;
|
|
22
28
|
/** Custom sections to append to overview page */
|
|
23
29
|
extraSections?: Array<{ title: string; content: string }>;
|
|
24
|
-
/** Standalone pages added to the sidebar after Overview */
|
|
25
|
-
extraPages?: Array<{ slug: string; title: string; description?: string; content: string; sidebar?: boolean }>;
|
|
26
30
|
/** Slugs of auto-generated pages to suppress (e.g. "pseudo-parameters") */
|
|
27
31
|
suppressPages?: string[];
|
|
28
32
|
/** Source directory for scanning rule files (defaults to srcDir sibling of distDir) */
|
|
@@ -31,12 +35,36 @@ export interface DocsConfig {
|
|
|
31
35
|
basePath?: string;
|
|
32
36
|
/** Root directory for resolving {{file:...}} markers in extra page content */
|
|
33
37
|
examplesDir?: string;
|
|
34
|
-
/**
|
|
35
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Directory of authored `.mdx` pages, each tagged with a Diátaxis quadrant
|
|
40
|
+
* (chant #1733). Defaults to `<outDir>/pages`. See docs-pages.ts. This is
|
|
41
|
+
* the only way a lexicon adds prose pages; `extraPages` (prose in docs.ts
|
|
42
|
+
* template literals) and `sidebarExtra` (hand-listed content files) were
|
|
43
|
+
* removed in chant #1757.
|
|
44
|
+
*/
|
|
45
|
+
pagesDir?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Diátaxis quadrant (https://diataxis.fr). */
|
|
49
|
+
export type Quadrant = "tutorial" | "how-to" | "reference" | "explanation";
|
|
50
|
+
|
|
51
|
+
/** One sidebar entry, as the quadrant-grouped sidebar builder sees it. */
|
|
52
|
+
export interface SidebarPage {
|
|
53
|
+
slug: string;
|
|
54
|
+
label: string;
|
|
55
|
+
quadrant: Quadrant;
|
|
56
|
+
/** Nested subgroup label inside the quadrant. */
|
|
57
|
+
group?: string;
|
|
58
|
+
/** Lower sorts first within its group; unordered pages follow, by label. */
|
|
59
|
+
order?: number;
|
|
60
|
+
/** Keep out of the sidebar. */
|
|
61
|
+
hidden?: boolean;
|
|
36
62
|
}
|
|
37
63
|
|
|
38
64
|
export interface DocsResult {
|
|
39
65
|
pages: Map<string, string>;
|
|
66
|
+
/** Every page the sidebar should list, authored and generated, by quadrant. */
|
|
67
|
+
sidebarPages: SidebarPage[];
|
|
40
68
|
stats: {
|
|
41
69
|
resources: number;
|
|
42
70
|
properties: number;
|
package/src/codegen/docs.ts
CHANGED
|
@@ -12,17 +12,38 @@ import { join } from "path";
|
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
14
|
import { expandFileMarkers } from "./docs-file-markers";
|
|
15
|
+
import { readAuthoredPages } from "./docs-pages";
|
|
15
16
|
import { scanRules, generateRules } from "./docs-rule-scanning";
|
|
16
17
|
import { buildSidebar } from "./docs-sidebar";
|
|
17
18
|
import { generateOverview, generateIntrinsics, generatePseudoParameters, generateSerialization } from "./docs-sections";
|
|
18
|
-
import type { DocsConfig, DocsResult, ManifestJSON, MetaEntry } from "./docs-types";
|
|
19
|
+
import type { DocsConfig, DocsResult, ManifestJSON, MetaEntry, SidebarPage } from "./docs-types";
|
|
19
20
|
|
|
20
21
|
// Re-export all public types and functions so existing importers continue to work.
|
|
21
22
|
export { expandFileMarkers } from "./docs-file-markers";
|
|
22
|
-
export type { DocsConfig, DocsResult } from "./docs-types";
|
|
23
|
+
export type { DocsConfig, DocsResult, Quadrant, SidebarPage } from "./docs-types";
|
|
24
|
+
export { QUADRANTS, QUADRANT_LABELS, readAuthoredPages } from "./docs-pages";
|
|
23
25
|
|
|
24
26
|
// ── Pipeline ───────────────────────────────────────────────────────
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The version rendered into the docs (chant #1377).
|
|
30
|
+
*
|
|
31
|
+
* `dist/manifest.json` is written by `npm run bundle`, which runs only under
|
|
32
|
+
* `prepack`, so its `version` is whatever was last bundled on this machine
|
|
33
|
+
* and can trail `package.json` by several releases. `package.json` is the
|
|
34
|
+
* source of truth and is never stale, so it wins whenever it is present.
|
|
35
|
+
* The manifest's own version is the fallback for a dist with no package
|
|
36
|
+
* beside it (test fixtures, relocated dist directories).
|
|
37
|
+
*/
|
|
38
|
+
export function lexiconVersion(config: DocsConfig, manifest: ManifestJSON): string {
|
|
39
|
+
const pkgPath = config.packageJsonPath ?? join(config.distDir, "..", "package.json");
|
|
40
|
+
if (existsSync(pkgPath)) {
|
|
41
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { version?: unknown };
|
|
42
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
43
|
+
}
|
|
44
|
+
return manifest.version;
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
/**
|
|
27
48
|
* Run the documentation pipeline with the supplied config.
|
|
28
49
|
*/
|
|
@@ -30,6 +51,7 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
30
51
|
const manifest = JSON.parse(
|
|
31
52
|
readFileSync(join(config.distDir, "manifest.json"), "utf-8"),
|
|
32
53
|
) as ManifestJSON;
|
|
54
|
+
manifest.version = lexiconVersion(config, manifest);
|
|
33
55
|
|
|
34
56
|
const meta = JSON.parse(
|
|
35
57
|
readFileSync(join(config.distDir, "meta.json"), "utf-8"),
|
|
@@ -71,50 +93,47 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
71
93
|
}
|
|
72
94
|
pages.set("index.mdx", overviewContent);
|
|
73
95
|
const suppress = new Set(config.suppressPages ?? []);
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
[
|
|
86
|
-
"---",
|
|
87
|
-
`title: "${page.title}"`,
|
|
88
|
-
page.description ? `description: "${page.description}"` : "",
|
|
89
|
-
"---",
|
|
90
|
-
"",
|
|
91
|
-
content,
|
|
92
|
-
"",
|
|
93
|
-
]
|
|
94
|
-
.filter(Boolean)
|
|
95
|
-
.join("\n"),
|
|
96
|
-
);
|
|
97
|
-
}
|
|
96
|
+
const sidebarPages: SidebarPage[] = [];
|
|
97
|
+
|
|
98
|
+
// Authored pages under docs/pages/ (#1733). Each names its Diátaxis
|
|
99
|
+
// quadrant; the sidebar is grouped from that. Written with a provenance
|
|
100
|
+
// marker that points back at the source file.
|
|
101
|
+
const authored = readAuthoredPages(config);
|
|
102
|
+
const authoredSources = new Map<string, string>();
|
|
103
|
+
for (const page of authored) {
|
|
104
|
+
pages.set(`${page.slug}.mdx`, page.content);
|
|
105
|
+
authoredSources.set(`${page.slug}.mdx`, page.file);
|
|
106
|
+
if (!page.hidden) sidebarPages.push(page);
|
|
98
107
|
}
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
109
|
+
const extraSlugs = new Set(authored.map((p) => p.slug));
|
|
110
|
+
|
|
111
|
+
// A generated page must not overwrite one the lexicon authored. Authored
|
|
112
|
+
// pages are written into `pages` first, so an unguarded `pages.set` below
|
|
113
|
+
// would silently discard them: helm once declared its own "Intrinsics
|
|
114
|
+
// Reference" and pre-synth rules pages and shipped neither for as long as
|
|
115
|
+
// both slugs collided (#1312). Authorship wins, and the collision is
|
|
116
|
+
// reported rather than resolved in silence.
|
|
106
117
|
const claimed = (slug: string): boolean => {
|
|
107
118
|
if (suppress.has(slug)) return true;
|
|
108
119
|
if (!extraSlugs.has(slug)) return false;
|
|
109
120
|
console.warn(
|
|
110
|
-
`[docs:${config.name}]
|
|
111
|
-
` Add "${slug}" to suppressPages to make that explicit, or rename the
|
|
121
|
+
`[docs:${config.name}] docs/pages/${slug}.mdx has the slug of a generated page — keeping the authored one.\n` +
|
|
122
|
+
` Add "${slug}" to suppressPages to make that explicit, or rename the authored page if both are wanted.`,
|
|
112
123
|
);
|
|
113
124
|
return true;
|
|
114
125
|
};
|
|
115
126
|
|
|
127
|
+
// Generated reference tables sort after authored reference pages, in the
|
|
128
|
+
// order the old flat sidebar listed them.
|
|
129
|
+
let generatedOrder = 1000;
|
|
130
|
+
const generated = (slug: string, label: string): void => {
|
|
131
|
+
sidebarPages.push({ slug, label, quadrant: "reference", order: generatedOrder++ });
|
|
132
|
+
};
|
|
133
|
+
|
|
116
134
|
if (!claimed("intrinsics") && manifest.intrinsics && manifest.intrinsics.length > 0) {
|
|
117
135
|
pages.set("intrinsics.mdx", generateIntrinsics(config, manifest));
|
|
136
|
+
generated("intrinsics", "Intrinsics");
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
if (
|
|
@@ -126,14 +145,21 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
126
145
|
"pseudo-parameters.mdx",
|
|
127
146
|
generatePseudoParameters(config, manifest),
|
|
128
147
|
);
|
|
148
|
+
generated("pseudo-parameters", "Pseudo-Parameters");
|
|
129
149
|
}
|
|
130
150
|
|
|
151
|
+
// Every lexicon links its generated rules table, whether or not it also
|
|
152
|
+
// ships a prose `lint-rules` page. Skipping it when one existed was how gcp
|
|
153
|
+
// ended up emitting a page nothing pointed at (#1312). The label
|
|
154
|
+
// distinguishes the generated table from a prose page.
|
|
131
155
|
if (!claimed("rules") && rules.length > 0) {
|
|
132
156
|
pages.set("rules.mdx", generateRules(config, rules));
|
|
157
|
+
generated("rules", "All Rules");
|
|
133
158
|
}
|
|
134
159
|
|
|
135
160
|
if (!claimed("serialization")) {
|
|
136
161
|
pages.set("serialization.mdx", generateSerialization(config));
|
|
162
|
+
generated("serialization", "Serialization");
|
|
137
163
|
}
|
|
138
164
|
|
|
139
165
|
// Stamp every emitted page with its provenance. These files look exactly
|
|
@@ -142,11 +168,12 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
142
168
|
// intrinsics guide's #1044 claim were both fixed in the emitted `.mdx` and
|
|
143
169
|
// silently reverted by the next regen (#1312).
|
|
144
170
|
for (const [filename, content] of pages) {
|
|
145
|
-
pages.set(filename, withGeneratedMarker(config, content));
|
|
171
|
+
pages.set(filename, withGeneratedMarker(config, content, authoredSources.get(filename)));
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
return {
|
|
149
175
|
pages,
|
|
176
|
+
sidebarPages,
|
|
150
177
|
stats: {
|
|
151
178
|
resources: resources.size,
|
|
152
179
|
properties: properties.size,
|
|
@@ -163,8 +190,11 @@ export function docsPipeline(config: DocsConfig): DocsResult {
|
|
|
163
190
|
* MDX parses `<!-- -->` as JSX rather than a comment, so this uses the
|
|
164
191
|
* `{/* … *\/}` form the rest of the docs already use for generated markers.
|
|
165
192
|
*/
|
|
166
|
-
function withGeneratedMarker(config: DocsConfig, content: string): string {
|
|
167
|
-
const
|
|
193
|
+
function withGeneratedMarker(config: DocsConfig, content: string, source?: string): string {
|
|
194
|
+
const edit = source
|
|
195
|
+
? `Edit lexicons/${config.name}/docs/pages/${source} instead`
|
|
196
|
+
: `Edit lexicons/${config.name}/src/codegen/docs.ts instead`;
|
|
197
|
+
const marker = `{/* ${GENERATED_MARKER_TAG} by \`npm run docs -w @intentius/chant-lexicon-${config.name}\` — DO NOT EDIT.\n ${edit}; changes here are overwritten. */}`;
|
|
168
198
|
const lines = content.split("\n");
|
|
169
199
|
// Frontmatter is the leading `---` … `---` block; the marker goes after it.
|
|
170
200
|
if (lines[0] === "---") {
|
|
@@ -302,7 +332,7 @@ export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
|
|
|
302
332
|
if (unreachable.length > 0) {
|
|
303
333
|
console.warn(
|
|
304
334
|
`[docs:${config.name}] ${unreachable.length} page(s) in no sidebar entry — reachable only by direct URL: ${unreachable.join(", ")}.\n` +
|
|
305
|
-
`
|
|
335
|
+
` Move them to docs/pages/ with a \`diataxis\` field to surface them (chant #1731).`,
|
|
306
336
|
);
|
|
307
337
|
}
|
|
308
338
|
|
|
@@ -349,12 +379,20 @@ export function writeDocsSite(config: DocsConfig, result: DocsResult): void {
|
|
|
349
379
|
mkdirSync(join(outDir, "src"), { recursive: true });
|
|
350
380
|
writeFileSync(
|
|
351
381
|
join(outDir, "src", "content.config.ts"),
|
|
352
|
-
`import { defineCollection } from 'astro:content';
|
|
382
|
+
`import { defineCollection, z } from 'astro:content';
|
|
353
383
|
import { docsLoader } from '@astrojs/starlight/loaders';
|
|
354
384
|
import { docsSchema } from '@astrojs/starlight/schema';
|
|
355
385
|
|
|
356
386
|
export const collections = {
|
|
357
|
-
docs: defineCollection({
|
|
387
|
+
docs: defineCollection({
|
|
388
|
+
loader: docsLoader(),
|
|
389
|
+
schema: docsSchema({
|
|
390
|
+
extend: z.object({
|
|
391
|
+
// Diátaxis quadrant (https://diataxis.fr), chant #1731.
|
|
392
|
+
diataxis: z.enum(['tutorial', 'how-to', 'reference', 'explanation']).optional(),
|
|
393
|
+
}),
|
|
394
|
+
}),
|
|
395
|
+
}),
|
|
358
396
|
};
|
|
359
397
|
`,
|
|
360
398
|
);
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { existsSync, readFileSync, writeFileSync, readdirSync, type Dirent } from "fs";
|
|
26
26
|
import { join } from "path";
|
|
27
27
|
import { execSync } from "child_process";
|
|
28
|
+
import { SNAPSHOT_UPDATE_ENV } from "./validate";
|
|
28
29
|
import {
|
|
29
30
|
extractSurface,
|
|
30
31
|
diffSurface,
|
|
@@ -114,6 +115,17 @@ export interface RegenOptions {
|
|
|
114
115
|
* sha256:<64-hex-chars> <relative-path-to-spec-file>
|
|
115
116
|
*/
|
|
116
117
|
pinnedDigestPath?: string;
|
|
118
|
+
/**
|
|
119
|
+
* This run is about to rewrite the baseline snapshot (#1825).
|
|
120
|
+
*
|
|
121
|
+
* The `validate` step runs with {@link SNAPSHOT_UPDATE_ENV} set so its
|
|
122
|
+
* `surface-matches-snapshot` check is skipped. A lexicon whose snapshot gate
|
|
123
|
+
* runs in `"always"` mode (#1475) fails validate on a stale baseline, and a
|
|
124
|
+
* stale baseline is exactly what a re-baseline starts from — without this
|
|
125
|
+
* exemption the documented update flow deadlocks. Every other validate check
|
|
126
|
+
* still gates the run, so a broken generate cannot be baselined.
|
|
127
|
+
*/
|
|
128
|
+
updatingSnapshot?: boolean;
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
// ── Pipeline ──────────────────────────────────────────────────────────
|
|
@@ -132,6 +144,7 @@ export async function regenLexicon(opts: RegenOptions): Promise<RegenResult> {
|
|
|
132
144
|
skipBuild = false,
|
|
133
145
|
skipLint = false,
|
|
134
146
|
skipExamples = true,
|
|
147
|
+
updatingSnapshot = false,
|
|
135
148
|
} = opts;
|
|
136
149
|
|
|
137
150
|
const failures: RegenFailure[] = [];
|
|
@@ -177,7 +190,12 @@ export async function regenLexicon(opts: RegenOptions): Promise<RegenResult> {
|
|
|
177
190
|
}
|
|
178
191
|
|
|
179
192
|
// Step 4: validate
|
|
180
|
-
|
|
193
|
+
// A re-baseline run is exempt from the surface-matches-snapshot check only
|
|
194
|
+
// (#1825) — see RegenOptions.updatingSnapshot.
|
|
195
|
+
const validateEnv: NodeJS.ProcessEnv = updatingSnapshot
|
|
196
|
+
? { ...process.env, [SNAPSHOT_UPDATE_ENV]: "1" }
|
|
197
|
+
: process.env;
|
|
198
|
+
const validateFail = runScript(lexiconDir, "validate", validateEnv, verbose);
|
|
181
199
|
if (validateFail) {
|
|
182
200
|
failures.push(validateFail);
|
|
183
201
|
}
|
|
@@ -524,13 +542,15 @@ function makeResult(
|
|
|
524
542
|
added: [],
|
|
525
543
|
changed: [],
|
|
526
544
|
removed: [],
|
|
545
|
+
renamed: [],
|
|
527
546
|
severity: "none",
|
|
528
547
|
};
|
|
529
548
|
const effectiveDelta = delta ?? emptyDelta;
|
|
530
549
|
const changed =
|
|
531
550
|
effectiveDelta.added.length > 0 ||
|
|
532
551
|
effectiveDelta.changed.length > 0 ||
|
|
533
|
-
effectiveDelta.removed.length > 0
|
|
552
|
+
effectiveDelta.removed.length > 0 ||
|
|
553
|
+
effectiveDelta.renamed.length > 0;
|
|
534
554
|
const deltaText = changed ? formatDelta(effectiveDelta) : "";
|
|
535
555
|
|
|
536
556
|
return {
|
|
@@ -93,7 +93,7 @@ function makeRegen(
|
|
|
93
93
|
return async () => {
|
|
94
94
|
const delta = fresh
|
|
95
95
|
? diffSurface(baseline, fresh)
|
|
96
|
-
: { added: [], changed: [], removed: [], severity: "none" as const };
|
|
96
|
+
: { added: [], changed: [], removed: [], renamed: [], severity: "none" as const };
|
|
97
97
|
const changed =
|
|
98
98
|
delta.added.length > 0 || delta.changed.length > 0 || delta.removed.length > 0;
|
|
99
99
|
return {
|
|
@@ -293,7 +293,7 @@ export function classifyDelta(delta: SurfaceDelta): {
|
|
|
293
293
|
severity: ChangeSeverity;
|
|
294
294
|
} {
|
|
295
295
|
const hasUpgrade =
|
|
296
|
-
delta.added.length > 0 || delta.changed.length > 0 || delta.removed.length > 0;
|
|
296
|
+
delta.added.length > 0 || delta.changed.length > 0 || delta.removed.length > 0 || delta.renamed.length > 0;
|
|
297
297
|
return { hasUpgrade, severity: hasUpgrade ? delta.severity : "none" };
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -428,6 +428,133 @@ describe("serializeSnapshot / parseSnapshot", () => {
|
|
|
428
428
|
|
|
429
429
|
// ── formatDelta ───────────────────────────────────────────────────────
|
|
430
430
|
|
|
431
|
+
describe("diffSurface — renames (#1460)", () => {
|
|
432
|
+
test("a removal and an addition with the same resourceType pair as one rename", () => {
|
|
433
|
+
const baseline = makeSnapshot({
|
|
434
|
+
MacieSession: { kind: "resource", resourceType: "AWS::Macie::Session", props: ["Status:false"] },
|
|
435
|
+
});
|
|
436
|
+
const fresh = makeSnapshot({
|
|
437
|
+
Session: { kind: "resource", resourceType: "AWS::Macie::Session", props: ["Status:false"] },
|
|
438
|
+
});
|
|
439
|
+
const delta = diffSurface(baseline, fresh);
|
|
440
|
+
expect(delta.added).toEqual([]);
|
|
441
|
+
expect(delta.removed).toEqual([]);
|
|
442
|
+
expect(delta.renamed).toEqual([
|
|
443
|
+
{ from: "MacieSession", to: "Session", entry: fresh.entries.Session },
|
|
444
|
+
]);
|
|
445
|
+
// The old import still breaks, so a rename stays breaking for 1.x.
|
|
446
|
+
expect(delta.severity).toBe("breaking");
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("property types rename the same way, and a same-kind match is required", () => {
|
|
450
|
+
const baseline = makeSnapshot({
|
|
451
|
+
BCMDataExportsExport_DataQuery: { kind: "property", resourceType: "AWS::BCMDataExports::Export.DataQuery" },
|
|
452
|
+
Thing: { kind: "resource", resourceType: "X::Thing" },
|
|
453
|
+
});
|
|
454
|
+
const fresh = makeSnapshot({
|
|
455
|
+
Export_DataQuery: { kind: "property", resourceType: "AWS::BCMDataExports::Export.DataQuery" },
|
|
456
|
+
Thing_Prop: { kind: "property", resourceType: "X::Thing" },
|
|
457
|
+
});
|
|
458
|
+
const delta = diffSurface(baseline, fresh);
|
|
459
|
+
expect(delta.renamed.map((r) => `${r.from} -> ${r.to}`)).toEqual([
|
|
460
|
+
"BCMDataExportsExport_DataQuery -> Export_DataQuery",
|
|
461
|
+
]);
|
|
462
|
+
// A resource and a property under the same type string are not a rename.
|
|
463
|
+
expect(delta.removed.map((r) => r.name)).toEqual(["Thing"]);
|
|
464
|
+
expect(delta.added.map((a) => a.name)).toEqual(["Thing_Prop"]);
|
|
465
|
+
expect(delta.severity).toBe("breaking");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("a rename that also changed shape carries the structural diff", () => {
|
|
469
|
+
const baseline = makeSnapshot({
|
|
470
|
+
PanoramaPackage: { kind: "resource", resourceType: "AWS::Panorama::Package", props: ["PackageName:true", "Tags:false"] },
|
|
471
|
+
});
|
|
472
|
+
const fresh = makeSnapshot({
|
|
473
|
+
Package: { kind: "resource", resourceType: "AWS::Panorama::Package", props: ["PackageName:true"], attrs: ["Arn"] },
|
|
474
|
+
});
|
|
475
|
+
const delta = diffSurface(baseline, fresh);
|
|
476
|
+
expect(delta.renamed).toHaveLength(1);
|
|
477
|
+
expect(delta.renamed[0].changes).toEqual({ removedProps: ["Tags"], addedAttrs: ["Arn"] });
|
|
478
|
+
expect(delta.changed).toEqual([]);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("several old and new names under one resourceType pair by similarity; the rest stay add/remove", () => {
|
|
482
|
+
const rt = "AWS::SageMaker::Space";
|
|
483
|
+
const baseline = makeSnapshot({
|
|
484
|
+
Space_OwnershipSettings: { kind: "property", resourceType: rt },
|
|
485
|
+
Space_SpaceSettings: { kind: "property", resourceType: rt },
|
|
486
|
+
Space_Gone: { kind: "property", resourceType: rt },
|
|
487
|
+
});
|
|
488
|
+
const fresh = makeSnapshot({
|
|
489
|
+
SageMakerSpace_SpaceSettings: { kind: "property", resourceType: rt },
|
|
490
|
+
SageMakerSpace_OwnershipSettings: { kind: "property", resourceType: rt },
|
|
491
|
+
});
|
|
492
|
+
const delta = diffSurface(baseline, fresh);
|
|
493
|
+
expect(delta.renamed.map((r) => `${r.from} -> ${r.to}`)).toEqual([
|
|
494
|
+
"Space_OwnershipSettings -> SageMakerSpace_OwnershipSettings",
|
|
495
|
+
"Space_SpaceSettings -> SageMakerSpace_SpaceSettings",
|
|
496
|
+
]);
|
|
497
|
+
expect(delta.removed.map((r) => r.name)).toEqual(["Space_Gone"]);
|
|
498
|
+
expect(delta.added).toEqual([]);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("entries with an empty resourceType never pair", () => {
|
|
502
|
+
const baseline = makeSnapshot({ A: { kind: "property", resourceType: "" } });
|
|
503
|
+
const fresh = makeSnapshot({ B: { kind: "property", resourceType: "" } });
|
|
504
|
+
const delta = diffSurface(baseline, fresh);
|
|
505
|
+
expect(delta.renamed).toEqual([]);
|
|
506
|
+
expect(delta.removed).toHaveLength(1);
|
|
507
|
+
expect(delta.added).toHaveLength(1);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test("the #1423 shape: 28 renames among 148 removals and 83 additions", () => {
|
|
511
|
+
// 3 resources renamed, each dragging 25/3-ish property types with them —
|
|
512
|
+
// 28 renames in total, 120 plain removals, 55 plain additions.
|
|
513
|
+
const baseEntries: SurfaceSnapshot["entries"] = {};
|
|
514
|
+
const freshEntries: SurfaceSnapshot["entries"] = {};
|
|
515
|
+
|
|
516
|
+
const renamedResources: Array<[string, string, string]> = [
|
|
517
|
+
["MacieSession", "Session", "AWS::Macie::Session"],
|
|
518
|
+
["PanoramaPackage", "Package", "AWS::Panorama::Package"],
|
|
519
|
+
["Space", "SageMakerSpace", "AWS::SageMaker::Space"],
|
|
520
|
+
];
|
|
521
|
+
let renames = 0;
|
|
522
|
+
for (const [oldName, newName, rt] of renamedResources) {
|
|
523
|
+
baseEntries[oldName] = { kind: "resource", resourceType: rt };
|
|
524
|
+
freshEntries[newName] = { kind: "resource", resourceType: rt };
|
|
525
|
+
renames++;
|
|
526
|
+
}
|
|
527
|
+
// 25 property renames spread across the three parents
|
|
528
|
+
for (let i = 0; i < 25; i++) {
|
|
529
|
+
const [oldName, newName, rt] = renamedResources[i % 3];
|
|
530
|
+
baseEntries[`${oldName}_Prop${i}`] = { kind: "property", resourceType: `${rt}.Prop${i}` };
|
|
531
|
+
freshEntries[`${newName}_Prop${i}`] = { kind: "property", resourceType: `${rt}.Prop${i}` };
|
|
532
|
+
renames++;
|
|
533
|
+
}
|
|
534
|
+
expect(renames).toBe(28);
|
|
535
|
+
|
|
536
|
+
for (let i = 0; i < 120; i++) {
|
|
537
|
+
baseEntries[`Gone${i}`] = { kind: "resource", resourceType: `AWS::Gone::Type${i}` };
|
|
538
|
+
}
|
|
539
|
+
for (let i = 0; i < 55; i++) {
|
|
540
|
+
freshEntries[`Fresh${i}`] = { kind: "resource", resourceType: `AWS::Fresh::Type${i}` };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const delta = diffSurface(makeSnapshot(baseEntries), makeSnapshot(freshEntries));
|
|
544
|
+
expect(delta.renamed).toHaveLength(28);
|
|
545
|
+
expect(delta.removed).toHaveLength(120);
|
|
546
|
+
expect(delta.added).toHaveLength(55);
|
|
547
|
+
expect(delta.severity).toBe("breaking");
|
|
548
|
+
|
|
549
|
+
const text = formatDelta(delta);
|
|
550
|
+
expect(text).toContain("Renamed (28):");
|
|
551
|
+
expect(text).toContain("Removed (120):");
|
|
552
|
+
expect(text).toContain("Added (55):");
|
|
553
|
+
expect(text).toContain(" ~ MacieSession -> Session [resource] (AWS::Macie::Session)");
|
|
554
|
+
expect(text).toContain("Severity: breaking");
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
|
|
431
558
|
describe("formatDelta", () => {
|
|
432
559
|
test("includes severity in output", () => {
|
|
433
560
|
const delta = diffSurface(
|
|
@@ -457,5 +584,17 @@ describe("formatDelta", () => {
|
|
|
457
584
|
expect(text).toContain("Added (1):");
|
|
458
585
|
expect(text).toContain("Removed (1):");
|
|
459
586
|
expect(text).toContain("Changed (1):");
|
|
587
|
+
expect(text).not.toContain("Renamed");
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("renames print as Old -> New with the shared resourceType", () => {
|
|
591
|
+
const baseline = makeSnapshot({ Old: { kind: "resource", resourceType: "X" } });
|
|
592
|
+
const fresh = makeSnapshot({ New: { kind: "resource", resourceType: "X" } });
|
|
593
|
+
const text = formatDelta(diffSurface(baseline, fresh));
|
|
594
|
+
expect(text).toContain("Renamed (1):");
|
|
595
|
+
expect(text).toContain(" ~ Old -> New [resource] (X)");
|
|
596
|
+
expect(text).not.toContain("Added (");
|
|
597
|
+
expect(text).not.toContain("Removed (");
|
|
598
|
+
expect(text).toContain("Severity: breaking");
|
|
460
599
|
});
|
|
461
600
|
});
|