Skip to content

Commit 2751faa

Browse files
committed
Support export * from 'module' ESM syntax
This change adds support for modules that export entities through the `export * from 'module'` ESM syntax. This resolves issue #31.
1 parent 96c506e commit 2751faa

File tree

6 files changed

+79
-11
lines changed

6 files changed

+79
-11
lines changed

hook.js

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,23 +121,63 @@ function createHook (meta) {
121121

122122
const iitmURL = new URL('lib/register.js', meta.url).toString()
123123
async function getSource (url, context, parentGetSource) {
124+
const imports = []
125+
const namespaceIds = []
126+
124127
if (hasIitm(url)) {
125128
const realUrl = deleteIitm(url)
126129
const exportNames = await getExports(realUrl, context, parentGetSource)
130+
const isExportAllLine = /^\* from /
131+
const setters = []
132+
for (const n of exportNames) {
133+
if (isExportAllLine.test(n) === true) {
134+
// Encountered a `export * from 'module'` line. Thus, we need to
135+
// get all exports from the specified module and shim them into the
136+
// current module.
137+
const [_, modFile] = n.split('* from ')
138+
const modName = Buffer.from(modFile, 'hex') + Date.now()
139+
const modUrl = new URL(modFile, url).toString()
140+
const innerExports = await getExports(modUrl, context, parentGetSource)
141+
const innerSetters = []
142+
143+
for (const _n of innerExports) {
144+
innerSetters.push(`
145+
let $${_n} = _.${_n}
146+
export { $${_n} as ${_n} }
147+
set.${_n} = (v) => {
148+
$${_n} = v
149+
return true
150+
}
151+
`)
152+
}
153+
154+
imports.push(`import * as $${modName} from ${JSON.stringify(modUrl)}`)
155+
namespaceIds.push(`$${modName}`)
156+
setters.push(innerSetters.join('\n'))
157+
continue
158+
}
159+
160+
setters.push(`
161+
let $${n} = _.${n}
162+
export { $${n} as ${n} }
163+
set.${n} = (v) => {
164+
$${n} = v
165+
return true
166+
}
167+
`)
168+
}
169+
127170
return {
128171
source: `
129172
import { register } from '${iitmURL}'
130173
import * as namespace from ${JSON.stringify(url)}
174+
${imports.join('\n')}
175+
176+
const _ = Object.assign({}, ...[namespace, ${namespaceIds.join(', ')}])
131177
const set = {}
132-
${exportNames.map((n) => `
133-
let $${n} = namespace.${n}
134-
export { $${n} as ${n} }
135-
set.${n} = (v) => {
136-
$${n} = v
137-
return true
138-
}
139-
`).join('\n')}
140-
register(${JSON.stringify(realUrl)}, namespace, set, ${JSON.stringify(specifiers.get(realUrl))})
178+
179+
${setters.join('\n')}
180+
register(${JSON.stringify(realUrl)}, _, set, ${JSON.stringify(specifiers.get(realUrl))})
141181
`
142182
}
143183
}

lib/get-esm-exports.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function getEsmExports (moduleStr) {
3434
if (node.exported) {
3535
exportedNames.add(node.exported.name)
3636
} else {
37-
exportedNames.add('*')
37+
exportedNames.add(`* from ${node.source.value}`)
3838
}
3939
break
4040
default:

test/fixtures/bundle.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import bar from './something.mjs'
2+
export default bar
3+
export * from './fantasia.mjs'

test/fixtures/esm-exports.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default class { /* … */ } //| default
2323
export default function* () { /* … */ } //| default
2424

2525
// Aggregating modules
26-
export * from "module-name"; //| *
26+
export * from "module-name"; //| * from module-name
2727
export * as name1 from "module-name"; //| name1
2828
export { name1, /* …, */ nameN } from "module-name"; //| name1,nameN
2929
export { import1 as name1, import2 as name2, /* …, */ nameN } from "module-name"; //| name1,name2,nameN

test/fixtures/fantasia.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function sayName() {
2+
return 'Moon Child'
3+
}
4+
5+
export const Morla = 'Ancient one'

test/hook/static-import-star.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { strictEqual } from 'assert'
2+
import Hook from '../../index.js'
3+
Hook((exports, name) => {
4+
if (/bundle\.mjs/.test(name) === false) return
5+
6+
const bar = exports.default
7+
exports.default = function wrappedBar() {
8+
return bar() + '-wrapped'
9+
}
10+
11+
const sayName = exports.sayName
12+
exports.sayName = function wrappedSayName() {
13+
return `Bastion: "${sayName()}"`
14+
}
15+
})
16+
17+
import { default as bar, sayName } from '../fixtures/bundle.mjs'
18+
19+
strictEqual(bar(), '42-wrapped')
20+
strictEqual(sayName(), 'Bastion: "Moon Child"')

0 commit comments

Comments
 (0)