@webpieces/dev-config 0.0.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +306 -0
- package/bin/set-version.sh +86 -0
- package/bin/setup-claude-patterns.sh +51 -0
- package/bin/start.sh +107 -0
- package/bin/stop.sh +65 -0
- package/bin/use-local-webpieces.sh +89 -0
- package/bin/use-published-webpieces.sh +33 -0
- package/config/eslint/base.mjs +91 -0
- package/config/typescript/tsconfig.base.json +25 -0
- package/eslint-plugin/__tests__/catch-error-pattern.test.ts +360 -0
- package/eslint-plugin/__tests__/max-file-lines.test.ts +195 -0
- package/eslint-plugin/__tests__/max-method-lines.test.ts +246 -0
- package/eslint-plugin/index.d.ts +14 -0
- package/eslint-plugin/index.js +19 -0
- package/eslint-plugin/index.js.map +1 -0
- package/eslint-plugin/index.ts +18 -0
- package/eslint-plugin/rules/catch-error-pattern.d.ts +11 -0
- package/eslint-plugin/rules/catch-error-pattern.js +196 -0
- package/eslint-plugin/rules/catch-error-pattern.js.map +1 -0
- package/eslint-plugin/rules/catch-error-pattern.ts +281 -0
- package/eslint-plugin/rules/max-file-lines.d.ts +12 -0
- package/eslint-plugin/rules/max-file-lines.js +257 -0
- package/eslint-plugin/rules/max-file-lines.js.map +1 -0
- package/eslint-plugin/rules/max-file-lines.ts +272 -0
- package/eslint-plugin/rules/max-method-lines.d.ts +12 -0
- package/eslint-plugin/rules/max-method-lines.js +257 -0
- package/eslint-plugin/rules/max-method-lines.js.map +1 -0
- package/eslint-plugin/rules/max-method-lines.ts +304 -0
- package/package.json +54 -0
- package/patterns/CLAUDE.md +293 -0
- package/patterns/claude.patterns.md +798 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Switch back to published webpieces packages from npm
|
|
5
|
+
# This script removes local symlinks and reinstalls from npm
|
|
6
|
+
|
|
7
|
+
# Detect project root
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
|
|
10
|
+
if [[ "$SCRIPT_DIR" == *"node_modules/@webpieces/dev-config"* ]]; then
|
|
11
|
+
# Running in consumer project
|
|
12
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
|
13
|
+
else
|
|
14
|
+
# Running in webpieces-ts workspace
|
|
15
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
cd "$PROJECT_ROOT" || exit 1
|
|
19
|
+
|
|
20
|
+
echo "📦 Switching to published @webpieces packages..."
|
|
21
|
+
|
|
22
|
+
# Remove all @webpieces symlinks
|
|
23
|
+
echo " Removing local symlinks..."
|
|
24
|
+
rm -rf node_modules/@webpieces
|
|
25
|
+
|
|
26
|
+
# Reinstall from npm
|
|
27
|
+
echo " Reinstalling from npm..."
|
|
28
|
+
npm install
|
|
29
|
+
|
|
30
|
+
echo ""
|
|
31
|
+
echo "✅ Successfully switched to published @webpieces packages from npm"
|
|
32
|
+
echo " To switch back to local development, run: wp-use-local"
|
|
33
|
+
echo " (Make sure to set WEBPIECES_ROOT environment variable first)"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// @webpieces/dev-config base ESLint configuration
|
|
2
|
+
// Consumer projects can extend this configuration
|
|
3
|
+
|
|
4
|
+
import webpiecesPlugin from '../../eslint-plugin/index.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WebPieces base ESLint configuration using flat config format.
|
|
8
|
+
*
|
|
9
|
+
* This provides sensible defaults for TypeScript projects following
|
|
10
|
+
* WebPieces patterns and conventions.
|
|
11
|
+
*
|
|
12
|
+
* Includes custom WebPieces rules:
|
|
13
|
+
* - catch-error-pattern: Enforces toError() usage in catch blocks
|
|
14
|
+
*
|
|
15
|
+
* Usage in consumer projects:
|
|
16
|
+
*
|
|
17
|
+
* ```javascript
|
|
18
|
+
* // eslint.config.mjs
|
|
19
|
+
* import webpiecesConfig from '@webpieces/dev-config/eslint';
|
|
20
|
+
* import nx from '@nx/eslint-plugin';
|
|
21
|
+
*
|
|
22
|
+
* export default [
|
|
23
|
+
* ...webpiecesConfig,
|
|
24
|
+
* ...nx.configs['flat/typescript'],
|
|
25
|
+
* {
|
|
26
|
+
* // Project-specific overrides
|
|
27
|
+
* rules: {}
|
|
28
|
+
* }
|
|
29
|
+
* ];
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export default [
|
|
33
|
+
{
|
|
34
|
+
// Ignore common directories
|
|
35
|
+
ignores: [
|
|
36
|
+
'**/dist',
|
|
37
|
+
'**/out-tsc',
|
|
38
|
+
'**/tmp',
|
|
39
|
+
'**/coverage',
|
|
40
|
+
'**/node_modules',
|
|
41
|
+
'**/.nx',
|
|
42
|
+
'**/.vscode',
|
|
43
|
+
'**/.idea',
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
48
|
+
plugins: {
|
|
49
|
+
'@webpieces': webpiecesPlugin,
|
|
50
|
+
},
|
|
51
|
+
languageOptions: {
|
|
52
|
+
ecmaVersion: 2021,
|
|
53
|
+
sourceType: 'module',
|
|
54
|
+
},
|
|
55
|
+
rules: {
|
|
56
|
+
// WebPieces custom rules
|
|
57
|
+
'@webpieces/catch-error-pattern': 'error',
|
|
58
|
+
// General code quality
|
|
59
|
+
'no-console': 'off', // Allow console for logging
|
|
60
|
+
'no-debugger': 'warn',
|
|
61
|
+
'no-alert': 'warn',
|
|
62
|
+
'no-var': 'error',
|
|
63
|
+
'prefer-const': 'warn',
|
|
64
|
+
'prefer-arrow-callback': 'warn',
|
|
65
|
+
|
|
66
|
+
// TypeScript rules (when @typescript-eslint is available)
|
|
67
|
+
'@typescript-eslint/no-explicit-any': 'warn', // Prefer unknown over any
|
|
68
|
+
'@typescript-eslint/explicit-function-return-type': 'off', // Allow inference
|
|
69
|
+
'@typescript-eslint/no-unused-vars': [
|
|
70
|
+
'warn',
|
|
71
|
+
{
|
|
72
|
+
argsIgnorePattern: '^_',
|
|
73
|
+
varsIgnorePattern: '^_',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
'@typescript-eslint/no-empty-interface': 'off', // WebPieces uses classes for data
|
|
77
|
+
'@typescript-eslint/no-empty-function': 'off',
|
|
78
|
+
|
|
79
|
+
// Import organization
|
|
80
|
+
'sort-imports': 'off', // Handled by IDE
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
// Specific rules for test files
|
|
85
|
+
files: ['**/*.spec.ts', '**/*.test.ts'],
|
|
86
|
+
rules: {
|
|
87
|
+
'@typescript-eslint/no-explicit-any': 'off', // Allow any in tests
|
|
88
|
+
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compileOnSave": false,
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"sourceMap": true,
|
|
6
|
+
"inlineSources": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"emitDecoratorMetadata": true,
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"importHelpers": true,
|
|
12
|
+
"target": "ES2021",
|
|
13
|
+
"module": "commonjs",
|
|
14
|
+
"lib": ["ES2021"],
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"skipDefaultLibCheck": true,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"esModuleInterop": true,
|
|
19
|
+
"resolveJsonModule": true,
|
|
20
|
+
"forceConsistentCasingInFileNames": true,
|
|
21
|
+
"noImplicitReturns": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true
|
|
23
|
+
},
|
|
24
|
+
"exclude": ["node_modules", "tmp", "dist", "out-tsc", "**/*.spec.ts", "**/*.test.ts"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for catch-error-pattern ESLint rule
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint-disable @webpieces/catch-error-pattern */
|
|
6
|
+
import { RuleTester } from 'eslint';
|
|
7
|
+
import rule from '../rules/catch-error-pattern';
|
|
8
|
+
|
|
9
|
+
// Use require to load parser at runtime (avoids TypeScript import issues)
|
|
10
|
+
const tsParser = require('@typescript-eslint/parser');
|
|
11
|
+
|
|
12
|
+
const ruleTester = new RuleTester({
|
|
13
|
+
languageOptions: {
|
|
14
|
+
parser: tsParser,
|
|
15
|
+
parserOptions: {
|
|
16
|
+
ecmaVersion: 2020,
|
|
17
|
+
sourceType: 'module',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
ruleTester.run('catch-error-pattern', rule, {
|
|
23
|
+
valid: [
|
|
24
|
+
// Pattern 1: Standard toError usage
|
|
25
|
+
{
|
|
26
|
+
code: `try {
|
|
27
|
+
doSomething();
|
|
28
|
+
} catch (err: any) {
|
|
29
|
+
const error = toError(err);
|
|
30
|
+
}`,
|
|
31
|
+
},
|
|
32
|
+
// Pattern 1 with additional statements after toError
|
|
33
|
+
{
|
|
34
|
+
code: `try {
|
|
35
|
+
doSomething();
|
|
36
|
+
} catch (err: any) {
|
|
37
|
+
const error = toError(err);
|
|
38
|
+
console.log('Error occurred:', error);
|
|
39
|
+
throw error;
|
|
40
|
+
}`,
|
|
41
|
+
},
|
|
42
|
+
// Pattern 2: Explicitly ignored error
|
|
43
|
+
{
|
|
44
|
+
code: `try {
|
|
45
|
+
doSomething();
|
|
46
|
+
} catch (err: any) {
|
|
47
|
+
//const error = toError(err);
|
|
48
|
+
}`,
|
|
49
|
+
},
|
|
50
|
+
// Pattern 2 with extra whitespace
|
|
51
|
+
{
|
|
52
|
+
code: `try {
|
|
53
|
+
doSomething();
|
|
54
|
+
} catch (err: any) {
|
|
55
|
+
// const error = toError(err);
|
|
56
|
+
}`,
|
|
57
|
+
},
|
|
58
|
+
// Pattern 3: Nested catch blocks
|
|
59
|
+
{
|
|
60
|
+
code: `try {
|
|
61
|
+
doSomething();
|
|
62
|
+
} catch (err: any) {
|
|
63
|
+
const error = toError(err);
|
|
64
|
+
try {
|
|
65
|
+
cleanup();
|
|
66
|
+
} catch (err2: any) {
|
|
67
|
+
const error2 = toError(err2);
|
|
68
|
+
}
|
|
69
|
+
}`,
|
|
70
|
+
},
|
|
71
|
+
// Triple nested
|
|
72
|
+
{
|
|
73
|
+
code: `try {
|
|
74
|
+
operation1();
|
|
75
|
+
} catch (err: any) {
|
|
76
|
+
const error = toError(err);
|
|
77
|
+
try {
|
|
78
|
+
operation2();
|
|
79
|
+
} catch (err2: any) {
|
|
80
|
+
const error2 = toError(err2);
|
|
81
|
+
try {
|
|
82
|
+
operation3();
|
|
83
|
+
} catch (err3: any) {
|
|
84
|
+
const error3 = toError(err3);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}`,
|
|
88
|
+
},
|
|
89
|
+
// With finally block
|
|
90
|
+
{
|
|
91
|
+
code: `try {
|
|
92
|
+
doSomething();
|
|
93
|
+
} catch (err: any) {
|
|
94
|
+
const error = toError(err);
|
|
95
|
+
} finally {
|
|
96
|
+
cleanup();
|
|
97
|
+
}`,
|
|
98
|
+
},
|
|
99
|
+
// Re-throwing after toError
|
|
100
|
+
{
|
|
101
|
+
code: `try {
|
|
102
|
+
doSomething();
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
const error = toError(err);
|
|
105
|
+
logger.error(error);
|
|
106
|
+
throw error;
|
|
107
|
+
}`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
|
|
111
|
+
invalid: [
|
|
112
|
+
// Wrong parameter name (e instead of err)
|
|
113
|
+
{
|
|
114
|
+
code: `
|
|
115
|
+
try {
|
|
116
|
+
doSomething();
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
const error = toError(e);
|
|
119
|
+
}
|
|
120
|
+
`,
|
|
121
|
+
errors: [
|
|
122
|
+
{
|
|
123
|
+
messageId: 'wrongParameterName',
|
|
124
|
+
data: { actual: 'e' },
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
// Wrong parameter name (error instead of err) AND wrong variable name
|
|
129
|
+
{
|
|
130
|
+
code: `
|
|
131
|
+
try {
|
|
132
|
+
doSomething();
|
|
133
|
+
} catch (error: any) {
|
|
134
|
+
const error2 = toError(error);
|
|
135
|
+
}
|
|
136
|
+
`,
|
|
137
|
+
errors: [
|
|
138
|
+
{
|
|
139
|
+
messageId: 'wrongParameterName',
|
|
140
|
+
data: { actual: 'error' },
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
messageId: 'wrongVariableName',
|
|
144
|
+
data: { expected: 'error', actual: 'error2' },
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
// Missing type annotation
|
|
149
|
+
{
|
|
150
|
+
code: `
|
|
151
|
+
try {
|
|
152
|
+
doSomething();
|
|
153
|
+
} catch (err) {
|
|
154
|
+
const error = toError(err);
|
|
155
|
+
}
|
|
156
|
+
`,
|
|
157
|
+
errors: [
|
|
158
|
+
{
|
|
159
|
+
messageId: 'missingTypeAnnotation',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
// Wrong type annotation (Error instead of any)
|
|
164
|
+
{
|
|
165
|
+
code: `
|
|
166
|
+
try {
|
|
167
|
+
doSomething();
|
|
168
|
+
} catch (err: Error) {
|
|
169
|
+
const error = toError(err);
|
|
170
|
+
}
|
|
171
|
+
`,
|
|
172
|
+
errors: [
|
|
173
|
+
{
|
|
174
|
+
messageId: 'missingTypeAnnotation',
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
// Empty catch block
|
|
179
|
+
{
|
|
180
|
+
code: `
|
|
181
|
+
try {
|
|
182
|
+
doSomething();
|
|
183
|
+
} catch (err: any) {
|
|
184
|
+
}
|
|
185
|
+
`,
|
|
186
|
+
errors: [
|
|
187
|
+
{
|
|
188
|
+
messageId: 'missingToError',
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
// Missing toError call
|
|
193
|
+
{
|
|
194
|
+
code: `
|
|
195
|
+
try {
|
|
196
|
+
doSomething();
|
|
197
|
+
} catch (err: any) {
|
|
198
|
+
console.log(err);
|
|
199
|
+
}
|
|
200
|
+
`,
|
|
201
|
+
errors: [
|
|
202
|
+
{
|
|
203
|
+
messageId: 'missingToError',
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
// Wrong variable name (e instead of error)
|
|
208
|
+
{
|
|
209
|
+
code: `
|
|
210
|
+
try {
|
|
211
|
+
doSomething();
|
|
212
|
+
} catch (err: any) {
|
|
213
|
+
const e = toError(err);
|
|
214
|
+
}
|
|
215
|
+
`,
|
|
216
|
+
errors: [
|
|
217
|
+
{
|
|
218
|
+
messageId: 'wrongVariableName',
|
|
219
|
+
data: { expected: 'error', actual: 'e' },
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
},
|
|
223
|
+
// Wrong variable name (myError instead of error)
|
|
224
|
+
{
|
|
225
|
+
code: `
|
|
226
|
+
try {
|
|
227
|
+
doSomething();
|
|
228
|
+
} catch (err: any) {
|
|
229
|
+
const myError = toError(err);
|
|
230
|
+
}
|
|
231
|
+
`,
|
|
232
|
+
errors: [
|
|
233
|
+
{
|
|
234
|
+
messageId: 'wrongVariableName',
|
|
235
|
+
data: { expected: 'error', actual: 'myError' },
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
// toError not first statement
|
|
240
|
+
{
|
|
241
|
+
code: `
|
|
242
|
+
try {
|
|
243
|
+
doSomething();
|
|
244
|
+
} catch (err: any) {
|
|
245
|
+
console.log('caught error');
|
|
246
|
+
const error = toError(err);
|
|
247
|
+
}
|
|
248
|
+
`,
|
|
249
|
+
errors: [
|
|
250
|
+
{
|
|
251
|
+
messageId: 'missingToError',
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
// Using wrong function (not toError)
|
|
256
|
+
{
|
|
257
|
+
code: `
|
|
258
|
+
try {
|
|
259
|
+
doSomething();
|
|
260
|
+
} catch (err: any) {
|
|
261
|
+
const error = handleError(err);
|
|
262
|
+
}
|
|
263
|
+
`,
|
|
264
|
+
errors: [
|
|
265
|
+
{
|
|
266
|
+
messageId: 'missingToError',
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
// Nested: wrong parameter name for err2
|
|
271
|
+
{
|
|
272
|
+
code: `
|
|
273
|
+
try {
|
|
274
|
+
operation1();
|
|
275
|
+
} catch (err: any) {
|
|
276
|
+
const error = toError(err);
|
|
277
|
+
try {
|
|
278
|
+
operation2();
|
|
279
|
+
} catch (e: any) {
|
|
280
|
+
const error2 = toError(e);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
`,
|
|
284
|
+
errors: [
|
|
285
|
+
{
|
|
286
|
+
messageId: 'wrongParameterName',
|
|
287
|
+
data: { actual: 'e' },
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
// Nested: wrong variable name for error2
|
|
292
|
+
{
|
|
293
|
+
code: `
|
|
294
|
+
try {
|
|
295
|
+
operation1();
|
|
296
|
+
} catch (err: any) {
|
|
297
|
+
const error = toError(err);
|
|
298
|
+
try {
|
|
299
|
+
operation2();
|
|
300
|
+
} catch (err2: any) {
|
|
301
|
+
const err = toError(err2);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
`,
|
|
305
|
+
errors: [
|
|
306
|
+
{
|
|
307
|
+
messageId: 'wrongVariableName',
|
|
308
|
+
data: { expected: 'error2', actual: 'err' },
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
},
|
|
312
|
+
// No parameter at all
|
|
313
|
+
{
|
|
314
|
+
code: `
|
|
315
|
+
try {
|
|
316
|
+
doSomething();
|
|
317
|
+
} catch {
|
|
318
|
+
console.log('error');
|
|
319
|
+
}
|
|
320
|
+
`,
|
|
321
|
+
errors: [
|
|
322
|
+
{
|
|
323
|
+
messageId: 'missingTypeAnnotation',
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
// Variable declared but not initialized
|
|
328
|
+
{
|
|
329
|
+
code: `
|
|
330
|
+
try {
|
|
331
|
+
doSomething();
|
|
332
|
+
} catch (err: any) {
|
|
333
|
+
const error;
|
|
334
|
+
}
|
|
335
|
+
`,
|
|
336
|
+
errors: [
|
|
337
|
+
{
|
|
338
|
+
messageId: 'missingToError',
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
},
|
|
342
|
+
// Using new Error instead of toError
|
|
343
|
+
{
|
|
344
|
+
code: `
|
|
345
|
+
try {
|
|
346
|
+
doSomething();
|
|
347
|
+
} catch (err: any) {
|
|
348
|
+
const error = new Error(err.message);
|
|
349
|
+
}
|
|
350
|
+
`,
|
|
351
|
+
errors: [
|
|
352
|
+
{
|
|
353
|
+
messageId: 'missingToError',
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
console.log('✅ All catch-error-pattern rule tests passed!');
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for max-file-lines ESLint rule
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RuleTester } from 'eslint';
|
|
6
|
+
import rule from '../rules/max-file-lines';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
|
|
10
|
+
// Use require to load parser at runtime (avoids TypeScript import issues)
|
|
11
|
+
const tsParser = require('@typescript-eslint/parser');
|
|
12
|
+
|
|
13
|
+
const ruleTester = new RuleTester({
|
|
14
|
+
languageOptions: {
|
|
15
|
+
parser: tsParser,
|
|
16
|
+
parserOptions: {
|
|
17
|
+
ecmaVersion: 2020,
|
|
18
|
+
sourceType: 'module',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
ruleTester.run('max-file-lines', rule, {
|
|
24
|
+
valid: [
|
|
25
|
+
// Short file (well under limit)
|
|
26
|
+
{
|
|
27
|
+
code: `function shortFunc() {
|
|
28
|
+
return 42;
|
|
29
|
+
}`,
|
|
30
|
+
},
|
|
31
|
+
// File with exactly 700 lines (default limit)
|
|
32
|
+
{
|
|
33
|
+
code: Array(700)
|
|
34
|
+
.fill(0)
|
|
35
|
+
.map((_, i) => `const line${i} = ${i};`)
|
|
36
|
+
.join('\n'),
|
|
37
|
+
},
|
|
38
|
+
// File with 699 lines (just under default limit)
|
|
39
|
+
{
|
|
40
|
+
code: Array(699)
|
|
41
|
+
.fill(0)
|
|
42
|
+
.map((_, i) => `const line${i} = ${i};`)
|
|
43
|
+
.join('\n'),
|
|
44
|
+
},
|
|
45
|
+
// Custom limit: 10 lines
|
|
46
|
+
{
|
|
47
|
+
code: `function shortFunc() {
|
|
48
|
+
const a = 1;
|
|
49
|
+
const b = 2;
|
|
50
|
+
const c = 3;
|
|
51
|
+
const d = 4;
|
|
52
|
+
const e = 5;
|
|
53
|
+
return a + b + c + d + e;
|
|
54
|
+
}`,
|
|
55
|
+
options: [{ max: 10 }],
|
|
56
|
+
},
|
|
57
|
+
// Empty file
|
|
58
|
+
{
|
|
59
|
+
code: '',
|
|
60
|
+
},
|
|
61
|
+
// File with comments and blank lines (all count)
|
|
62
|
+
{
|
|
63
|
+
code: `// Comment line 1
|
|
64
|
+
// Comment line 2
|
|
65
|
+
|
|
66
|
+
function func() {
|
|
67
|
+
return 42;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Another comment`,
|
|
71
|
+
options: [{ max: 10 }],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
|
|
75
|
+
invalid: [
|
|
76
|
+
// File with 701 lines (exceeds default limit)
|
|
77
|
+
{
|
|
78
|
+
code: Array(701)
|
|
79
|
+
.fill(0)
|
|
80
|
+
.map((_, i) => `const line${i} = ${i};`)
|
|
81
|
+
.join('\n'),
|
|
82
|
+
errors: [
|
|
83
|
+
{
|
|
84
|
+
messageId: 'tooLong',
|
|
85
|
+
data: { actual: '701', max: '700' },
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
// File with 1000 lines (way over limit)
|
|
90
|
+
{
|
|
91
|
+
code: Array(1000)
|
|
92
|
+
.fill(0)
|
|
93
|
+
.map((_, i) => `const line${i} = ${i};`)
|
|
94
|
+
.join('\n'),
|
|
95
|
+
errors: [
|
|
96
|
+
{
|
|
97
|
+
messageId: 'tooLong',
|
|
98
|
+
data: { actual: '1000', max: '700' },
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
// Custom limit: exceed 5 lines
|
|
103
|
+
{
|
|
104
|
+
code: `function func() {
|
|
105
|
+
const a = 1;
|
|
106
|
+
const b = 2;
|
|
107
|
+
const c = 3;
|
|
108
|
+
const d = 4;
|
|
109
|
+
return a + b + c + d;
|
|
110
|
+
}`,
|
|
111
|
+
options: [{ max: 5 }],
|
|
112
|
+
errors: [
|
|
113
|
+
{
|
|
114
|
+
messageId: 'tooLong',
|
|
115
|
+
data: { actual: '7', max: '5' },
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
// Custom limit: exceed 100 lines
|
|
120
|
+
{
|
|
121
|
+
code: Array(101)
|
|
122
|
+
.fill(0)
|
|
123
|
+
.map((_, i) => `const line${i} = ${i};`)
|
|
124
|
+
.join('\n'),
|
|
125
|
+
options: [{ max: 100 }],
|
|
126
|
+
errors: [
|
|
127
|
+
{
|
|
128
|
+
messageId: 'tooLong',
|
|
129
|
+
data: { actual: '101', max: '100' },
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
// File with blank lines and comments (all lines count)
|
|
134
|
+
{
|
|
135
|
+
code: `// Line 1
|
|
136
|
+
// Line 2
|
|
137
|
+
// Line 3
|
|
138
|
+
|
|
139
|
+
function func() {
|
|
140
|
+
return 42;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Line 9
|
|
144
|
+
// Line 10
|
|
145
|
+
// Line 11`,
|
|
146
|
+
options: [{ max: 10 }],
|
|
147
|
+
errors: [
|
|
148
|
+
{
|
|
149
|
+
messageId: 'tooLong',
|
|
150
|
+
data: { actual: '11', max: '10' },
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
console.log('✅ All max-file-lines rule tests passed!');
|
|
158
|
+
|
|
159
|
+
// Test documentation file creation
|
|
160
|
+
const docPath = path.join(process.cwd(), 'tmp', 'webpieces', 'webpieces.filesize.md');
|
|
161
|
+
|
|
162
|
+
// Run a test that triggers violation
|
|
163
|
+
try {
|
|
164
|
+
ruleTester.run('max-file-lines-doc-test', rule, {
|
|
165
|
+
valid: [],
|
|
166
|
+
invalid: [
|
|
167
|
+
{
|
|
168
|
+
code: Array(800)
|
|
169
|
+
.fill(0)
|
|
170
|
+
.map((_, i) => `const line${i} = ${i};`)
|
|
171
|
+
.join('\n'),
|
|
172
|
+
errors: [{ messageId: 'tooLong' }],
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
} catch (err: any) {
|
|
177
|
+
//const error = toError(err);
|
|
178
|
+
// Test may fail, but file should be created
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Verify file was created
|
|
182
|
+
if (!fs.existsSync(docPath)) {
|
|
183
|
+
throw new Error('Documentation file was not created at ' + docPath);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Verify content has AI directive
|
|
187
|
+
const content = fs.readFileSync(docPath, 'utf-8');
|
|
188
|
+
if (!content.includes('READ THIS FILE to fix files that are too long')) {
|
|
189
|
+
throw new Error('Documentation file missing AI directive');
|
|
190
|
+
}
|
|
191
|
+
if (!content.includes('SINGLE COHESIVE UNIT')) {
|
|
192
|
+
throw new Error('Documentation file missing single cohesive unit principle');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('✅ Documentation file creation test passed!');
|