@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,24 @@
1
+ import type { Scope } from "eslint";
2
+ import type { Identifier, MemberExpression } from "estree";
3
+ /** Strip any TS-only wrappers from an expression, returning the node inside. */
4
+ export declare function unwrapWrappers<T>(expr: T): T;
5
+ /**
6
+ * The leftmost identifier a member chain reads from — `CfnAlarm` in
7
+ * `cdk.aws_cloudwatch.CfnAlarm`, unwrapping TS wrappers as it goes.
8
+ *
9
+ * Returns `undefined` when a call or other expression breaks the chain:
10
+ * `f().CfnAlarm` reads a runtime value rather than the imported binding, so no
11
+ * rule that reasons about imports should fire on it.
12
+ */
13
+ export declare function chainRoot(expr: MemberExpression["object"]): Identifier | undefined;
14
+ /**
15
+ * The module specifier `name` was imported from, or `undefined` when it
16
+ * resolves to anything else — a global, a local declaration, a parameter.
17
+ *
18
+ * Walks outward through the scope chain so a local that shadows an import name
19
+ * wins, matching what the runtime would do.
20
+ */
21
+ export declare function importSourceOf(scope: Scope.Scope, name: string): string | undefined;
22
+ /** True for `aws-cdk-lib` and any submodule of it. */
23
+ export declare function isCdkSource(source: string): boolean;
24
+ //# sourceMappingURL=imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../../../../src/rules/lib/imports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AA4B3D,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAM5C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAAG,UAAU,GAAG,SAAS,CAMlF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAmBnF;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * TS-only wrappers a developer might use to silence types. Peeled by
3
+ * {@link unwrapWrappers} and {@link chainRoot} so they cannot smuggle an
4
+ * expression past a rule.
5
+ */
6
+ const TS_WRAPPERS = new Set([
7
+ "ChainExpression",
8
+ "TSAsExpression",
9
+ "TSNonNullExpression",
10
+ "TSSatisfiesExpression",
11
+ "TSTypeAssertion",
12
+ ]);
13
+ /** Strip any TS-only wrappers from an expression, returning the node inside. */
14
+ export function unwrapWrappers(expr) {
15
+ let current = expr;
16
+ while (TS_WRAPPERS.has(current.type)) {
17
+ current = current.expression;
18
+ }
19
+ return current;
20
+ }
21
+ /**
22
+ * The leftmost identifier a member chain reads from — `CfnAlarm` in
23
+ * `cdk.aws_cloudwatch.CfnAlarm`, unwrapping TS wrappers as it goes.
24
+ *
25
+ * Returns `undefined` when a call or other expression breaks the chain:
26
+ * `f().CfnAlarm` reads a runtime value rather than the imported binding, so no
27
+ * rule that reasons about imports should fire on it.
28
+ */
29
+ export function chainRoot(expr) {
30
+ let current = unwrapWrappers(expr);
31
+ while (current.type === "MemberExpression") {
32
+ current = unwrapWrappers(current.object);
33
+ }
34
+ return current.type === "Identifier" ? current : undefined;
35
+ }
36
+ /**
37
+ * The module specifier `name` was imported from, or `undefined` when it
38
+ * resolves to anything else — a global, a local declaration, a parameter.
39
+ *
40
+ * Walks outward through the scope chain so a local that shadows an import name
41
+ * wins, matching what the runtime would do.
42
+ */
43
+ export function importSourceOf(scope, name) {
44
+ for (let current = scope; current !== null; current = current.upper) {
45
+ const variable = current.set.get(name);
46
+ if (variable === undefined)
47
+ continue;
48
+ // `.at(0)` (not `[0]`) — defs can be empty at runtime for predefined
49
+ // globals (e.g. `console`), even though `Variable.defs: Definition[]` says
50
+ // otherwise; `.at` gives us the honest `Definition | undefined`.
51
+ const def = variable.defs.at(0);
52
+ if (def?.type !== "ImportBinding")
53
+ return undefined;
54
+ // `def.parent` is typed as `ImportDeclaration` but at runtime
55
+ // typescript-eslint also reports `ImportBinding` for `import x =
56
+ // require(...)`, whose parent is a `TSImportEqualsDeclaration` with no
57
+ // `.source`. Widen and check the discriminator before reading `source`.
58
+ const parent = def.parent;
59
+ if (parent.type !== "ImportDeclaration")
60
+ return undefined;
61
+ const source = parent.source?.value;
62
+ return typeof source === "string" ? source : undefined;
63
+ }
64
+ return undefined;
65
+ }
66
+ /** True for `aws-cdk-lib` and any submodule of it. */
67
+ export function isCdkSource(source) {
68
+ return source === "aws-cdk-lib" || source.startsWith("aws-cdk-lib/");
69
+ }
70
+ //# sourceMappingURL=imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imports.js","sourceRoot":"","sources":["../../../../src/rules/lib/imports.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,iBAAiB;IACjB,gBAAgB;IAChB,qBAAqB;IACrB,uBAAuB;IACvB,iBAAiB;CAClB,CAAC,CAAC;AAEH,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAI,IAAO;IACvC,IAAI,OAAO,GAAG,IAA2B,CAAC;IAC1C,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,GAAG,OAAO,CAAC,UAAsB,CAAC;IAC3C,CAAC;IACD,OAAO,OAAuB,CAAC;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAgC;IACxD,IAAI,OAAO,GAAG,cAAc,CAAC,IAAI,CAAwB,CAAC;IAC1D,OAAO,OAAO,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QAC3C,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAa,CAAC;IACvD,CAAC;IACD,OAAO,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAE,OAAiC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,IAAY;IAC7D,KAAK,IAAI,OAAO,GAAuB,KAAK,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACxF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,QAAQ,KAAK,SAAS;YAAE,SAAS;QACrC,qEAAqE;QACrE,2EAA2E;QAC3E,iEAAiE;QACjE,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,GAAG,EAAE,IAAI,KAAK,eAAe;YAAE,OAAO,SAAS,CAAC;QACpD,8DAA8D;QAC9D,iEAAiE;QACjE,uEAAuE;QACvE,wEAAwE;QACxE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAwD,CAAC;QAC5E,IAAI,MAAM,CAAC,IAAI,KAAK,mBAAmB;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QACpC,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,MAAM,KAAK,aAAa,IAAI,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Flags a `Lifecycle` class whose `build` takes no `context` parameter although
4
+ * the class body uses `Resolvable<…>`.
5
+ *
6
+ * A builder holding a `Resolvable<…>` accepts refs when it is configured, but
7
+ * resolving one needs the build context. With no `context` parameter the
8
+ * resolution receives `undefined` and the ref throws at synth, so the builder
9
+ * offers an API it cannot honour.
10
+ *
11
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/lifecycle-build-context-required.md | the rule documentation}.
12
+ */
13
+ export declare const rule: Rule.RuleModule;
14
+ //# sourceMappingURL=lifecycle-build-context-required.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-build-context-required.d.ts","sourceRoot":"","sources":["../../../src/rules/lifecycle-build-context-required.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAiCvB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Flags a `Lifecycle` class whose `build` takes no `context` parameter although
3
+ * the class body uses `Resolvable<…>`.
4
+ *
5
+ * A builder holding a `Resolvable<…>` accepts refs when it is configured, but
6
+ * resolving one needs the build context. With no `context` parameter the
7
+ * resolution receives `undefined` and the ref throws at synth, so the builder
8
+ * offers an API it cannot honour.
9
+ *
10
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/lifecycle-build-context-required.md | the rule documentation}.
11
+ */
12
+ export const rule = {
13
+ meta: {
14
+ type: "problem",
15
+ docs: {
16
+ description: "Lifecycle.build() must accept context when the builder uses Resolvable<…>",
17
+ },
18
+ schema: [],
19
+ messages: {
20
+ missingContext: "`build()` is missing the `context` parameter, but this class uses `Resolvable<…>`. " +
21
+ "Refs need context to resolve — add `context?: Record<string, object>` (or a typed dependency record) and pass it to `resolve(value, context)`.",
22
+ },
23
+ },
24
+ create(ctx) {
25
+ const sourceCode = ctx.sourceCode;
26
+ return {
27
+ ClassBody(node) {
28
+ const build = node.body.find((member) => member.type === "MethodDefinition" &&
29
+ member.key.type === "Identifier" &&
30
+ member.key.name === "build");
31
+ if (!build)
32
+ return;
33
+ // Lifecycle.build(scope, id, context?) — 3rd param is context.
34
+ if (build.value.params.length >= 3)
35
+ return;
36
+ if (/\bResolvable\s*</.test(sourceCode.getText(node))) {
37
+ ctx.report({ node: build, messageId: "missingContext" });
38
+ }
39
+ },
40
+ };
41
+ },
42
+ };
43
+ //# sourceMappingURL=lifecycle-build-context-required.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-build-context-required.js","sourceRoot":"","sources":["../../../src/rules/lifecycle-build-context-required.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,2EAA2E;SACzF;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,cAAc,EACZ,qFAAqF;gBACrF,gJAAgJ;SACnJ;KACF;IACD,MAAM,CAAC,GAAG;QACR,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;QAClC,OAAO;YACL,SAAS,CAAC,IAAe;gBACvB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAC1B,CAAC,MAAM,EAA8B,EAAE,CACrC,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBAClC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY;oBAChC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,OAAO,CAC9B,CAAC;gBACF,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,+DAA+D;gBAC/D,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC;oBAAE,OAAO;gBAE3C,IAAI,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;oBACtD,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Flags a two-argument `builder.build(scope, id)` call, which gives the
4
+ * sub-builder no context to resolve refs against.
5
+ *
6
+ * A builder that delegates must pass its context on. When it does not, the
7
+ * sub-builder resolves against an empty context, and any ref the caller supplied
8
+ * through a `configure` callback fails at synth with "component not found in
9
+ * context".
10
+ *
11
+ * This is the call-site counterpart to `lifecycle-build-context-required`, which
12
+ * checks the declaration. Accepting the parameter is not enough — the common
13
+ * failure is a builder that takes `context` and then drops it on the way down.
14
+ *
15
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/lifecycle-build-must-forward-context.md | the rule documentation}.
16
+ */
17
+ export declare const rule: Rule.RuleModule;
18
+ //# sourceMappingURL=lifecycle-build-must-forward-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-build-must-forward-context.d.ts","sourceRoot":"","sources":["../../../src/rules/lifecycle-build-must-forward-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAgCvB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Flags a two-argument `builder.build(scope, id)` call, which gives the
3
+ * sub-builder no context to resolve refs against.
4
+ *
5
+ * A builder that delegates must pass its context on. When it does not, the
6
+ * sub-builder resolves against an empty context, and any ref the caller supplied
7
+ * through a `configure` callback fails at synth with "component not found in
8
+ * context".
9
+ *
10
+ * This is the call-site counterpart to `lifecycle-build-context-required`, which
11
+ * checks the declaration. Accepting the parameter is not enough — the common
12
+ * failure is a builder that takes `context` and then drops it on the way down.
13
+ *
14
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/lifecycle-build-must-forward-context.md | the rule documentation}.
15
+ */
16
+ export const rule = {
17
+ meta: {
18
+ type: "problem",
19
+ docs: {
20
+ description: "A Lifecycle build() call must forward the build context to sub-builders",
21
+ },
22
+ schema: [],
23
+ messages: {
24
+ missingContext: "`build(scope, id)` drops the build context. A sub-builder resolves refs against `{}` " +
25
+ "without it, so a `ref()` passed through a `configure` callback throws " +
26
+ '"component not found in context". Pass the context as the third argument — ' +
27
+ "threading it through any helper function in between. If this is a deliberate root " +
28
+ "build with no context to forward, disable this rule on the line and say why.",
29
+ },
30
+ },
31
+ create(ctx) {
32
+ return {
33
+ CallExpression(node) {
34
+ const callee = node.callee;
35
+ if (callee.type !== "MemberExpression")
36
+ return;
37
+ if (callee.computed)
38
+ return;
39
+ if (callee.property.type !== "Identifier" || callee.property.name !== "build")
40
+ return;
41
+ // Exactly (scope, id) — see the "Why exactly two arguments" note above.
42
+ if (node.arguments.length !== 2)
43
+ return;
44
+ if (node.arguments.some((arg) => arg.type === "SpreadElement"))
45
+ return;
46
+ ctx.report({ node: node, messageId: "missingContext" });
47
+ },
48
+ };
49
+ },
50
+ };
51
+ //# sourceMappingURL=lifecycle-build-must-forward-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle-build-must-forward-context.js","sourceRoot":"","sources":["../../../src/rules/lifecycle-build-must-forward-context.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,yEAAyE;SACvF;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,cAAc,EACZ,uFAAuF;gBACvF,wEAAwE;gBACxE,6EAA6E;gBAC7E,oFAAoF;gBACpF,8EAA8E;SACjF;KACF;IACD,MAAM,CAAC,GAAG;QACR,OAAO;YACL,cAAc,CAAC,IAAoB;gBACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBAC3B,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB;oBAAE,OAAO;gBAC/C,IAAI,MAAM,CAAC,QAAQ;oBAAE,OAAO;gBAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO;oBAAE,OAAO;gBAEtF,wEAAwE;gBACxE,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBACxC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,eAAe,CAAC;oBAAE,OAAO;gBAEvE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAuB,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAC7E,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Flags an `aws-cdk-lib` API newer than the oldest CDK version this package
4
+ * supports.
5
+ *
6
+ * Such a call type-checks and passes tests, because both run against whichever
7
+ * CDK is installed here — the newest. It then throws at runtime for anyone whose
8
+ * own `aws-cdk-lib` is older, while still inside the range the package declares
9
+ * it supports. The fix is a form of the same check that works across the whole
10
+ * range; the rule's message names one per banned API.
11
+ *
12
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-cdk-api-above-floor.md | the rule documentation}.
13
+ */
14
+ export declare const rule: Rule.RuleModule;
15
+ //# sourceMappingURL=no-cdk-api-above-floor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cdk-api-above-floor.d.ts","sourceRoot":"","sources":["../../../src/rules/no-cdk-api-above-floor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAqDnC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAiCvB,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { chainRoot, importSourceOf, isCdkSource } from "./lib/imports.js";
2
+ /**
3
+ * Foundational `isCfn*` guards that predate the floor (core, since v2.0) and
4
+ * are therefore allowed — `isCfnResource` is in fact the portable replacement
5
+ * for the banned per-resource guards.
6
+ */
7
+ const ALLOWED_CFN_GUARDS = new Set(["isCfnResource", "isCfnElement"]);
8
+ /**
9
+ * The ban list. Add an entry whenever an `aws-cdk-lib` API we reach for turns
10
+ * out to postdate the supported floor — this list is expected to grow as the
11
+ * floor is pinned and lowered ahead of 1.0.0.
12
+ *
13
+ * Seeded with the per-resource `isCfn<Resource>` static type guards, which
14
+ * aws-cdk-lib first shipped on every generated L1 in 2.231.0 (verified by
15
+ * installing real versions: 2.230.0 lacks them, 2.231.0 has them). Calling
16
+ * them (e.g. `CfnAlarm.isCfnAlarm(node)`) throws `TypeError` on the older
17
+ * versions in our `^2` peer range — see issue #146.
18
+ */
19
+ const FORBIDDEN = [
20
+ {
21
+ matches: (name) => /^isCfn[A-Z]/.test(name) && !ALLOWED_CFN_GUARDS.has(name),
22
+ label: "the per-resource `Cfn<Resource>.isCfn<Resource>` L1 static type guards",
23
+ since: "2.231.0",
24
+ use: "`CfnResource.isCfnResource(x) && x.cfnResourceType === Cfn<Resource>.CFN_RESOURCE_TYPE_NAME` " +
25
+ "(what the static does internally, but valid across the whole peer range), e.g. the " +
26
+ "`isCfnAlarm` helper in @composurecdk/cloudwatch",
27
+ },
28
+ ];
29
+ /**
30
+ * Flags an `aws-cdk-lib` API newer than the oldest CDK version this package
31
+ * supports.
32
+ *
33
+ * Such a call type-checks and passes tests, because both run against whichever
34
+ * CDK is installed here — the newest. It then throws at runtime for anyone whose
35
+ * own `aws-cdk-lib` is older, while still inside the range the package declares
36
+ * it supports. The fix is a form of the same check that works across the whole
37
+ * range; the rule's message names one per banned API.
38
+ *
39
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-cdk-api-above-floor.md | the rule documentation}.
40
+ */
41
+ export const rule = {
42
+ meta: {
43
+ type: "problem",
44
+ docs: {
45
+ description: "Ban aws-cdk-lib APIs newer than the supported peer-dependency floor",
46
+ },
47
+ schema: [],
48
+ messages: {
49
+ aboveFloor: "{{label}} were added in aws-cdk-lib {{since}}, above the supported floor — they throw on " +
50
+ "older versions in the peer range. Use {{use}}.",
51
+ },
52
+ },
53
+ create(ctx) {
54
+ return {
55
+ MemberExpression(node) {
56
+ if (node.property.type !== "Identifier")
57
+ return;
58
+ const property = node.property;
59
+ const root = chainRoot(node.object);
60
+ if (root === undefined)
61
+ return;
62
+ const source = importSourceOf(ctx.sourceCode.getScope(node), root.name);
63
+ if (source === undefined || !isCdkSource(source))
64
+ return;
65
+ const forbidden = FORBIDDEN.find((entry) => entry.matches(property.name));
66
+ if (forbidden === undefined)
67
+ return;
68
+ ctx.report({
69
+ node: property,
70
+ messageId: "aboveFloor",
71
+ data: { label: forbidden.label, since: forbidden.since, use: forbidden.use },
72
+ });
73
+ },
74
+ };
75
+ },
76
+ };
77
+ //# sourceMappingURL=no-cdk-api-above-floor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cdk-api-above-floor.js","sourceRoot":"","sources":["../../../src/rules/no-cdk-api-above-floor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAqB1E;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,MAAM,SAAS,GAAsB;IACnC;QACE,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;QAC5E,KAAK,EAAE,wEAAwE;QAC/E,KAAK,EAAE,SAAS;QAChB,GAAG,EACD,+FAA+F;YAC/F,qFAAqF;YACrF,iDAAiD;KACpD;CACF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,qEAAqE;SACnF;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,UAAU,EACR,2FAA2F;gBAC3F,gDAAgD;SACnD;KACF;IACD,MAAM,CAAC,GAAG;QACR,OAAO;YACL,gBAAgB,CAAC,IAAsB;gBACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBAAE,OAAO;gBAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;gBAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACpC,IAAI,IAAI,KAAK,SAAS;oBAAE,OAAO;gBAC/B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxE,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;oBAAE,OAAO;gBAEzD,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1E,IAAI,SAAS,KAAK,SAAS;oBAAE,OAAO;gBACpC,GAAG,CAAC,MAAM,CAAC;oBACT,IAAI,EAAE,QAAQ;oBACd,SAAS,EAAE,YAAY;oBACvB,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE;iBAC7E,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Flags syntax with no CommonJS equivalent: `import.meta`, top-level `await` and
4
+ * top-level `for await…of`.
5
+ *
6
+ * These packages are published as both ECMAScript and CommonJS modules from one
7
+ * source. All three constructs are valid ESM that the CommonJS compile cannot
8
+ * emit, so each one breaks half the build. Reporting them as you type beats
9
+ * discovering it when the second dialect compiles.
10
+ *
11
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-cjs-incompatible-syntax.md | the rule documentation}.
12
+ */
13
+ export declare const rule: Rule.RuleModule;
14
+ //# sourceMappingURL=no-cjs-incompatible-syntax.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cjs-incompatible-syntax.d.ts","sourceRoot":"","sources":["../../../src/rules/no-cjs-incompatible-syntax.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AASnC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAuCvB,CAAC"}
@@ -0,0 +1,52 @@
1
+ const FUNCTION_TYPES = new Set([
2
+ "FunctionDeclaration",
3
+ "FunctionExpression",
4
+ "ArrowFunctionExpression",
5
+ ]);
6
+ /**
7
+ * Flags syntax with no CommonJS equivalent: `import.meta`, top-level `await` and
8
+ * top-level `for await…of`.
9
+ *
10
+ * These packages are published as both ECMAScript and CommonJS modules from one
11
+ * source. All three constructs are valid ESM that the CommonJS compile cannot
12
+ * emit, so each one breaks half the build. Reporting them as you type beats
13
+ * discovering it when the second dialect compiles.
14
+ *
15
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-cjs-incompatible-syntax.md | the rule documentation}.
16
+ */
17
+ export const rule = {
18
+ meta: {
19
+ type: "problem",
20
+ docs: {
21
+ description: "Ban ESM-only syntax (import.meta, top-level await) that cannot emit to CommonJS",
22
+ },
23
+ schema: [],
24
+ messages: {
25
+ importMeta: "`import.meta` has no CommonJS equivalent, so it breaks the CommonJS half of a " +
26
+ "dual-published build. Take the value from a parameter or a caller instead.",
27
+ topLevelAwait: "Top-level `await` has no CommonJS equivalent, so it breaks the CommonJS half of a " +
28
+ "dual-published build. Move the `await` inside an async function.",
29
+ },
30
+ },
31
+ create(ctx) {
32
+ const isTopLevel = (node) => !ctx.sourceCode.getAncestors(node).some((ancestor) => FUNCTION_TYPES.has(ancestor.type));
33
+ return {
34
+ MetaProperty(node) {
35
+ if (node.meta.name === "import" && node.property.name === "meta") {
36
+ ctx.report({ node, messageId: "importMeta" });
37
+ }
38
+ },
39
+ AwaitExpression(node) {
40
+ if (isTopLevel(node)) {
41
+ ctx.report({ node, messageId: "topLevelAwait" });
42
+ }
43
+ },
44
+ "ForOfStatement[await=true]"(node) {
45
+ if (isTopLevel(node)) {
46
+ ctx.report({ node, messageId: "topLevelAwait" });
47
+ }
48
+ },
49
+ };
50
+ },
51
+ };
52
+ //# sourceMappingURL=no-cjs-incompatible-syntax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-cjs-incompatible-syntax.js","sourceRoot":"","sources":["../../../src/rules/no-cjs-incompatible-syntax.ts"],"names":[],"mappings":"AAGA,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,qBAAqB;IACrB,oBAAoB;IACpB,yBAAyB;CAC1B,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,iFAAiF;SACpF;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,UAAU,EACR,gFAAgF;gBAChF,4EAA4E;YAC9E,aAAa,EACX,oFAAoF;gBACpF,kEAAkE;SACrE;KACF;IACD,MAAM,CAAC,GAAG;QACR,MAAM,UAAU,GAAG,CAAC,IAAU,EAAW,EAAE,CACzC,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,OAAO;YACL,YAAY,CAAC,IAAkB;gBAC7B,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBACjE,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;YACD,eAAe,CAAC,IAAqB;gBACnC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;YACD,4BAA4B,CAAC,IAAoB;gBAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrB,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Bans `instanceof` against a class reached through an `import`.
4
+ *
5
+ * These packages ship as both ECMAScript and CommonJS modules, and both copies
6
+ * can load in the same process — a consumer importing one while a dependency
7
+ * requires the other. Each copy evaluates its own class objects, so an instance
8
+ * created by one fails `instanceof` against the other's class: the check returns
9
+ * `false` for a value that is plainly of that type.
10
+ *
11
+ * It fails silently, which is what makes it dangerous — a deduplication quietly
12
+ * skipped, a guard quietly bypassed, and no error to trace. Use a `Symbol.for(…)`
13
+ * brand instead, which is shared across copies.
14
+ *
15
+ * A relative import is no safer by default: it resolves separately in each copy.
16
+ * A package nothing can install twice may say so with
17
+ * `assumeNeverInstalledAsADependency`, which treats its own modules as
18
+ * same-realm while still flagging dependencies. An application is the archetype;
19
+ * being private is not enough, since a private package can still be a
20
+ * dependency.
21
+ *
22
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-realm-bound-instanceof.md | the rule documentation}.
23
+ */
24
+ export declare const rule: Rule.RuleModule;
25
+ //# sourceMappingURL=no-realm-bound-instanceof.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-realm-bound-instanceof.d.ts","sourceRoot":"","sources":["../../../src/rules/no-realm-bound-instanceof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAInC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UA8EvB,CAAC"}
@@ -0,0 +1,95 @@
1
+ import { chainRoot, importSourceOf, isCdkSource, unwrapWrappers } from "./lib/imports.js";
2
+ /**
3
+ * Bans `instanceof` against a class reached through an `import`.
4
+ *
5
+ * These packages ship as both ECMAScript and CommonJS modules, and both copies
6
+ * can load in the same process — a consumer importing one while a dependency
7
+ * requires the other. Each copy evaluates its own class objects, so an instance
8
+ * created by one fails `instanceof` against the other's class: the check returns
9
+ * `false` for a value that is plainly of that type.
10
+ *
11
+ * It fails silently, which is what makes it dangerous — a deduplication quietly
12
+ * skipped, a guard quietly bypassed, and no error to trace. Use a `Symbol.for(…)`
13
+ * brand instead, which is shared across copies.
14
+ *
15
+ * A relative import is no safer by default: it resolves separately in each copy.
16
+ * A package nothing can install twice may say so with
17
+ * `assumeNeverInstalledAsADependency`, which treats its own modules as
18
+ * same-realm while still flagging dependencies. An application is the archetype;
19
+ * being private is not enough, since a private package can still be a
20
+ * dependency.
21
+ *
22
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-realm-bound-instanceof.md | the rule documentation}.
23
+ */
24
+ export const rule = {
25
+ meta: {
26
+ type: "problem",
27
+ docs: {
28
+ description: "Ban realm-bound `instanceof` against imported classes",
29
+ },
30
+ schema: [
31
+ {
32
+ type: "object",
33
+ properties: {
34
+ assumeNeverInstalledAsADependency: {
35
+ type: "boolean",
36
+ description: "Exempt this package's own relative imports, on the basis that nothing can " +
37
+ "install it twice. An application's claim to make; a library must not, since a " +
38
+ "consumer can install two versions of it side by side.",
39
+ },
40
+ },
41
+ additionalProperties: false,
42
+ },
43
+ ],
44
+ messages: {
45
+ cdkClass: "`instanceof {{name}}` is realm-bound: it returns false for a value created by another " +
46
+ "copy of `aws-cdk-lib`. Identify the construct by its L1 instead — " +
47
+ "`CfnResource.isCfnResource(x) && x.cfnResourceType === Cfn{{name}}.CFN_RESOURCE_TYPE_NAME`.",
48
+ ownClass: "`instanceof {{name}}` is realm-bound: `{{source}}` resolves separately in each copy of " +
49
+ "this package, so the check returns false for a value that is plainly of that type. " +
50
+ "Brand the class with `Symbol.for(...)` and test for that brand instead.",
51
+ dependencyClass: "`instanceof {{name}}` is realm-bound: `{{source}}` can load twice in one process, so the " +
52
+ "check returns false for a value that is plainly of that type. Prefer a type guard the " +
53
+ "package provides, or test a distinguishing property rather than identity. If the class " +
54
+ "is one you own, brand it with `Symbol.for(...)`.",
55
+ },
56
+ },
57
+ create(ctx) {
58
+ const { assumeNeverInstalledAsADependency = false } = (ctx.options[0] ?? {});
59
+ return {
60
+ BinaryExpression(node) {
61
+ if (node.operator !== "instanceof")
62
+ return;
63
+ const root = chainRoot(node.right);
64
+ if (root === undefined)
65
+ return;
66
+ const source = importSourceOf(ctx.sourceCode.getScope(node), root.name);
67
+ if (source === undefined)
68
+ return;
69
+ // A relative specifier resolves inside this package, so it duplicates
70
+ // only if the package itself does. Deliberately not `#` subpaths: the
71
+ // `imports` field can map one to an external package.
72
+ if (assumeNeverInstalledAsADependency && source.startsWith("."))
73
+ return;
74
+ // Name the class, not the namespace it was reached through:
75
+ // `cdk.aws_s3.Bucket` is "Bucket", and the cdk message interpolates
76
+ // that into `Cfn<name>.CFN_RESOURCE_TYPE_NAME`.
77
+ const target = unwrapWrappers(node.right);
78
+ const name = target.type === "MemberExpression" && target.property.type === "Identifier"
79
+ ? target.property.name
80
+ : root.name;
81
+ // Report on the class reference, not the whole expression: the fix
82
+ // replaces the right-hand side, and a narrow squiggle points at it.
83
+ // The specifier says whether the reader can brand the class: a relative
84
+ // import is their own module, anything else is someone else's package.
85
+ const messageId = isCdkSource(source)
86
+ ? "cdkClass"
87
+ : source.startsWith(".")
88
+ ? "ownClass"
89
+ : "dependencyClass";
90
+ ctx.report({ node: node.right, messageId, data: { name, source } });
91
+ },
92
+ };
93
+ },
94
+ };
95
+ //# sourceMappingURL=no-realm-bound-instanceof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-realm-bound-instanceof.js","sourceRoot":"","sources":["../../../src/rules/no-realm-bound-instanceof.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE1F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,IAAI,GAAoB;IACnC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EAAE,uDAAuD;SACrE;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,iCAAiC,EAAE;wBACjC,IAAI,EAAE,SAAS;wBACf,WAAW,EACT,4EAA4E;4BAC5E,gFAAgF;4BAChF,uDAAuD;qBAC1D;iBACF;gBACD,oBAAoB,EAAE,KAAK;aAC5B;SACF;QACD,QAAQ,EAAE;YACR,QAAQ,EACN,wFAAwF;gBACxF,oEAAoE;gBACpE,6FAA6F;YAC/F,QAAQ,EACN,yFAAyF;gBACzF,qFAAqF;gBACrF,yEAAyE;YAC3E,eAAe,EACb,2FAA2F;gBAC3F,wFAAwF;gBACxF,yFAAyF;gBACzF,kDAAkD;SACrD;KACF;IACD,MAAM,CAAC,GAAG;QACR,MAAM,EAAE,iCAAiC,GAAG,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAE1E,CAAC;QAEF,OAAO;YACL,gBAAgB,CAAC,IAAsB;gBACrC,IAAI,IAAI,CAAC,QAAQ,KAAK,YAAY;oBAAE,OAAO;gBAC3C,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACnC,IAAI,IAAI,KAAK,SAAS;oBAAE,OAAO;gBAE/B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxE,IAAI,MAAM,KAAK,SAAS;oBAAE,OAAO;gBACjC,sEAAsE;gBACtE,sEAAsE;gBACtE,sDAAsD;gBACtD,IAAI,iCAAiC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,OAAO;gBAExE,4DAA4D;gBAC5D,oEAAoE;gBACpE,gDAAgD;gBAChD,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1C,MAAM,IAAI,GACR,MAAM,CAAC,IAAI,KAAK,kBAAkB,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBACzE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI;oBACtB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAEhB,mEAAmE;gBACnE,oEAAoE;gBACpE,wEAAwE;gBACxE,uEAAuE;gBACvE,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC;oBACnC,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;wBACtB,CAAC,CAAC,UAAU;wBACZ,CAAC,CAAC,iBAAiB,CAAC;gBAExB,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACtE,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Bans the TypeScript `private` modifier in favour of ECMAScript private fields.
4
+ *
5
+ * The two are not interchangeable once a class is generic over its own members.
6
+ * A TypeScript `private` member is still part of the type, so it appears in
7
+ * `keyof T`, survives into a mapped type, and reaches the emitted `.d.ts` — where
8
+ * a consumer's compiler cannot name it and reports TS4094. A `#field` is not
9
+ * part of the type at all, so it never gets that far.
10
+ *
11
+ * A private constructor stays allowed: `#constructor` is not valid syntax, so
12
+ * there is nothing to prefer over it.
13
+ *
14
+ * See {@link https://github.com/laazyj/composureCDK/blob/main/packages/eslint-plugin/docs/rules/no-typescript-private-modifier.md | the rule documentation}.
15
+ */
16
+ export declare const rule: Rule.RuleModule;
17
+ //# sourceMappingURL=no-typescript-private-modifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-typescript-private-modifier.d.ts","sourceRoot":"","sources":["../../../src/rules/no-typescript-private-modifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAkCnC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,IAAI,EAAE,IAAI,CAAC,UAwDvB,CAAC"}