@trackunit/eslint-plugin-trackunit 0.0.2

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 (147) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +117 -0
  3. package/package.json +31 -0
  4. package/src/index.d.ts +8 -0
  5. package/src/index.js +20 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/config/fragments/ignores.d.ts +2 -0
  8. package/src/lib/config/fragments/ignores.js +18 -0
  9. package/src/lib/config/fragments/ignores.js.map +1 -0
  10. package/src/lib/config/fragments/import-rules.d.ts +3 -0
  11. package/src/lib/config/fragments/import-rules.js +58 -0
  12. package/src/lib/config/fragments/import-rules.js.map +1 -0
  13. package/src/lib/config/fragments/jest-overrides.d.ts +2 -0
  14. package/src/lib/config/fragments/jest-overrides.js +30 -0
  15. package/src/lib/config/fragments/jest-overrides.js.map +1 -0
  16. package/src/lib/config/fragments/jsdoc-rules.d.ts +3 -0
  17. package/src/lib/config/fragments/jsdoc-rules.js +71 -0
  18. package/src/lib/config/fragments/jsdoc-rules.js.map +1 -0
  19. package/src/lib/config/fragments/module-boundaries.d.ts +2 -0
  20. package/src/lib/config/fragments/module-boundaries.js +92 -0
  21. package/src/lib/config/fragments/module-boundaries.js.map +1 -0
  22. package/src/lib/config/fragments/react-rules.d.ts +5 -0
  23. package/src/lib/config/fragments/react-rules.js +137 -0
  24. package/src/lib/config/fragments/react-rules.js.map +1 -0
  25. package/src/lib/config/fragments/restricted-imports.d.ts +2 -0
  26. package/src/lib/config/fragments/restricted-imports.js +58 -0
  27. package/src/lib/config/fragments/restricted-imports.js.map +1 -0
  28. package/src/lib/config/fragments/testing-library.d.ts +2 -0
  29. package/src/lib/config/fragments/testing-library.js +7 -0
  30. package/src/lib/config/fragments/testing-library.js.map +1 -0
  31. package/src/lib/config/fragments/typescript-rules.d.ts +2 -0
  32. package/src/lib/config/fragments/typescript-rules.js +97 -0
  33. package/src/lib/config/fragments/typescript-rules.js.map +1 -0
  34. package/src/lib/config/index.d.ts +863 -0
  35. package/src/lib/config/index.js +10 -0
  36. package/src/lib/config/index.js.map +1 -0
  37. package/src/lib/config/plugins.d.ts +90 -0
  38. package/src/lib/config/plugins.js +44 -0
  39. package/src/lib/config/plugins.js.map +1 -0
  40. package/src/lib/config/presets/base.d.ts +265 -0
  41. package/src/lib/config/presets/base.js +145 -0
  42. package/src/lib/config/presets/base.js.map +1 -0
  43. package/src/lib/config/presets/e2e.d.ts +10 -0
  44. package/src/lib/config/presets/e2e.js +19 -0
  45. package/src/lib/config/presets/e2e.js.map +1 -0
  46. package/src/lib/config/presets/public-api.d.ts +147 -0
  47. package/src/lib/config/presets/public-api.js +62 -0
  48. package/src/lib/config/presets/public-api.js.map +1 -0
  49. package/src/lib/config/presets/react.d.ts +598 -0
  50. package/src/lib/config/presets/react.js +97 -0
  51. package/src/lib/config/presets/react.js.map +1 -0
  52. package/src/lib/config/presets/server.d.ts +36 -0
  53. package/src/lib/config/presets/server.js +37 -0
  54. package/src/lib/config/presets/server.js.map +1 -0
  55. package/src/lib/config/utils.d.ts +6 -0
  56. package/src/lib/config/utils.js +28 -0
  57. package/src/lib/config/utils.js.map +1 -0
  58. package/src/lib/config-helpers/create-skip-when.d.ts +35 -0
  59. package/src/lib/config-helpers/create-skip-when.js +54 -0
  60. package/src/lib/config-helpers/create-skip-when.js.map +1 -0
  61. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.d.ts +16 -0
  62. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.js +83 -0
  63. package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.js.map +1 -0
  64. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.d.ts +4 -0
  65. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.js +297 -0
  66. package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.js.map +1 -0
  67. package/src/lib/rules/no-internal-barrel-files/examples.d.ts +80 -0
  68. package/src/lib/rules/no-internal-barrel-files/examples.js +84 -0
  69. package/src/lib/rules/no-internal-barrel-files/examples.js.map +1 -0
  70. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.d.ts +29 -0
  71. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.js +178 -0
  72. package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.js.map +1 -0
  73. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.d.ts +5 -0
  74. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js +67 -0
  75. package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js.map +1 -0
  76. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.d.ts +2 -0
  77. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js +34 -0
  78. package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js.map +1 -0
  79. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.d.ts +16 -0
  80. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.js +55 -0
  81. package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.js.map +1 -0
  82. package/src/lib/rules/no-typescript-assertion/examples.d.ts +1 -0
  83. package/src/lib/rules/no-typescript-assertion/examples.js +45 -0
  84. package/src/lib/rules/no-typescript-assertion/examples.js.map +1 -0
  85. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.d.ts +20 -0
  86. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js +83 -0
  87. package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js.map +1 -0
  88. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.d.ts +73 -0
  89. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.js +333 -0
  90. package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.js.map +1 -0
  91. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.d.ts +56 -0
  92. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.js +225 -0
  93. package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.js.map +1 -0
  94. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.d.ts +49 -0
  95. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.js +75 -0
  96. package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.js.map +1 -0
  97. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.d.ts +32 -0
  98. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.js +143 -0
  99. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.js.map +1 -0
  100. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.d.ts +27 -0
  101. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.js +196 -0
  102. package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.js.map +1 -0
  103. package/src/lib/rules/prefer-event-specific-callback-naming/utils.d.ts +76 -0
  104. package/src/lib/rules/prefer-event-specific-callback-naming/utils.js +245 -0
  105. package/src/lib/rules/prefer-event-specific-callback-naming/utils.js.map +1 -0
  106. package/src/lib/rules/prefer-field-components/prefer-field-components.d.ts +4 -0
  107. package/src/lib/rules/prefer-field-components/prefer-field-components.js +289 -0
  108. package/src/lib/rules/prefer-field-components/prefer-field-components.js.map +1 -0
  109. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.d.ts +26 -0
  110. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.js +402 -0
  111. package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.js.map +1 -0
  112. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.d.ts +13 -0
  113. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.js +271 -0
  114. package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.js.map +1 -0
  115. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.d.ts +15 -0
  116. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js +245 -0
  117. package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js.map +1 -0
  118. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.d.ts +17 -0
  119. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.js +133 -0
  120. package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.js.map +1 -0
  121. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.d.ts +12 -0
  122. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.js +128 -0
  123. package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.js.map +1 -0
  124. package/src/lib/rules-map.d.ts +66 -0
  125. package/src/lib/rules-map.js +34 -0
  126. package/src/lib/rules-map.js.map +1 -0
  127. package/src/lib/utils/ast-utils.d.ts +85 -0
  128. package/src/lib/utils/ast-utils.js +530 -0
  129. package/src/lib/utils/ast-utils.js.map +1 -0
  130. package/src/lib/utils/classname-utils.d.ts +150 -0
  131. package/src/lib/utils/classname-utils.js +492 -0
  132. package/src/lib/utils/classname-utils.js.map +1 -0
  133. package/src/lib/utils/file-utils.d.ts +14 -0
  134. package/src/lib/utils/file-utils.js +106 -0
  135. package/src/lib/utils/file-utils.js.map +1 -0
  136. package/src/lib/utils/import-utils.d.ts +85 -0
  137. package/src/lib/utils/import-utils.js +193 -0
  138. package/src/lib/utils/import-utils.js.map +1 -0
  139. package/src/lib/utils/nx-utils.d.ts +59 -0
  140. package/src/lib/utils/nx-utils.js +103 -0
  141. package/src/lib/utils/nx-utils.js.map +1 -0
  142. package/src/lib/utils/package-utils.d.ts +38 -0
  143. package/src/lib/utils/package-utils.js +74 -0
  144. package/src/lib/utils/package-utils.js.map +1 -0
  145. package/src/lib/utils/typescript-utils.d.ts +29 -0
  146. package/src/lib/utils/typescript-utils.js +213 -0
  147. package/src/lib/utils/typescript-utils.js.map +1 -0
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clearMetadataCache = exports.projectMatchesCriteria = exports.getProjectMetadata = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const file_utils_1 = require("./file-utils");
7
+ /**
8
+ * Cache for project metadata to avoid reading files multiple times.
9
+ * Key is the absolute path to project.json, value is the parsed metadata.
10
+ */
11
+ const metadataCache = new Map();
12
+ /**
13
+ * Get NX project metadata from the nearest project.json file.
14
+ *
15
+ * @param filePath - Absolute path to a file in the project
16
+ * @returns Project metadata or null if no project.json found
17
+ * @example
18
+ * const metadata = getProjectMetadata("/workspace/libs/my-lib/src/index.ts");
19
+ * // Returns: { name: "my-lib", tags: ["type:react"], targets: ["build", "test"], ... }
20
+ */
21
+ const getProjectMetadata = (filePath) => {
22
+ const projectJsonPath = (0, file_utils_1.findNearestFile)(filePath, "project.json");
23
+ if (!projectJsonPath) {
24
+ return null;
25
+ }
26
+ // Check cache first
27
+ if (metadataCache.has(projectJsonPath)) {
28
+ return metadataCache.get(projectJsonPath) ?? null;
29
+ }
30
+ try {
31
+ const content = fs.readFileSync(projectJsonPath, "utf8");
32
+ const projectJson = JSON.parse(content);
33
+ const metadata = {
34
+ name: projectJson.name ?? "",
35
+ tags: projectJson.tags ?? [],
36
+ targets: typeof projectJson.targets === "object" && projectJson.targets !== null ? Object.keys(projectJson.targets) : [],
37
+ projectJsonPath,
38
+ };
39
+ // Cache the result
40
+ metadataCache.set(projectJsonPath, metadata);
41
+ return metadata;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ };
47
+ exports.getProjectMetadata = getProjectMetadata;
48
+ /**
49
+ * Check if project metadata matches the given criteria.
50
+ *
51
+ * @param metadata - Project metadata to check
52
+ * @param criteria - Criteria to match against
53
+ * @returns True if metadata matches all criteria
54
+ * @example
55
+ * // Check if project has the "scope:tool" tag
56
+ * projectMatchesCriteria(metadata, { tags: ["scope:tool"] })
57
+ * @example
58
+ * // Check if project has ALL specified tags
59
+ * projectMatchesCriteria(metadata, {
60
+ * tags: ["scope:tool", "type:vanilla"],
61
+ * tagsMatchMode: "every"
62
+ * })
63
+ * @example
64
+ * // Check if project has ANY of the specified tags
65
+ * projectMatchesCriteria(metadata, {
66
+ * tags: ["scope:tool", "scope:client"],
67
+ * tagsMatchMode: "some"
68
+ * })
69
+ * @example
70
+ * // Check if project has specific targets
71
+ * projectMatchesCriteria(metadata, { targets: ["build", "test"] })
72
+ */
73
+ const projectMatchesCriteria = (metadata, criteria) => {
74
+ const { tags, targets, tagsMatchMode = "some", targetsMatchMode = "some" } = criteria;
75
+ // Check tags if specified
76
+ if (tags && tags.length > 0) {
77
+ const hasMatchingTags = tagsMatchMode === "every"
78
+ ? tags.every(tag => metadata.tags.includes(tag))
79
+ : tags.some(tag => metadata.tags.includes(tag));
80
+ if (!hasMatchingTags) {
81
+ return false;
82
+ }
83
+ }
84
+ // Check targets if specified
85
+ if (targets && targets.length > 0) {
86
+ const hasMatchingTargets = targetsMatchMode === "every"
87
+ ? targets.every(target => metadata.targets.includes(target))
88
+ : targets.some(target => metadata.targets.includes(target));
89
+ if (!hasMatchingTargets) {
90
+ return false;
91
+ }
92
+ }
93
+ return true;
94
+ };
95
+ exports.projectMatchesCriteria = projectMatchesCriteria;
96
+ /**
97
+ * Clear the metadata cache. Useful for testing.
98
+ */
99
+ const clearMetadataCache = () => {
100
+ metadataCache.clear();
101
+ };
102
+ exports.clearMetadataCache = clearMetadataCache;
103
+ //# sourceMappingURL=nx-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nx-utils.js","sourceRoot":"","sources":["../../../../../../../libs/eslint/plugin-trackunit/src/lib/utils/nx-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"fs\";\nimport { findNearestFile } from \"./file-utils\";\n\n/**\n * Utility functions for working with NX project metadata in ESLint rules.\n *\n * ## Main Functions\n * - `getProjectMetadata()` - Get NX project metadata from project.json\n * - `projectMatchesCriteria()` - Check if project matches given criteria\n */\n\nexport type ProjectMetadata = {\n name: string;\n tags: Array<string>;\n targets: Array<string>;\n projectJsonPath: string;\n};\n\nexport type MatchCriteria = {\n tags?: Array<string>;\n targets?: Array<string>;\n tagsMatchMode?: \"some\" | \"every\";\n targetsMatchMode?: \"some\" | \"every\";\n};\n\n/**\n * Cache for project metadata to avoid reading files multiple times.\n * Key is the absolute path to project.json, value is the parsed metadata.\n */\nconst metadataCache = new Map<string, ProjectMetadata>();\n\n/**\n * Get NX project metadata from the nearest project.json file.\n *\n * @param filePath - Absolute path to a file in the project\n * @returns Project metadata or null if no project.json found\n * @example\n * const metadata = getProjectMetadata(\"/workspace/libs/my-lib/src/index.ts\");\n * // Returns: { name: \"my-lib\", tags: [\"type:react\"], targets: [\"build\", \"test\"], ... }\n */\nexport const getProjectMetadata = (filePath: string): ProjectMetadata | null => {\n const projectJsonPath = findNearestFile(filePath, \"project.json\");\n\n if (!projectJsonPath) {\n return null;\n }\n\n // Check cache first\n if (metadataCache.has(projectJsonPath)) {\n return metadataCache.get(projectJsonPath) ?? null;\n }\n\n try {\n const content = fs.readFileSync(projectJsonPath, \"utf8\");\n const projectJson = JSON.parse(content);\n\n const metadata: ProjectMetadata = {\n name: projectJson.name ?? \"\",\n tags: projectJson.tags ?? [],\n targets:\n typeof projectJson.targets === \"object\" && projectJson.targets !== null ? Object.keys(projectJson.targets) : [],\n projectJsonPath,\n };\n\n // Cache the result\n metadataCache.set(projectJsonPath, metadata);\n\n return metadata;\n } catch {\n return null;\n }\n};\n\n/**\n * Check if project metadata matches the given criteria.\n *\n * @param metadata - Project metadata to check\n * @param criteria - Criteria to match against\n * @returns True if metadata matches all criteria\n * @example\n * // Check if project has the \"scope:tool\" tag\n * projectMatchesCriteria(metadata, { tags: [\"scope:tool\"] })\n * @example\n * // Check if project has ALL specified tags\n * projectMatchesCriteria(metadata, {\n * tags: [\"scope:tool\", \"type:vanilla\"],\n * tagsMatchMode: \"every\"\n * })\n * @example\n * // Check if project has ANY of the specified tags\n * projectMatchesCriteria(metadata, {\n * tags: [\"scope:tool\", \"scope:client\"],\n * tagsMatchMode: \"some\"\n * })\n * @example\n * // Check if project has specific targets\n * projectMatchesCriteria(metadata, { targets: [\"build\", \"test\"] })\n */\nexport const projectMatchesCriteria = (metadata: ProjectMetadata, criteria: MatchCriteria): boolean => {\n const { tags, targets, tagsMatchMode = \"some\", targetsMatchMode = \"some\" } = criteria;\n\n // Check tags if specified\n if (tags && tags.length > 0) {\n const hasMatchingTags =\n tagsMatchMode === \"every\"\n ? tags.every(tag => metadata.tags.includes(tag))\n : tags.some(tag => metadata.tags.includes(tag));\n\n if (!hasMatchingTags) {\n return false;\n }\n }\n\n // Check targets if specified\n if (targets && targets.length > 0) {\n const hasMatchingTargets =\n targetsMatchMode === \"every\"\n ? targets.every(target => metadata.targets.includes(target))\n : targets.some(target => metadata.targets.includes(target));\n\n if (!hasMatchingTargets) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Clear the metadata cache. Useful for testing.\n */\nexport const clearMetadataCache = (): void => {\n metadataCache.clear();\n};\n"]}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Utility functions for working with package.json and npm packages in ESLint rules.
3
+ *
4
+ * ## Main Functions
5
+ * - `getDependencies()` - Get dependencies of a specific type from package.json
6
+ * - `getPackageNameFromImport()` - Extract base package name from import source
7
+ */
8
+ export type DependencyType = "dependencies" | "devDependencies" | "all";
9
+ /**
10
+ * Get dependencies from package.json based on the specified type.
11
+ *
12
+ * @param packageJsonPath - Absolute path to package.json
13
+ * @param type - Type of dependencies to retrieve
14
+ * @returns Set of package names
15
+ * @example
16
+ * // Get only production dependencies
17
+ * const deps = getDependencies("/path/to/package.json", "dependencies");
18
+ * @example
19
+ * // Get only dev dependencies
20
+ * const devDeps = getDependencies("/path/to/package.json", "devDependencies");
21
+ * @example
22
+ * // Get all dependencies (union of both)
23
+ * const allDeps = getDependencies("/path/to/package.json", "all");
24
+ */
25
+ export declare const getDependencies: (packageJsonPath: string, type: DependencyType) => Set<string>;
26
+ /**
27
+ * Extract the base package name from an import source.
28
+ * Handles scoped packages and sub-path imports.
29
+ *
30
+ * @param importSource - The import source from an ImportDeclaration node
31
+ * @returns The base package name
32
+ * @example
33
+ * getPackageNameFromImport("lodash/fp") // → "lodash"
34
+ * getPackageNameFromImport("@scope/package/sub") // → "@scope/package"
35
+ * getPackageNameFromImport("react") // → "react"
36
+ * getPackageNameFromImport("./relative") // → "./relative" (caller should filter)
37
+ */
38
+ export declare const getPackageNameFromImport: (importSource: string) => string;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPackageNameFromImport = exports.getDependencies = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ /**
7
+ * Get dependencies from package.json based on the specified type.
8
+ *
9
+ * @param packageJsonPath - Absolute path to package.json
10
+ * @param type - Type of dependencies to retrieve
11
+ * @returns Set of package names
12
+ * @example
13
+ * // Get only production dependencies
14
+ * const deps = getDependencies("/path/to/package.json", "dependencies");
15
+ * @example
16
+ * // Get only dev dependencies
17
+ * const devDeps = getDependencies("/path/to/package.json", "devDependencies");
18
+ * @example
19
+ * // Get all dependencies (union of both)
20
+ * const allDeps = getDependencies("/path/to/package.json", "all");
21
+ */
22
+ const getDependencies = (packageJsonPath, type) => {
23
+ const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
24
+ const packageJson = JSON.parse(packageJsonContent);
25
+ const dependencies = packageJson.dependencies ?? {};
26
+ const devDependencies = packageJson.devDependencies ?? {};
27
+ switch (type) {
28
+ case "dependencies":
29
+ return new Set(Object.keys(dependencies));
30
+ case "devDependencies":
31
+ return new Set(Object.keys(devDependencies));
32
+ case "all":
33
+ return new Set([...Object.keys(dependencies), ...Object.keys(devDependencies)]);
34
+ default: {
35
+ const exhaustiveCheck = type;
36
+ throw new Error(`Unknown dependency type: ${exhaustiveCheck}`);
37
+ }
38
+ }
39
+ };
40
+ exports.getDependencies = getDependencies;
41
+ /**
42
+ * Extract the base package name from an import source.
43
+ * Handles scoped packages and sub-path imports.
44
+ *
45
+ * @param importSource - The import source from an ImportDeclaration node
46
+ * @returns The base package name
47
+ * @example
48
+ * getPackageNameFromImport("lodash/fp") // → "lodash"
49
+ * getPackageNameFromImport("@scope/package/sub") // → "@scope/package"
50
+ * getPackageNameFromImport("react") // → "react"
51
+ * getPackageNameFromImport("./relative") // → "./relative" (caller should filter)
52
+ */
53
+ const getPackageNameFromImport = (importSource) => {
54
+ // Handle relative imports - return as-is, caller should filter
55
+ if (importSource.startsWith(".") || importSource.startsWith("/")) {
56
+ return importSource;
57
+ }
58
+ // Handle scoped packages: @scope/package/sub-path → @scope/package
59
+ if (importSource.startsWith("@")) {
60
+ const parts = importSource.split("/");
61
+ if (parts.length >= 2) {
62
+ return `${parts[0]}/${parts[1]}`;
63
+ }
64
+ return importSource;
65
+ }
66
+ // Handle regular packages: package/sub-path → package
67
+ const firstSlash = importSource.indexOf("/");
68
+ if (firstSlash !== -1) {
69
+ return importSource.substring(0, firstSlash);
70
+ }
71
+ return importSource;
72
+ };
73
+ exports.getPackageNameFromImport = getPackageNameFromImport;
74
+ //# sourceMappingURL=package-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-utils.js","sourceRoot":"","sources":["../../../../../../../libs/eslint/plugin-trackunit/src/lib/utils/package-utils.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AAiBzB;;;;;;;;;;;;;;;GAeG;AACI,MAAM,eAAe,GAAG,CAAC,eAAuB,EAAE,IAAoB,EAAe,EAAE;IAC5F,MAAM,kBAAkB,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACpE,MAAM,WAAW,GAAgB,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAEhE,MAAM,YAAY,GAAG,WAAW,CAAC,YAAY,IAAI,EAAE,CAAC;IACpD,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,IAAI,EAAE,CAAC;IAE1D,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,cAAc;YACjB,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC5C,KAAK,iBAAiB;YACpB,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;QAC/C,KAAK,KAAK;YACR,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAClF,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,eAAe,GAAU,IAAI,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,4BAA4B,eAAe,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAnBW,QAAA,eAAe,mBAmB1B;AAEF;;;;;;;;;;;GAWG;AACI,MAAM,wBAAwB,GAAG,CAAC,YAAoB,EAAU,EAAE;IACvE,+DAA+D;IAC/D,IAAI,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,mEAAmE;IACnE,IAAI,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,sDAAsD;IACtD,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACtB,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAtBW,QAAA,wBAAwB,4BAsBnC","sourcesContent":["import * as fs from \"fs\";\n\n/**\n * Utility functions for working with package.json and npm packages in ESLint rules.\n *\n * ## Main Functions\n * - `getDependencies()` - Get dependencies of a specific type from package.json\n * - `getPackageNameFromImport()` - Extract base package name from import source\n */\n\nexport type DependencyType = \"dependencies\" | \"devDependencies\" | \"all\";\n\ntype PackageJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n};\n\n/**\n * Get dependencies from package.json based on the specified type.\n *\n * @param packageJsonPath - Absolute path to package.json\n * @param type - Type of dependencies to retrieve\n * @returns Set of package names\n * @example\n * // Get only production dependencies\n * const deps = getDependencies(\"/path/to/package.json\", \"dependencies\");\n * @example\n * // Get only dev dependencies\n * const devDeps = getDependencies(\"/path/to/package.json\", \"devDependencies\");\n * @example\n * // Get all dependencies (union of both)\n * const allDeps = getDependencies(\"/path/to/package.json\", \"all\");\n */\nexport const getDependencies = (packageJsonPath: string, type: DependencyType): Set<string> => {\n const packageJsonContent = fs.readFileSync(packageJsonPath, \"utf8\");\n const packageJson: PackageJson = JSON.parse(packageJsonContent);\n\n const dependencies = packageJson.dependencies ?? {};\n const devDependencies = packageJson.devDependencies ?? {};\n\n switch (type) {\n case \"dependencies\":\n return new Set(Object.keys(dependencies));\n case \"devDependencies\":\n return new Set(Object.keys(devDependencies));\n case \"all\":\n return new Set([...Object.keys(dependencies), ...Object.keys(devDependencies)]);\n default: {\n const exhaustiveCheck: never = type;\n throw new Error(`Unknown dependency type: ${exhaustiveCheck}`);\n }\n }\n};\n\n/**\n * Extract the base package name from an import source.\n * Handles scoped packages and sub-path imports.\n *\n * @param importSource - The import source from an ImportDeclaration node\n * @returns The base package name\n * @example\n * getPackageNameFromImport(\"lodash/fp\") // → \"lodash\"\n * getPackageNameFromImport(\"@scope/package/sub\") // → \"@scope/package\"\n * getPackageNameFromImport(\"react\") // → \"react\"\n * getPackageNameFromImport(\"./relative\") // → \"./relative\" (caller should filter)\n */\nexport const getPackageNameFromImport = (importSource: string): string => {\n // Handle relative imports - return as-is, caller should filter\n if (importSource.startsWith(\".\") || importSource.startsWith(\"/\")) {\n return importSource;\n }\n\n // Handle scoped packages: @scope/package/sub-path → @scope/package\n if (importSource.startsWith(\"@\")) {\n const parts = importSource.split(\"/\");\n if (parts.length >= 2) {\n return `${parts[0]}/${parts[1]}`;\n }\n return importSource;\n }\n\n // Handle regular packages: package/sub-path → package\n const firstSlash = importSource.indexOf(\"/\");\n if (firstSlash !== -1) {\n return importSource.substring(0, firstSlash);\n }\n\n return importSource;\n};\n"]}
@@ -0,0 +1,29 @@
1
+ import { TSESLint, TSESTree } from "@typescript-eslint/utils";
2
+ export type OptionalProp = {
3
+ name: string;
4
+ type: "boolean";
5
+ } | {
6
+ name: string;
7
+ type: "string";
8
+ } | {
9
+ name: string;
10
+ type: "number";
11
+ } | {
12
+ name: string;
13
+ type: "object";
14
+ } | {
15
+ name: string;
16
+ type: "stringLiteral";
17
+ stringLiterals: Array<string>;
18
+ } | {
19
+ name: string;
20
+ type: "enum";
21
+ enumValues: Array<string | number>;
22
+ } | {
23
+ name: string;
24
+ type: "unknown";
25
+ };
26
+ /**
27
+ * Helper function to extract optional properties from a TypeScript type annotation
28
+ */
29
+ export declare const getOptionalPropsFromType: (context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>, typeNode: TSESTree.TypeNode | undefined) => Array<OptionalProp>;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getOptionalPropsFromType = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const utils_1 = require("@typescript-eslint/utils");
6
+ const ts = tslib_1.__importStar(require("typescript"));
7
+ function isStringLiteralType(type) {
8
+ return (type.flags & ts.TypeFlags.StringLiteral) !== 0 && "value" in type;
9
+ }
10
+ function isEnumLiteralType(type) {
11
+ return (type.flags & ts.TypeFlags.EnumLiteral) !== 0 && "value" in type;
12
+ }
13
+ // Type guard for TSESTree literal nodes
14
+ function isTSLiteralTypeWithStringLiteral(node) {
15
+ return (node.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
16
+ node.literal.type === utils_1.AST_NODE_TYPES.Literal &&
17
+ typeof node.literal.value === "string");
18
+ }
19
+ function isTSLiteralTypeWithLiteral(node) {
20
+ return node.type === utils_1.AST_NODE_TYPES.TSLiteralType && node.literal.type === utils_1.AST_NODE_TYPES.Literal;
21
+ }
22
+ /**
23
+ * Unified helper function to analyze prop types
24
+ */
25
+ const analyzeTypeProperties = (typeNode, tsType) => {
26
+ // Try TypeScript type checker first (more accurate)
27
+ if (tsType) {
28
+ const flags = tsType.getFlags();
29
+ // Check for string literal types (including unions like "a" | "b")
30
+ const isStringLiteral = (flags & ts.TypeFlags.StringLiteral) !== 0 ||
31
+ (tsType.isUnion() &&
32
+ tsType.types
33
+ .filter(t => (t.getFlags() & ts.TypeFlags.Undefined) === 0)
34
+ .every(t => (t.getFlags() & ts.TypeFlags.StringLiteral) !== 0) &&
35
+ tsType.types.some(t => (t.getFlags() & ts.TypeFlags.StringLiteral) !== 0));
36
+ if (isStringLiteral) {
37
+ let stringLiterals;
38
+ if (tsType.isUnion()) {
39
+ stringLiterals = tsType.types.filter(isStringLiteralType).map(t => t.value);
40
+ }
41
+ else if (isStringLiteralType(tsType)) {
42
+ stringLiterals = [tsType.value];
43
+ }
44
+ else {
45
+ stringLiterals = [];
46
+ }
47
+ return { type: "stringLiteral", stringLiterals };
48
+ }
49
+ // Check for enum types
50
+ if (isEnumLiteralType(tsType)) {
51
+ return { type: "enum", enumValues: [tsType.value] };
52
+ }
53
+ else if (tsType.isUnion()) {
54
+ const enumTypes = tsType.types.filter(isEnumLiteralType);
55
+ if (enumTypes.length > 0) {
56
+ const enumValues = enumTypes.map(t => t.value);
57
+ return { type: "enum", enumValues };
58
+ }
59
+ }
60
+ // Check for union types that contain specific primitives (e.g., boolean | undefined)
61
+ if (tsType.isUnion()) {
62
+ const nonUndefinedTypes = tsType.types.filter(t => (t.getFlags() & ts.TypeFlags.Undefined) === 0);
63
+ // Check if all non-undefined types are booleans
64
+ if (nonUndefinedTypes.length > 0 &&
65
+ nonUndefinedTypes.every(t => (t.getFlags() & ts.TypeFlags.Boolean) !== 0 || (t.getFlags() & ts.TypeFlags.BooleanLiteral) !== 0)) {
66
+ return { type: "boolean" };
67
+ }
68
+ // Check if all non-undefined types are strings
69
+ if (nonUndefinedTypes.length > 0 && nonUndefinedTypes.every(t => (t.getFlags() & ts.TypeFlags.String) !== 0)) {
70
+ return { type: "string" };
71
+ }
72
+ // Check if all non-undefined types are numbers
73
+ if (nonUndefinedTypes.length > 0 &&
74
+ nonUndefinedTypes.every(t => (t.getFlags() & ts.TypeFlags.Number) !== 0 || (t.getFlags() & ts.TypeFlags.NumberLiteral) !== 0)) {
75
+ return { type: "number" };
76
+ }
77
+ }
78
+ // Check for other primitive types
79
+ if ((flags & ts.TypeFlags.Boolean) !== 0 || (flags & ts.TypeFlags.BooleanLiteral) !== 0) {
80
+ return { type: "boolean" };
81
+ }
82
+ if ((flags & ts.TypeFlags.String) !== 0) {
83
+ return { type: "string" };
84
+ }
85
+ if ((flags & ts.TypeFlags.Number) !== 0 || (flags & ts.TypeFlags.NumberLiteral) !== 0) {
86
+ return { type: "number" };
87
+ }
88
+ if ((flags & ts.TypeFlags.Object) !== 0) {
89
+ return { type: "object" };
90
+ }
91
+ // If we couldn't identify the type, mark it as unknown
92
+ return { type: "unknown" };
93
+ }
94
+ // Fallback to AST-based analysis
95
+ if (!typeNode) {
96
+ return { type: "unknown" };
97
+ }
98
+ // Check for string literal types
99
+ const isStringLiteralAST = isTSLiteralTypeWithStringLiteral(typeNode) ||
100
+ (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType && typeNode.types.every(isTSLiteralTypeWithStringLiteral));
101
+ if (isStringLiteralAST) {
102
+ let stringLiterals;
103
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType) {
104
+ stringLiterals = typeNode.types.filter(isTSLiteralTypeWithStringLiteral).map(t => t.literal.value);
105
+ }
106
+ else if (isTSLiteralTypeWithStringLiteral(typeNode)) {
107
+ stringLiterals = [typeNode.literal.value];
108
+ }
109
+ else {
110
+ stringLiterals = [];
111
+ }
112
+ return { type: "stringLiteral", stringLiterals };
113
+ }
114
+ // Check for enum types (AST-based approximation)
115
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType &&
116
+ typeNode.types.every(t => t.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
117
+ t.literal.type === utils_1.AST_NODE_TYPES.Literal &&
118
+ (typeof t.literal.value === "string" || typeof t.literal.value === "number"))) {
119
+ const enumValues = typeNode.types
120
+ .filter(isTSLiteralTypeWithLiteral)
121
+ .map(t => t.literal.value)
122
+ .filter((value) => typeof value === "string" || typeof value === "number");
123
+ return { type: "enum", enumValues };
124
+ }
125
+ // Check for other primitive types
126
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword ||
127
+ (typeNode.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
128
+ typeNode.literal.type === utils_1.AST_NODE_TYPES.Literal &&
129
+ typeof typeNode.literal.value === "boolean")) {
130
+ return { type: "boolean" };
131
+ }
132
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSStringKeyword) {
133
+ return { type: "string" };
134
+ }
135
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSNumberKeyword ||
136
+ (typeNode.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
137
+ typeNode.literal.type === utils_1.AST_NODE_TYPES.Literal &&
138
+ typeof typeNode.literal.value === "number")) {
139
+ return { type: "number" };
140
+ }
141
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeLiteral ||
142
+ typeNode.type === utils_1.AST_NODE_TYPES.TSArrayType ||
143
+ (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
144
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
145
+ /^(Array|Object|Record|Map|Set)$/.test(typeNode.typeName.name))) {
146
+ return { type: "object" };
147
+ }
148
+ return { type: "unknown" };
149
+ };
150
+ /**
151
+ * Helper function to extract optional properties from a TypeScript type annotation
152
+ */
153
+ const getOptionalPropsFromType = (context, typeNode) => {
154
+ if (!typeNode)
155
+ return [];
156
+ const optionalProps = [];
157
+ const processType = (node) => {
158
+ if (node.type === utils_1.AST_NODE_TYPES.TSTypeReference && node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
159
+ // Try to get type information using the type checker
160
+ const services = utils_1.ESLintUtils.getParserServices(context);
161
+ try {
162
+ const checker = services.program.getTypeChecker();
163
+ const tsNode = services.esTreeNodeToTSNodeMap.get(node);
164
+ const type = checker.getTypeFromTypeNode(tsNode);
165
+ // Get the symbol properties - this works for resolved intersection types
166
+ const props = checker.getPropertiesOfType(type);
167
+ props.forEach(prop => {
168
+ const propType = checker.getTypeOfSymbolAtLocation(prop, tsNode);
169
+ // Check if the property is optional by examining all declarations
170
+ const isOptional = prop.declarations?.some(decl => "questionToken" in decl && decl.questionToken !== undefined) ?? false;
171
+ if (isOptional) {
172
+ const typeAnalysis = analyzeTypeProperties(undefined, propType);
173
+ optionalProps.push({
174
+ name: prop.getName(),
175
+ ...typeAnalysis,
176
+ });
177
+ }
178
+ });
179
+ }
180
+ catch (_error) {
181
+ // If type checking fails, fall back to the original behavior
182
+ return;
183
+ }
184
+ return;
185
+ }
186
+ if (node.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
187
+ node.members.forEach(member => {
188
+ if (member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
189
+ member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
190
+ member.optional) {
191
+ const propName = member.key.name;
192
+ const propType = member.typeAnnotation?.typeAnnotation;
193
+ const typeAnalysis = analyzeTypeProperties(propType);
194
+ optionalProps.push({
195
+ name: propName,
196
+ ...typeAnalysis,
197
+ });
198
+ }
199
+ });
200
+ }
201
+ if (node.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
202
+ node.types.forEach(processType);
203
+ }
204
+ if (node.type === utils_1.AST_NODE_TYPES.TSUnionType) {
205
+ // For union types, we'll be conservative and process all types
206
+ node.types.forEach(processType);
207
+ }
208
+ };
209
+ processType(typeNode);
210
+ return optionalProps;
211
+ };
212
+ exports.getOptionalPropsFromType = getOptionalPropsFromType;
213
+ //# sourceMappingURL=typescript-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-utils.js","sourceRoot":"","sources":["../../../../../../../libs/eslint/plugin-trackunit/src/lib/utils/typescript-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { MappedOmit } from \"@trackunit/shared-utils\";\nimport { AST_NODE_TYPES, ESLintUtils, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport * as ts from \"typescript\";\n\n// Type guards for TypeScript API\ntype StringLiteralType = ts.Type & { value: string };\ntype EnumLiteralType = ts.Type & { value: string | number };\n\nfunction isStringLiteralType(type: ts.Type): type is StringLiteralType {\n return (type.flags & ts.TypeFlags.StringLiteral) !== 0 && \"value\" in type;\n}\n\nfunction isEnumLiteralType(type: ts.Type): type is EnumLiteralType {\n return (type.flags & ts.TypeFlags.EnumLiteral) !== 0 && \"value\" in type;\n}\n\n// Type guard for TSESTree literal nodes\nfunction isTSLiteralTypeWithStringLiteral(\n node: TSESTree.TypeNode\n): node is TSESTree.TSLiteralType & { literal: TSESTree.Literal & { value: string } } {\n return (\n node.type === AST_NODE_TYPES.TSLiteralType &&\n node.literal.type === AST_NODE_TYPES.Literal &&\n typeof node.literal.value === \"string\"\n );\n}\n\nfunction isTSLiteralTypeWithLiteral(\n node: TSESTree.TypeNode\n): node is TSESTree.TSLiteralType & { literal: TSESTree.Literal } {\n return node.type === AST_NODE_TYPES.TSLiteralType && node.literal.type === AST_NODE_TYPES.Literal;\n}\n\nexport type OptionalProp =\n | { name: string; type: \"boolean\" }\n | { name: string; type: \"string\" }\n | { name: string; type: \"number\" }\n | { name: string; type: \"object\" }\n | { name: string; type: \"stringLiteral\"; stringLiterals: Array<string> }\n | { name: string; type: \"enum\"; enumValues: Array<string | number> }\n | { name: string; type: \"unknown\" };\n\n/**\n * Unified helper function to analyze prop types\n */\nconst analyzeTypeProperties = (\n typeNode: TSESTree.TypeNode | undefined,\n tsType?: ts.Type\n): MappedOmit<OptionalProp, \"name\"> => {\n // Try TypeScript type checker first (more accurate)\n if (tsType) {\n const flags = tsType.getFlags();\n\n // Check for string literal types (including unions like \"a\" | \"b\")\n const isStringLiteral =\n (flags & ts.TypeFlags.StringLiteral) !== 0 ||\n (tsType.isUnion() &&\n tsType.types\n .filter(t => (t.getFlags() & ts.TypeFlags.Undefined) === 0)\n .every(t => (t.getFlags() & ts.TypeFlags.StringLiteral) !== 0) &&\n tsType.types.some(t => (t.getFlags() & ts.TypeFlags.StringLiteral) !== 0));\n\n if (isStringLiteral) {\n let stringLiterals: Array<string>;\n if (tsType.isUnion()) {\n stringLiterals = tsType.types.filter(isStringLiteralType).map(t => t.value);\n } else if (isStringLiteralType(tsType)) {\n stringLiterals = [tsType.value];\n } else {\n stringLiterals = [];\n }\n return { type: \"stringLiteral\", stringLiterals };\n }\n\n // Check for enum types\n if (isEnumLiteralType(tsType)) {\n return { type: \"enum\", enumValues: [tsType.value] };\n } else if (tsType.isUnion()) {\n const enumTypes = tsType.types.filter(isEnumLiteralType);\n if (enumTypes.length > 0) {\n const enumValues = enumTypes.map(t => t.value);\n return { type: \"enum\", enumValues };\n }\n }\n\n // Check for union types that contain specific primitives (e.g., boolean | undefined)\n if (tsType.isUnion()) {\n const nonUndefinedTypes = tsType.types.filter(t => (t.getFlags() & ts.TypeFlags.Undefined) === 0);\n\n // Check if all non-undefined types are booleans\n if (\n nonUndefinedTypes.length > 0 &&\n nonUndefinedTypes.every(\n t => (t.getFlags() & ts.TypeFlags.Boolean) !== 0 || (t.getFlags() & ts.TypeFlags.BooleanLiteral) !== 0\n )\n ) {\n return { type: \"boolean\" };\n }\n\n // Check if all non-undefined types are strings\n if (nonUndefinedTypes.length > 0 && nonUndefinedTypes.every(t => (t.getFlags() & ts.TypeFlags.String) !== 0)) {\n return { type: \"string\" };\n }\n\n // Check if all non-undefined types are numbers\n if (\n nonUndefinedTypes.length > 0 &&\n nonUndefinedTypes.every(\n t => (t.getFlags() & ts.TypeFlags.Number) !== 0 || (t.getFlags() & ts.TypeFlags.NumberLiteral) !== 0\n )\n ) {\n return { type: \"number\" };\n }\n }\n\n // Check for other primitive types\n if ((flags & ts.TypeFlags.Boolean) !== 0 || (flags & ts.TypeFlags.BooleanLiteral) !== 0) {\n return { type: \"boolean\" };\n }\n if ((flags & ts.TypeFlags.String) !== 0) {\n return { type: \"string\" };\n }\n if ((flags & ts.TypeFlags.Number) !== 0 || (flags & ts.TypeFlags.NumberLiteral) !== 0) {\n return { type: \"number\" };\n }\n if ((flags & ts.TypeFlags.Object) !== 0) {\n return { type: \"object\" };\n }\n\n // If we couldn't identify the type, mark it as unknown\n return { type: \"unknown\" };\n }\n\n // Fallback to AST-based analysis\n if (!typeNode) {\n return { type: \"unknown\" };\n }\n\n // Check for string literal types\n const isStringLiteralAST =\n isTSLiteralTypeWithStringLiteral(typeNode) ||\n (typeNode.type === AST_NODE_TYPES.TSUnionType && typeNode.types.every(isTSLiteralTypeWithStringLiteral));\n\n if (isStringLiteralAST) {\n let stringLiterals: Array<string>;\n if (typeNode.type === AST_NODE_TYPES.TSUnionType) {\n stringLiterals = typeNode.types.filter(isTSLiteralTypeWithStringLiteral).map(t => t.literal.value);\n } else if (isTSLiteralTypeWithStringLiteral(typeNode)) {\n stringLiterals = [typeNode.literal.value];\n } else {\n stringLiterals = [];\n }\n return { type: \"stringLiteral\", stringLiterals };\n }\n\n // Check for enum types (AST-based approximation)\n if (\n typeNode.type === AST_NODE_TYPES.TSUnionType &&\n typeNode.types.every(\n t =>\n t.type === AST_NODE_TYPES.TSLiteralType &&\n t.literal.type === AST_NODE_TYPES.Literal &&\n (typeof t.literal.value === \"string\" || typeof t.literal.value === \"number\")\n )\n ) {\n const enumValues = typeNode.types\n .filter(isTSLiteralTypeWithLiteral)\n .map(t => t.literal.value)\n .filter((value): value is string | number => typeof value === \"string\" || typeof value === \"number\");\n return { type: \"enum\", enumValues };\n }\n\n // Check for other primitive types\n if (\n typeNode.type === AST_NODE_TYPES.TSBooleanKeyword ||\n (typeNode.type === AST_NODE_TYPES.TSLiteralType &&\n typeNode.literal.type === AST_NODE_TYPES.Literal &&\n typeof typeNode.literal.value === \"boolean\")\n ) {\n return { type: \"boolean\" };\n }\n\n if (typeNode.type === AST_NODE_TYPES.TSStringKeyword) {\n return { type: \"string\" };\n }\n\n if (\n typeNode.type === AST_NODE_TYPES.TSNumberKeyword ||\n (typeNode.type === AST_NODE_TYPES.TSLiteralType &&\n typeNode.literal.type === AST_NODE_TYPES.Literal &&\n typeof typeNode.literal.value === \"number\")\n ) {\n return { type: \"number\" };\n }\n\n if (\n typeNode.type === AST_NODE_TYPES.TSTypeLiteral ||\n typeNode.type === AST_NODE_TYPES.TSArrayType ||\n (typeNode.type === AST_NODE_TYPES.TSTypeReference &&\n typeNode.typeName.type === AST_NODE_TYPES.Identifier &&\n /^(Array|Object|Record|Map|Set)$/.test(typeNode.typeName.name))\n ) {\n return { type: \"object\" };\n }\n\n return { type: \"unknown\" };\n};\n\n/**\n * Helper function to extract optional properties from a TypeScript type annotation\n */\nexport const getOptionalPropsFromType = (\n context: TSESLint.RuleContext<string, ReadonlyArray<unknown>>,\n typeNode: TSESTree.TypeNode | undefined\n): Array<OptionalProp> => {\n if (!typeNode) return [];\n\n const optionalProps: Array<OptionalProp> = [];\n\n const processType = (node: TSESTree.TypeNode) => {\n if (node.type === AST_NODE_TYPES.TSTypeReference && node.typeName.type === AST_NODE_TYPES.Identifier) {\n // Try to get type information using the type checker\n const services = ESLintUtils.getParserServices(context);\n\n try {\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n const type = checker.getTypeFromTypeNode(tsNode);\n\n // Get the symbol properties - this works for resolved intersection types\n const props = checker.getPropertiesOfType(type);\n props.forEach(prop => {\n const propType = checker.getTypeOfSymbolAtLocation(prop, tsNode);\n\n // Check if the property is optional by examining all declarations\n const isOptional =\n prop.declarations?.some(decl => \"questionToken\" in decl && decl.questionToken !== undefined) ?? false;\n\n if (isOptional) {\n const typeAnalysis = analyzeTypeProperties(undefined, propType);\n optionalProps.push({\n name: prop.getName(),\n ...typeAnalysis,\n });\n }\n });\n } catch (_error) {\n // If type checking fails, fall back to the original behavior\n return;\n }\n\n return;\n }\n\n if (node.type === AST_NODE_TYPES.TSTypeLiteral) {\n node.members.forEach(member => {\n if (\n member.type === AST_NODE_TYPES.TSPropertySignature &&\n member.key.type === AST_NODE_TYPES.Identifier &&\n member.optional\n ) {\n const propName = member.key.name;\n const propType = member.typeAnnotation?.typeAnnotation;\n\n const typeAnalysis = analyzeTypeProperties(propType);\n optionalProps.push({\n name: propName,\n ...typeAnalysis,\n });\n }\n });\n }\n\n if (node.type === AST_NODE_TYPES.TSIntersectionType) {\n node.types.forEach(processType);\n }\n\n if (node.type === AST_NODE_TYPES.TSUnionType) {\n // For union types, we'll be conservative and process all types\n node.types.forEach(processType);\n }\n };\n\n processType(typeNode);\n return optionalProps;\n};\n"]}