1
1
import vm from 'node:vm'
2
2
3
3
import type { TSESTree } from '@typescript-eslint/utils'
4
+ import type { RuleFixer } from '@typescript-eslint/utils/dist/ts-eslint'
4
5
5
6
import { createRule } from '../utils'
6
7
@@ -16,6 +17,9 @@ type MessageId =
16
17
| 'paddedSpaces'
17
18
| 'webpackComment'
18
19
| 'chunknameFormat'
20
+ | 'webpackEagerModeNoChunkName'
21
+ | 'webpackRemoveEagerMode'
22
+ | 'webpackRemoveChunkName'
19
23
20
24
export = createRule < [ Options ?] , MessageId > ( {
21
25
name : 'dynamic-import-chunkname' ,
@@ -26,6 +30,7 @@ export = createRule<[Options?], MessageId>({
26
30
description :
27
31
'Enforce a leading comment with the webpackChunkName for dynamic imports.' ,
28
32
} ,
33
+ hasSuggestions : true ,
29
34
schema : [
30
35
{
31
36
type : 'object' ,
@@ -56,7 +61,11 @@ export = createRule<[Options?], MessageId>({
56
61
webpackComment :
57
62
'dynamic imports require a "webpack" comment with valid syntax' ,
58
63
chunknameFormat :
59
- 'dynamic imports require a leading comment in the form /*{{format}}*/' ,
64
+ 'dynamic imports require a leading comment in the form /* {{format}} */' ,
65
+ webpackEagerModeNoChunkName :
66
+ 'dynamic imports using eager mode do not need a webpackChunkName' ,
67
+ webpackRemoveEagerMode : 'Remove webpackMode' ,
68
+ webpackRemoveChunkName : 'Remove webpackChunkName' ,
60
69
} ,
61
70
} ,
62
71
defaultOptions : [ ] ,
@@ -69,9 +78,12 @@ export = createRule<[Options?], MessageId>({
69
78
70
79
const paddedCommentRegex = / ^ ( \S [ \S \s ] + \S ) $ /
71
80
const commentStyleRegex =
72
- / ^ ( ( ( w e b p a c k C h u n k N a m e : .+ ) | ( ( w e b p a c k P r e f e t c h | w e b p a c k P r e l o a d ) : ( t r u e | f a l s e | - ? \d + ) ) | ( w e b p a c k I g n o r e : ( t r u e | f a l s e ) ) | ( ( w e b p a c k I n c l u d e | w e b p a c k E x c l u d e ) : \/ .* \/ ) | ( w e b p a c k M o d e : [ " ' ] ( l a z y | l a z y - o n c e | e a g e r | w e a k ) [ " ' ] ) | ( w e b p a c k E x p o r t s : ( [ " ' ] \w + [ " ' ] | \[ ( [ " ' ] \w + [ " ' ] , * ) + ( [ " ' ] \w + [ " ' ] * ) ] ) ) ) , ? ) + $ /
73
- const chunkSubstrFormat = ` webpackChunkName: ["']${ webpackChunknameFormat } ["'],? `
81
+ / ^ ( ( ( ( w e b p a c k C h u n k N a m e | w e b p a c k F e t c h P r i o r i t y ) : .+ ) | ( ( w e b p a c k P r e f e t c h | w e b p a c k P r e l o a d ) : ( t r u e | f a l s e | - ? \d + ) ) | ( w e b p a c k I g n o r e : ( t r u e | f a l s e ) ) | ( ( w e b p a c k I n c l u d e | w e b p a c k E x c l u d e ) : \/ .+ \/ ) | ( w e b p a c k M o d e : [ " ' ] ( l a z y | l a z y - o n c e | e a g e r | w e a k ) [ " ' ] ) | ( w e b p a c k E x p o r t s : ( [ " ' ] \w + [ " ' ] | \[ ( [ " ' ] \w + [ " ' ] , * ) + ( [ " ' ] \w + [ " ' ] * ) ] ) ) ) , ? ) + $ /
82
+
83
+ const chunkSubstrFormat = `webpackChunkName: ["']${ webpackChunknameFormat } ["'],?`
74
84
const chunkSubstrRegex = new RegExp ( chunkSubstrFormat )
85
+ const eagerModeFormat = `webpackMode: ["']eager["'],?`
86
+ const eagerModeRegex = new RegExp ( eagerModeFormat )
75
87
76
88
function run ( node : TSESTree . Node , arg : TSESTree . Node ) {
77
89
const { sourceCode } = context
@@ -86,6 +98,7 @@ export = createRule<[Options?], MessageId>({
86
98
}
87
99
88
100
let isChunknamePresent = false
101
+ let isEagerModePresent = false
89
102
90
103
for ( const comment of leadingComments ) {
91
104
if ( comment . type !== 'Block' ) {
@@ -123,12 +136,83 @@ export = createRule<[Options?], MessageId>({
123
136
return
124
137
}
125
138
139
+ if ( eagerModeRegex . test ( comment . value ) ) {
140
+ isEagerModePresent = true
141
+ }
142
+
126
143
if ( chunkSubstrRegex . test ( comment . value ) ) {
127
144
isChunknamePresent = true
128
145
}
129
146
}
130
147
131
- if ( ! isChunknamePresent && ! allowEmpty ) {
148
+ const removeCommentsAndLeadingSpaces = (
149
+ fixer : RuleFixer ,
150
+ comment : TSESTree . Comment ,
151
+ ) => {
152
+ const leftToken = sourceCode . getTokenBefore ( comment )
153
+ const leftComments = sourceCode . getCommentsBefore ( comment )
154
+ if ( leftToken ) {
155
+ if ( leftComments . length > 0 ) {
156
+ return fixer . removeRange ( [
157
+ Math . max (
158
+ leftToken . range [ 1 ] ,
159
+ leftComments [ leftComments . length - 1 ] . range [ 1 ] ,
160
+ ) ,
161
+ comment . range [ 1 ] ,
162
+ ] )
163
+ }
164
+ return fixer . removeRange ( [ leftToken . range [ 1 ] , comment . range [ 1 ] ] )
165
+ }
166
+ return fixer . remove ( comment )
167
+ }
168
+
169
+ if ( isChunknamePresent && isEagerModePresent ) {
170
+ context . report ( {
171
+ node,
172
+ messageId : 'webpackEagerModeNoChunkName' ,
173
+ suggest : [
174
+ {
175
+ messageId : 'webpackRemoveChunkName' ,
176
+ fix ( fixer ) {
177
+ for ( const comment of leadingComments ) {
178
+ if ( chunkSubstrRegex . test ( comment . value ) ) {
179
+ const replacement = comment . value
180
+ . replace ( chunkSubstrRegex , '' )
181
+ . trim ( )
182
+ . replace ( / , $ / , '' )
183
+
184
+ return replacement === ''
185
+ ? removeCommentsAndLeadingSpaces ( fixer , comment )
186
+ : fixer . replaceText ( comment , `/* ${ replacement } */` )
187
+ }
188
+ }
189
+
190
+ return null
191
+ } ,
192
+ } ,
193
+ {
194
+ messageId : 'webpackRemoveEagerMode' ,
195
+ fix ( fixer ) {
196
+ for ( const comment of leadingComments ) {
197
+ if ( eagerModeRegex . test ( comment . value ) ) {
198
+ const replacement = comment . value
199
+ . replace ( eagerModeRegex , '' )
200
+ . trim ( )
201
+ . replace ( / , $ / , '' )
202
+ return replacement === ''
203
+ ? removeCommentsAndLeadingSpaces ( fixer , comment )
204
+ : fixer . replaceText ( comment , `/* ${ replacement } */` )
205
+ }
206
+ }
207
+
208
+ return null
209
+ } ,
210
+ } ,
211
+ ] ,
212
+ } )
213
+ }
214
+
215
+ if ( ! isChunknamePresent && ! allowEmpty && ! isEagerModePresent ) {
132
216
context . report ( {
133
217
node,
134
218
messageId : 'chunknameFormat' ,
0 commit comments