diff --git a/hook.js b/hook.js index 7b430d1..8e534e0 100644 --- a/hook.js +++ b/hook.js @@ -264,14 +264,16 @@ function createHook (meta) { async function getSource (url, context, parentGetSource) { if (hasIitm(url)) { const realUrl = deleteIitm(url) - const setters = await processModule({ - srcUrl: realUrl, - context, - parentGetSource, - parentResolve: cachedResolve - }) - return { - source: ` + + try { + const setters = await processModule({ + srcUrl: realUrl, + context, + parentGetSource, + parentResolve: cachedResolve + }) + return { + source: ` import { register } from '${iitmURL}' import * as namespace from ${JSON.stringify(realUrl)} @@ -286,6 +288,25 @@ ${Array.from(setters.values()).join('\n')} register(${JSON.stringify(realUrl)}, _, set, ${JSON.stringify(specifiers.get(realUrl))}) ` + } + } catch (cause) { + // If there are other ESM loader hooks registered as well as iitm, + // depending on the order they are registered, source might not be + // JavaScript. + // + // If we fail to parse a module for exports, we should fall back to the + // parent loader. These modules will not be wrapped with proxies and + // cannot be Hook'ed but at least this does not take down the entire app + // and block iitm from being used. + // + // We log the error because there might be bugs in iitm and without this + // it would be very tricky to debug + const err = new Error(`'import-in-the-middle' failed to wrap '${realUrl}'`) + err.cause = cause + console.warn(err) + + // Revert back to the non-iitm URL + url = realUrl } } diff --git a/test/other/executable b/test/fixtures/executable similarity index 100% rename from test/other/executable rename to test/fixtures/executable diff --git a/test/fixtures/json-attributes.mjs b/test/fixtures/json-attributes.mjs new file mode 100644 index 0000000..f3a807a --- /dev/null +++ b/test/fixtures/json-attributes.mjs @@ -0,0 +1,5 @@ +import coolFile from './something.json' with { type: 'json' } + +export default { + data: coolFile.data +} diff --git a/test/hook/v20.10-static-import-attributes.mjs b/test/hook/v20.10-static-import-attributes.mjs new file mode 100644 index 0000000..687cca3 --- /dev/null +++ b/test/hook/v20.10-static-import-attributes.mjs @@ -0,0 +1,17 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. + +import jsonMjs from '../fixtures/json-attributes.mjs' +import { strictEqual } from 'assert' + +// Acorn does not support import attributes so an error is logged but the import +// still works! +// +// Hook((exports, name) => { +// if (name.match(/json\.mjs/)) { +// exports.default.data += '-dawg' +// } +// }) + +strictEqual(jsonMjs.data, 'dog') diff --git a/test/other/import-executable.mjs b/test/other/import-executable.mjs deleted file mode 100644 index baa3ddf..0000000 --- a/test/other/import-executable.mjs +++ /dev/null @@ -1,19 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License. -// -// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. - -import { rejects } from 'assert' -(async () => { - const [processMajor, processMinor] = process.versions.node.split('.').map(Number) - const extensionlessSupported = processMajor >= 21 || - (processMajor === 20 && processMinor >= 10) || - (processMajor === 18 && processMinor >= 19) - if (extensionlessSupported) { - // Files without extension are supported in Node.js ^21, ^20.10.0, and ^18.19.0 - return - } - await rejects(() => import('./executable'), { - name: 'TypeError', - code: 'ERR_UNKNOWN_FILE_EXTENSION' - }) -})() diff --git a/test/other/v20.10-import-executable.mjs b/test/other/v20.10-import-executable.mjs new file mode 100644 index 0000000..4533c2a --- /dev/null +++ b/test/other/v20.10-import-executable.mjs @@ -0,0 +1,8 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2.0 License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021 Datadog, Inc. + +(async () => { + const lib = await import('../fixtures/executable') + console.assert(lib) +})()