@anzusystems/common-admin 1.47.0-beta.dev-1771894836 → 1.47.0-beta.dev-1771894840

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/package.json CHANGED
@@ -2,7 +2,8 @@
2
2
  "name": "@anzusystems/common-admin",
3
3
  "packageManager": "yarn@4.13.0",
4
4
  "files": [
5
- "dist"
5
+ "dist",
6
+ "src/eslint"
6
7
  ],
7
8
  "module": "./dist/common-admin.es.js",
8
9
  "types": "./dist/common-admin.d.ts",
@@ -16,9 +17,10 @@
16
17
  "import": "./dist/labs.js",
17
18
  "types": "./dist/labs.d.ts"
18
19
  },
20
+ "./eslint": "./src/eslint/plugin.mjs",
19
21
  "./*": "./*"
20
22
  },
21
- "version": "1.47.0-beta.dev-1771894836",
23
+ "version": "1.47.0-beta.dev-1771894840",
22
24
  "license": "Apache-2.0",
23
25
  "repository": {
24
26
  "type": "git",
@@ -0,0 +1,406 @@
1
+ const DEFAULT_DEPRECATED_IMPORTS = [
2
+ 'AFilterWrapper',
3
+ 'AFilterBooleanSelect',
4
+ 'AFilterBooleanGroup',
5
+ 'AFilterDatetimePicker',
6
+ 'AFilterInteger',
7
+ 'AFilterRemoteAutocomplete',
8
+ 'AFilterRemoteAutocompleteWithMinimal',
9
+ 'AFilterString',
10
+ 'AFilterValueObjectOptionsSelect',
11
+ 'ADatatableOrdering',
12
+ 'ADatatablePagination',
13
+ 'AFormRemoteAutocomplete',
14
+ 'ASubjectSelect',
15
+ 'usePagination',
16
+ 'useFilterHelpers',
17
+ 'createDatatableColumnsConfig',
18
+ 'useSubjectSelect',
19
+ 'useApiQueryBuilder',
20
+ 'useJobApi',
21
+ 'Pagination',
22
+ 'makeFilterHelper',
23
+ 'apiFetchList',
24
+ 'FilterBag',
25
+ 'Filter',
26
+ 'apiFetchByIds',
27
+ 'apiAnyRequest',
28
+ 'apiCreateOne',
29
+ 'apiDeleteOne',
30
+ 'apiFetchOne',
31
+ 'apiUpdateOne',
32
+ ]
33
+
34
+ const DEFAULT_INTERNAL_DEPRECATED_IMPORTS = [
35
+ {
36
+ path: '@/services/api/apiFetchList',
37
+ imports: ['apiFetchList'],
38
+ },
39
+ {
40
+ path: '@/services/api/apiFetchListBatch',
41
+ imports: ['apiFetchListBatch'],
42
+ },
43
+ {
44
+ path: '@/composables/system/pagination',
45
+ imports: ['usePagination', 'Pagination'],
46
+ },
47
+ {
48
+ path: '@/composables/filter/filterHelpers',
49
+ imports: ['useFilterHelpers', 'makeFilterHelper'],
50
+ },
51
+ {
52
+ path: '@/composables/system/datatableColumns',
53
+ imports: ['createDatatableColumnsConfig'],
54
+ },
55
+ {
56
+ path: '@/components/subjectSelect/useSubjectSelect',
57
+ imports: ['useSubjectSelect'],
58
+ },
59
+ {
60
+ path: '@/services/api/queryBuilder',
61
+ imports: ['useApiQueryBuilder'],
62
+ },
63
+ {
64
+ path: '@/services/api/job/jobApi',
65
+ imports: ['useJobApi'],
66
+ },
67
+ {
68
+ path: '@/types/Filter',
69
+ imports: ['FilterBag', 'Filter'],
70
+ },
71
+ {
72
+ path: '@/components/filter/AFilterWrapper',
73
+ imports: ['AFilterWrapper'],
74
+ },
75
+ {
76
+ path: '@/components/filter/AFilterBooleanSelect',
77
+ imports: ['AFilterBooleanSelect'],
78
+ },
79
+ {
80
+ path: '@/components/filter/AFilterBooleanGroup',
81
+ imports: ['AFilterBooleanGroup'],
82
+ },
83
+ {
84
+ path: '@/components/filter/AFilterDatetimePicker',
85
+ imports: ['AFilterDatetimePicker'],
86
+ },
87
+ {
88
+ path: '@/components/filter/AFilterInteger',
89
+ imports: ['AFilterInteger'],
90
+ },
91
+ {
92
+ path: '@/components/filter/AFilterRemoteAutocomplete',
93
+ imports: ['AFilterRemoteAutocomplete'],
94
+ },
95
+ {
96
+ path: '@/components/filter/AFilterRemoteAutocompleteWithMinimal',
97
+ imports: ['AFilterRemoteAutocompleteWithMinimal'],
98
+ },
99
+ {
100
+ path: '@/components/filter/AFilterString',
101
+ imports: ['AFilterString'],
102
+ },
103
+ {
104
+ path: '@/components/filter/AFilterValueObjectOptionsSelect',
105
+ imports: ['AFilterValueObjectOptionsSelect'],
106
+ },
107
+ {
108
+ path: '@/components/ADatatableOrdering',
109
+ imports: ['ADatatableOrdering'],
110
+ },
111
+ {
112
+ path: '@/components/ADatatablePagination',
113
+ imports: ['ADatatablePagination'],
114
+ },
115
+ {
116
+ path: '@/components/form/AFormRemoteAutocomplete',
117
+ imports: ['AFormRemoteAutocomplete'],
118
+ },
119
+ {
120
+ path: '@/components/subjectSelect/ASubjectSelect',
121
+ imports: ['ASubjectSelect'],
122
+ },
123
+ ]
124
+
125
+ const anzuPlugin = {
126
+ rules: {
127
+ 'no-ts-extension': {
128
+ meta: {
129
+ type: 'problem',
130
+ docs: {
131
+ description: 'Disallow .ts extension in import statements',
132
+ },
133
+ fixable: 'code',
134
+ schema: [],
135
+ },
136
+ create(context) {
137
+ return {
138
+ ImportDeclaration(node) {
139
+ const source = node.source.value
140
+ if (typeof source === 'string' && source.endsWith('.ts')) {
141
+ context.report({
142
+ node,
143
+ message: 'Do not include .ts extension in import paths',
144
+ fix(fixer) {
145
+ const sourceText = node.source.raw
146
+ const newSource = sourceText.replace(/\.ts(['"])$/, '$1')
147
+ return fixer.replaceText(node.source, newSource)
148
+ },
149
+ })
150
+ }
151
+ },
152
+ }
153
+ },
154
+ },
155
+
156
+ 'no-deprecated-imports': {
157
+ meta: {
158
+ type: 'problem',
159
+ docs: {
160
+ description: 'Disallow usage of deprecated imports',
161
+ },
162
+ schema: [
163
+ {
164
+ type: 'object',
165
+ properties: {
166
+ rules: {
167
+ type: 'array',
168
+ items: {
169
+ type: 'object',
170
+ properties: {
171
+ path: { type: 'string' },
172
+ module: { type: 'string' },
173
+ imports: {
174
+ type: 'array',
175
+ items: { type: 'string' },
176
+ },
177
+ },
178
+ required: ['imports'],
179
+ additionalProperties: false,
180
+ },
181
+ },
182
+ skipFiles: {
183
+ type: 'array',
184
+ items: { type: 'string' },
185
+ },
186
+ },
187
+ additionalProperties: false,
188
+ },
189
+ ],
190
+ },
191
+ create(context) {
192
+ const options = context.options[0] || {}
193
+ const deprecationRules = options.rules || []
194
+ const skipFiles = options.skipFiles || []
195
+
196
+ // Collect source file paths from path-based rules for auto-skip
197
+ const ruleFilePaths = deprecationRules
198
+ .filter((rule) => rule.path)
199
+ .map((rule) => {
200
+ if (rule.path.startsWith('@/')) {
201
+ return rule.path.replace('@/', 'src/')
202
+ }
203
+ return rule.path
204
+ })
205
+
206
+ return {
207
+ ImportDeclaration(node) {
208
+ const filename = context.filename
209
+ const normalizedFilename = filename.replace(/\\/g, '/')
210
+
211
+ // Check manual skip list
212
+ if (skipFiles.some((skip) => normalizedFilename.endsWith(skip))) return
213
+
214
+ // Auto-skip source files of path-based rules
215
+ if (
216
+ ruleFilePaths.some(
217
+ (rulePath) =>
218
+ normalizedFilename.endsWith(rulePath + '.ts') ||
219
+ normalizedFilename.endsWith(rulePath + '.js') ||
220
+ normalizedFilename.endsWith(rulePath + '.vue') ||
221
+ normalizedFilename.endsWith(rulePath)
222
+ )
223
+ ) return
224
+
225
+ const source = node.source.value
226
+ if (typeof source !== 'string') return
227
+
228
+ for (const rule of deprecationRules) {
229
+ const matchPath = rule.path || rule.module
230
+ if (!matchPath || source !== matchPath) continue
231
+
232
+ const deprecatedImports = node.specifiers
233
+ .filter((spec) => spec.type === 'ImportSpecifier')
234
+ .filter((spec) => rule.imports.includes(spec.imported.name))
235
+
236
+ for (const importSpec of deprecatedImports) {
237
+ context.report({
238
+ node: importSpec,
239
+ message: `'${importSpec.imported.name}' from '${matchPath}' is deprecated`,
240
+ })
241
+ }
242
+ }
243
+ },
244
+ }
245
+ },
246
+ },
247
+
248
+ 'no-fatal-error-axios-check': {
249
+ meta: {
250
+ type: 'problem',
251
+ docs: {
252
+ description:
253
+ 'Disallow isAnzuFatalError + axios.isAxiosError(error.cause) pattern.' +
254
+ ' Labs API throws AnzuApiAxiosError instead.',
255
+ },
256
+ schema: [],
257
+ },
258
+ create(context) {
259
+ return {
260
+ LogicalExpression(node) {
261
+ if (node.operator !== '&&') return
262
+ if (node.parent.type === 'LogicalExpression' && node.parent.operator === '&&') return
263
+
264
+ const parts = []
265
+ let current = node
266
+ while (current.type === 'LogicalExpression' && current.operator === '&&') {
267
+ parts.unshift(current.right)
268
+ current = current.left
269
+ }
270
+ parts.unshift(current)
271
+
272
+ const hasFatalCheck = parts.some(
273
+ (part) => part.type === 'CallExpression' && part.callee.name === 'isAnzuFatalError'
274
+ )
275
+ const hasAxiosCheck = parts.some(
276
+ (part) =>
277
+ part.type === 'CallExpression' &&
278
+ part.callee.type === 'MemberExpression' &&
279
+ part.callee.object.name === 'axios' &&
280
+ part.callee.property.name === 'isAxiosError'
281
+ )
282
+
283
+ if (hasFatalCheck && hasAxiosCheck) {
284
+ context.report({
285
+ node,
286
+ message:
287
+ 'Replace isAnzuFatalError(error) && axios.isAxiosError(error.cause)' +
288
+ ' with isAnzuApiAxiosError(error).' +
289
+ ' Labs API throws AnzuApiAxiosError with typed AxiosError cause.',
290
+ })
291
+ }
292
+ },
293
+ }
294
+ },
295
+ },
296
+ },
297
+ }
298
+
299
+ /**
300
+ * Creates an ESLint flat config entry for Anzu rules.
301
+ *
302
+ * @param {Object} [options]
303
+ * @param {boolean|'error'|'warn'|'off'} [options.noTsExtension='error'] - Severity for no-ts-extension rule.
304
+ * @param {boolean|'error'|'warn'|'off'} [options.noFatalErrorAxiosCheck='error']
305
+ * - Severity for no-fatal-error-axios-check rule.
306
+ * @param {boolean|'error'|'warn'|'off'|Object} [options.deprecatedImports='error'] - Severity or config object.
307
+ * @param {string[]} [options.deprecatedImports.exclude] - Import names to remove from the default list.
308
+ * @param {string[]} [options.deprecatedImports.include] - Additional import names to add to the default list.
309
+ * @param {Array} [options.deprecatedImports.extraRules]
310
+ * - Additional rule entries ({ path, imports } or { module, imports }).
311
+ * @param {string[]} [options.deprecatedImports.skipFiles] - Files to skip (matched by suffix).
312
+ * @param {'error'|'warn'} [options.deprecatedImports.severity='error'] - Severity level.
313
+ * @param {'consumer'|'internal'} [options.deprecatedImports.mode='consumer'] - 'consumer' uses module-based defaults,
314
+ * 'internal' uses path-based defaults for common-admin development.
315
+ * @returns {Object} ESLint flat config entry
316
+ */
317
+ export function recommended(options = {}) {
318
+ const {
319
+ noTsExtension = 'error',
320
+ noFatalErrorAxiosCheck = 'error',
321
+ deprecatedImports = 'error',
322
+ } = options
323
+
324
+ const rules = {}
325
+
326
+ // no-ts-extension
327
+ const tsExtSeverity = normalizeSeverity(noTsExtension)
328
+ if (tsExtSeverity) {
329
+ rules['anzu/no-ts-extension'] = tsExtSeverity
330
+ }
331
+
332
+ // no-fatal-error-axios-check
333
+ const fatalSeverity = normalizeSeverity(noFatalErrorAxiosCheck)
334
+ if (fatalSeverity) {
335
+ rules['anzu/no-fatal-error-axios-check'] = fatalSeverity
336
+ }
337
+
338
+ // no-deprecated-imports
339
+ if (deprecatedImports !== false && deprecatedImports !== 'off') {
340
+ let severity = 'error'
341
+ const ruleEntries = []
342
+ let skipFiles = []
343
+
344
+ if (typeof deprecatedImports === 'object') {
345
+ severity = deprecatedImports.severity || 'error'
346
+ const mode = deprecatedImports.mode || 'consumer'
347
+
348
+ if (mode === 'internal') {
349
+ // Internal mode: path-based rules for common-admin development
350
+ ruleEntries.push(...DEFAULT_INTERNAL_DEPRECATED_IMPORTS)
351
+ } else {
352
+ // Consumer mode: module-based rules for projects using common-admin
353
+ let importsList = [...DEFAULT_DEPRECATED_IMPORTS]
354
+ if (deprecatedImports.exclude) {
355
+ importsList = importsList.filter((name) => !deprecatedImports.exclude.includes(name))
356
+ }
357
+ if (deprecatedImports.include) {
358
+ importsList.push(...deprecatedImports.include)
359
+ }
360
+ ruleEntries.push({
361
+ module: '@anzusystems/common-admin',
362
+ imports: importsList,
363
+ })
364
+ }
365
+
366
+ if (deprecatedImports.extraRules) {
367
+ ruleEntries.push(...deprecatedImports.extraRules)
368
+ }
369
+ if (deprecatedImports.skipFiles) {
370
+ skipFiles = deprecatedImports.skipFiles
371
+ }
372
+ } else {
373
+ if (deprecatedImports === 'warn') {
374
+ severity = 'warn'
375
+ }
376
+ // Default consumer mode
377
+ ruleEntries.push({
378
+ module: '@anzusystems/common-admin',
379
+ imports: [...DEFAULT_DEPRECATED_IMPORTS],
380
+ })
381
+ }
382
+
383
+ const ruleConfig = { rules: ruleEntries }
384
+ if (skipFiles.length > 0) {
385
+ ruleConfig.skipFiles = skipFiles
386
+ }
387
+
388
+ rules['anzu/no-deprecated-imports'] = [severity, ruleConfig]
389
+ }
390
+
391
+ return {
392
+ plugins: {
393
+ anzu: anzuPlugin,
394
+ },
395
+ rules,
396
+ }
397
+ }
398
+
399
+ function normalizeSeverity(value) {
400
+ if (value === false || value === 'off') return null
401
+ if (value === true || value === 'error') return 'error'
402
+ if (value === 'warn') return 'warn'
403
+ return 'error'
404
+ }
405
+
406
+ export { anzuPlugin, DEFAULT_DEPRECATED_IMPORTS, DEFAULT_INTERNAL_DEPRECATED_IMPORTS }