|
1 | 1 | 'use strict'; |
2 | 2 |
|
3 | | -const fs = require('fs'); |
4 | | -const path = require('path'); |
| 3 | +import { Rule } from 'eslint'; |
| 4 | +import fs from 'fs'; |
| 5 | +import path from 'path'; |
5 | 6 |
|
6 | | -function findDirWithFile(filename) { |
| 7 | +function findDirWithFile(filename: string) { |
7 | 8 | let dir = path.resolve(filename); |
8 | 9 |
|
9 | 10 | do { |
10 | 11 | dir = path.dirname(dir); |
11 | 12 | } while (!fs.existsSync(path.join(dir, filename)) && dir !== '/'); |
12 | 13 |
|
13 | 14 | if (!fs.existsSync(path.join(dir, filename))) { |
14 | | - return; |
| 15 | + return null; |
15 | 16 | } |
16 | 17 |
|
17 | 18 | return dir; |
18 | 19 | } |
19 | 20 |
|
20 | | -function findAlias(baseDir, importPath, filePath, ignoredPaths = []) { |
| 21 | +function findAlias( |
| 22 | + baseDir: string, |
| 23 | + importPath: string, |
| 24 | + filePath: string, |
| 25 | + ignoredPaths: string[] = [], |
| 26 | +) { |
21 | 27 | if (fs.existsSync(path.join(baseDir, 'tsconfig.json'))) { |
22 | 28 | const tsconfig = JSON.parse( |
23 | 29 | fs.readFileSync(path.join(baseDir, 'tsconfig.json')).toString('utf8'), |
24 | 30 | ); |
25 | 31 |
|
26 | | - const paths = tsconfig?.compilerOptions?.paths ?? {}; |
| 32 | + const paths: Record<string, string[]> = tsconfig?.compilerOptions?.paths ?? {}; |
27 | 33 | for (const [alias, aliasPaths] of Object.entries(paths)) { |
28 | 34 | // TODO: support full featured glob patterns instead of trivial cases like `@utils/*` and `src/utils/*` |
29 | 35 | const matchedPath = aliasPaths.find((dirPath) => { |
@@ -72,55 +78,58 @@ function findAlias(baseDir, importPath, filePath, ignoredPaths = []) { |
72 | 78 |
|
73 | 79 | // TODO: implement option to force relative path instead of alias (for remove alias case) |
74 | 80 | // TODO: add tests |
75 | | -// TODO: move to package |
76 | | -module.exports = { |
77 | | - 'proper-import-aliases': { |
78 | | - meta: { |
79 | | - fixable: true, |
80 | | - }, |
81 | | - create: function (context) { |
82 | | - const baseDir = findDirWithFile('package.json'); |
83 | | - |
84 | | - return { |
85 | | - ImportDeclaration(node) { |
86 | | - const [{ ignoredPaths = [] } = {}] = context.options; |
| 81 | +const rule: Rule.RuleModule = { |
| 82 | + meta: { |
| 83 | + fixable: 'code', |
| 84 | + }, |
| 85 | + create(context) { |
| 86 | + const baseDir = findDirWithFile('package.json'); |
| 87 | + |
| 88 | + if (!baseDir) throw new Error("Can't find base dir"); |
| 89 | + |
| 90 | + return { |
| 91 | + ImportDeclaration(node) { |
| 92 | + const [{ ignoredPaths = [] } = {}] = context.options as [ |
| 93 | + { ignoredPaths: string[] }, |
| 94 | + ]; |
| 95 | + |
| 96 | + const source = node.source.value; |
| 97 | + if (typeof source === 'string' && source.startsWith('.')) { |
| 98 | + const filename = context.getFilename(); |
87 | 99 |
|
88 | 100 | const resolvedIgnoredPaths = ignoredPaths.map((ignoredPath) => |
89 | 101 | path.normalize(path.join(path.dirname(filename), ignoredPath)), |
90 | 102 | ); |
91 | 103 |
|
92 | | - const source = node.source.value; |
93 | | - if (source.startsWith('.')) { |
94 | | - const filename = context.getFilename(); |
95 | | - |
96 | | - const absolutePath = path.normalize( |
97 | | - path.join(path.dirname(filename), source), |
98 | | - ); |
99 | | - |
100 | | - const replacement = findAlias( |
101 | | - baseDir, |
102 | | - absolutePath, |
103 | | - filename, |
104 | | - resolvedIgnoredPaths, |
105 | | - ); |
106 | | - |
107 | | - if (!replacement) return; |
108 | | - |
109 | | - context.report({ |
110 | | - node, |
111 | | - message: `Update import to ${replacement}`, |
112 | | - fix: function (fixer) { |
113 | | - // TODO: preserve quotes |
114 | | - const quote = `'`; |
115 | | - return fixer.replaceText( |
116 | | - node.source, |
117 | | - quote + replacement + quote, |
118 | | - ); |
119 | | - }, |
120 | | - }); |
121 | | - } |
122 | | - }, |
123 | | - }; |
124 | | - }, |
| 104 | + const absolutePath = path.normalize( |
| 105 | + path.join(path.dirname(filename), source), |
| 106 | + ); |
| 107 | + |
| 108 | + const replacement = findAlias( |
| 109 | + baseDir, |
| 110 | + absolutePath, |
| 111 | + filename, |
| 112 | + resolvedIgnoredPaths, |
| 113 | + ); |
| 114 | + |
| 115 | + if (!replacement) return; |
| 116 | + |
| 117 | + context.report({ |
| 118 | + node, |
| 119 | + message: `Update import to ${replacement}`, |
| 120 | + fix(fixer) { |
| 121 | + // TODO: preserve quotes |
| 122 | + const quote = `'`; |
| 123 | + return fixer.replaceText( |
| 124 | + node.source, |
| 125 | + quote + replacement + quote, |
| 126 | + ); |
| 127 | + }, |
| 128 | + }); |
| 129 | + } |
| 130 | + }, |
| 131 | + }; |
125 | 132 | }, |
126 | 133 | }; |
| 134 | + |
| 135 | +export default rule; |
0 commit comments