@@ -3,8 +3,11 @@ const path = require('path')
3
3
const { createRequire } = require ( 'module' )
4
4
const debug = require ( 'debug' ) ( 'wdg' )
5
5
const filewatcher = require ( 'filewatcher' )
6
- const parser = require ( '@babel/parser' )
7
- const traverse = require ( '@babel/traverse' ) . default
6
+ const { parse } = require ( 'es-module-lexer' )
7
+ const stripComments = require ( 'strip-comments' )
8
+
9
+ const ESM_IMPORT_REGEX = / (?< ! [ ^ ; \n ] ) [ ] * i m p o r t (?: [ " ' \s ] * ( [ \w * $ { } \n \r \t , ] + ) \s * f r o m \s * ) ? \s * [ " ' ] ( .* ?) [ " ' ] / gm
10
+ const ESM_DYNAMIC_IMPORT_REGEX = / (?< ! \. ) \b i m p o r t \( (?: [ ' " ] .+ [ ' " ] | ` [ ^ $ ] + ` ) \) / gm
8
11
9
12
function emitter ( ) {
10
13
let events = { }
@@ -56,43 +59,38 @@ function clearUp (ids, tree, parentPointers) {
56
59
}
57
60
}
58
61
62
+ /**
63
+ * Lifted from snowpack, props to their team
64
+ *
65
+ * @see https://github.com/snowpackjs/snowpack/blob/f75de1375fe14155674112d88bf211ca3721ac7c/snowpack/src/scan-imports.ts#L119
66
+ */
67
+ function cleanCodeForParsing ( code ) {
68
+ code = stripComments ( code )
69
+ const allMatches = [ ]
70
+ let match
71
+ const importRegex = new RegExp ( ESM_IMPORT_REGEX )
72
+ while ( ( match = importRegex . exec ( code ) ) ) {
73
+ allMatches . push ( match )
74
+ }
75
+ const dynamicImportRegex = new RegExp ( ESM_DYNAMIC_IMPORT_REGEX )
76
+ while ( ( match = dynamicImportRegex . exec ( code ) ) ) {
77
+ allMatches . push ( match )
78
+ }
79
+ return allMatches . map ( ( [ full ] ) => full ) . join ( '\n' )
80
+ }
81
+
59
82
/*
60
83
* Read file, parse, traverse, resolve children modules IDs
61
84
*/
62
- function getChildrenModuleIds ( { id, alias } ) {
85
+ async function getChildrenModuleIds ( { id, alias } ) {
63
86
const raw = fs . readFileSync ( id , 'utf-8' )
64
- const ast = parser . parse ( raw , {
65
- sourceType : 'module' ,
66
- plugins : [ 'jsx' , 'typescript' , 'dynamicImport' ]
67
- } )
87
+ let children = [ ]
68
88
69
- const children = [ ]
70
-
71
- traverse ( ast , {
72
- enter ( path ) {
73
- if ( path . node . type === 'CallExpression' ) {
74
- const callee = path . get ( 'callee' )
75
- const isDynamicImport = callee . isImport ( )
76
- if ( callee . isIdentifier ( { name : 'require' } ) || isDynamicImport ) {
77
- const arg = path . node . arguments [ 0 ]
78
- if ( arg . type === 'StringLiteral' ) {
79
- children . push ( arg . value )
80
- } else {
81
- children . push ( src . slice ( arg . start , arg . end ) )
82
- }
83
- }
84
- } else if (
85
- path . node . type === 'ImportDeclaration' ||
86
- path . node . type === 'ExportNamedDeclaration' ||
87
- path . node . type === 'ExportAllDeclaration'
88
- ) {
89
- const { source } = path . node
90
- if ( source && source . value ) {
91
- children . push ( source . value )
92
- }
93
- }
94
- }
95
- } )
89
+ try {
90
+ children = ( await parse ( raw ) ) [ 0 ] . map ( i => i . n )
91
+ } catch ( e ) {
92
+ children = ( await parse ( cleanCodeForParsing ( raw ) ) ) [ 0 ] . map ( i => i . n )
93
+ }
96
94
97
95
return children
98
96
. map ( moduleId => {
@@ -167,7 +165,7 @@ module.exports = function graph ({ alias = {} } = {}) {
167
165
*
168
166
* Also used to walk a leaf directly, in which case bootstrapping will be false
169
167
*/
170
- function walk ( id , context ) {
168
+ async function walk ( id , context ) {
171
169
let { entryPointer, parentPointer, visitedIds, bootstrapping } = context
172
170
173
171
// on first call of walk with a fresh entry
@@ -232,7 +230,7 @@ module.exports = function graph ({ alias = {} } = {}) {
232
230
233
231
try {
234
232
const childModuleIds = isTraversable
235
- ? getChildrenModuleIds ( { id, alias } )
233
+ ? await getChildrenModuleIds ( { id, alias } )
236
234
: [ ]
237
235
238
236
// if this isn't the first time we've traversed this leaf, check for any removed modules
@@ -247,7 +245,7 @@ module.exports = function graph ({ alias = {} } = {}) {
247
245
// walk each child module if it hasn't already been done
248
246
for ( const _id of childModuleIds ) {
249
247
if ( ! parentLeaf . childrenPointers . includes ( ids . indexOf ( _id ) ) ) {
250
- walk ( _id , {
248
+ await walk ( _id , {
251
249
entryPointer,
252
250
parentPointer : pointer ,
253
251
visitedIds,
@@ -333,7 +331,7 @@ module.exports = function graph ({ alias = {} } = {}) {
333
331
handleChange ( file )
334
332
335
333
// fabricate entry/parent pointers if we're jumping into a leaf and not an entry
336
- walk ( file , {
334
+ await walk ( file , {
337
335
visitedIds : [ ] ,
338
336
entryPointer : isEntry ? undefined : tree [ file ] . entryPointers [ 0 ] ,
339
337
parentPointer : isEntry ? undefined : tree [ file ] . parentPointers [ 0 ]
@@ -356,7 +354,7 @@ module.exports = function graph ({ alias = {} } = {}) {
356
354
watcher . removeAll ( )
357
355
watcher . removeAllListeners ( )
358
356
} ,
359
- add ( files ) {
357
+ async add ( files ) {
360
358
files = [ ] . concat ( files ) . filter ( entry => {
361
359
// filter out any already watched files
362
360
if ( entryIds . includes ( entry ) ) return false
@@ -370,7 +368,7 @@ module.exports = function graph ({ alias = {} } = {}) {
370
368
371
369
// walk each entry
372
370
for ( const id of entryIds ) {
373
- walk ( id , {
371
+ await walk ( id , {
374
372
visitedIds : [ ] ,
375
373
bootstrapping : true
376
374
} )
0 commit comments