@composurecdk/eslint-plugin 0.10.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +138 -0
  3. package/dist/commonjs/configs/presets.d.ts +42 -0
  4. package/dist/commonjs/configs/presets.d.ts.map +1 -0
  5. package/dist/commonjs/configs/presets.js +58 -0
  6. package/dist/commonjs/configs/presets.js.map +1 -0
  7. package/dist/commonjs/index.d.ts +84 -0
  8. package/dist/commonjs/index.d.ts.map +1 -0
  9. package/dist/commonjs/index.js +63 -0
  10. package/dist/commonjs/index.js.map +1 -0
  11. package/dist/commonjs/package.json +3 -0
  12. package/dist/commonjs/rules/builder-must-be-tagged.d.ts +15 -0
  13. package/dist/commonjs/rules/builder-must-be-tagged.d.ts.map +1 -0
  14. package/dist/commonjs/rules/builder-must-be-tagged.js +70 -0
  15. package/dist/commonjs/rules/builder-must-be-tagged.js.map +1 -0
  16. package/dist/commonjs/rules/builder-must-implement-copy-state.d.ts +13 -0
  17. package/dist/commonjs/rules/builder-must-implement-copy-state.d.ts.map +1 -0
  18. package/dist/commonjs/rules/builder-must-implement-copy-state.js +123 -0
  19. package/dist/commonjs/rules/builder-must-implement-copy-state.js.map +1 -0
  20. package/dist/commonjs/rules/constraint-metadata-required.d.ts +16 -0
  21. package/dist/commonjs/rules/constraint-metadata-required.d.ts.map +1 -0
  22. package/dist/commonjs/rules/constraint-metadata-required.js +79 -0
  23. package/dist/commonjs/rules/constraint-metadata-required.js.map +1 -0
  24. package/dist/commonjs/rules/index.d.ts +14 -0
  25. package/dist/commonjs/rules/index.d.ts.map +1 -0
  26. package/dist/commonjs/rules/index.js +52 -0
  27. package/dist/commonjs/rules/index.js.map +1 -0
  28. package/dist/commonjs/rules/lib/imports.d.ts +24 -0
  29. package/dist/commonjs/rules/lib/imports.d.ts.map +1 -0
  30. package/dist/commonjs/rules/lib/imports.js +76 -0
  31. package/dist/commonjs/rules/lib/imports.js.map +1 -0
  32. package/dist/commonjs/rules/lifecycle-build-context-required.d.ts +14 -0
  33. package/dist/commonjs/rules/lifecycle-build-context-required.d.ts.map +1 -0
  34. package/dist/commonjs/rules/lifecycle-build-context-required.js +46 -0
  35. package/dist/commonjs/rules/lifecycle-build-context-required.js.map +1 -0
  36. package/dist/commonjs/rules/lifecycle-build-must-forward-context.d.ts +18 -0
  37. package/dist/commonjs/rules/lifecycle-build-must-forward-context.d.ts.map +1 -0
  38. package/dist/commonjs/rules/lifecycle-build-must-forward-context.js +54 -0
  39. package/dist/commonjs/rules/lifecycle-build-must-forward-context.js.map +1 -0
  40. package/dist/commonjs/rules/no-cdk-api-above-floor.d.ts +15 -0
  41. package/dist/commonjs/rules/no-cdk-api-above-floor.d.ts.map +1 -0
  42. package/dist/commonjs/rules/no-cdk-api-above-floor.js +80 -0
  43. package/dist/commonjs/rules/no-cdk-api-above-floor.js.map +1 -0
  44. package/dist/commonjs/rules/no-cjs-incompatible-syntax.d.ts +14 -0
  45. package/dist/commonjs/rules/no-cjs-incompatible-syntax.d.ts.map +1 -0
  46. package/dist/commonjs/rules/no-cjs-incompatible-syntax.js +55 -0
  47. package/dist/commonjs/rules/no-cjs-incompatible-syntax.js.map +1 -0
  48. package/dist/commonjs/rules/no-realm-bound-instanceof.d.ts +25 -0
  49. package/dist/commonjs/rules/no-realm-bound-instanceof.d.ts.map +1 -0
  50. package/dist/commonjs/rules/no-realm-bound-instanceof.js +98 -0
  51. package/dist/commonjs/rules/no-realm-bound-instanceof.js.map +1 -0
  52. package/dist/commonjs/rules/no-typescript-private-modifier.d.ts +17 -0
  53. package/dist/commonjs/rules/no-typescript-private-modifier.d.ts.map +1 -0
  54. package/dist/commonjs/rules/no-typescript-private-modifier.js +91 -0
  55. package/dist/commonjs/rules/no-typescript-private-modifier.js.map +1 -0
  56. package/dist/commonjs/rules/redeclared-prop-must-track-cdk-type.d.ts +17 -0
  57. package/dist/commonjs/rules/redeclared-prop-must-track-cdk-type.d.ts.map +1 -0
  58. package/dist/commonjs/rules/redeclared-prop-must-track-cdk-type.js +152 -0
  59. package/dist/commonjs/rules/redeclared-prop-must-track-cdk-type.js.map +1 -0
  60. package/dist/esm/configs/presets.d.ts +42 -0
  61. package/dist/esm/configs/presets.d.ts.map +1 -0
  62. package/dist/esm/configs/presets.js +55 -0
  63. package/dist/esm/configs/presets.js.map +1 -0
  64. package/dist/esm/index.d.ts +84 -0
  65. package/dist/esm/index.d.ts.map +1 -0
  66. package/dist/esm/index.js +60 -0
  67. package/dist/esm/index.js.map +1 -0
  68. package/dist/esm/package.json +3 -0
  69. package/dist/esm/rules/builder-must-be-tagged.d.ts +15 -0
  70. package/dist/esm/rules/builder-must-be-tagged.d.ts.map +1 -0
  71. package/dist/esm/rules/builder-must-be-tagged.js +67 -0
  72. package/dist/esm/rules/builder-must-be-tagged.js.map +1 -0
  73. package/dist/esm/rules/builder-must-implement-copy-state.d.ts +13 -0
  74. package/dist/esm/rules/builder-must-implement-copy-state.d.ts.map +1 -0
  75. package/dist/esm/rules/builder-must-implement-copy-state.js +120 -0
  76. package/dist/esm/rules/builder-must-implement-copy-state.js.map +1 -0
  77. package/dist/esm/rules/constraint-metadata-required.d.ts +16 -0
  78. package/dist/esm/rules/constraint-metadata-required.d.ts.map +1 -0
  79. package/dist/esm/rules/constraint-metadata-required.js +76 -0
  80. package/dist/esm/rules/constraint-metadata-required.js.map +1 -0
  81. package/dist/esm/rules/index.d.ts +14 -0
  82. package/dist/esm/rules/index.d.ts.map +1 -0
  83. package/dist/esm/rules/index.js +49 -0
  84. package/dist/esm/rules/index.js.map +1 -0
  85. package/dist/esm/rules/lib/imports.d.ts +24 -0
  86. package/dist/esm/rules/lib/imports.d.ts.map +1 -0
  87. package/dist/esm/rules/lib/imports.js +70 -0
  88. package/dist/esm/rules/lib/imports.js.map +1 -0
  89. package/dist/esm/rules/lifecycle-build-context-required.d.ts +14 -0
  90. package/dist/esm/rules/lifecycle-build-context-required.d.ts.map +1 -0
  91. package/dist/esm/rules/lifecycle-build-context-required.js +43 -0
  92. package/dist/esm/rules/lifecycle-build-context-required.js.map +1 -0
  93. package/dist/esm/rules/lifecycle-build-must-forward-context.d.ts +18 -0
  94. package/dist/esm/rules/lifecycle-build-must-forward-context.d.ts.map +1 -0
  95. package/dist/esm/rules/lifecycle-build-must-forward-context.js +51 -0
  96. package/dist/esm/rules/lifecycle-build-must-forward-context.js.map +1 -0
  97. package/dist/esm/rules/no-cdk-api-above-floor.d.ts +15 -0
  98. package/dist/esm/rules/no-cdk-api-above-floor.d.ts.map +1 -0
  99. package/dist/esm/rules/no-cdk-api-above-floor.js +77 -0
  100. package/dist/esm/rules/no-cdk-api-above-floor.js.map +1 -0
  101. package/dist/esm/rules/no-cjs-incompatible-syntax.d.ts +14 -0
  102. package/dist/esm/rules/no-cjs-incompatible-syntax.d.ts.map +1 -0
  103. package/dist/esm/rules/no-cjs-incompatible-syntax.js +52 -0
  104. package/dist/esm/rules/no-cjs-incompatible-syntax.js.map +1 -0
  105. package/dist/esm/rules/no-realm-bound-instanceof.d.ts +25 -0
  106. package/dist/esm/rules/no-realm-bound-instanceof.d.ts.map +1 -0
  107. package/dist/esm/rules/no-realm-bound-instanceof.js +95 -0
  108. package/dist/esm/rules/no-realm-bound-instanceof.js.map +1 -0
  109. package/dist/esm/rules/no-typescript-private-modifier.d.ts +17 -0
  110. package/dist/esm/rules/no-typescript-private-modifier.d.ts.map +1 -0
  111. package/dist/esm/rules/no-typescript-private-modifier.js +88 -0
  112. package/dist/esm/rules/no-typescript-private-modifier.js.map +1 -0
  113. package/dist/esm/rules/redeclared-prop-must-track-cdk-type.d.ts +17 -0
  114. package/dist/esm/rules/redeclared-prop-must-track-cdk-type.d.ts.map +1 -0
  115. package/dist/esm/rules/redeclared-prop-must-track-cdk-type.js +149 -0
  116. package/dist/esm/rules/redeclared-prop-must-track-cdk-type.js.map +1 -0
  117. package/docs/rules/builder-must-be-tagged.md +46 -0
  118. package/docs/rules/builder-must-implement-copy-state.md +50 -0
  119. package/docs/rules/constraint-metadata-required.md +44 -0
  120. package/docs/rules/lifecycle-build-context-required.md +39 -0
  121. package/docs/rules/lifecycle-build-must-forward-context.md +50 -0
  122. package/docs/rules/no-cdk-api-above-floor.md +56 -0
  123. package/docs/rules/no-cjs-incompatible-syntax.md +48 -0
  124. package/docs/rules/no-realm-bound-instanceof.md +87 -0
  125. package/docs/rules/no-typescript-private-modifier.md +72 -0
  126. package/docs/rules/redeclared-prop-must-track-cdk-type.md +60 -0
  127. package/package.json +81 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../../src/configs/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;;;;;;GAOG;AAEH;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,WAIrC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,WAIvC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAExC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAIlC,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The rule sets behind each preset, grouped by **what must be true of the
3
+ * consumer** for the rule to be correct — not by subject matter. Every rule
4
+ * belongs to exactly one, which a test enforces.
5
+ *
6
+ * The reasoning behind the tiers, and what moving a rule between them costs,
7
+ * is in ADR-0019.
8
+ */
9
+ /**
10
+ * True for anyone writing a `Lifecycle` or a builder, in any shape of project —
11
+ * an application as much as a library.
12
+ */
13
+ export const recommendedRules = {
14
+ "composurecdk/builder-must-implement-copy-state": "error",
15
+ "composurecdk/lifecycle-build-context-required": "error",
16
+ "composurecdk/no-realm-bound-instanceof": "error",
17
+ };
18
+ /**
19
+ * Additionally true for a package other people compile against.
20
+ *
21
+ * Each hazard here exists only once you emit a `.d.ts` someone else consumes:
22
+ * TS4094 and TS2883 need an exported mapped type, and a narrowed prop type only
23
+ * hurts when it is your published API. An application emits none of that.
24
+ *
25
+ * `lifecycle-build-must-forward-context` sits here for a different reason — a
26
+ * root-level build has no context to forward and is correct, and in an
27
+ * application nearly every build is a root build. That is a property of where
28
+ * the call sits in the composition graph, so no amount of narrowing the rule
29
+ * fixes it.
30
+ */
31
+ export const libraryAuthorRules = {
32
+ "composurecdk/lifecycle-build-must-forward-context": "error",
33
+ "composurecdk/no-typescript-private-modifier": "error",
34
+ "composurecdk/redeclared-prop-must-track-cdk-type": "error",
35
+ };
36
+ /**
37
+ * Additionally true for a package shipping both an ESM and a CommonJS build.
38
+ * Out of `recommended` because banning `import.meta` is a false positive for an
39
+ * ESM-only library, where it is legal and idiomatic.
40
+ */
41
+ export const dualPublishingRules = {
42
+ "composurecdk/no-cjs-incompatible-syntax": "error",
43
+ };
44
+ /**
45
+ * composureCDK's own house rules. Each encodes a convention or a hardcoded
46
+ * value — this repo's CDK floor, its tagging wrapper, its constraint catalogue
47
+ * — that is wrong elsewhere. Published so the preset is reproducible, not as a
48
+ * recommendation.
49
+ */
50
+ export const internalRules = {
51
+ "composurecdk/builder-must-be-tagged": "error",
52
+ "composurecdk/constraint-metadata-required": "error",
53
+ "composurecdk/no-cdk-api-above-floor": "error",
54
+ };
55
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../../src/configs/presets.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD,gDAAgD,EAAE,OAAO;IACzD,+CAA+C,EAAE,OAAO;IACxD,wCAAwC,EAAE,OAAO;CAClD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAuB;IACpD,mDAAmD,EAAE,OAAO;IAC5D,6CAA6C,EAAE,OAAO;IACtD,kDAAkD,EAAE,OAAO;CAC5D,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAuB;IACrD,yCAAyC,EAAE,OAAO;CACnD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,qCAAqC,EAAE,OAAO;IAC9C,2CAA2C,EAAE,OAAO;IACpD,qCAAqC,EAAE,OAAO;CAC/C,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { rules } from "./rules/index.js";
2
+ /**
3
+ * Self-contained flat config entries — each registers the plugin, so a consumer
4
+ * needs no `plugins` entry of their own, and they compose:
5
+ *
6
+ * ```js
7
+ * import composurecdk from "@composurecdk/eslint-plugin";
8
+ * export default [
9
+ * {
10
+ * files: ["src/**\/*.ts"],
11
+ * extends: [composurecdk.configs.recommended, composurecdk.configs.dualPublishing],
12
+ * },
13
+ * ];
14
+ * ```
15
+ *
16
+ * None declares `files`: the rules target library source, but only the consumer
17
+ * knows where theirs lives. See `./configs/presets.ts` for which rules each
18
+ * holds and why.
19
+ */
20
+ export declare const configs: {
21
+ recommended: {
22
+ name: string;
23
+ plugins: {
24
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
25
+ };
26
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
27
+ };
28
+ libraryAuthor: {
29
+ name: string;
30
+ plugins: {
31
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
32
+ };
33
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
34
+ };
35
+ dualPublishing: {
36
+ name: string;
37
+ plugins: {
38
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
39
+ };
40
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
41
+ };
42
+ internal: {
43
+ name: string;
44
+ plugins: {
45
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
46
+ };
47
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
48
+ };
49
+ };
50
+ export { rules };
51
+ declare const _default: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin & {
52
+ configs: {
53
+ recommended: {
54
+ name: string;
55
+ plugins: {
56
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
57
+ };
58
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
59
+ };
60
+ libraryAuthor: {
61
+ name: string;
62
+ plugins: {
63
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
64
+ };
65
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
66
+ };
67
+ dualPublishing: {
68
+ name: string;
69
+ plugins: {
70
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
71
+ };
72
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
73
+ };
74
+ internal: {
75
+ name: string;
76
+ plugins: {
77
+ composurecdk: import("@eslint/core", { with: { "resolution-mode": "require" } }).Plugin;
78
+ };
79
+ rules: import("@eslint/core", { with: { "resolution-mode": "require" } }).RulesConfig;
80
+ };
81
+ };
82
+ };
83
+ export default _default;
84
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAkBzC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBqB,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEjB,wBAAkD"}
@@ -0,0 +1,60 @@
1
+ import { dualPublishingRules, internalRules, libraryAuthorRules, recommendedRules, } from "./configs/presets.js";
2
+ import { rules } from "./rules/index.js";
3
+ /**
4
+ * The plugin object, built before `configs` so every preset can register *this*
5
+ * object under `plugins`.
6
+ *
7
+ * The identity matters. ESLint rejects a flat config whose entries register two
8
+ * different objects under one plugin name, so a consumer extending two presets
9
+ * at once — or extending one and writing `plugins: { composurecdk }` themselves
10
+ * — must be handing ESLint the same reference each time. `Object.assign` below
11
+ * attaches `configs` to this object rather than producing a second one.
12
+ */
13
+ const plugin = {
14
+ // Names the plugin in `--print-config` output and the flat config inspector.
15
+ meta: { name: "@composurecdk/eslint-plugin" },
16
+ rules,
17
+ };
18
+ /**
19
+ * Self-contained flat config entries — each registers the plugin, so a consumer
20
+ * needs no `plugins` entry of their own, and they compose:
21
+ *
22
+ * ```js
23
+ * import composurecdk from "@composurecdk/eslint-plugin";
24
+ * export default [
25
+ * {
26
+ * files: ["src/**\/*.ts"],
27
+ * extends: [composurecdk.configs.recommended, composurecdk.configs.dualPublishing],
28
+ * },
29
+ * ];
30
+ * ```
31
+ *
32
+ * None declares `files`: the rules target library source, but only the consumer
33
+ * knows where theirs lives. See `./configs/presets.ts` for which rules each
34
+ * holds and why.
35
+ */
36
+ export const configs = {
37
+ recommended: {
38
+ name: "composurecdk/recommended",
39
+ plugins: { composurecdk: plugin },
40
+ rules: recommendedRules,
41
+ },
42
+ libraryAuthor: {
43
+ name: "composurecdk/library-author",
44
+ plugins: { composurecdk: plugin },
45
+ rules: libraryAuthorRules,
46
+ },
47
+ dualPublishing: {
48
+ name: "composurecdk/dual-publishing",
49
+ plugins: { composurecdk: plugin },
50
+ rules: dualPublishingRules,
51
+ },
52
+ internal: {
53
+ name: "composurecdk/internal",
54
+ plugins: { composurecdk: plugin },
55
+ rules: internalRules,
56
+ },
57
+ };
58
+ export { rules };
59
+ export default Object.assign(plugin, { configs });
60
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,MAAM,GAAkB;IAC5B,6EAA6E;IAC7E,IAAI,EAAE,EAAE,IAAI,EAAE,6BAA6B,EAAE;IAC7C,KAAK;CACN,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,WAAW,EAAE;QACX,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;QACjC,KAAK,EAAE,gBAAgB;KACxB;IACD,aAAa,EAAE;QACb,IAAI,EAAE,6BAA6B;QACnC,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;QACjC,KAAK,EAAE,kBAAkB;KAC1B;IACD,cAAc,EAAE;QACd,IAAI,EAAE,8BAA8B;QACpC,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;QACjC,KAAK,EAAE,mBAAmB;KAC3B;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;QACjC,KAAK,EAAE,aAAa;KACrB;CACsC,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,eAAe,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,15 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Flags `Builder()` or `IBuilder<…>` from `@composurecdk/core` in a builder that
4
+ * wraps a taggable resource.
5
+ *
6
+ * Tagging is cross-cutting: every deployable resource needs it, and a consumer
7
+ * expects the same tagging surface on every builder rather than on whichever
8
+ * ones remembered to add it. `taggedBuilder` / `ITaggedBuilder` from
9
+ * `@composurecdk/cloudformation` supply that surface; the core builder does not,
10
+ * because core stays independent of CloudFormation.
11
+ *
12
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/builder-must-be-tagged.md | the rule documentation}.
13
+ */
14
+ export declare const rule: Rule.RuleModule;
15
+ //# sourceMappingURL=builder-must-be-tagged.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder-must-be-tagged.d.ts","sourceRoot":"","sources":["../../../src/rules/builder-must-be-tagged.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAanC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAkDvB,CAAC"}
@@ -0,0 +1,67 @@
1
+ const taggedSuffix = "If the wrapped CFN resource has no Tags property (Route53 records, IAM ManagedPolicy, " +
2
+ "SNS Subscription, AWS Budgets), disable this rule on the offending line with a directive " +
3
+ "naming the resource: `// eslint-disable-next-line composurecdk/builder-must-be-tagged -- AWS::… has no Tags property`.";
4
+ /**
5
+ * Flags `Builder()` or `IBuilder<…>` from `@composurecdk/core` in a builder that
6
+ * wraps a taggable resource.
7
+ *
8
+ * Tagging is cross-cutting: every deployable resource needs it, and a consumer
9
+ * expects the same tagging surface on every builder rather than on whichever
10
+ * ones remembered to add it. `taggedBuilder` / `ITaggedBuilder` from
11
+ * `@composurecdk/cloudformation` supply that surface; the core builder does not,
12
+ * because core stays independent of CloudFormation.
13
+ *
14
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/builder-must-be-tagged.md | the rule documentation}.
15
+ */
16
+ export const rule = {
17
+ meta: {
18
+ type: "problem",
19
+ docs: {
20
+ description: "Library builders must use `taggedBuilder` / `ITaggedBuilder` from `@composurecdk/cloudformation` " +
21
+ "unless the wrapped CFN resource has no Tags property.",
22
+ },
23
+ schema: [],
24
+ messages: {
25
+ restrictedCall: "Use `taggedBuilder` from `@composurecdk/cloudformation` instead of `Builder` from `@composurecdk/core`. " +
26
+ taggedSuffix,
27
+ restrictedType: "Use `ITaggedBuilder` from `@composurecdk/cloudformation` instead of `IBuilder` from `@composurecdk/core`. " +
28
+ taggedSuffix,
29
+ },
30
+ },
31
+ create(ctx) {
32
+ const localBuilderNames = new Set();
33
+ const localIBuilderNames = new Set();
34
+ const listener = {
35
+ ImportDeclaration(node) {
36
+ if (node.source.value !== "@composurecdk/core")
37
+ return;
38
+ for (const spec of node.specifiers) {
39
+ if (spec.type !== "ImportSpecifier")
40
+ continue;
41
+ if (spec.imported.type !== "Identifier")
42
+ continue;
43
+ if (spec.imported.name === "Builder")
44
+ localBuilderNames.add(spec.local.name);
45
+ if (spec.imported.name === "IBuilder")
46
+ localIBuilderNames.add(spec.local.name);
47
+ }
48
+ },
49
+ CallExpression(node) {
50
+ if (node.callee.type === "Identifier" && localBuilderNames.has(node.callee.name)) {
51
+ ctx.report({ node: node.callee, messageId: "restrictedCall" });
52
+ }
53
+ },
54
+ };
55
+ listener.TSTypeReference = (node) => {
56
+ const ref = node;
57
+ const name = ref.typeName.name;
58
+ if (ref.typeName.type === "Identifier" &&
59
+ name !== undefined &&
60
+ localIBuilderNames.has(name)) {
61
+ ctx.report({ node: ref.typeName, messageId: "restrictedType" });
62
+ }
63
+ };
64
+ return listener;
65
+ },
66
+ };
67
+ //# sourceMappingURL=builder-must-be-tagged.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder-must-be-tagged.js","sourceRoot":"","sources":["../../../src/rules/builder-must-be-tagged.ts"],"names":[],"mappings":"AAGA,MAAM,YAAY,GAChB,wFAAwF;IACxF,2FAA2F;IAC3F,wHAAwH,CAAC;AAO3H;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,mGAAmG;gBACnG,uDAAuD;SAC1D;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,cAAc,EACZ,0GAA0G;gBAC1G,YAAY;YACd,cAAc,EACZ,4GAA4G;gBAC5G,YAAY;SACf;KACF;IACD,MAAM,CAAC,GAAG;QACR,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;QAC5C,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;QAC7C,MAAM,QAAQ,GAAsB;YAClC,iBAAiB,CAAC,IAAI;gBACpB,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,oBAAoB;oBAAE,OAAO;gBACvD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACnC,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB;wBAAE,SAAS;oBAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;wBAAE,SAAS;oBAClD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS;wBAAE,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC7E,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,UAAU;wBAAE,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACjF,CAAC;YACH,CAAC;YACD,cAAc,CAAC,IAAI;gBACjB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjF,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;SACF,CAAC;QACF,QAAQ,CAAC,eAAe,GAAG,CAAC,IAAU,EAAE,EAAE;YACxC,MAAM,GAAG,GAAG,IAAsC,CAAC;YACnD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC/B,IACE,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;gBAClC,IAAI,KAAK,SAAS;gBAClB,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,EAC5B,CAAC;gBACD,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC,CAAC;QACF,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Flags a builder class holding private state with no `[COPY_STATE]` hook.
4
+ *
5
+ * `.copy()` shallow-clones the builder's props. State held in private fields sits
6
+ * outside that copy, so without the hook it is silently dropped: the clone looks
7
+ * correct and behaves differently. Implement `[COPY_STATE]` to carry such state
8
+ * onto the clone.
9
+ *
10
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/builder-must-implement-copy-state.md | the rule documentation}.
11
+ */
12
+ export declare const rule: Rule.RuleModule;
13
+ //# sourceMappingURL=builder-must-implement-copy-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder-must-implement-copy-state.d.ts","sourceRoot":"","sources":["../../../src/rules/builder-must-implement-copy-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,QAAQ,CAAC;AAkB/C;;;;;;;;;GASG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAkGvB,CAAC"}
@@ -0,0 +1,120 @@
1
+ function readIgnoreMarker(fieldNode, sourceCode) {
2
+ const comments = sourceCode.getCommentsBefore(fieldNode);
3
+ for (const comment of comments) {
4
+ const match = /@copy-state:\s*ignore(.*)$/i.exec(comment.value);
5
+ if (!match)
6
+ continue;
7
+ const tail = match[1];
8
+ return /--\s*\S/.test(tail) ? "valid" : "missing-justification";
9
+ }
10
+ return "absent";
11
+ }
12
+ /**
13
+ * Flags a builder class holding private state with no `[COPY_STATE]` hook.
14
+ *
15
+ * `.copy()` shallow-clones the builder's props. State held in private fields sits
16
+ * outside that copy, so without the hook it is silently dropped: the clone looks
17
+ * correct and behaves differently. Implement `[COPY_STATE]` to carry such state
18
+ * onto the clone.
19
+ *
20
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/builder-must-implement-copy-state.md | the rule documentation}.
21
+ */
22
+ export const rule = {
23
+ meta: {
24
+ type: "problem",
25
+ docs: {
26
+ description: "Builder classes with private fields must implement `[COPY_STATE]` " +
27
+ "(or annotate fields with `// @copy-state: ignore -- reason`). See ADR-0005.",
28
+ },
29
+ schema: [],
30
+ messages: {
31
+ missingHook: "Builder class `{{className}}` has private field(s) {{fields}} but no `[COPY_STATE]` hook. " +
32
+ "`.copy()` shallow-clones props only, so these fields are silently dropped from the " +
33
+ "clone. Implement `[COPY_STATE](target) { … }` to carry them across, or annotate each " +
34
+ "field with `// @copy-state: ignore -- reason`.",
35
+ ignoreMarkerNeedsJustification: "`@copy-state: ignore` on field `{{field}}` must include a justification after `--` " +
36
+ "(e.g. `// @copy-state: ignore -- regenerated per build`). The reason survives refactors " +
37
+ "and shows up in code review.",
38
+ },
39
+ },
40
+ create(ctx) {
41
+ const sourceCode = ctx.sourceCode;
42
+ const builderFactoryNames = new Set();
43
+ const classNodes = new Map();
44
+ const builderClassNames = new Set();
45
+ return {
46
+ ImportDeclaration(node) {
47
+ if (node.source.value !== "@composurecdk/core" &&
48
+ node.source.value !== "@composurecdk/cloudformation") {
49
+ return;
50
+ }
51
+ for (const spec of node.specifiers) {
52
+ if (spec.type !== "ImportSpecifier")
53
+ continue;
54
+ if (spec.imported.type !== "Identifier")
55
+ continue;
56
+ if (spec.imported.name === "Builder" || spec.imported.name === "taggedBuilder") {
57
+ builderFactoryNames.add(spec.local.name);
58
+ }
59
+ }
60
+ },
61
+ ClassDeclaration(node) {
62
+ classNodes.set(node.id.name, node);
63
+ },
64
+ CallExpression(node) {
65
+ if (node.callee.type !== "Identifier")
66
+ return;
67
+ if (!builderFactoryNames.has(node.callee.name))
68
+ return;
69
+ if (node.arguments.length === 0)
70
+ return;
71
+ const arg = node.arguments[0];
72
+ if (arg.type === "Identifier")
73
+ builderClassNames.add(arg.name);
74
+ },
75
+ "Program:exit"() {
76
+ for (const className of builderClassNames) {
77
+ const classNode = classNodes.get(className);
78
+ if (!classNode)
79
+ continue;
80
+ const privateFields = [];
81
+ let hasCopyStateHook = false;
82
+ for (const member of classNode.body.body) {
83
+ if (member.type === "PropertyDefinition" && member.key.type === "PrivateIdentifier") {
84
+ privateFields.push(member);
85
+ }
86
+ else if (member.type === "MethodDefinition" &&
87
+ member.computed &&
88
+ member.key.type === "Identifier" &&
89
+ member.key.name === "COPY_STATE") {
90
+ hasCopyStateHook = true;
91
+ }
92
+ }
93
+ const fieldsNeedingHook = [];
94
+ for (const field of privateFields) {
95
+ const ignoreState = readIgnoreMarker(field, sourceCode);
96
+ if (ignoreState === "valid")
97
+ continue;
98
+ if (ignoreState === "missing-justification") {
99
+ ctx.report({
100
+ node: field,
101
+ messageId: "ignoreMarkerNeedsJustification",
102
+ data: { field: `#${field.key.name}` },
103
+ });
104
+ continue;
105
+ }
106
+ fieldsNeedingHook.push(`#${field.key.name}`);
107
+ }
108
+ if (!hasCopyStateHook && fieldsNeedingHook.length > 0) {
109
+ ctx.report({
110
+ node: classNode.id,
111
+ messageId: "missingHook",
112
+ data: { className, fields: fieldsNeedingHook.join(", ") },
113
+ });
114
+ }
115
+ }
116
+ },
117
+ };
118
+ },
119
+ };
120
+ //# sourceMappingURL=builder-must-implement-copy-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder-must-implement-copy-state.js","sourceRoot":"","sources":["../../../src/rules/builder-must-implement-copy-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Requires every `stringConstraint({ … })` call to set a non-empty `name`,
4
+ * `allowed` and `source`.
5
+ *
6
+ * Those three fields are what turn a synth-time validation failure into
7
+ * something actionable — they name the offending property, list the characters
8
+ * it accepts, and link the AWS documentation. The factory's type already makes
9
+ * the keys required, so the gap this closes is the one the type checker cannot
10
+ * see: a present-but-empty string compiles happily while degrading every message
11
+ * the constraint will ever produce.
12
+ *
13
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/constraint-metadata-required.md | the rule documentation}.
14
+ */
15
+ export declare const rule: Rule.RuleModule;
16
+ //# sourceMappingURL=constraint-metadata-required.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraint-metadata-required.d.ts","sourceRoot":"","sources":["../../../src/rules/constraint-metadata-required.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAgBnC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAgDvB,CAAC"}
@@ -0,0 +1,76 @@
1
+ const REQUIRED_FIELDS = ["name", "allowed", "source"];
2
+ function propertyKey(property) {
3
+ if (property.computed)
4
+ return undefined;
5
+ if (property.key.type === "Identifier")
6
+ return property.key.name;
7
+ if (property.key.type === "Literal")
8
+ return String(property.key.value);
9
+ return undefined;
10
+ }
11
+ function isEmptyStringLiteral(value) {
12
+ return value.type === "Literal" && typeof value.value === "string" && value.value.trim() === "";
13
+ }
14
+ /**
15
+ * Requires every `stringConstraint({ … })` call to set a non-empty `name`,
16
+ * `allowed` and `source`.
17
+ *
18
+ * Those three fields are what turn a synth-time validation failure into
19
+ * something actionable — they name the offending property, list the characters
20
+ * it accepts, and link the AWS documentation. The factory's type already makes
21
+ * the keys required, so the gap this closes is the one the type checker cannot
22
+ * see: a present-but-empty string compiles happily while degrading every message
23
+ * the constraint will ever produce.
24
+ *
25
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/constraint-metadata-required.md | the rule documentation}.
26
+ */
27
+ export const rule = {
28
+ meta: {
29
+ type: "problem",
30
+ docs: {
31
+ description: "stringConstraint() must set a non-empty name, allowed, and source so every validation error names the allowed set and links the AWS doc",
32
+ },
33
+ schema: [],
34
+ messages: {
35
+ missingField: "stringConstraint() is missing required `{{field}}`. Every catalogue entry must set " +
36
+ "name, allowed and source, so a synth-time error can name the allowed set and link the AWS doc.",
37
+ emptyField: "stringConstraint() `{{field}}` is empty. It is surfaced verbatim in the validation " +
38
+ "error message — give it a meaningful value.",
39
+ },
40
+ },
41
+ create(ctx) {
42
+ return {
43
+ CallExpression(node) {
44
+ if (node.callee.type !== "Identifier" || node.callee.name !== "stringConstraint") {
45
+ return;
46
+ }
47
+ if (node.arguments.length === 0)
48
+ return;
49
+ const arg = node.arguments[0];
50
+ if (arg.type !== "ObjectExpression")
51
+ return;
52
+ // A spread could supply any of the required fields — can't verify statically.
53
+ if (arg.properties.some((p) => p.type === "SpreadElement"))
54
+ return;
55
+ const values = new Map();
56
+ for (const property of arg.properties) {
57
+ if (property.type !== "Property")
58
+ continue;
59
+ const key = propertyKey(property);
60
+ if (key !== undefined)
61
+ values.set(key, property.value);
62
+ }
63
+ for (const field of REQUIRED_FIELDS) {
64
+ const value = values.get(field);
65
+ if (value === undefined) {
66
+ ctx.report({ node, messageId: "missingField", data: { field } });
67
+ }
68
+ else if (isEmptyStringLiteral(value)) {
69
+ ctx.report({ node: value, messageId: "emptyField", data: { field } });
70
+ }
71
+ }
72
+ },
73
+ };
74
+ },
75
+ };
76
+ //# sourceMappingURL=constraint-metadata-required.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constraint-metadata-required.js","sourceRoot":"","sources":["../../../src/rules/constraint-metadata-required.ts"],"names":[],"mappings":"AAGA,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAE/D,SAAS,WAAW,CAAC,QAAkB;IACrC,IAAI,QAAQ,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;IACjE,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,oBAAoB,CAAC,KAA2B;IACvD,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAClG,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,yIAAyI;SAC5I;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,YAAY,EACV,qFAAqF;gBACrF,gGAAgG;YAClG,UAAU,EACR,qFAAqF;gBACrF,6CAA6C;SAChD;KACF;IACD,MAAM,CAAC,GAAG;QACR,OAAO;YACL,cAAc,CAAC,IAAoB;gBACjC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;oBACjF,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBACxC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI,GAAG,CAAC,IAAI,KAAK,kBAAkB;oBAAE,OAAO;gBAE5C,8EAA8E;gBAC9E,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC;oBAAE,OAAO;gBAEnE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAgC,CAAC;gBACvD,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;oBACtC,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU;wBAAE,SAAS;oBAC3C,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;oBAClC,IAAI,GAAG,KAAK,SAAS;wBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACzD,CAAC;gBAED,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;oBACpC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAChC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;oBACnE,CAAC;yBAAM,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;wBACvC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;oBACxE,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Rule } from "eslint";
2
+ export declare const rules: {
3
+ "builder-must-be-tagged": Rule.RuleModule;
4
+ "builder-must-implement-copy-state": Rule.RuleModule;
5
+ "constraint-metadata-required": Rule.RuleModule;
6
+ "lifecycle-build-context-required": Rule.RuleModule;
7
+ "lifecycle-build-must-forward-context": Rule.RuleModule;
8
+ "no-cdk-api-above-floor": Rule.RuleModule;
9
+ "no-cjs-incompatible-syntax": Rule.RuleModule;
10
+ "no-realm-bound-instanceof": Rule.RuleModule;
11
+ "no-typescript-private-modifier": Rule.RuleModule;
12
+ "redeclared-prop-must-track-cdk-type": Rule.RuleModule;
13
+ };
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AA6CnC,eAAO,MAAM,KAAK;;;;;;;;;;;CAWhB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { rule as builderMustBeTagged } from "./builder-must-be-tagged.js";
2
+ import { rule as builderMustImplementCopyState } from "./builder-must-implement-copy-state.js";
3
+ import { rule as constraintMetadataRequired } from "./constraint-metadata-required.js";
4
+ import { rule as lifecycleBuildContextRequired } from "./lifecycle-build-context-required.js";
5
+ import { rule as lifecycleBuildMustForwardContext } from "./lifecycle-build-must-forward-context.js";
6
+ import { rule as noCdkApiAboveFloor } from "./no-cdk-api-above-floor.js";
7
+ import { rule as noCjsIncompatibleSyntax } from "./no-cjs-incompatible-syntax.js";
8
+ import { rule as noRealmBoundInstanceof } from "./no-realm-bound-instanceof.js";
9
+ import { rule as noTypescriptPrivateModifier } from "./no-typescript-private-modifier.js";
10
+ import { rule as redeclaredPropMustTrackCdkType } from "./redeclared-prop-must-track-cdk-type.js";
11
+ /**
12
+ * Where each rule's documentation lives — the page an editor opens from a
13
+ * reported violation.
14
+ *
15
+ * Pinned to `main` rather than the release tag: reading the package's own
16
+ * version needs a JSON import, which does not emit cleanly to CommonJS
17
+ * (ADR-0007). The cost is that a consumer on an older version is shown current
18
+ * docs, which is the same trade most published plugins make.
19
+ */
20
+ const DOCS_BASE = "https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules";
21
+ /**
22
+ * Attaches `meta.docs.url` to every rule, derived from the name it is
23
+ * registered under.
24
+ *
25
+ * Deriving beats writing it per rule: the link an editor offers cannot disagree
26
+ * with the rule id ESLint reports, and adding a rule cannot forget it.
27
+ */
28
+ function withDocsUrl(entries) {
29
+ return Object.fromEntries(Object.entries(entries).map(([name, rule]) => [
30
+ name,
31
+ {
32
+ ...rule,
33
+ meta: { ...rule.meta, docs: { ...rule.meta?.docs, url: `${DOCS_BASE}/${name}.md` } },
34
+ },
35
+ ]));
36
+ }
37
+ export const rules = withDocsUrl({
38
+ "builder-must-be-tagged": builderMustBeTagged,
39
+ "builder-must-implement-copy-state": builderMustImplementCopyState,
40
+ "constraint-metadata-required": constraintMetadataRequired,
41
+ "lifecycle-build-context-required": lifecycleBuildContextRequired,
42
+ "lifecycle-build-must-forward-context": lifecycleBuildMustForwardContext,
43
+ "no-cdk-api-above-floor": noCdkApiAboveFloor,
44
+ "no-cjs-incompatible-syntax": noCjsIncompatibleSyntax,
45
+ "no-realm-bound-instanceof": noRealmBoundInstanceof,
46
+ "no-typescript-private-modifier": noTypescriptPrivateModifier,
47
+ "redeclared-prop-must-track-cdk-type": redeclaredPropMustTrackCdkType,
48
+ });
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,IAAI,IAAI,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AAC/F,OAAO,EAAE,IAAI,IAAI,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,IAAI,IAAI,6BAA6B,EAAE,MAAM,uCAAuC,CAAC;AAC9F,OAAO,EAAE,IAAI,IAAI,gCAAgC,EAAE,MAAM,2CAA2C,CAAC;AACrG,OAAO,EAAE,IAAI,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,IAAI,IAAI,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,IAAI,IAAI,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,IAAI,IAAI,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAC1F,OAAO,EAAE,IAAI,IAAI,8BAA8B,EAAE,MAAM,0CAA0C,CAAC;AAElG;;;;;;;;GAQG;AACH,MAAM,SAAS,GACb,oFAAoF,CAAC;AAEvF;;;;;;GAMG;AACH,SAAS,WAAW,CAA4C,OAAU;IACxE,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QAC5C,IAAI;QACJ;YACE,GAAG,IAAI;YACP,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE;SACrF;KACF,CAAC,CAGE,CAAC;AACT,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC;IAC/B,wBAAwB,EAAE,mBAAmB;IAC7C,mCAAmC,EAAE,6BAA6B;IAClE,8BAA8B,EAAE,0BAA0B;IAC1D,kCAAkC,EAAE,6BAA6B;IACjE,sCAAsC,EAAE,gCAAgC;IACxE,wBAAwB,EAAE,kBAAkB;IAC5C,4BAA4B,EAAE,uBAAuB;IACrD,2BAA2B,EAAE,sBAAsB;IACnD,gCAAgC,EAAE,2BAA2B;IAC7D,qCAAqC,EAAE,8BAA8B;CACtE,CAAC,CAAC"}