1
1
import { Rule } from 'eslint' ;
2
2
import * as EsTree from 'estree' ;
3
- import { outputFileSync , removeSync } from 'fs-extra' ;
3
+ import { copyFileSync , removeSync } from 'fs-extra' ;
4
4
import * as glob from 'glob' ;
5
5
import { basename , dirname , join , relative , resolve } from 'path' ;
6
6
import { ERROR_MOVED_FILE } from './config' ;
7
7
import { getIn } from './lib/get-in' ;
8
8
import { interpolate } from './lib/path-reader' ;
9
9
10
- type FixList = Array < ( fixer : Rule . RuleFixer ) => Rule . Fix > ;
11
-
12
10
interface FileIndex {
13
11
[ source : string ] : string ;
14
12
}
15
13
16
- interface ModuleStrategy {
17
- getModuleId : ( node : any ) => string ;
18
- getQuotes : ( node : any ) => string ;
19
- getSource : ( node : any ) => EsTree . Identifier ;
14
+ interface VisitedFiles {
15
+ [ source : string ] : boolean ;
20
16
}
21
17
18
+ const visitedFiles : VisitedFiles = { } ;
19
+
22
20
const isDir = ( path : string ) => ! basename ( path ) . includes ( '.' ) ;
23
21
24
22
const withLeadingDot = ( moduleId : string ) =>
@@ -29,7 +27,7 @@ const withLeadingDot = (moduleId: string) =>
29
27
const withoutFileExtension = ( filePath : string ) =>
30
28
filePath . replace ( / \. [ ^ . ] + $ / , '' ) ;
31
29
32
- const getNewModuleId = ( filePath : string ) =>
30
+ const getNewDepId = ( filePath : string ) =>
33
31
withLeadingDot ( withoutFileExtension ( filePath ) ) ;
34
32
35
33
const withFileExtension = ( filePath : string ) => {
@@ -41,27 +39,39 @@ const withFileExtension = (filePath: string) => {
41
39
} ;
42
40
43
41
const updateMovedFile = (
42
+ context : Rule . RuleContext ,
44
43
files : FileIndex ,
45
- dirPath : string ,
44
+ fileDirPath : string ,
46
45
newFilePath : string ,
47
46
n : EsTree . Node ,
48
- fixes : FixList ,
49
- { getModuleId, getQuotes, getSource } : ModuleStrategy
47
+ getDepId : ( node : any ) => string
50
48
) => {
51
49
const node = n as any ;
52
- const moduleId = getModuleId ( node ) ;
53
- if ( ! moduleId . startsWith ( '.' ) ) {
50
+ const depId = getDepId ( node ) ;
51
+ if ( ! depId . startsWith ( '.' ) ) {
54
52
return ;
55
53
}
56
- const newDirPath = dirname ( newFilePath ) ;
57
- if ( newDirPath !== dirPath ) {
58
- const quotes = getQuotes ( node ) ;
59
- const rawModulePath = withFileExtension ( resolve ( dirPath , moduleId ) ) ;
60
- const modulePath = files [ rawModulePath ] || rawModulePath ;
61
- const newPathToModule = relative ( newDirPath , modulePath ) ;
62
- const newModuleId = getNewModuleId ( newPathToModule ) ;
63
- const withQuotes = `${ quotes } ${ newModuleId } ${ quotes } ` ;
64
- fixes . push ( ( fixer ) => fixer . replaceText ( getSource ( node ) , withQuotes ) ) ;
54
+ const newFileDirPath = dirname ( newFilePath ) ;
55
+ if ( newFileDirPath !== fileDirPath ) {
56
+ const depPath = withFileExtension ( resolve ( fileDirPath , depId ) ) ;
57
+ const newDepPath = files [ depPath ] || depPath ;
58
+ const newPathToDep = relative ( newFileDirPath , newDepPath ) ;
59
+ const newDepId = getNewDepId ( newPathToDep ) ;
60
+ return context . report ( {
61
+ fix : ( fixer ) =>
62
+ fixer . replaceText (
63
+ node ,
64
+ context
65
+ . getSourceCode ( )
66
+ . getText ( node )
67
+ . replace ( depId , newDepId )
68
+ ) ,
69
+ message : ERROR_MOVED_FILE (
70
+ withLeadingDot ( relative ( process . cwd ( ) , depPath ) ) ,
71
+ withLeadingDot ( relative ( process . cwd ( ) , newDepPath ) )
72
+ ) ,
73
+ node
74
+ } ) ;
65
75
}
66
76
} ;
67
77
@@ -70,41 +80,37 @@ const updateConsumer = (
70
80
files : FileIndex ,
71
81
dirPath : string ,
72
82
n : EsTree . Node ,
73
- { getModuleId , getQuotes , getSource } : ModuleStrategy
83
+ getDepId : ( node : any ) => string
74
84
) => {
75
85
const node = n as any ;
76
- const moduleId = getModuleId ( node ) ;
86
+ const moduleId = getDepId ( node ) ;
77
87
if ( ! moduleId . startsWith ( '.' ) ) {
78
88
return ;
79
89
}
80
- const quotes = getQuotes ( node ) ;
81
90
const modulePath = withFileExtension ( resolve ( dirPath , moduleId ) ) ;
82
91
const newModulePath = files [ modulePath ] ;
83
92
if ( newModulePath ) {
84
- const newModuleId = getNewModuleId ( relative ( dirPath , newModulePath ) ) ;
85
- const withQuotes = `${ quotes } ${ newModuleId } ${ quotes } ` ;
93
+ const newModuleId = getNewDepId ( relative ( dirPath , newModulePath ) ) ;
86
94
return context . report ( {
87
- fix : ( fixer ) => fixer . replaceText ( getSource ( node ) , withQuotes ) ,
95
+ fix : ( fixer ) =>
96
+ fixer . replaceText (
97
+ node ,
98
+ context
99
+ . getSourceCode ( )
100
+ . getText ( node )
101
+ . replace ( moduleId , newModuleId )
102
+ ) ,
88
103
message : ERROR_MOVED_FILE (
89
104
withLeadingDot ( relative ( process . cwd ( ) , modulePath ) ) ,
90
105
withLeadingDot ( relative ( process . cwd ( ) , newModulePath ) )
91
106
) ,
92
- node : getSource ( node )
107
+ node
93
108
} ) ;
94
109
}
95
110
} ;
96
111
97
- const requireStrategy : ModuleStrategy = {
98
- getModuleId : ( node : any ) => getIn ( 'arguments.0.value' , node , '' ) ,
99
- getQuotes : ( node : any ) => node . arguments [ 0 ] . raw . charAt ( 0 ) ,
100
- getSource : ( node : any ) => node . arguments [ 0 ]
101
- } ;
102
-
103
- const importStrategy : ModuleStrategy = {
104
- getModuleId : ( node : any ) => node . source . value ,
105
- getQuotes : ( node : any ) => node . source . raw . charAt ( 0 ) ,
106
- getSource : ( node : any ) => node . source
107
- } ;
112
+ const getRequireDepId = ( node : any ) => getIn ( 'arguments.0.value' , node , '' ) ;
113
+ const getImportDepId = ( node : any ) => node . source . value ;
108
114
109
115
const rule : Rule . RuleModule = {
110
116
meta : {
@@ -130,7 +136,14 @@ const rule: Rule.RuleModule = {
130
136
]
131
137
} ,
132
138
create : ( context ) => {
133
- const sourceCode = context . getSourceCode ( ) ;
139
+ const currentFilePath = context . getFilename ( ) ;
140
+
141
+ if ( visitedFiles [ currentFilePath ] ) {
142
+ return { } ;
143
+ }
144
+
145
+ visitedFiles [ currentFilePath ] = true ;
146
+
134
147
const patterns : FileIndex = getIn ( 'options.0.files' , context , { } ) ;
135
148
const files : FileIndex = { } ;
136
149
@@ -147,43 +160,41 @@ const rule: Rule.RuleModule = {
147
160
} ) ;
148
161
} ) ;
149
162
150
- const currentFilePath = context . getFilename ( ) ;
151
163
const dirPath = dirname ( currentFilePath ) ;
152
164
const newFilePath = files [ currentFilePath ] ;
153
165
const isFileBeingMoved = Boolean ( newFilePath ) ;
154
166
155
167
if ( isFileBeingMoved ) {
156
- const fixes : FixList = [ ] ;
157
168
return {
158
169
'CallExpression[callee.name="require"]' ( n : EsTree . Node ) {
159
170
return updateMovedFile (
171
+ context ,
160
172
files ,
161
173
dirPath ,
162
174
newFilePath ,
163
175
n ,
164
- fixes ,
165
- requireStrategy
176
+ getRequireDepId
166
177
) ;
167
178
} ,
168
179
ImportDeclaration ( n : EsTree . Node ) {
169
180
return updateMovedFile (
181
+ context ,
170
182
files ,
171
183
dirPath ,
172
184
newFilePath ,
173
185
n ,
174
- fixes ,
175
- importStrategy
186
+ getImportDepId
176
187
) ;
177
188
} ,
178
189
'Program:exit' ( n : EsTree . Node ) {
179
190
const node = n as EsTree . Program ;
180
191
return context . report ( {
181
192
fix ( fixer ) {
182
- const contents = sourceCode . getText ( ) ;
183
- process . nextTick ( ( ) => removeSync ( currentFilePath ) ) ;
184
- outputFileSync ( newFilePath , contents ) ;
185
- // ESLint's types don't reflect that an array of fixes can be returned
186
- return ( fixes . map ( ( fn ) => fn ( fixer ) ) as unknown ) as Rule . Fix ;
193
+ process . nextTick ( ( ) => {
194
+ copyFileSync ( currentFilePath , newFilePath ) ;
195
+ removeSync ( currentFilePath ) ;
196
+ } ) ;
197
+ return fixer . insertTextAfter ( node , '' ) ;
187
198
} ,
188
199
message : ERROR_MOVED_FILE (
189
200
withLeadingDot ( relative ( process . cwd ( ) , currentFilePath ) ) ,
@@ -197,10 +208,10 @@ const rule: Rule.RuleModule = {
197
208
198
209
return {
199
210
'CallExpression[callee.name="require"]' ( n : EsTree . Node ) {
200
- return updateConsumer ( context , files , dirPath , n , requireStrategy ) ;
211
+ return updateConsumer ( context , files , dirPath , n , getRequireDepId ) ;
201
212
} ,
202
213
ImportDeclaration ( n : EsTree . Node ) {
203
- return updateConsumer ( context , files , dirPath , n , importStrategy ) ;
214
+ return updateConsumer ( context , files , dirPath , n , getImportDepId ) ;
204
215
}
205
216
} ;
206
217
}
0 commit comments