Skip to content

Commit bf3a4fb

Browse files
authored
fix: Resolve re-exports of external modules (#78)
Fixes (potentially) #59, #62, #63 `parentResolve` is cached and later used when attempting to load bare specifiers.
1 parent 0d9f351 commit bf3a4fb

File tree

7 files changed

+69
-2
lines changed

7 files changed

+69
-2
lines changed

hook.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc.
44

55
const { randomBytes } = require('crypto')
6+
const { URL } = require('url')
67
const specifiers = new Map()
78
const isWin = process.platform === 'win32'
89

@@ -91,6 +92,30 @@ function isStarExportLine (line) {
9192
return /^\* from /.test(line)
9293
}
9394

95+
function isBareSpecifier (specifier) {
96+
// Relative and absolute paths are not bare specifiers.
97+
if (
98+
specifier.startsWith('.') ||
99+
specifier.startsWith('/')) {
100+
return false
101+
}
102+
103+
// Valid URLs are not bare specifiers. (file:, http:, node:, etc.)
104+
105+
// eslint-disable-next-line no-prototype-builtins
106+
if (URL.hasOwnProperty('canParse')) {
107+
return !URL.canParse(specifier)
108+
}
109+
110+
try {
111+
// eslint-disable-next-line no-new
112+
new URL(specifier)
113+
return false
114+
} catch (err) {
115+
return true
116+
}
117+
}
118+
94119
/**
95120
* @typedef {object} ProcessedModule
96121
* @property {string[]} imports A set of ESM import lines to be added to the
@@ -128,6 +153,7 @@ async function processModule ({
128153
srcUrl,
129154
context,
130155
parentGetSource,
156+
parentResolve,
131157
ns = 'namespace',
132158
defaultAs = 'default'
133159
}) {
@@ -154,13 +180,22 @@ async function processModule ({
154180
if (isStarExportLine(n) === true) {
155181
const [, modFile] = n.split('* from ')
156182
const normalizedModName = normalizeModName(modFile)
157-
const modUrl = new URL(modFile, srcUrl).toString()
158183
const modName = Buffer.from(modFile, 'hex') + Date.now() + randomBytes(4).toString('hex')
159184

185+
let modUrl
186+
if (isBareSpecifier(modFile)) {
187+
// Bare specifiers need to be resolved relative to the parent module.
188+
const result = await parentResolve(modFile, { parentURL: srcUrl })
189+
modUrl = result.url
190+
} else {
191+
modUrl = new URL(modFile, srcUrl).href
192+
}
193+
160194
const data = await processModule({
161195
srcUrl: modUrl,
162196
context,
163197
parentGetSource,
198+
parentResolve,
164199
ns: `$${modName}`,
165200
defaultAs: normalizedModName
166201
})
@@ -229,7 +264,9 @@ function addIitm (url) {
229264
}
230265

231266
function createHook (meta) {
267+
let cachedResolve
232268
async function resolve (specifier, context, parentResolve) {
269+
cachedResolve = parentResolve
233270
const { parentURL = '' } = context
234271
const newSpecifier = deleteIitm(specifier)
235272
if (isWin && parentURL.indexOf('file:node') === 0) {
@@ -278,7 +315,8 @@ function createHook (meta) {
278315
const { imports, namespaces, setters: mapSetters } = await processModule({
279316
srcUrl: realUrl,
280317
context,
281-
parentGetSource
318+
parentGetSource,
319+
parentResolve: cachedResolve
282320
})
283321
const setters = Array.from(mapSetters.values())
284322

test/fixtures/node_modules/some-external-module/index.mjs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/some-external-module/package.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/node_modules/some-external-module/sub.mjs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from 'some-external-module'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from 'some-external-module/sub'

test/hook/re-export-star-module.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Hook from '../../index.js'
2+
import { foo } from '../fixtures/re-export-star-external.mjs'
3+
import { bar } from '../fixtures/sub-directory/re-export-star-external.mjs'
4+
import { strictEqual } from 'assert'
5+
6+
Hook((exports, name) => {
7+
if (name.endsWith('fixtures/re-export-star-external.mjs')) {
8+
exports.foo = '1'
9+
}
10+
11+
if (name.endsWith('fixtures/sub-directory/re-export-star-external.mjs')) {
12+
exports.bar = '2'
13+
}
14+
})
15+
16+
strictEqual(foo, '1')
17+
strictEqual(bar, '2')

0 commit comments

Comments
 (0)