@@ -8,7 +8,11 @@ const firstNode = (c) => c.at(0).nodes()[0]
88const lastNode = ( c ) => c . at ( - 1 ) . nodes ( ) [ 0 ]
99
1010module . exports = function addImports ( root , _statements ) {
11- const found = findImports ( root , _statements )
11+ const statements = Array . isArray ( _statements ) ? _statements : [ _statements ]
12+ const found = findImports (
13+ root ,
14+ statements . filter ( ( s ) => s . type !== 'ExpressionStatement' )
15+ )
1216 for ( const name in found ) {
1317 if ( found [ name ] . type === 'Identifier' ) found [ name ] = found [ name ] . name
1418 else delete found [ name ]
@@ -31,7 +35,6 @@ module.exports = function addImports(root, _statements) {
3135 } catch ( error ) {
3236 // ignore
3337 }
34- const statements = Array . isArray ( _statements ) ? _statements : [ _statements ]
3538
3639 const preventNameConflict = babelScope
3740 ? ( _id ) => {
@@ -54,12 +57,18 @@ module.exports = function addImports(root, _statements) {
5457 return id
5558 }
5659
57- statements . forEach ( ( statement ) => {
60+ for ( const statement of statements ) {
5861 if ( statement . type === 'ImportDeclaration' ) {
5962 const { importKind } = statement
6063 const source = { value : statement . source . value }
6164 const filter = { source }
6265 if ( ! definitelyFlow ) filter . importKind = importKind
66+ if ( ! statement . specifiers . length ) {
67+ if ( ! isSourceImported ( root , statement . source . value ) ) {
68+ addStatements ( root , statement )
69+ }
70+ continue
71+ }
6372 let existing = root . find ( j . ImportDeclaration , filter )
6473 for ( let specifier of statement . specifiers ) {
6574 if ( found [ specifier . local . name ] ) continue
@@ -99,12 +108,7 @@ module.exports = function addImports(root, _statements) {
99108 j . stringLiteral ( statement . source . value ) ,
100109 importKind
101110 )
102- const allImports = root . find ( j . ImportDeclaration )
103- if ( allImports . size ( ) ) {
104- lastPath ( allImports ) . insertAfter ( newDeclaration )
105- } else {
106- insertProgramStatement ( root , newDeclaration )
107- }
111+ addStatements ( root , newDeclaration )
108112 existing = root . find ( j . ImportDeclaration , { source } )
109113 }
110114 }
@@ -118,6 +122,7 @@ module.exports = function addImports(root, _statements) {
118122 } ,
119123 init : {
120124 type : 'CallExpression' ,
125+ callee : { type : 'Identifier' , name : 'require' } ,
121126 arguments : [ { value : declarator . init . arguments [ 0 ] . value } ] ,
122127 } ,
123128 } )
@@ -139,12 +144,7 @@ module.exports = function addImports(root, _statements) {
139144 const newDeclaration = j . variableDeclaration ( 'const' , [
140145 j . variableDeclarator ( j . objectPattern ( [ prop ] ) , declarator . init ) ,
141146 ] )
142- const allImports = root . find ( j . ImportDeclaration )
143- if ( allImports . size ( ) ) {
144- lastPath ( allImports ) . insertAfter ( newDeclaration )
145- } else {
146- insertProgramStatement ( root , newDeclaration )
147- }
147+ addStatements ( root , newDeclaration )
148148 }
149149 }
150150 } else if ( declarator . id . type === 'Identifier' ) {
@@ -155,21 +155,86 @@ module.exports = function addImports(root, _statements) {
155155 const newDeclaration = j . variableDeclaration ( 'const' , [
156156 j . variableDeclarator ( declarator . id , declarator . init ) ,
157157 ] )
158- const allImports = root . find ( j . ImportDeclaration )
159- if ( allImports . size ( ) ) {
160- lastPath ( allImports ) . insertAfter ( newDeclaration )
161- } else {
162- insertProgramStatement ( root , newDeclaration )
163- }
158+ addStatements ( root , newDeclaration )
164159 }
165160 }
166161 } )
162+ } else if ( statement . type === 'ExpressionStatement' ) {
163+ if ( isNodeRequireCall ( statement . expression ) ) {
164+ if ( ! isSourceImported ( root , getSource ( statement . expression ) ) ) {
165+ addStatements ( root , statement )
166+ }
167+ } else {
168+ throw new Error ( `statement must be an import or require` )
169+ }
167170 }
168- } )
171+ }
169172
170173 return found
171174}
172175
176+ function findTopLevelImports ( root , predicate = ( ) => true ) {
177+ const program = root . find ( j . Program ) . at ( 0 ) . paths ( ) [ 0 ]
178+ if ( ! program ) return [ ]
179+ return j (
180+ program
181+ . get ( 'body' )
182+ . filter ( ( p ) => p . node . type === 'ImportDeclaration' && predicate ( p ) )
183+ )
184+ }
185+
186+ function isNodeRequireCall ( node ) {
187+ return (
188+ node . type === 'CallExpression' &&
189+ node . callee . type === 'Identifier' &&
190+ node . callee . name === 'require' &&
191+ node . arguments [ 0 ] &&
192+ ( node . arguments [ 0 ] . type === 'StringLiteral' ||
193+ node . arguments [ 0 ] . type === 'Literal' )
194+ )
195+ }
196+
197+ function isPathRequireCall ( path ) {
198+ return isNodeRequireCall ( path . node ) && ! path . scope . lookup ( 'require' )
199+ }
200+
201+ function findTopLevelRequires ( root , predicate = ( ) => true ) {
202+ const paths = [ ]
203+ const program = root . find ( j . Program ) . at ( 0 ) . paths ( ) [ 0 ]
204+ if ( program ) {
205+ program . get ( 'body' ) . each ( ( path ) => {
206+ if ( path . node . type === 'ExpressionStatement' ) {
207+ const expression = path . get ( 'expression' )
208+ if ( isPathRequireCall ( expression ) && predicate ( expression ) )
209+ paths . push ( expression )
210+ } else if ( path . node . type === 'VariableDeclaration' ) {
211+ for ( const declaration of path . get ( 'declarations' ) ) {
212+ const init = declaration . get ( 'init' )
213+ if ( isPathRequireCall ( init ) && predicate ( init ) ) paths . push ( init )
214+ }
215+ }
216+ } )
217+ }
218+ return j ( paths )
219+ }
220+
221+ function getSource ( node ) {
222+ if ( node . type === 'ImportDeclaration' ) return node . source . value
223+ if ( isNodeRequireCall ( node ) ) {
224+ const arg = node . arguments [ 0 ]
225+ if ( arg && ( arg . type === 'Literal' || arg . type === 'StringLiteral' ) )
226+ return arg . value
227+ }
228+ }
229+
230+ function isSourceImported ( root , source ) {
231+ const hasSource = ( p ) => getSource ( p . node ) === source
232+ return (
233+ findTopLevelImports ( root , hasSource ) . size ( ) ||
234+ findTopLevelRequires ( root , hasSource ) . size ( )
235+ )
236+ }
237+
173238function insertProgramStatement ( root , ...statements ) {
174239 const program = root . find ( j . Program ) . at ( 0 ) . nodes ( ) [ 0 ]
175240 const firstProgramStatement = program . body [ 0 ]
@@ -193,3 +258,11 @@ function insertProgramStatement(root, ...statements) {
193258 }
194259 program . body . unshift ( ...statements )
195260}
261+
262+ function addStatements ( root , ...statements ) {
263+ const imports = findTopLevelImports ( root )
264+ if ( imports . size ( ) ) {
265+ const last = lastPath ( imports )
266+ for ( const statement of statements . reverse ( ) ) last . insertAfter ( statement )
267+ } else insertProgramStatement ( root , ...statements )
268+ }
0 commit comments