-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
110 lines (95 loc) · 2.98 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import fs from "fs";
import path from "path";
import JSON5 from "json5";
import { Rule } from "eslint";
function has(map: Record<string, any>, path: string) {
let inner = map;
for (let step of path.split(".")) {
inner = inner[step];
if (inner === undefined) {
return false;
}
}
return true;
}
function findDirWithFile(filename: string) {
let dir = path.resolve(filename);
do {
dir = path.dirname(dir);
} while (!fs.existsSync(path.join(dir, filename)) && dir !== "/");
if (!fs.existsSync(path.join(dir, filename))) {
throw new Error(`Could not find ${filename} in any parent directory`);
}
return dir;
}
function getBaseUrl(baseDir: string) {
let url = "";
if (fs.existsSync(path.join(baseDir, "tsconfig.json"))) {
const tsconfig = JSON5.parse(
fs.readFileSync(path.join(baseDir, "tsconfig.json"), 'utf8')
);
if (has(tsconfig, "compilerOptions.baseUrl")) {
url = tsconfig.compilerOptions.baseUrl;
}
} else if (fs.existsSync(path.join(baseDir, "jsconfig.json"))) {
const jsconfig = JSON5.parse(
fs.readFileSync(path.join(baseDir, "jsconfig.json"), 'utf8')
);
if (has(jsconfig, "compilerOptions.baseUrl")) {
url = jsconfig.compilerOptions.baseUrl;
}
}
return path.join(baseDir, url);
}
function normalizeFilename(filename: string) {
if (filename.endsWith('.js') || filename.endsWith('.ts')) {
filename = filename.slice(0, -3);
}
if (filename.endsWith('.tsx') || filename.endsWith('.jsx')) {
filename = filename.slice(0, -4);
}
if (filename.endsWith('/index')) {
filename = filename.slice(0, -6);
}
return filename;
}
const preferTsPaths: Rule.RuleModule = {
meta: {
fixable: "code",
},
create: function (context) {
const baseDir = findDirWithFile("tsconfig.json");
const paths = JSON5.parse(fs.readFileSync(path.join(baseDir, 'tsconfig.json'), 'utf-8'))?.compilerOptions.paths ?? {};
const baseUrl = getBaseUrl(baseDir);
const reversePaths = Object.entries(paths as Record<string, string[]>).reduce((acc, [key, value]) => {
value?.forEach((v) => {
acc[normalizeFilename(v)] = key;
});
return acc;
} , {} as Record<string, string>);
return {
ImportDeclaration(node) {
const source = node.source.value as string;
if (source?.startsWith(".")) {
const filename = context.getFilename();
const absolutePath = path.normalize(
path.join(path.dirname(filename), source)
);
const expectedPath = path.relative(baseUrl, absolutePath);
if (reversePaths[normalizeFilename(expectedPath)]) {
context.report({
node,
message: `Import should be absolute`,
fix(fixer) {
return fixer.replaceText(node.source, `'${reversePaths[normalizeFilename(expectedPath)]}'`);
},
});
}
}
},
}
}
}
module.exports.rules = {
"prefer-ts-paths": preferTsPaths,
};