Skip to content

Commit 0552627

Browse files
Merge pull request #8 from sure-thing/lexer
feat(lexer): swap in ex-module-lexer in place of babel
2 parents 1ffd1e9 + 0ede6e6 commit 0552627

File tree

4 files changed

+119
-286
lines changed

4 files changed

+119
-286
lines changed

index.js

+38-40
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ const path = require('path')
33
const { createRequire } = require('module')
44
const debug = require('debug')('wdg')
55
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])[ ]*import(?:["'\s]*([\w*${}\n\r\t, ]+)\s*from\s*)?\s*["'](.*?)["']/gm
10+
const ESM_DYNAMIC_IMPORT_REGEX = /(?<!\.)\bimport\((?:['"].+['"]|`[^$]+`)\)/gm
811

912
function emitter () {
1013
let events = {}
@@ -56,43 +59,38 @@ function clearUp (ids, tree, parentPointers) {
5659
}
5760
}
5861

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+
5982
/*
6083
* Read file, parse, traverse, resolve children modules IDs
6184
*/
62-
function getChildrenModuleIds ({ id, alias }) {
85+
async function getChildrenModuleIds ({ id, alias }) {
6386
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 = []
6888

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+
}
9694

9795
return children
9896
.map(moduleId => {
@@ -167,7 +165,7 @@ module.exports = function graph ({ alias = {} } = {}) {
167165
*
168166
* Also used to walk a leaf directly, in which case bootstrapping will be false
169167
*/
170-
function walk (id, context) {
168+
async function walk (id, context) {
171169
let { entryPointer, parentPointer, visitedIds, bootstrapping } = context
172170

173171
// on first call of walk with a fresh entry
@@ -232,7 +230,7 @@ module.exports = function graph ({ alias = {} } = {}) {
232230

233231
try {
234232
const childModuleIds = isTraversable
235-
? getChildrenModuleIds({ id, alias })
233+
? await getChildrenModuleIds({ id, alias })
236234
: []
237235

238236
// 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 = {} } = {}) {
247245
// walk each child module if it hasn't already been done
248246
for (const _id of childModuleIds) {
249247
if (!parentLeaf.childrenPointers.includes(ids.indexOf(_id))) {
250-
walk(_id, {
248+
await walk(_id, {
251249
entryPointer,
252250
parentPointer: pointer,
253251
visitedIds,
@@ -333,7 +331,7 @@ module.exports = function graph ({ alias = {} } = {}) {
333331
handleChange(file)
334332

335333
// fabricate entry/parent pointers if we're jumping into a leaf and not an entry
336-
walk(file, {
334+
await walk(file, {
337335
visitedIds: [],
338336
entryPointer: isEntry ? undefined : tree[file].entryPointers[0],
339337
parentPointer: isEntry ? undefined : tree[file].parentPointers[0]
@@ -356,7 +354,7 @@ module.exports = function graph ({ alias = {} } = {}) {
356354
watcher.removeAll()
357355
watcher.removeAllListeners()
358356
},
359-
add (files) {
357+
async add (files) {
360358
files = [].concat(files).filter(entry => {
361359
// filter out any already watched files
362360
if (entryIds.includes(entry)) return false
@@ -370,7 +368,7 @@ module.exports = function graph ({ alias = {} } = {}) {
370368

371369
// walk each entry
372370
for (const id of entryIds) {
373-
walk(id, {
371+
await walk(id, {
374372
visitedIds: [],
375373
bootstrapping: true
376374
})

package-lock.json

+19-156
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)