@futdevpro/dynamo-eslint 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/.eslintrc.json +16 -0
  2. package/.github/workflows/main.yml +393 -0
  3. package/INTEGRATION.md +74 -0
  4. package/POC-README.md +147 -0
  5. package/README.md +36 -0
  6. package/build/configs/base.d.ts +81 -0
  7. package/build/configs/base.d.ts.map +1 -0
  8. package/build/configs/base.js +51 -0
  9. package/build/configs/base.js.map +1 -0
  10. package/build/configs/fsm.d.ts +82 -0
  11. package/build/configs/fsm.d.ts.map +1 -0
  12. package/build/configs/fsm.js +7 -0
  13. package/build/configs/fsm.js.map +1 -0
  14. package/build/configs/ngx-package.d.ts +81 -0
  15. package/build/configs/ngx-package.d.ts.map +1 -0
  16. package/build/configs/ngx-package.js +7 -0
  17. package/build/configs/ngx-package.js.map +1 -0
  18. package/build/configs/ngx.d.ts +81 -0
  19. package/build/configs/ngx.d.ts.map +1 -0
  20. package/build/configs/ngx.js +12 -0
  21. package/build/configs/ngx.js.map +1 -0
  22. package/build/configs/nts-package.d.ts +83 -0
  23. package/build/configs/nts-package.d.ts.map +1 -0
  24. package/build/configs/nts-package.js +12 -0
  25. package/build/configs/nts-package.js.map +1 -0
  26. package/build/configs/nts.d.ts +82 -0
  27. package/build/configs/nts.d.ts.map +1 -0
  28. package/build/configs/nts.js +13 -0
  29. package/build/configs/nts.js.map +1 -0
  30. package/build/index.d.ts +2 -0
  31. package/build/index.d.ts.map +1 -0
  32. package/build/index.js +3 -0
  33. package/build/index.js.map +1 -0
  34. package/build/plugin/index.d.ts +16 -0
  35. package/build/plugin/index.d.ts.map +1 -0
  36. package/build/plugin/index.js +19 -0
  37. package/build/plugin/index.js.map +1 -0
  38. package/build/plugin/rules/import-order.d.ts +4 -0
  39. package/build/plugin/rules/import-order.d.ts.map +1 -0
  40. package/build/plugin/rules/import-order.js +134 -0
  41. package/build/plugin/rules/import-order.js.map +1 -0
  42. package/build/plugin/rules/import-order.spec.d.ts +2 -0
  43. package/build/plugin/rules/import-order.spec.d.ts.map +1 -0
  44. package/build/plugin/rules/import-order.spec.js +181 -0
  45. package/build/plugin/rules/import-order.spec.js.map +1 -0
  46. package/build/plugin/rules/naming-patterns.d.ts +4 -0
  47. package/build/plugin/rules/naming-patterns.d.ts.map +1 -0
  48. package/build/plugin/rules/naming-patterns.js +23 -0
  49. package/build/plugin/rules/naming-patterns.js.map +1 -0
  50. package/build/plugin/rules/naming-patterns.spec.d.ts +2 -0
  51. package/build/plugin/rules/naming-patterns.spec.d.ts.map +1 -0
  52. package/build/plugin/rules/naming-patterns.spec.js +36 -0
  53. package/build/plugin/rules/naming-patterns.spec.js.map +1 -0
  54. package/build/scripts/eslintrc-audit.d.ts +3 -0
  55. package/build/scripts/eslintrc-audit.d.ts.map +1 -0
  56. package/build/scripts/eslintrc-audit.js +36 -0
  57. package/build/scripts/eslintrc-audit.js.map +1 -0
  58. package/build/scripts/validate-imports.d.ts +3 -0
  59. package/build/scripts/validate-imports.d.ts.map +1 -0
  60. package/build/scripts/validate-imports.js +76 -0
  61. package/build/scripts/validate-imports.js.map +1 -0
  62. package/build/scripts/validate-naming.d.ts +3 -0
  63. package/build/scripts/validate-naming.d.ts.map +1 -0
  64. package/build/scripts/validate-naming.js +76 -0
  65. package/build/scripts/validate-naming.js.map +1 -0
  66. package/build-test/plugin/rules/import-order.d.ts +3 -0
  67. package/build-test/plugin/rules/import-order.js +23 -0
  68. package/build-test/plugin/rules/import-order.spec.d.ts +1 -0
  69. package/build-test/plugin/rules/import-order.spec.js +44 -0
  70. package/build-test/plugin/rules/naming-patterns.d.ts +3 -0
  71. package/build-test/plugin/rules/naming-patterns.js +22 -0
  72. package/build-test/plugin/rules/naming-patterns.spec.d.ts +1 -0
  73. package/build-test/plugin/rules/naming-patterns.spec.js +41 -0
  74. package/futdevpro-dynamo-eslint-01.12.01.tgz +0 -0
  75. package/package.json +95 -0
  76. package/samples/base/.eslintrc.json +6 -0
  77. package/samples/ngx/.eslintrc.json +6 -0
  78. package/samples/nts/.eslintrc.json +6 -0
  79. package/samples/poc-sample.ts +38 -0
  80. package/samples/poc-violations.ts +25 -0
  81. package/spec/support/jasmine.json +24 -0
  82. package/src/configs/base.ts +51 -0
  83. package/src/configs/fsm.ts +9 -0
  84. package/src/configs/ngx-package.ts +9 -0
  85. package/src/configs/ngx.ts +14 -0
  86. package/src/configs/nts-package.ts +14 -0
  87. package/src/configs/nts.ts +15 -0
  88. package/src/index.ts +4 -0
  89. package/src/plugin/index.ts +19 -0
  90. package/src/plugin/rules/import-order.spec.ts +197 -0
  91. package/src/plugin/rules/import-order.ts +167 -0
  92. package/src/plugin/rules/naming-patterns.spec.ts +39 -0
  93. package/src/plugin/rules/naming-patterns.ts +25 -0
  94. package/src/scripts/eslintrc-audit.ts +39 -0
  95. package/src/scripts/validate-imports.ts +98 -0
  96. package/src/scripts/validate-naming.ts +97 -0
  97. package/tsconfig.json +32 -0
@@ -0,0 +1,197 @@
1
+ import importOrderRule from './import-order';
2
+
3
+ describe('| import-order', () => {
4
+ it('| should be a valid ESLint rule', () => {
5
+ expect(importOrderRule.meta?.type).toBe('suggestion');
6
+ expect(importOrderRule.meta?.docs?.description).toContain('import ordering');
7
+ expect(importOrderRule.meta?.fixable).toBe('code');
8
+ });
9
+
10
+ it('| should have create function that returns object with Program method', () => {
11
+ const mockContext = {
12
+ getSourceCode: () => ({
13
+ ast: {},
14
+ getLines: () => ['line1', 'line2', 'line3']
15
+ }),
16
+ getFilename: () => 'test.ts',
17
+ report: () => {},
18
+ } as any;
19
+
20
+ const result = importOrderRule.create(mockContext);
21
+ expect(typeof result).toBe('object');
22
+ expect(typeof result.Program).toBe('function');
23
+ });
24
+
25
+ it('| should detect forbidden NPM-packages imports', () => {
26
+ const mockContext = {
27
+ getSourceCode: () => ({
28
+ ast: {},
29
+ getLines: () => ['import { Something } from \'../../../NPM-packages/some-package\';']
30
+ }),
31
+ getFilename: () => 'test.ts',
32
+ report: (options: any) => {
33
+ expect(options.messageId).toBe('forbiddenNpmPackages');
34
+ },
35
+ } as any;
36
+
37
+ const mockNode = {
38
+ type: 'Program',
39
+ sourceType: 'module',
40
+ body: [
41
+ {
42
+ type: 'ImportDeclaration',
43
+ source: { value: '../../../NPM-packages/some-package' },
44
+ importKind: 'value',
45
+ loc: { start: { line: 1 }, end: { line: 1 } }
46
+ }
47
+ ]
48
+ } as any;
49
+
50
+ const rule = importOrderRule.create(mockContext);
51
+ rule.Program(mockNode);
52
+ });
53
+
54
+ it('| should detect forbidden .js extension imports', () => {
55
+ const mockContext = {
56
+ getSourceCode: () => ({
57
+ ast: {},
58
+ getLines: () => ['import { Something } from \'./some-file.js\';']
59
+ }),
60
+ getFilename: () => 'test.ts',
61
+ report: (options: any) => {
62
+ expect(options.messageId).toBe('forbiddenJsExtension');
63
+ },
64
+ } as any;
65
+
66
+ const mockNode = {
67
+ type: 'Program',
68
+ sourceType: 'module',
69
+ body: [
70
+ {
71
+ type: 'ImportDeclaration',
72
+ source: { value: './some-file.js' },
73
+ importKind: 'value',
74
+ loc: { start: { line: 1 }, end: { line: 1 } }
75
+ }
76
+ ]
77
+ } as any;
78
+
79
+ const rule = importOrderRule.create(mockContext);
80
+ rule.Program(mockNode);
81
+ });
82
+
83
+ it('| should detect forbidden import type usage', () => {
84
+ const mockContext = {
85
+ getSourceCode: () => ({
86
+ ast: {},
87
+ getLines: () => ['import type { SomeType } from \'./some-file\';']
88
+ }),
89
+ getFilename: () => 'test.ts',
90
+ report: (options: any) => {
91
+ expect(options.messageId).toBe('forbiddenImportType');
92
+ },
93
+ } as any;
94
+
95
+ const mockNode = {
96
+ type: 'Program',
97
+ sourceType: 'module',
98
+ body: [
99
+ {
100
+ type: 'ImportDeclaration',
101
+ source: { value: './some-file' },
102
+ importKind: 'type',
103
+ loc: { start: { line: 1 }, end: { line: 1 } }
104
+ }
105
+ ]
106
+ } as any;
107
+
108
+ const rule = importOrderRule.create(mockContext);
109
+ rule.Program(mockNode);
110
+ });
111
+
112
+ it('| should detect misordered imports', () => {
113
+ let reportCount = 0;
114
+ const mockContext = {
115
+ getSourceCode: () => ({
116
+ ast: {},
117
+ getLines: () => [
118
+ 'import { DyFM_Error } from \'@futdevpro/fsm-dynamo\';',
119
+ 'import { Component } from \'@angular/core\';'
120
+ ]
121
+ }),
122
+ getFilename: () => 'test.ts',
123
+ report: (options: any) => {
124
+ if (options.messageId === 'misordered') {
125
+ reportCount++;
126
+ }
127
+ },
128
+ } as any;
129
+
130
+ const mockNode = {
131
+ type: 'Program',
132
+ sourceType: 'module',
133
+ body: [
134
+ {
135
+ type: 'ImportDeclaration',
136
+ source: { value: '@futdevpro/fsm-dynamo' },
137
+ importKind: 'value',
138
+ loc: { start: { line: 1 }, end: { line: 1 } }
139
+ },
140
+ {
141
+ type: 'ImportDeclaration',
142
+ source: { value: '@angular/core' },
143
+ importKind: 'value',
144
+ loc: { start: { line: 2 }, end: { line: 2 } }
145
+ }
146
+ ]
147
+ } as any;
148
+
149
+ const rule = importOrderRule.create(mockContext);
150
+ rule.Program(mockNode);
151
+
152
+ expect(reportCount).toBeGreaterThan(0);
153
+ });
154
+
155
+ it('| should detect missing empty line between groups', () => {
156
+ let reportCount = 0;
157
+ const mockContext = {
158
+ getSourceCode: () => ({
159
+ ast: {},
160
+ getLines: () => [
161
+ 'import { Component } from \'@angular/core\';',
162
+ 'import { DyFM_Error } from \'@futdevpro/fsm-dynamo\';'
163
+ ]
164
+ }),
165
+ getFilename: () => 'test.ts',
166
+ report: (options: any) => {
167
+ if (options.messageId === 'missingEmptyLine') {
168
+ reportCount++;
169
+ }
170
+ },
171
+ } as any;
172
+
173
+ const mockNode = {
174
+ type: 'Program',
175
+ sourceType: 'module',
176
+ body: [
177
+ {
178
+ type: 'ImportDeclaration',
179
+ source: { value: '@angular/core' },
180
+ importKind: 'value',
181
+ loc: { start: { line: 1 }, end: { line: 1 } }
182
+ },
183
+ {
184
+ type: 'ImportDeclaration',
185
+ source: { value: '@futdevpro/fsm-dynamo' },
186
+ importKind: 'value',
187
+ loc: { start: { line: 2 }, end: { line: 2 } }
188
+ }
189
+ ]
190
+ } as any;
191
+
192
+ const rule = importOrderRule.create(mockContext);
193
+ rule.Program(mockNode);
194
+
195
+ expect(reportCount).toBeGreaterThan(0);
196
+ });
197
+ });
@@ -0,0 +1,167 @@
1
+ import type { Rule } from 'eslint';
2
+
3
+ interface ImportGroup {
4
+ type: 'non-futdevpro' | 'futdevpro' | 'other-modules' | 'same-module';
5
+ imports: any[];
6
+ }
7
+
8
+ const rule: Rule.RuleModule = {
9
+ meta: {
10
+ type: 'suggestion',
11
+ docs: { description: 'Validate import ordering based on Dynamo conventions' },
12
+ schema: [],
13
+ messages: {
14
+ misordered: 'Import statements should be grouped and ordered by convention.',
15
+ forbiddenNpmPackages: 'Import from "*/../NPM-packages/*" is forbidden. Use "@futdevpro/*" instead.',
16
+ forbiddenJsExtension: 'Import with ".js" extension is forbidden. Use ".ts" or no extension.',
17
+ forbiddenImportType: 'Use of "import type" is forbidden.',
18
+ missingEmptyLine: 'Missing empty line between import groups.',
19
+ extraEmptyLine: 'Extra empty line detected.',
20
+ },
21
+ fixable: 'code',
22
+ },
23
+ create(context) {
24
+ const sourceCode = context.getSourceCode();
25
+ const filename = context.getFilename();
26
+
27
+ function getImportGroup(importNode: any): ImportGroup['type'] {
28
+ const source = importNode.source.value as string;
29
+
30
+ // Check for forbidden patterns first
31
+ if (source.includes('/NPM-packages/')) {
32
+ return 'non-futdevpro'; // Will be flagged as error
33
+ }
34
+
35
+ if (source.endsWith('.js')) {
36
+ return 'non-futdevpro'; // Will be flagged as error
37
+ }
38
+
39
+ // FutDevPro packages
40
+ if (source.startsWith('@futdevpro/')) {
41
+ return 'futdevpro';
42
+ }
43
+
44
+ // Same module (relative imports with specific patterns)
45
+ if (source.startsWith('./') || source.startsWith('../')) {
46
+ const pathParts = source.split('/');
47
+ const upLevels = pathParts.filter(part => part === '..').length;
48
+
49
+ // Same module: up to 3 ".." and starts with "_", or less than 2 ".."
50
+ if (upLevels <= 2 || (upLevels <= 3 && pathParts.some(part => part.startsWith('_')))) {
51
+ return 'same-module';
52
+ }
53
+
54
+ // Other modules
55
+ return 'other-modules';
56
+ }
57
+
58
+ // Non-FutDevPro packages
59
+ return 'non-futdevpro';
60
+ }
61
+
62
+ function validateImportDeclaration(importNode: any) {
63
+ const source = importNode.source.value as string;
64
+
65
+ // Check for forbidden patterns
66
+ if (source.includes('/NPM-packages/')) {
67
+ context.report({
68
+ node: importNode.source,
69
+ messageId: 'forbiddenNpmPackages',
70
+ });
71
+ }
72
+
73
+ if (source.endsWith('.js')) {
74
+ context.report({
75
+ node: importNode.source,
76
+ messageId: 'forbiddenJsExtension',
77
+ });
78
+ }
79
+
80
+ // Check for import type usage
81
+ if (importNode.importKind === 'type') {
82
+ context.report({
83
+ node: importNode,
84
+ messageId: 'forbiddenImportType',
85
+ });
86
+ }
87
+ }
88
+
89
+ function validateImportOrdering(importNodes: any[]) {
90
+ if (importNodes.length === 0) return;
91
+
92
+ // Group imports
93
+ const groups: ImportGroup[] = [
94
+ { type: 'non-futdevpro', imports: [] },
95
+ { type: 'futdevpro', imports: [] },
96
+ { type: 'other-modules', imports: [] },
97
+ { type: 'same-module', imports: [] },
98
+ ];
99
+
100
+ importNodes.forEach(importNode => {
101
+ const groupType = getImportGroup(importNode);
102
+ const group = groups.find(g => g.type === groupType);
103
+ if (group) {
104
+ group.imports.push(importNode);
105
+ }
106
+ });
107
+
108
+ // Remove empty groups
109
+ const nonEmptyGroups = groups.filter(group => group.imports.length > 0);
110
+
111
+ // Check if imports are in correct order
112
+ let currentGroupIndex = 0;
113
+ for (let i = 0; i < importNodes.length; i++) {
114
+ const importNode = importNodes[i];
115
+ const expectedGroupType = getImportGroup(importNode);
116
+ const expectedGroup = nonEmptyGroups.find(g => g.type === expectedGroupType);
117
+ const expectedGroupIndex = nonEmptyGroups.findIndex(g => g.type === expectedGroupType);
118
+
119
+ if (expectedGroupIndex < currentGroupIndex) {
120
+ context.report({
121
+ node: importNode,
122
+ messageId: 'misordered',
123
+ });
124
+ }
125
+
126
+ currentGroupIndex = expectedGroupIndex;
127
+ }
128
+
129
+ // Check empty lines between groups
130
+ for (let i = 0; i < nonEmptyGroups.length - 1; i++) {
131
+ const currentGroup = nonEmptyGroups[i];
132
+ const nextGroup = nonEmptyGroups[i + 1];
133
+ const lastImportOfCurrentGroup = currentGroup.imports[currentGroup.imports.length - 1];
134
+ const firstImportOfNextGroup = nextGroup.imports[0];
135
+
136
+ const lines = sourceCode.getLines();
137
+ const lastImportLine = lastImportOfCurrentGroup.loc?.end.line || 0;
138
+ const firstImportLine = firstImportOfNextGroup.loc?.start.line || 0;
139
+
140
+ if (firstImportLine - lastImportLine < 2) {
141
+ context.report({
142
+ node: firstImportOfNextGroup,
143
+ messageId: 'missingEmptyLine',
144
+ });
145
+ }
146
+ }
147
+ }
148
+
149
+ return {
150
+ Program(node: any) {
151
+ const importNodes = node.body.filter(
152
+ (statement: any) => statement.type === 'ImportDeclaration'
153
+ );
154
+
155
+ // Validate individual import declarations
156
+ importNodes.forEach(validateImportDeclaration);
157
+
158
+ // Validate import ordering
159
+ validateImportOrdering(importNodes);
160
+ },
161
+ };
162
+ },
163
+ };
164
+
165
+ export default rule;
166
+
167
+
@@ -0,0 +1,39 @@
1
+ import namingPatternsRule from './naming-patterns';
2
+
3
+ describe('| naming-patterns', () => {
4
+ it('| should be a valid ESLint rule', () => {
5
+ expect(namingPatternsRule.meta?.type).toBe('suggestion');
6
+ expect(namingPatternsRule.meta?.docs?.description).toContain('naming conventions');
7
+ });
8
+
9
+ it('| should have create function that returns object with Identifier method', () => {
10
+ const mockContext = {
11
+ report: () => {},
12
+ } as any;
13
+
14
+ const result = namingPatternsRule.create(mockContext);
15
+ expect(typeof result).toBe('object');
16
+ expect(typeof result.Identifier).toBe('function');
17
+ });
18
+
19
+ it('| should handle valid identifiers without errors', () => {
20
+ const mockContext = {
21
+ report: (options: any) => {
22
+ // Should not be called for valid identifiers
23
+ fail('Should not report errors for valid identifiers');
24
+ },
25
+ } as any;
26
+
27
+ const mockNode = {
28
+ type: 'Identifier' as const,
29
+ name: 'validVariableName',
30
+ loc: { start: { line: 1 }, end: { line: 1 } }
31
+ } as any;
32
+
33
+ const rule = namingPatternsRule.create(mockContext);
34
+ rule.Identifier(mockNode);
35
+
36
+ // If we get here without the report function being called, the test passes
37
+ expect(true).toBe(true);
38
+ });
39
+ });
@@ -0,0 +1,25 @@
1
+ import type { Rule } from 'eslint';
2
+ // import * as dyfmUtils from '@futdevpro/fsm-dynamo';
3
+
4
+ const rule: Rule.RuleModule = {
5
+ meta: {
6
+ type: 'suggestion',
7
+ docs: { description: 'Enforce naming conventions using DyFM patterns' },
8
+ schema: [],
9
+ messages: {
10
+ invalidName: 'Identifier "{{name}}" does not match required naming pattern.',
11
+ },
12
+ },
13
+ create(context) {
14
+ return {
15
+ Identifier(node) {
16
+ // Future: use dyfmUtils naming patterns to validate identifiers
17
+ void node;
18
+ },
19
+ };
20
+ },
21
+ };
22
+
23
+ export default rule;
24
+
25
+
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import fg from 'fast-glob';
3
+ import { readFileSync } from 'fs';
4
+
5
+ type AnyJson = any;
6
+
7
+ function safeParse(jsonPath: string): AnyJson | null {
8
+ try {
9
+ const content = readFileSync(jsonPath, 'utf8');
10
+ return JSON.parse(content);
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+
16
+ async function main() {
17
+ const files = await fg(['**/.eslintrc.json'], { ignore: ['**/node_modules/**'] });
18
+ const ruleCounts: Record<string, number> = {};
19
+ const total = files.length;
20
+
21
+ for (const p of files) {
22
+ const cfg = safeParse(p);
23
+ if (!cfg?.rules) continue;
24
+ for (const key of Object.keys(cfg.rules)) {
25
+ ruleCounts[key] = (ruleCounts[key] ?? 0) + 1;
26
+ }
27
+ }
28
+
29
+ const entries = Object.entries(ruleCounts).sort((a, b) => b[1] - a[1]);
30
+ console.log(`[dynamo-eslintrc-audit] analyzed ${total} files`);
31
+ console.table(entries.slice(0, 50).map(([rule, count]) => ({ rule, count })));
32
+ }
33
+
34
+ main().catch((err) => {
35
+ console.error(err);
36
+ process.exit(1);
37
+ });
38
+
39
+
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ import fg from 'fast-glob';
3
+ import { readFileSync } from 'fs';
4
+ import { ESLint } from 'eslint';
5
+ import path from 'path';
6
+
7
+ interface ImportValidationResult {
8
+ file: string;
9
+ errors: Array<{
10
+ line: number;
11
+ column: number;
12
+ message: string;
13
+ ruleId: string;
14
+ }>;
15
+ }
16
+
17
+ async function validateImportsInFile(filePath: string): Promise<ImportValidationResult> {
18
+ const eslint = new ESLint({
19
+ baseConfig: {
20
+ parser: '@typescript-eslint/parser',
21
+ parserOptions: {
22
+ ecmaVersion: 2020,
23
+ sourceType: 'module',
24
+ },
25
+ rules: {
26
+ '@futdevpro/dynamo/import-order': 'error',
27
+ },
28
+ plugins: {
29
+ '@futdevpro/dynamo': {
30
+ rules: {
31
+ 'import-order': require('../plugin/rules/import-order').default,
32
+ },
33
+ },
34
+ },
35
+ } as any,
36
+ });
37
+
38
+ const results = await eslint.lintFiles([filePath]);
39
+ const result = results[0];
40
+
41
+ return {
42
+ file: filePath,
43
+ errors: result.messages.map(msg => ({
44
+ line: msg.line,
45
+ column: msg.column,
46
+ message: msg.message,
47
+ ruleId: msg.ruleId || 'unknown',
48
+ })),
49
+ };
50
+ }
51
+
52
+ async function main() {
53
+ const files = await fg(['**/*.{ts,tsx}'], {
54
+ ignore: ['**/node_modules/**', '**/build/**', '**/dist/**', '**/*.spec.ts', '**/*.test.ts']
55
+ });
56
+
57
+ console.log(`[dynamo-validate-imports] Scanning ${files.length} files...`);
58
+
59
+ const results: ImportValidationResult[] = [];
60
+ let totalErrors = 0;
61
+
62
+ for (const file of files) {
63
+ try {
64
+ const result = await validateImportsInFile(file);
65
+ results.push(result);
66
+ totalErrors += result.errors.length;
67
+ } catch (error) {
68
+ console.error(`Error processing ${file}:`, error);
69
+ }
70
+ }
71
+
72
+ // Report results
73
+ const filesWithErrors = results.filter(r => r.errors.length > 0);
74
+
75
+ if (filesWithErrors.length === 0) {
76
+ console.log('āœ… All import validations passed!');
77
+ return;
78
+ }
79
+
80
+ console.log(`\nāŒ Found ${totalErrors} import validation errors in ${filesWithErrors.length} files:\n`);
81
+
82
+ filesWithErrors.forEach(result => {
83
+ console.log(`šŸ“ ${result.file}:`);
84
+ result.errors.forEach(error => {
85
+ console.log(` ${error.line}:${error.column} - ${error.message}`);
86
+ });
87
+ console.log('');
88
+ });
89
+
90
+ process.exit(1);
91
+ }
92
+
93
+ main().catch((err) => {
94
+ console.error('Validation failed:', err);
95
+ process.exit(1);
96
+ });
97
+
98
+
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import fg from 'fast-glob';
3
+ import { readFileSync } from 'fs';
4
+ import { ESLint } from 'eslint';
5
+
6
+ interface NamingValidationResult {
7
+ file: string;
8
+ errors: Array<{
9
+ line: number;
10
+ column: number;
11
+ message: string;
12
+ ruleId: string;
13
+ }>;
14
+ }
15
+
16
+ async function validateNamingInFile(filePath: string): Promise<NamingValidationResult> {
17
+ const eslint = new ESLint({
18
+ baseConfig: {
19
+ parser: '@typescript-eslint/parser',
20
+ parserOptions: {
21
+ ecmaVersion: 2020,
22
+ sourceType: 'module',
23
+ },
24
+ rules: {
25
+ '@futdevpro/dynamo/naming-patterns': 'error',
26
+ },
27
+ plugins: {
28
+ '@futdevpro/dynamo': {
29
+ rules: {
30
+ 'naming-patterns': require('../plugin/rules/naming-patterns').default,
31
+ },
32
+ },
33
+ },
34
+ } as any,
35
+ });
36
+
37
+ const results = await eslint.lintFiles([filePath]);
38
+ const result = results[0];
39
+
40
+ return {
41
+ file: filePath,
42
+ errors: result.messages.map(msg => ({
43
+ line: msg.line,
44
+ column: msg.column,
45
+ message: msg.message,
46
+ ruleId: msg.ruleId || 'unknown',
47
+ })),
48
+ };
49
+ }
50
+
51
+ async function main() {
52
+ const files = await fg(['**/*.{ts,tsx}'], {
53
+ ignore: ['**/node_modules/**', '**/build/**', '**/dist/**', '**/*.spec.ts', '**/*.test.ts']
54
+ });
55
+
56
+ console.log(`[dynamo-validate-naming] Scanning ${files.length} files...`);
57
+
58
+ const results: NamingValidationResult[] = [];
59
+ let totalErrors = 0;
60
+
61
+ for (const file of files) {
62
+ try {
63
+ const result = await validateNamingInFile(file);
64
+ results.push(result);
65
+ totalErrors += result.errors.length;
66
+ } catch (error) {
67
+ console.error(`Error processing ${file}:`, error);
68
+ }
69
+ }
70
+
71
+ // Report results
72
+ const filesWithErrors = results.filter(r => r.errors.length > 0);
73
+
74
+ if (filesWithErrors.length === 0) {
75
+ console.log('āœ… All naming validations passed!');
76
+ return;
77
+ }
78
+
79
+ console.log(`\nāŒ Found ${totalErrors} naming validation errors in ${filesWithErrors.length} files:\n`);
80
+
81
+ filesWithErrors.forEach(result => {
82
+ console.log(`šŸ“ ${result.file}:`);
83
+ result.errors.forEach(error => {
84
+ console.log(` ${error.line}:${error.column} - ${error.message}`);
85
+ });
86
+ console.log('');
87
+ });
88
+
89
+ process.exit(1);
90
+ }
91
+
92
+ main().catch((err) => {
93
+ console.error('Validation failed:', err);
94
+ process.exit(1);
95
+ });
96
+
97
+
package/tsconfig.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "incremental": false,
4
+ "module": "commonjs",
5
+ "target": "es2022",
6
+ "lib": [
7
+ "es6",
8
+ "es2018",
9
+ "es2022",
10
+ "dom"
11
+ ],
12
+ "strict": false,
13
+ "allowJs": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true,
17
+ "outDir": "./build",
18
+ "rootDir": "./src",
19
+ "importHelpers": true,
20
+ "moduleResolution": "node",
21
+ "esModuleInterop": true,
22
+ "skipLibCheck": true,
23
+ "noImplicitOverride": true,
24
+ "allowSyntheticDefaultImports": true,
25
+ "forceConsistentCasingInFileNames": true,
26
+ "resolveJsonModule": true
27
+ },
28
+ "include": [
29
+ "src",
30
+ "src/**/assets/*"
31
+ ]
32
+ }