-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[EngSys] add a workaround to load ESM tests for Mocha (#28556)
Currently most of our '*.js' tests and sources under dist-esm/ are transformed by `esm` package (now outdated and archived) before being feeded to Mocha because when a package.json has `"type": "commonjs"` thus Mocha treats `.js` files as CommonJS. This PR takes another approach by telling Mocha that the loaded modules under dist-esm/ are ESM via a custom loader (`esm4mocha.mjs`). This is done mainly in two parts: 1. During resolving, this loader checks the path of resolved modules and if it includes `"dist-esm/"` we set the format of the resolved module to `"module"`. 2. Also in ESM the full extension is required for relative modules in import/export declarations, so a transformation is applied to add `".js"` extension to relative imports/exports on the fly when loading the module. ServiceBus integration-test:node script has been updated to use this approach. For now, the dev-tool command is unrolled and the old workaround is replaced by the new one: ```diff - -r ../../../common/tools/esm-workaround -r esm + --loader=../../../common/tools/esm4mocha.mjs ``` In the future, the dev-tool command would be updated to use the new workaround for the majority of our packages.
- Loading branch information
1 parent
b0f24ec
commit b82222b
Showing
5 changed files
with
126 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
import { readFile, stat, constants } from "node:fs/promises"; | ||
import { dirname, join } from "node:path"; | ||
import { fileURLToPath } from "url"; | ||
import { Project } from "./dev-tool/node_modules/ts-morph/dist/ts-morph.js"; | ||
|
||
// if modules are loaded from dist-esm/ treat them as ESM | ||
export async function resolve(specifier, context, defaultResolve) { | ||
consoleDir({ label: "resolving", specifier, context }); | ||
const resolved = await defaultResolve(specifier, context, defaultResolve); | ||
consoleDir({ resolved }); | ||
if (resolved.url.includes("dist-esm/")) { | ||
resolved.format = "module"; | ||
} | ||
return resolved; | ||
} | ||
|
||
// if modules are loaded from dist-esm/ transform code to have ".js" extension | ||
// for relative imports/exports as that is required by ESM. | ||
export async function load(url, context, defaultLoad) { | ||
consoleDir({ label: "loading...", url, context }); | ||
if (url.includes("dist-esm/")) { | ||
const path = fileURLToPath(url); | ||
const source = await readFile(path); | ||
const transformed = await addJsExtensionToRelativeModules(source, path); | ||
return { | ||
format: context.format, | ||
source: transformed, | ||
shortCircuit: true, | ||
}; | ||
} | ||
const { source } = await defaultLoad(url, context, defaultLoad); | ||
return { format: context.format, source, shortCircuit: true }; | ||
} | ||
|
||
async function updateSpecifierValueIfRelative(declaration, base) { | ||
const specifierValue = declaration.getModuleSpecifierValue(); | ||
consoleDir({ base, specifierValue }); | ||
if (specifierValue?.startsWith(".")) { | ||
const sourcePath = join(base, specifierValue); | ||
try { | ||
const s = await stat(sourcePath, constants.R_OK); | ||
consoleDir({ sourcePath }); | ||
if (s.isDirectory()) { | ||
declaration.setModuleSpecifier(`${specifierValue}/index.js`); | ||
} else { | ||
declaration.setModuleSpecifier(`${specifierValue}.js`); | ||
} | ||
} catch (ex) { | ||
consoleDir({ error: ex }); | ||
declaration.setModuleSpecifier(`${specifierValue}.js`); | ||
} | ||
consoleLog(` specifier updated to ${declaration.getModuleSpecifierValue()}`); | ||
} | ||
} | ||
|
||
async function addJsExtensionToRelativeModules(source, path) { | ||
const base = dirname(path); | ||
const project = new Project({ useInMemoryFileSystem: true }); | ||
const text = source.toString("utf-8"); | ||
const f = project.createSourceFile("file.ts", text); | ||
const imports = f.getImportDeclarations(); | ||
const exports = f.getExportDeclarations(); | ||
consoleDir({ | ||
l: "input", | ||
path, | ||
head: text.substring(0, 1200), | ||
imports: imports.map((i) => i.getText()), | ||
exports: exports.map((e) => e.getText()), | ||
}); | ||
for (const i of imports) { | ||
await updateSpecifierValueIfRelative(i, base); | ||
} | ||
for (const e of exports) { | ||
await updateSpecifierValueIfRelative(e, base); | ||
} | ||
|
||
consoleDir({ l: "output", head: f.getFullText().substring(0, 1200) }); | ||
return f.getFullText(); | ||
} | ||
|
||
function consoleLog(...args) { | ||
if (process.env.DEBUG && process.env.DEBUG.startsWith("esm4mocha")) { | ||
console.log(args); | ||
} | ||
} | ||
|
||
function consoleDir(...args) { | ||
if (process.env.DEBUG && process.env.DEBUG.startsWith("esm4mocha")) { | ||
console.dir(args); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters