Skip to content

Commit 80d056b

Browse files
Thiry1hardfist
andauthored
feat(rule): implement import/no-webpack-loader-syntax (#339)
Co-authored-by: hardfist <[email protected]>
1 parent ac5eb1a commit 80d056b

File tree

6 files changed

+256
-4
lines changed

6 files changed

+256
-4
lines changed

internal/plugins/import/all.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package import_plugin
22

33
import (
44
"github.com/web-infra-dev/rslint/internal/plugins/import/rules/no_self_import"
5+
"github.com/web-infra-dev/rslint/internal/plugins/import/rules/no_webpack_loader_syntax"
56
"github.com/web-infra-dev/rslint/internal/rule"
67
)
78

89
func GetAllRules() []rule.Rule {
910
return []rule.Rule{
1011
no_self_import.NoSelfImportRule,
12+
no_webpack_loader_syntax.NoWebpackLoaderSyntax,
1113
}
1214
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package no_webpack_loader_syntax
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/microsoft/typescript-go/shim/ast"
8+
"github.com/web-infra-dev/rslint/internal/rule"
9+
)
10+
11+
func hasWebpackLoaderSyntax(modulePath string) bool {
12+
return strings.Contains(modulePath, "!")
13+
}
14+
15+
func buildRuleMessage(modulePath string) rule.RuleMessage {
16+
return rule.RuleMessage{
17+
Id: "import/no-webpack-loader-syntax",
18+
// https://github.com/import-js/eslint-plugin-import/blob/01c9eb04331d2efa8d63f2d7f4bfec3bc44c94f3/src/rules/no-webpack-loader-syntax.js#L6C27-L6C110
19+
Description: fmt.Sprintf("Unexpected '!' in '%s'. Do not use import syntax to configure webpack loaders.", modulePath),
20+
}
21+
}
22+
23+
// See: https://github.com/import-js/eslint-plugin-import/blob/01c9eb04331d2efa8d63f2d7f4bfec3bc44c94f3/src/rules/no-webpack-loader-syntax.js
24+
var NoWebpackLoaderSyntax = rule.Rule{
25+
Name: "import/no-webpack-loader-syntax",
26+
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
27+
return rule.RuleListeners{
28+
ast.KindImportDeclaration: func(node *ast.Node) {
29+
specifier := node.ModuleSpecifier()
30+
if specifier == nil || specifier.Kind != ast.KindStringLiteral {
31+
return
32+
}
33+
modulePath := specifier.AsStringLiteral().Text
34+
35+
if hasWebpackLoaderSyntax(modulePath) {
36+
ctx.ReportNode(specifier, buildRuleMessage(modulePath))
37+
}
38+
},
39+
ast.KindCallExpression: func(node *ast.Node) {
40+
callExpression := node.AsCallExpression()
41+
expr := callExpression.Expression
42+
if expr.Kind != ast.KindIdentifier || expr.AsIdentifier().Text != "require" {
43+
return
44+
}
45+
// ensure there is at least one argument
46+
if len(callExpression.Arguments.Nodes) == 0 {
47+
return
48+
}
49+
arg := callExpression.Arguments.Nodes[0]
50+
if arg.Kind != ast.KindStringLiteral {
51+
return
52+
}
53+
modulePath := arg.AsStringLiteral().Text
54+
if hasWebpackLoaderSyntax(modulePath) {
55+
// report at the string literal argument location for accuracy
56+
ctx.ReportNode(arg, buildRuleMessage(modulePath))
57+
}
58+
},
59+
}
60+
},
61+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package no_webpack_loader_syntax_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/web-infra-dev/rslint/internal/plugins/import/fixtures"
7+
"github.com/web-infra-dev/rslint/internal/plugins/import/rules/no_webpack_loader_syntax"
8+
"github.com/web-infra-dev/rslint/internal/rule_tester"
9+
)
10+
11+
func TestNoWebpackLoaderSyntaxRule(t *testing.T) {
12+
rule_tester.RunRuleTester(
13+
fixtures.GetRootDir(),
14+
"tsconfig.json",
15+
t,
16+
&no_webpack_loader_syntax.NoWebpackLoaderSyntax,
17+
[]rule_tester.ValidTestCase{
18+
{Code: `import _ from "lodash"`},
19+
{Code: `import find from "lodash.find"`},
20+
{Code: `import foo from "./foo.css"`},
21+
{Code: `import data from "@scope/my-package/data.json"`},
22+
{Code: `var _ = require("lodash")`},
23+
{Code: `var find = require("lodash.find")`},
24+
{Code: `var foo = require("./foo")`},
25+
{Code: `var foo = require("../foo")`},
26+
{Code: `var foo = require("foo")`},
27+
{Code: `var foo = require("./")`},
28+
{Code: `var foo = require("@scope/foo")`},
29+
{Code: `var foo = fn("babel!lodash")`},
30+
},
31+
[]rule_tester.InvalidTestCase{
32+
{
33+
Code: `import _ from "babel!lodash"`,
34+
FileName: "foo.ts",
35+
Errors: []rule_tester.InvalidTestCaseError{
36+
{
37+
MessageId: "import/no-webpack-loader-syntax",
38+
},
39+
},
40+
},
41+
{
42+
Code: `import find from "-babel-loader!lodash.find"`,
43+
FileName: "foo.ts",
44+
Errors: []rule_tester.InvalidTestCaseError{
45+
{
46+
MessageId: "import/no-webpack-loader-syntax",
47+
},
48+
},
49+
},
50+
{
51+
Code: `import foo from "style!css!./foo.css"`,
52+
FileName: "foo.ts",
53+
Errors: []rule_tester.InvalidTestCaseError{
54+
{
55+
MessageId: "import/no-webpack-loader-syntax",
56+
},
57+
},
58+
},
59+
{
60+
Code: `import data from "json!@scope/my-package/data.json"`,
61+
FileName: "foo.ts",
62+
Errors: []rule_tester.InvalidTestCaseError{
63+
{
64+
MessageId: "import/no-webpack-loader-syntax",
65+
},
66+
},
67+
},
68+
{
69+
Code: `var _ = require("babel!lodash")`,
70+
FileName: "foo.ts",
71+
Errors: []rule_tester.InvalidTestCaseError{
72+
{
73+
MessageId: "import/no-webpack-loader-syntax",
74+
},
75+
},
76+
},
77+
{
78+
Code: `var find = require("-babel-loader!lodash.find")`,
79+
FileName: "foo.ts",
80+
Errors: []rule_tester.InvalidTestCaseError{
81+
{
82+
MessageId: "import/no-webpack-loader-syntax",
83+
},
84+
},
85+
},
86+
{
87+
Code: `var foo = require("style!css!./foo.css")`,
88+
FileName: "foo.ts",
89+
Errors: []rule_tester.InvalidTestCaseError{
90+
{
91+
MessageId: "import/no-webpack-loader-syntax",
92+
},
93+
},
94+
},
95+
{
96+
Code: `var data = require("json!@scope/my-package/data.json")`,
97+
FileName: "foo.ts",
98+
Errors: []rule_tester.InvalidTestCaseError{
99+
{
100+
MessageId: "import/no-webpack-loader-syntax",
101+
},
102+
},
103+
},
104+
},
105+
)
106+
}

packages/rslint-test-tools/rstest.config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export default defineConfig({
99

1010
// eslint-plugin-import
1111
'./tests/eslint-plugin-import/rules/no-self-import.test.ts',
12+
'./tests/eslint-plugin-import/rules/no-webpack-loader-syntax.test.ts',
1213

1314
// typescript-eslint
1415
'./tests/typescript-eslint/rules/adjacent-overload-signatures.test.ts',

packages/rslint-test-tools/tests/eslint-plugin-import/rule-tester.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,13 @@ export class RuleTester {
8888
typeof validCase === 'string'
8989
? defaultFilename
9090
: (validCase.filename ?? defaultFilename);
91+
const absoluteFilename = path.resolve(import.meta.dirname, filename);
9192

9293
const diags = await lint({
9394
config,
9495
workingDirectory: cwd,
9596
fileContents: {
96-
[filename]: code,
97+
[absoluteFilename]: code,
9798
},
9899
ruleOptions: {
99100
[ruleName]: options,
@@ -125,12 +126,12 @@ export class RuleTester {
125126
typeof item === 'string'
126127
? defaultFilename
127128
: (item.filename ?? defaultFilename);
128-
129+
const absoluteFilename = path.resolve(import.meta.dirname, filename);
129130
const diags = await lint({
130131
config,
131132
workingDirectory: cwd,
132133
fileContents: {
133-
[filename]: code,
134+
[absoluteFilename]: code,
134135
},
135136
ruleOptions: {
136137
[ruleName]: options,
@@ -188,8 +189,11 @@ export class RuleTester {
188189
`Error at index ${i} has suggestions. Please convert the test error into an object and specify 'suggestions' property on it to test suggestions.`,
189190
);
190191
} else if (typeof error === 'object' && error !== null) {
191-
// TODO: handle object error
192+
// TODO: handle object error(currently partially implemented)
192193
// https://github.com/eslint/eslint/blob/34f0723e2d0faf8ac8dc95ec56e6d181bd6b67f2/lib/rule-tester/rule-tester.js#L1145
194+
if (typeof error.message === 'string') {
195+
assertMessageMatches(message.message, error.message);
196+
}
193197
}
194198
}
195199
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { test } from '../utils.js';
2+
3+
import { RuleTester } from '../rule-tester.js';
4+
5+
const ruleTester = new RuleTester();
6+
7+
const message = 'Do not use import syntax to configure webpack loaders.';
8+
9+
ruleTester.run('no-webpack-loader-syntax', null as never, {
10+
valid: [
11+
test({ code: 'import _ from "lodash"' }),
12+
test({ code: 'import find from "lodash.find"' }),
13+
test({ code: 'import foo from "./foo.css"' }),
14+
test({ code: 'import data from "@scope/my-package/data.json"' }),
15+
test({ code: 'var _ = require("lodash")' }),
16+
test({ code: 'var find = require("lodash.find")' }),
17+
test({ code: 'var foo = require("./foo")' }),
18+
test({ code: 'var foo = require("../foo")' }),
19+
test({ code: 'var foo = require("foo")' }),
20+
test({ code: 'var foo = require("./")' }),
21+
test({ code: 'var foo = require("@scope/foo")' }),
22+
test({ code: 'var foo = fn("babel!lodash")' }),
23+
],
24+
invalid: [
25+
test({
26+
code: 'import _ from "babel!lodash"',
27+
errors: [{ message: `Unexpected '!' in 'babel!lodash'. ${message}` }],
28+
}),
29+
test({
30+
code: 'import find from "-babel-loader!lodash.find"',
31+
errors: [
32+
{
33+
message: `Unexpected '!' in '-babel-loader!lodash.find'. ${message}`,
34+
},
35+
],
36+
}),
37+
test({
38+
code: 'import foo from "style!css!./foo.css"',
39+
errors: [
40+
{ message: `Unexpected '!' in 'style!css!./foo.css'. ${message}` },
41+
],
42+
}),
43+
test({
44+
code: 'import data from "json!@scope/my-package/data.json"',
45+
errors: [
46+
{
47+
message: `Unexpected '!' in 'json!@scope/my-package/data.json'. ${message}`,
48+
},
49+
],
50+
}),
51+
test({
52+
code: 'var _ = require("babel!lodash")',
53+
errors: [{ message: `Unexpected '!' in 'babel!lodash'. ${message}` }],
54+
}),
55+
test({
56+
code: 'var find = require("-babel-loader!lodash.find")',
57+
errors: [
58+
{
59+
message: `Unexpected '!' in '-babel-loader!lodash.find'. ${message}`,
60+
},
61+
],
62+
}),
63+
test({
64+
code: 'var foo = require("style!css!./foo.css")',
65+
errors: [
66+
{ message: `Unexpected '!' in 'style!css!./foo.css'. ${message}` },
67+
],
68+
}),
69+
test({
70+
code: 'var data = require("json!@scope/my-package/data.json")',
71+
errors: [
72+
{
73+
message: `Unexpected '!' in 'json!@scope/my-package/data.json'. ${message}`,
74+
},
75+
],
76+
}),
77+
],
78+
});

0 commit comments

Comments
 (0)