eslint-plugin-esm 0.8.1 → 0.8.2
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/dist/common.d.ts +6 -1
- package/dist/common.d.ts.map +1 -1
- package/dist/common.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/rules/no-dynamic-imports.d.ts +2 -1
- package/dist/rules/no-dynamic-imports.d.ts.map +1 -1
- package/dist/rules/no-dynamic-imports.js +1 -1
- package/dist/rules/no-empty-exports.d.ts +2 -1
- package/dist/rules/no-empty-exports.d.ts.map +1 -1
- package/dist/rules/no-empty-exports.js +1 -1
- package/dist/rules/no-query-suffixes.d.ts +5 -0
- package/dist/rules/no-query-suffixes.d.ts.map +1 -0
- package/dist/rules/no-query-suffixes.js +10 -0
- package/dist/rules/no-side-effect-imports.d.ts.map +1 -1
- package/dist/rules/no-side-effect-imports.js +1 -1
- package/dist/rules/top-side-effect-imports.d.ts.map +1 -1
- package/dist/rules/top-side-effect-imports.js +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -1
- package/package.json +5 -3
- package/CHANGELOG.md +0 -96
- package/doc/rules/existing-file-imports.md +0 -28
- package/doc/rules/no-declaration-file-imports.md +0 -35
- package/doc/rules/no-directory-imports.md +0 -30
- package/doc/rules/no-dynamic-imports.md +0 -30
- package/doc/rules/no-empty-exports.md +0 -29
- package/doc/rules/no-git-ignored-imports.md +0 -36
- package/doc/rules/no-phantom-dep-imports.md +0 -31
- package/doc/rules/no-relative-parent-imports.md +0 -33
- package/doc/rules/no-rename-exports.md +0 -29
- package/doc/rules/no-rename-imports.md +0 -28
- package/doc/rules/no-side-effect-imports.md +0 -30
- package/doc/rules/no-useless-path-segments.md +0 -54
- package/doc/rules/required-exports.md +0 -31
- package/doc/rules/top-side-effect-imports.md +0 -31
- package/src/common.ts +0 -97
- package/src/index.ts +0 -31
- package/src/rules/existing-file-imports.test.ts +0 -28
- package/src/rules/existing-file-imports.ts +0 -25
- package/src/rules/no-declaration-file-imports.test.ts +0 -33
- package/src/rules/no-declaration-file-imports.ts +0 -12
- package/src/rules/no-directory-imports.test.ts +0 -31
- package/src/rules/no-directory-imports.ts +0 -34
- package/src/rules/no-dynamic-imports.test.ts +0 -27
- package/src/rules/no-dynamic-imports.ts +0 -15
- package/src/rules/no-empty-exports.test.ts +0 -22
- package/src/rules/no-empty-exports.ts +0 -14
- package/src/rules/no-git-ignored-imports.test.ts +0 -40
- package/src/rules/no-git-ignored-imports.ts +0 -58
- package/src/rules/no-phantom-dep-imports.test.ts +0 -35
- package/src/rules/no-phantom-dep-imports.ts +0 -124
- package/src/rules/no-relative-parent-imports.test.ts +0 -28
- package/src/rules/no-relative-parent-imports.ts +0 -13
- package/src/rules/no-rename-exports.test.ts +0 -22
- package/src/rules/no-rename-exports.ts +0 -17
- package/src/rules/no-rename-imports.test.ts +0 -22
- package/src/rules/no-rename-imports.ts +0 -16
- package/src/rules/no-side-effect-imports.test.ts +0 -24
- package/src/rules/no-side-effect-imports.ts +0 -39
- package/src/rules/no-useless-path-segments.test.ts +0 -52
- package/src/rules/no-useless-path-segments.ts +0 -31
- package/src/rules/required-exports.test.ts +0 -24
- package/src/rules/required-exports.ts +0 -22
- package/src/rules/top-side-effect-imports.test.ts +0 -24
- package/src/rules/top-side-effect-imports.ts +0 -22
- package/src/utils.ts +0 -18
- package/tsconfig.json +0 -5
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import childProcess from "node:child_process";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import process from "node:process";
|
|
4
|
-
import { isNativeError } from "node:util/types";
|
|
5
|
-
import { create, createRule, getRuleName, getSourceType } from "../common.ts";
|
|
6
|
-
import { memoize } from "../utils.ts";
|
|
7
|
-
|
|
8
|
-
export const noGitIgnoredImports = createRule({
|
|
9
|
-
name: getRuleName(import.meta.url),
|
|
10
|
-
message: "Disallow to import module from a git-ignored path.",
|
|
11
|
-
create: (context) => create(context, checkIgnored),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
function checkIgnored(filename: string, source: string) {
|
|
15
|
-
// from node_modules
|
|
16
|
-
if (getSourceType(source) !== "local") {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
// out side of project root
|
|
20
|
-
if (source.startsWith("/") && !source.startsWith(process.cwd())) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
// This file of absolutePath may be a symbolic link
|
|
24
|
-
const absolutePath = path.resolve(path.dirname(filename), source);
|
|
25
|
-
if (!absolutePath.startsWith("/")) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
`ESLint plugin internal error. Absolute path incorrect: ${absolutePath}.`,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
return isIgnored(absolutePath);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const isIgnored = memoize((filePath: string) => {
|
|
34
|
-
try {
|
|
35
|
-
return (
|
|
36
|
-
childProcess
|
|
37
|
-
// Adding `stdio: 'pipe'` to depress unexpected error messages in console.
|
|
38
|
-
// For example, `git check-ignore /absolute/path/to/file` (even wrapped with try-catch) will print something unnecessary.
|
|
39
|
-
.execSync(`git check-ignore ${filePath}`, {
|
|
40
|
-
encoding: "utf8",
|
|
41
|
-
stdio: "pipe",
|
|
42
|
-
})
|
|
43
|
-
.trim() === filePath
|
|
44
|
-
);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
if (
|
|
47
|
-
isNativeError(e) &&
|
|
48
|
-
"stdout" in e &&
|
|
49
|
-
e.stdout === "" &&
|
|
50
|
-
"stderr" in e &&
|
|
51
|
-
e.stderr === ""
|
|
52
|
-
) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
// We cannot throw an error here. So we have to return true to report the filePath is bad.
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { noPhantomDepImports } from "./no-phantom-dep-imports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
{ code: "import foo from '/foo'" },
|
|
6
|
-
{ code: "import foo from './foo'" },
|
|
7
|
-
{ code: "import foo from '../foo'" },
|
|
8
|
-
{ code: "import foo from 'node:foo'" },
|
|
9
|
-
|
|
10
|
-
{ code: "import type Foo from 'estree'" },
|
|
11
|
-
{ code: "import type {Foo} from 'eslint'" },
|
|
12
|
-
{
|
|
13
|
-
code: "import foo from '@fenge/dev-utils'",
|
|
14
|
-
options: [{ allowDevDependencies: true }],
|
|
15
|
-
},
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
const invalid = [
|
|
19
|
-
{
|
|
20
|
-
code: "import type foo from 'foo'",
|
|
21
|
-
options: [{ allowDevDependencies: true }],
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
code: "import type foo from 'foo'",
|
|
25
|
-
options: [{ allowDevDependencies: false }],
|
|
26
|
-
},
|
|
27
|
-
{ code: "import {type Foo} from 'foo'" },
|
|
28
|
-
{ code: "import foo from 'foo'" },
|
|
29
|
-
|
|
30
|
-
{ code: "import {type Foo} from 'eslint'" },
|
|
31
|
-
{ code: "import {Foo} from 'eslint'" },
|
|
32
|
-
{ code: "import eslint from 'eslint'" },
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
test({ valid, invalid, ...noPhantomDepImports });
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import process from "node:process";
|
|
4
|
-
import { create, createRule, getRuleName, getSourceType } from "../common.ts";
|
|
5
|
-
|
|
6
|
-
function isObject(value: unknown) {
|
|
7
|
-
return value !== null && typeof value === "object";
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function isFile(filePath: string) {
|
|
11
|
-
try {
|
|
12
|
-
return fs.statSync(filePath).isFile();
|
|
13
|
-
} catch {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const cache = new Map<string, { path: string; content: object } | undefined>(); // key is dir, value is package.json
|
|
19
|
-
function getPkgJson(
|
|
20
|
-
dir: string,
|
|
21
|
-
): { path: string; content: object } | undefined {
|
|
22
|
-
if (cache.has(dir)) {
|
|
23
|
-
return cache.get(dir);
|
|
24
|
-
}
|
|
25
|
-
const pkgJsonPath = path.join(dir, "package.json");
|
|
26
|
-
if (isFile(pkgJsonPath)) {
|
|
27
|
-
const content: unknown = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
28
|
-
const result = isObject(content)
|
|
29
|
-
? { path: pkgJsonPath, content }
|
|
30
|
-
: undefined;
|
|
31
|
-
cache.set(dir, result);
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// if it is a directory
|
|
36
|
-
if (dir === process.cwd() || dir === "/") {
|
|
37
|
-
// stop here
|
|
38
|
-
cache.set(dir, undefined);
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return getPkgJson(path.join(dir, ".."));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const noPhantomDepImports = createRule({
|
|
46
|
-
name: getRuleName(import.meta.url),
|
|
47
|
-
message:
|
|
48
|
-
"Disallow importing from a module which the nearest `package.json` doesn't include it.",
|
|
49
|
-
schema: [
|
|
50
|
-
{
|
|
51
|
-
type: "object",
|
|
52
|
-
properties: {
|
|
53
|
-
allowDevDependencies: { type: "boolean" },
|
|
54
|
-
},
|
|
55
|
-
additionalProperties: false,
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
create: (context) =>
|
|
59
|
-
create(context, (filename, source, node) => {
|
|
60
|
-
const option = context.options[0];
|
|
61
|
-
// default false
|
|
62
|
-
const allowDevDependencies: boolean =
|
|
63
|
-
(typeof option === "object" &&
|
|
64
|
-
option &&
|
|
65
|
-
"allowDevDependencies" in option &&
|
|
66
|
-
typeof option.allowDevDependencies === "boolean" &&
|
|
67
|
-
option.allowDevDependencies) ??
|
|
68
|
-
false;
|
|
69
|
-
|
|
70
|
-
// ignore `import {foo} from './'`
|
|
71
|
-
// check `import {foo} from 'node:foo'` and `import {foo} from 'foo'`
|
|
72
|
-
if (getSourceType(source) === "local") {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
const pkgJson = getPkgJson(path.dirname(filename));
|
|
76
|
-
// cannot find package.json file
|
|
77
|
-
if (!pkgJson) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
const dep =
|
|
81
|
-
"dependencies" in pkgJson.content &&
|
|
82
|
-
isObject(pkgJson.content.dependencies)
|
|
83
|
-
? pkgJson.content.dependencies
|
|
84
|
-
: {};
|
|
85
|
-
const peerDep =
|
|
86
|
-
"peerDependencies" in pkgJson.content &&
|
|
87
|
-
isObject(pkgJson.content.peerDependencies)
|
|
88
|
-
? pkgJson.content.peerDependencies
|
|
89
|
-
: {};
|
|
90
|
-
const devDep =
|
|
91
|
-
"devDependencies" in pkgJson.content &&
|
|
92
|
-
isObject(pkgJson.content.devDependencies)
|
|
93
|
-
? pkgJson.content.devDependencies
|
|
94
|
-
: {};
|
|
95
|
-
|
|
96
|
-
// TODO: Optimize the error message which is reported on `import foo from 'node:foo'`
|
|
97
|
-
// 1. check `import foo from 'node:foo'`
|
|
98
|
-
if (source.startsWith("node:")) {
|
|
99
|
-
return !("@types/node" in devDep || "@types/node" in dep);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 2. check `import foo from 'foo'`
|
|
103
|
-
const moduleName = source
|
|
104
|
-
.split("/")
|
|
105
|
-
.slice(0, source.startsWith("@") ? 2 : 1)
|
|
106
|
-
.join("/");
|
|
107
|
-
|
|
108
|
-
const isInDep = moduleName in dep || moduleName in peerDep;
|
|
109
|
-
const isInDev = moduleName in devDep;
|
|
110
|
-
if ("importKind" in node && node.importKind === "type") {
|
|
111
|
-
const typeDepName = moduleName.startsWith("@")
|
|
112
|
-
? `@types/${moduleName.slice(1).replace("/", "__")}`
|
|
113
|
-
: `@types/${moduleName}`;
|
|
114
|
-
return !(
|
|
115
|
-
isInDep ||
|
|
116
|
-
isInDev ||
|
|
117
|
-
typeDepName in dep ||
|
|
118
|
-
typeDepName in devDep
|
|
119
|
-
);
|
|
120
|
-
} else {
|
|
121
|
-
return allowDevDependencies ? !(isInDep || isInDev) : !isInDep;
|
|
122
|
-
}
|
|
123
|
-
}),
|
|
124
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { noRelativeParentImports } from "./no-relative-parent-imports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
"import foo from 'foo'",
|
|
6
|
-
"import 'foo'",
|
|
7
|
-
"require('foo')",
|
|
8
|
-
"import('foo')",
|
|
9
|
-
"export * from 'foo'",
|
|
10
|
-
"export {name} from 'foo'",
|
|
11
|
-
|
|
12
|
-
"import foo from '.foo'",
|
|
13
|
-
"import foo from './foo'",
|
|
14
|
-
"import foo from '../foo'",
|
|
15
|
-
"import foo from '../../foo'",
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
const invalid = [
|
|
19
|
-
"import foo from '../../../foo'",
|
|
20
|
-
"import '../../../foo'",
|
|
21
|
-
"import('../../../foo')",
|
|
22
|
-
"export * from '../../../foo'",
|
|
23
|
-
"export {name} from '../../../foo'",
|
|
24
|
-
|
|
25
|
-
"import foo from '../../../../foo'",
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
test({ valid, invalid, ...noRelativeParentImports });
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { create, createRule, getRuleName } from "../common.ts";
|
|
2
|
-
|
|
3
|
-
const depth = 3;
|
|
4
|
-
|
|
5
|
-
export const noRelativeParentImports = createRule({
|
|
6
|
-
name: getRuleName(import.meta.url),
|
|
7
|
-
message: "Disallow importing module from a relative parent path too deeply.",
|
|
8
|
-
create: (context) => create(context, checkDepth),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
function checkDepth(_filename: string, source: string) {
|
|
12
|
-
return new RegExp(`^(\\.\\./){${depth.toString()},}`).test(source);
|
|
13
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { noRenameExports } from "./no-rename-exports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
"let foo=1; export {foo}",
|
|
6
|
-
"export let foo",
|
|
7
|
-
"export const foo = bar",
|
|
8
|
-
"export default foo",
|
|
9
|
-
"export default {}",
|
|
10
|
-
"export {}",
|
|
11
|
-
];
|
|
12
|
-
const invalid = [
|
|
13
|
-
"let foo=1; export {foo as bar}",
|
|
14
|
-
"let foo=1; export {foo as default}",
|
|
15
|
-
"export {foo as bar} from './foo'",
|
|
16
|
-
"export {default as foo} from './foo'",
|
|
17
|
-
// ts
|
|
18
|
-
"export {type Foo as Bar}",
|
|
19
|
-
"export type {Foo as Bar}",
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
test({ valid, invalid, ...noRenameExports });
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { createRule, DEFAULT_MESSAGE_ID, getRuleName } from "../common.ts";
|
|
2
|
-
|
|
3
|
-
export const noRenameExports = createRule({
|
|
4
|
-
name: getRuleName(import.meta.url),
|
|
5
|
-
message: "Disallow renaming the named-exports.",
|
|
6
|
-
create: (context) => ({
|
|
7
|
-
ExportSpecifier: (node) => {
|
|
8
|
-
if (
|
|
9
|
-
node.exported.type !== "Identifier" ||
|
|
10
|
-
node.local.type !== "Identifier" ||
|
|
11
|
-
node.exported.name !== node.local.name
|
|
12
|
-
) {
|
|
13
|
-
context.report({ node, messageId: DEFAULT_MESSAGE_ID });
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
}),
|
|
17
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { noRenameImports } from "./no-rename-imports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
"import Foo from 'foo'",
|
|
6
|
-
"import {foo, bar} from 'foo'",
|
|
7
|
-
// ts
|
|
8
|
-
"import {type foo} from 'foo'",
|
|
9
|
-
"import type {foo} from 'foo'",
|
|
10
|
-
"import type Foo from 'foo'",
|
|
11
|
-
];
|
|
12
|
-
const invalid = [
|
|
13
|
-
"import {foo as bar} from 'foo'",
|
|
14
|
-
"import {default as foo} from 'foo'",
|
|
15
|
-
// ts
|
|
16
|
-
"import type {foo as bar} from 'foo'",
|
|
17
|
-
"import {type foo as bar} from 'foo'",
|
|
18
|
-
"import type {default as foo} from 'foo'",
|
|
19
|
-
"import {type default as foo} from 'foo'",
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
test({ valid, invalid, ...noRenameImports });
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { createRule, DEFAULT_MESSAGE_ID, getRuleName } from "../common.ts";
|
|
2
|
-
|
|
3
|
-
export const noRenameImports = createRule({
|
|
4
|
-
name: getRuleName(import.meta.url),
|
|
5
|
-
message: "Disallow renaming the named-imports.",
|
|
6
|
-
create: (context) => ({
|
|
7
|
-
ImportSpecifier: (node) => {
|
|
8
|
-
if (
|
|
9
|
-
node.imported.type !== "Identifier" ||
|
|
10
|
-
node.imported.name !== node.local.name
|
|
11
|
-
) {
|
|
12
|
-
context.report({ node, messageId: DEFAULT_MESSAGE_ID });
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
}),
|
|
16
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { noSideEffectImports } from "./no-side-effect-imports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
"import 'reflect-metadata'",
|
|
6
|
-
"import {} from 'reflect-metadata'",
|
|
7
|
-
"import {foo} from 'foo'",
|
|
8
|
-
];
|
|
9
|
-
|
|
10
|
-
const invalid = [
|
|
11
|
-
"import 'foo'",
|
|
12
|
-
"import './foo'",
|
|
13
|
-
"import {} from 'foo'",
|
|
14
|
-
"import {} from './foo'",
|
|
15
|
-
"import './reflect-metadata'",
|
|
16
|
-
"import './foo.module.css'",
|
|
17
|
-
"import 'foo.module.css'",
|
|
18
|
-
|
|
19
|
-
"import 'foo.css'",
|
|
20
|
-
"import './foo.css'",
|
|
21
|
-
"import 'module.css'",
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
test({ valid, invalid, ...noSideEffectImports });
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { ImportDeclaration } from "estree";
|
|
2
|
-
import { createRule, DEFAULT_MESSAGE_ID, getRuleName } from "../common.ts";
|
|
3
|
-
|
|
4
|
-
const ignores = [
|
|
5
|
-
"^reflect-metadata$",
|
|
6
|
-
// https://github.com/vitejs/vite/blob/main/packages/vite/client.d.ts
|
|
7
|
-
// "(?<!\\.module)\\.css$",
|
|
8
|
-
// "(?<!\\.module)\\.scss$",
|
|
9
|
-
// "(?<!\\.module)\\.sass$",
|
|
10
|
-
// "(?<!\\.module)\\.less$",
|
|
11
|
-
// "(?<!\\.module)\\.styl$",
|
|
12
|
-
// "(?<!\\.module)\\.stylus$",
|
|
13
|
-
// "(?<!\\.module)\\.pcss$",
|
|
14
|
-
// "(?<!\\.module)\\.sss$",
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
|
18
|
-
export const noSideEffectImports = createRule({
|
|
19
|
-
name: getRuleName(import.meta.url),
|
|
20
|
-
message:
|
|
21
|
-
"Side effect import is often used for polyfills and css. It's unsafe to use it.",
|
|
22
|
-
create: (context) => {
|
|
23
|
-
const ignoreExps = ignores.map((ignore) => new RegExp(ignore));
|
|
24
|
-
return {
|
|
25
|
-
"ImportDeclaration[specifiers.length=0]": (node: ImportDeclaration) => {
|
|
26
|
-
if (
|
|
27
|
-
ignoreExps.some(
|
|
28
|
-
(exp) =>
|
|
29
|
-
typeof node.source.value === "string" &&
|
|
30
|
-
exp.test(node.source.value),
|
|
31
|
-
)
|
|
32
|
-
) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
context.report({ node, messageId: DEFAULT_MESSAGE_ID });
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
},
|
|
39
|
-
});
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { noUselessPathSegments } from "./no-useless-path-segments.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
'import xxx from "../a"',
|
|
6
|
-
'import "../a"',
|
|
7
|
-
'import("../a")',
|
|
8
|
-
'require("../a")',
|
|
9
|
-
'import xxx from "./a"',
|
|
10
|
-
'import xxx from "a"',
|
|
11
|
-
'import xxx from ".a"',
|
|
12
|
-
'export * from "a"',
|
|
13
|
-
'export * from "./a"',
|
|
14
|
-
'export {a} from "a"',
|
|
15
|
-
'export {a} from "./a"',
|
|
16
|
-
|
|
17
|
-
'import foo from "."',
|
|
18
|
-
"import foo from '..'",
|
|
19
|
-
"import foo from '../..'",
|
|
20
|
-
].map((code) => ({ code, filename: "/a/b/c/d/e.js" }));
|
|
21
|
-
|
|
22
|
-
const invalid = [
|
|
23
|
-
'import xxx from ".././../a"',
|
|
24
|
-
'import ".././../a"',
|
|
25
|
-
'import(".././../a")',
|
|
26
|
-
'export * from ".././../a"',
|
|
27
|
-
'export {a} from ".././../a"',
|
|
28
|
-
|
|
29
|
-
'import xxx from "./../a"',
|
|
30
|
-
'import "./../a"',
|
|
31
|
-
'import("./../a")',
|
|
32
|
-
'export * from "./../a"',
|
|
33
|
-
'export {a} from "./../a"',
|
|
34
|
-
|
|
35
|
-
'import "././foo"',
|
|
36
|
-
'import "./../.././foo"',
|
|
37
|
-
'import("./../.././foo")',
|
|
38
|
-
'export * from "./../.././foo"',
|
|
39
|
-
'export {a} from "./../.././foo"',
|
|
40
|
-
|
|
41
|
-
'import "./../foo"',
|
|
42
|
-
'import("./../foo")',
|
|
43
|
-
'export * from "./../foo"',
|
|
44
|
-
'export {a} from "./../foo"',
|
|
45
|
-
|
|
46
|
-
'import foo from "./"',
|
|
47
|
-
"import foo from '../'",
|
|
48
|
-
"import foo from '../../'",
|
|
49
|
-
"import foo from './..'",
|
|
50
|
-
].map((code) => ({ code, filename: "/a/b/c/d/e.js" }));
|
|
51
|
-
|
|
52
|
-
test({ valid, invalid, ...noUselessPathSegments });
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { create, createRule, getRuleName, getSourceType } from "../common.ts";
|
|
3
|
-
|
|
4
|
-
export const noUselessPathSegments = createRule({
|
|
5
|
-
name: getRuleName(import.meta.url),
|
|
6
|
-
message: "The relative source path should be a nearest relative path.",
|
|
7
|
-
create: (context) => create(context, check),
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
function check(filename: string, source: string) {
|
|
11
|
-
if (
|
|
12
|
-
getSourceType(source) !== "local" ||
|
|
13
|
-
source.startsWith("/") ||
|
|
14
|
-
source === "."
|
|
15
|
-
) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
if (source.endsWith("/")) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
const currentPath = path.dirname(filename);
|
|
22
|
-
const absoluteSource = path.resolve(currentPath, source);
|
|
23
|
-
// compatible with windows
|
|
24
|
-
let resultPath = path
|
|
25
|
-
.relative(currentPath, absoluteSource)
|
|
26
|
-
.replaceAll("\\", "/");
|
|
27
|
-
if (!resultPath.startsWith("./") && !resultPath.startsWith("..")) {
|
|
28
|
-
resultPath = `./${resultPath}`;
|
|
29
|
-
}
|
|
30
|
-
return resultPath !== source;
|
|
31
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { requiredExports } from "./required-exports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
"export {}",
|
|
6
|
-
"const foo = 'foo'; export {foo}",
|
|
7
|
-
"export const foo = {}",
|
|
8
|
-
"export default {}",
|
|
9
|
-
"export {foo} from 'foo'",
|
|
10
|
-
"export {} from 'foo'",
|
|
11
|
-
"export * as foo from 'foo'",
|
|
12
|
-
"export {}; let foo = ''",
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const invalid = [
|
|
16
|
-
"// This rule will also report on empty files",
|
|
17
|
-
"",
|
|
18
|
-
"console.log()",
|
|
19
|
-
"import foo from 'foo'",
|
|
20
|
-
"exports.foo = {}",
|
|
21
|
-
"module.exports = {}",
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
test({ valid, invalid, ...requiredExports });
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createRule, DEFAULT_MESSAGE_ID, getRuleName } from "../common.ts";
|
|
2
|
-
|
|
3
|
-
export const requiredExports = createRule({
|
|
4
|
-
name: getRuleName(import.meta.url),
|
|
5
|
-
message: "It's required at least one `export` statement in a file.",
|
|
6
|
-
create: (context) => {
|
|
7
|
-
let existExport = false;
|
|
8
|
-
const hasExport = () => {
|
|
9
|
-
existExport = true;
|
|
10
|
-
};
|
|
11
|
-
return {
|
|
12
|
-
ExportAllDeclaration: () => hasExport(),
|
|
13
|
-
ExportDefaultDeclaration: () => hasExport(),
|
|
14
|
-
ExportNamedDeclaration: () => hasExport(),
|
|
15
|
-
"Program:exit": (node) => {
|
|
16
|
-
if (!existExport) {
|
|
17
|
-
context.report({ node, messageId: DEFAULT_MESSAGE_ID });
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
},
|
|
22
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { test } from "@fenge/dev-utils";
|
|
2
|
-
import { topSideEffectImports } from "./top-side-effect-imports.ts";
|
|
3
|
-
|
|
4
|
-
const valid = [
|
|
5
|
-
"import 'reflect-metadata'; import {foo} from 'foo'",
|
|
6
|
-
"import 'foo.css'; import {bar} from 'bar'",
|
|
7
|
-
"import 'reflect-metadata'; import 'foo.css'",
|
|
8
|
-
"import 'foo.css'; import 'bar'",
|
|
9
|
-
"import 'reflect-metadata'; import {foo} from 'foo'; import {bar} from 'bar'",
|
|
10
|
-
"import 'foo.css'; import {foo} from 'foo'; import {bar} from 'bar'",
|
|
11
|
-
"import 'reflect-metadata'", // Single side-effect import
|
|
12
|
-
"import {foo} from 'foo'", // Single non-side-effect import
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
const invalid = [
|
|
16
|
-
"import {foo} from 'foo'; import 'reflect-metadata'",
|
|
17
|
-
"import {bar} from 'bar'; import 'foo.css'",
|
|
18
|
-
"import {foo} from 'foo'; import {} from 'bar'",
|
|
19
|
-
"import {foo} from 'foo'; import 'bar'; import * as foo from 'reflect-metadata'", // Three import statements
|
|
20
|
-
"import {foo} from 'foo'; import {} from 'reflect-metadata'; import * as bar from 'bar'", // Three import statements
|
|
21
|
-
"import 'bar'; import r from 'reflect-metadata'; import {} from 'foo'", // Three import statements
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
test({ valid, invalid, ...topSideEffectImports });
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ImportDeclaration } from "estree";
|
|
2
|
-
import { createRule, DEFAULT_MESSAGE_ID, getRuleName } from "../common.ts";
|
|
3
|
-
|
|
4
|
-
export const topSideEffectImports = createRule({
|
|
5
|
-
name: getRuleName(import.meta.url),
|
|
6
|
-
message: "Side effect imports must be placed before other import statements.",
|
|
7
|
-
create: (context) => {
|
|
8
|
-
let hasNonSideEffectImport = false;
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
ImportDeclaration: (node: ImportDeclaration) => {
|
|
12
|
-
if (node.specifiers.length > 0) {
|
|
13
|
-
hasNonSideEffectImport = true;
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
if (hasNonSideEffectImport) {
|
|
17
|
-
context.report({ node, messageId: DEFAULT_MESSAGE_ID });
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
},
|
|
22
|
-
});
|
package/src/utils.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Like [lodash.memoize](https://lodash.com/docs/4.17.15#memoize)
|
|
3
|
-
* Type `Res` must be non-nullable, otherwise it will cause bug.
|
|
4
|
-
*/
|
|
5
|
-
export function memoize<Arg, Res extends NonNullable<unknown>>(
|
|
6
|
-
fn: (arg: Arg) => Res,
|
|
7
|
-
): (arg: Arg) => Res {
|
|
8
|
-
const cache = new Map<Arg, Res>(); // memory leak
|
|
9
|
-
return (arg: Arg) => {
|
|
10
|
-
const cachedResult = cache.get(arg);
|
|
11
|
-
if (cachedResult !== undefined) {
|
|
12
|
-
return cachedResult;
|
|
13
|
-
}
|
|
14
|
-
const result = fn(arg);
|
|
15
|
-
cache.set(arg, result);
|
|
16
|
-
return result;
|
|
17
|
-
};
|
|
18
|
-
}
|