@scayle/eslint-auto-explicit-import 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/package.json +10 -12
- package/dist/eslint-plugin-unimport.mjs +0 -392
- package/dist/module.d.mts +0 -7
- package/dist/module.json +0 -9
- package/dist/module.mjs +0 -46
- package/dist/types.d.mts +0 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @scayle/storefront-eslint-auto-explicit-import
|
|
2
2
|
|
|
3
|
+
> **Deprecated.** This package is deprecated. Remove @scayle/eslint-auto-explicit-import from your Nuxt config and dependencies.
|
|
4
|
+
|
|
3
5
|
An extension of the official `@nuxt/eslint` module to insert more explicit import statement
|
|
4
6
|
automatically using `eslint --fix` into a Nuxt-based SCAYLE Storefront project.
|
|
5
7
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scayle/eslint-auto-explicit-import",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Automatically add explicit imports to ESLint",
|
|
5
|
+
"deprecated": "This package is deprecated and will be removed in a future release. Remove it from your dependencies and Nuxt config.",
|
|
5
6
|
"author": "SCAYLE Commerce Engine",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"publishConfig": {
|
|
@@ -36,15 +37,14 @@
|
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@nuxt/eslint": "1.14.0",
|
|
39
|
-
"@nuxt/kit": "^3.
|
|
40
|
-
"@
|
|
41
|
-
"@typescript-eslint/
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"@scayle/eslint-config-storefront": "4.8.0"
|
|
40
|
+
"@nuxt/kit": "^3.21.7",
|
|
41
|
+
"@scayle/eslint-config-storefront": "^4.8.0",
|
|
42
|
+
"@typescript-eslint/scope-manager": "8.60.0",
|
|
43
|
+
"@typescript-eslint/utils": "8.60.0",
|
|
44
|
+
"@nuxt/module-builder": "1.0.2",
|
|
45
|
+
"eslint": "10.7.0",
|
|
46
|
+
"typescript": "6.0.3",
|
|
47
|
+
"unbuild": "3.6.1"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">= 22.0.0"
|
|
@@ -56,8 +56,6 @@
|
|
|
56
56
|
"build": "nuxt-module-build build",
|
|
57
57
|
"lint": "eslint .",
|
|
58
58
|
"lint:ci": "eslint . --format gitlab",
|
|
59
|
-
"format": "dprint check",
|
|
60
|
-
"format:fix": "dprint fmt",
|
|
61
59
|
"typecheck": "tsc"
|
|
62
60
|
}
|
|
63
61
|
}
|
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
import { dirname, isAbsolute, relative } from 'pathe'
|
|
2
|
-
import { ESLintUtils } from '@typescript-eslint/utils'
|
|
3
|
-
import { analyze } from '@typescript-eslint/scope-manager'
|
|
4
|
-
import Debug from 'debug'
|
|
5
|
-
|
|
6
|
-
// Based on <https://github.com/antfu/eslint-plugin-unimport>
|
|
7
|
-
|
|
8
|
-
const version = '0.0.3'
|
|
9
|
-
|
|
10
|
-
const createRule = ESLintUtils.RuleCreator(
|
|
11
|
-
() => 'https://github.com/antfu/eslint-plugin-unimport',
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
function betterRelative(from, to) {
|
|
15
|
-
// eslint-disable-next-line sonarjs/single-char-in-character-classes
|
|
16
|
-
const r = relative(from, to).replace(/\.[\w]+/g, '')
|
|
17
|
-
|
|
18
|
-
if (r.startsWith('../')) {
|
|
19
|
-
return r
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return `./${r}`
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const debug = Debug('unimport:eslint')
|
|
26
|
-
|
|
27
|
-
function createImportsListeners(context, imports, onImportEntry) {
|
|
28
|
-
let _scopeManager
|
|
29
|
-
let _importsMap
|
|
30
|
-
const importedNames = /* @__PURE__ */ new Set()
|
|
31
|
-
|
|
32
|
-
function getScopeManager() {
|
|
33
|
-
if (!_scopeManager) {
|
|
34
|
-
_scopeManager = analyze(context.sourceCode.ast, {
|
|
35
|
-
sourceType: 'module',
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
_scopeManager.globalScope?.variables.forEach((node) => {
|
|
39
|
-
importedNames.add(node.name)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return _scopeManager
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getImportsMap() {
|
|
47
|
-
if (!_importsMap) {
|
|
48
|
-
_importsMap = /* @__PURE__ */ new Map()
|
|
49
|
-
imports.forEach((i) => {
|
|
50
|
-
_importsMap.set(i.as || i.name, i)
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return _importsMap
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
58
|
-
function checkId(node) {
|
|
59
|
-
if (typeof node.name !== 'string') {
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (importedNames.has(node.name)) {
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const importsMap = getImportsMap()
|
|
68
|
-
const item = importsMap.get(node.name)
|
|
69
|
-
|
|
70
|
-
if (!item) {
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (item.from === context.filename) {
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const scopeManager = getScopeManager()
|
|
79
|
-
|
|
80
|
-
if (importedNames.has(node.name)) {
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let parent = node.parent
|
|
85
|
-
let currentScope = null
|
|
86
|
-
|
|
87
|
-
while (parent && !currentScope) {
|
|
88
|
-
currentScope = scopeManager.acquire(parent)
|
|
89
|
-
|
|
90
|
-
if (currentScope) {
|
|
91
|
-
break
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
parent = parent.parent
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (!currentScope) {
|
|
98
|
-
currentScope = scopeManager.globalScope
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const visited = /* @__PURE__ */ new Set()
|
|
102
|
-
|
|
103
|
-
while (true) {
|
|
104
|
-
if (!currentScope || visited.has(currentScope)) {
|
|
105
|
-
break
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (const ref of currentScope.variables) {
|
|
109
|
-
if (ref.name === node.name) {
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
visited.add(currentScope)
|
|
115
|
-
currentScope = currentScope.upper
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
importedNames.add(node.name)
|
|
119
|
-
onImportEntry(node, item)
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
Identifier(node) {
|
|
123
|
-
if (/Declaration|Specifier|Property/.test(node.parent.type)) {
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
node.parent.type === 'MemberExpression' &&
|
|
129
|
-
node.parent.object !== node
|
|
130
|
-
) {
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
checkId(node)
|
|
135
|
-
},
|
|
136
|
-
ImportDeclaration(node) {
|
|
137
|
-
node.specifiers.forEach((s) => {
|
|
138
|
-
importedNames.add(s.local.name)
|
|
139
|
-
})
|
|
140
|
-
},
|
|
141
|
-
'Program:exit': function() {
|
|
142
|
-
const vueTemplate = context.sourceCode.ast.templateBody
|
|
143
|
-
|
|
144
|
-
if (!vueTemplate) {
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function visit(node) {
|
|
149
|
-
if (!node) {
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const expressionNode = node
|
|
154
|
-
|
|
155
|
-
switch (expressionNode.type) {
|
|
156
|
-
case 'Identifier':
|
|
157
|
-
checkId(expressionNode)
|
|
158
|
-
return
|
|
159
|
-
case 'MemberExpression':
|
|
160
|
-
visit(expressionNode.object)
|
|
161
|
-
return
|
|
162
|
-
case 'CallExpression':
|
|
163
|
-
visit(expressionNode.callee)
|
|
164
|
-
for (const arg of expressionNode.arguments) {
|
|
165
|
-
visit(arg)
|
|
166
|
-
}
|
|
167
|
-
return
|
|
168
|
-
case 'ConditionalExpression':
|
|
169
|
-
visit(expressionNode.test)
|
|
170
|
-
visit(expressionNode.consequent)
|
|
171
|
-
visit(expressionNode.alternate)
|
|
172
|
-
return
|
|
173
|
-
case 'FunctionExpression':
|
|
174
|
-
case 'ArrowFunctionExpression':
|
|
175
|
-
for (const param of expressionNode.params) {
|
|
176
|
-
visit(param)
|
|
177
|
-
}
|
|
178
|
-
visit(expressionNode.body)
|
|
179
|
-
return
|
|
180
|
-
case 'LogicalExpression':
|
|
181
|
-
case 'BinaryExpression':
|
|
182
|
-
visit(expressionNode.left)
|
|
183
|
-
visit(expressionNode.right)
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
switch (node.type) {
|
|
188
|
-
case 'VText':
|
|
189
|
-
return
|
|
190
|
-
case 'VExpressionContainer':
|
|
191
|
-
return visit(node.expression)
|
|
192
|
-
case 'VElement':
|
|
193
|
-
for (const attr of node.startTag.attributes) {
|
|
194
|
-
visit(attr)
|
|
195
|
-
}
|
|
196
|
-
for (const child of node.children) {
|
|
197
|
-
visit(child)
|
|
198
|
-
}
|
|
199
|
-
return
|
|
200
|
-
case 'VAttribute':
|
|
201
|
-
visit(node.value)
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if ('children' in node) {
|
|
206
|
-
for (const child of node.children) {
|
|
207
|
-
visit(child)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if ('body' in node) {
|
|
214
|
-
visit(node.body)
|
|
215
|
-
|
|
216
|
-
return
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
{
|
|
220
|
-
const { tokens, parent, range, loc, ...rest } = node
|
|
221
|
-
debug('Unknown VNode', rest)
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
visit(vueTemplate)
|
|
226
|
-
},
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const importPathDirNames = [
|
|
231
|
-
'/node_modules/',
|
|
232
|
-
'/packages/storefront-nuxt/',
|
|
233
|
-
'/packages/omnichannel-nuxt/', // Package needs to export appropriate `#omnichannel/composables` alias first
|
|
234
|
-
]
|
|
235
|
-
|
|
236
|
-
const validDirNames = {
|
|
237
|
-
storefrontNuxt: ['@scayle/storefront-nuxt', '/packages/storefront-nuxt'],
|
|
238
|
-
omnichannelNuxt: ['@scayle/omnichannel-nuxt', '/packages/omnichannel-nuxt'],
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Mapping normalized import paths of packages to pure package imports
|
|
242
|
-
// to avoid "Nuxt Build Error: [vite:load-fallback] Could not load (...)" if
|
|
243
|
-
// path can not properly be resolved within package dist directory.
|
|
244
|
-
const importsMap = {
|
|
245
|
-
'vue-i18n/dist/vue-i18n': 'vue-i18n',
|
|
246
|
-
'@nuxtjs/i18n/dist/runtime/composables/index': '#i18n', // https://github.com/nuxt-modules/i18n/blob/main/package.json#L34C6-L34C11
|
|
247
|
-
'nuxt/dist/pages/runtime/composables': '#imports', // Needs to be imported through #imports to properly work with TypeScript in the Nuxt context
|
|
248
|
-
'nuxt-jsonld/dist/runtime/composable': '#imports', // Needs to be imported through #imports to properly work with TypeScript in the Nuxt context
|
|
249
|
-
'@nuxt/image/dist/runtime/composables': '#imports', // Needs to be imported through #imports to properly work with TypeScript in the Nuxt context
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const transformPathToImport = (itemFrom, dirName) => {
|
|
253
|
-
const partialPath = itemFrom.split(dirName)[1]
|
|
254
|
-
|
|
255
|
-
// We check if we're trying to import composables from `@scayle/storefront-nuxt`
|
|
256
|
-
// or `@scayle/omnichannel-nuxt` and transform the import to their respective
|
|
257
|
-
// allowed import path or alias.
|
|
258
|
-
// NOTE: If a Nuxt project is part of a mono-repo setup and some of its used packages
|
|
259
|
-
// are located within the root repo, the package resolution using `mlly` will not
|
|
260
|
-
// resolve to a node_modules packages, but to the local package within the repository.
|
|
261
|
-
// https://github.com/unjs/mlly/issues/158
|
|
262
|
-
// For this reason we include a workaround that is able to check if we're within the
|
|
263
|
-
// mono-repo setup and transform the import paths appropriately.
|
|
264
|
-
if (
|
|
265
|
-
validDirNames.storefrontNuxt.some((element) => itemFrom.includes(element))
|
|
266
|
-
) {
|
|
267
|
-
return '#storefront/composables'
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (
|
|
271
|
-
validDirNames.omnichannelNuxt.some((element) => itemFrom.includes(element))
|
|
272
|
-
) {
|
|
273
|
-
return '#omnichannel/composables'
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// `nuxt-module-builder@0.7.0` changed the file extension for files within
|
|
277
|
-
// the build `/dist/runtime/` directory from `.mjs` to `.js`.
|
|
278
|
-
// https://github.com/nuxt/module-builder/commit/dbd05bb
|
|
279
|
-
const normalizedImportPath = partialPath.replace(/mjs|js/gi, '')
|
|
280
|
-
|
|
281
|
-
return importsMap[normalizedImportPath] ?? normalizedImportPath
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const createImportPaths = (item, context, srcAlias = '~') => {
|
|
285
|
-
const relativePath = betterRelative(
|
|
286
|
-
dirname(context.physicalFilename),
|
|
287
|
-
item.from,
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
if (!isAbsolute(item.from)) {
|
|
291
|
-
return item.from
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// We're checking if the item.from path contains one of the relevant directory names
|
|
295
|
-
// that we want to have transformed to a "plain" package import or alias import instead.
|
|
296
|
-
const dirName = importPathDirNames.find((name) => relativePath.includes(name))
|
|
297
|
-
|
|
298
|
-
if (dirName) {
|
|
299
|
-
return transformPathToImport(relativePath, dirName)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return `${srcAlias}/${relativePath.replaceAll('../', '')}`
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const autoInsert = createRule({
|
|
306
|
-
name: 'auto-insert',
|
|
307
|
-
meta: {
|
|
308
|
-
type: 'problem',
|
|
309
|
-
docs: {
|
|
310
|
-
description: 'Auto insert missing imports from unimport registry',
|
|
311
|
-
},
|
|
312
|
-
messages: {
|
|
313
|
-
missingImport:
|
|
314
|
-
`Unimport entry '{{name}}' from '{{from}}' is not imported.`,
|
|
315
|
-
},
|
|
316
|
-
schema: [{ type: 'array', items: { type: 'any' } }],
|
|
317
|
-
fixable: 'code',
|
|
318
|
-
},
|
|
319
|
-
defaultOptions: [],
|
|
320
|
-
create(context) {
|
|
321
|
-
return createImportsListeners(
|
|
322
|
-
context,
|
|
323
|
-
context.options[0] || [],
|
|
324
|
-
(node, item) => {
|
|
325
|
-
context.report({
|
|
326
|
-
node,
|
|
327
|
-
messageId: 'missingImport',
|
|
328
|
-
data: {
|
|
329
|
-
name: item.name,
|
|
330
|
-
from: createImportPaths(item, context),
|
|
331
|
-
},
|
|
332
|
-
fix(fixer) {
|
|
333
|
-
const resolvedFrom = createImportPaths(item, context)
|
|
334
|
-
const body = context.sourceCode.ast.body
|
|
335
|
-
|
|
336
|
-
let importName = ''
|
|
337
|
-
|
|
338
|
-
if (item.name === '*') {
|
|
339
|
-
importName = `* as ${item.as}`
|
|
340
|
-
} else if (item.name === 'default') {
|
|
341
|
-
// Default exports should be imported without curly braces
|
|
342
|
-
importName = item.as || item.name
|
|
343
|
-
} else if (!item.as || item.name === item.as) {
|
|
344
|
-
// Named exports should be imported with curly braces
|
|
345
|
-
importName = `{ ${item.name} }`
|
|
346
|
-
} else {
|
|
347
|
-
// Named exports with aliases should be imported with curly braces
|
|
348
|
-
importName = `{ ${item.name} as ${item.as} }`
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return fixer.insertTextBefore(
|
|
352
|
-
body[0],
|
|
353
|
-
`import ${importName} from '${resolvedFrom}'
|
|
354
|
-
`,
|
|
355
|
-
)
|
|
356
|
-
},
|
|
357
|
-
})
|
|
358
|
-
},
|
|
359
|
-
)
|
|
360
|
-
},
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
const plugin = {
|
|
364
|
-
meta: {
|
|
365
|
-
name: 'unimport',
|
|
366
|
-
version,
|
|
367
|
-
},
|
|
368
|
-
rules: {
|
|
369
|
-
'auto-insert': autoInsert,
|
|
370
|
-
},
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function createAutoInsert(options) {
|
|
374
|
-
return {
|
|
375
|
-
name: 'unimport:auto-insert',
|
|
376
|
-
plugins: {
|
|
377
|
-
unimport: plugin,
|
|
378
|
-
},
|
|
379
|
-
files: options.include ?? [
|
|
380
|
-
'**/*.ts',
|
|
381
|
-
'**/*.(c|m)js',
|
|
382
|
-
'**/*.(m|c)?tsx?',
|
|
383
|
-
'**/*.vue',
|
|
384
|
-
],
|
|
385
|
-
ignores: options.exclude ?? ['**/*.mdx?/**'],
|
|
386
|
-
rules: {
|
|
387
|
-
'unimport/auto-insert': ['error', options.imports],
|
|
388
|
-
},
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
export { createAutoInsert, plugin as default }
|
package/dist/module.d.mts
DELETED
package/dist/module.json
DELETED
package/dist/module.mjs
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, logger } from '@nuxt/kit';
|
|
2
|
-
|
|
3
|
-
const module$1 = defineNuxtModule({
|
|
4
|
-
meta: {
|
|
5
|
-
name: "storefront-eslint-auto-explicit-import"
|
|
6
|
-
},
|
|
7
|
-
setup(_, nuxt) {
|
|
8
|
-
const { resolve } = createResolver(import.meta.url);
|
|
9
|
-
let unimport;
|
|
10
|
-
nuxt.hook("imports:context", (ctx) => {
|
|
11
|
-
unimport = ctx;
|
|
12
|
-
});
|
|
13
|
-
nuxt.hook("eslint:config:addons", (addons) => {
|
|
14
|
-
addons.push({
|
|
15
|
-
name: "storefront-eslint-auto-explicit-import",
|
|
16
|
-
async getConfigs() {
|
|
17
|
-
if (!unimport) {
|
|
18
|
-
logger.warn(
|
|
19
|
-
"unimport is not ready for storefront-eslint-auto-explicit-import"
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
imports: [
|
|
24
|
-
{
|
|
25
|
-
from: resolve("./eslint-plugin-unimport.mjs"),
|
|
26
|
-
name: "createAutoInsert"
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
|
-
configs: [
|
|
30
|
-
[
|
|
31
|
-
"// storefront-eslint-auto-explicit-import",
|
|
32
|
-
"createAutoInsert({",
|
|
33
|
-
` imports: ${JSON.stringify(
|
|
34
|
-
await unimport?.getImports() || []
|
|
35
|
-
)}`,
|
|
36
|
-
"})"
|
|
37
|
-
].join("\n")
|
|
38
|
-
]
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export { module$1 as default };
|
package/dist/types.d.mts
DELETED