diff --git a/package-lock.json b/package-lock.json index 2c055f5..aaaeb0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@checkdigit/typescript-config", - "version": "10.1.1", + "version": "10.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@checkdigit/typescript-config", - "version": "10.1.1", + "version": "10.2.0", "license": "MIT", "bin": { "builder": "bin/builder.mjs" diff --git a/package.json b/package.json index c990cbc..18282f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@checkdigit/typescript-config", - "version": "10.1.1", + "version": "10.2.0", "description": "Check Digit standard Typescript configuration", "homepage": "https://github.com/checkdigit/typescript-config#readme", "bugs": { diff --git a/src/compile.spec.ts b/src/compile.spec.ts index 2fcbdda..fedfe1d 100644 --- a/src/compile.spec.ts +++ b/src/compile.spec.ts @@ -10,11 +10,12 @@ import compile from './compile.ts'; const commonJsCompatabilityBanner = `import { createRequire as __createRequire } from "node:module"; import { fileURLToPath as __fileURLToPath } from "node:url"; -import { default as __path } from "node:path"; const __filename = __fileURLToPath(import.meta.url); -const __dirname = __path.dirname(__filename); const require = __createRequire(import.meta.url);`; +const dirnameInject = `import path from "node:path"; +var __dirname = path.dirname(__filename);`; + const singleModule = { [`index.ts`]: `export const hello = 'world';`, }; @@ -271,6 +272,7 @@ describe('compile', () => { assert.deepEqual(await read(outDir), { 'two-modules.mjs': `${commonJsCompatabilityBanner}\n\n` + + `${dirnameInject}\n\n` + `var hello = "world";\n` + `\n` + `var two_modules_default = hello + "world";\n` + @@ -282,6 +284,41 @@ describe('compile', () => { assert.equal(output.default, 'worldworld'); }); + it('should bundle an ESM module that declares __dirname', async () => { + const id = crypto.randomUUID(); + const inDir = path.join(os.tmpdir(), `in-dir-${id}`, 'src'); + const outDir = path.join(os.tmpdir(), `out-dir-${id}`, 'build'); + await writeInput(inDir, { + 'index.ts': ` +const __dirname = 'declared in project'; +export const localDirname: string = __dirname; +export { injectedDirname } from './dependency.ts'; +`, + 'dependency.ts': `export const injectedDirname: string = __dirname;`, + }); + await writeOutput( + await compile({ + type: 'module', + entryPoint: 'index.ts', + outFile: 'index.mjs', + inDir, + outDir, + }), + ); + + const output = await import(path.join(outDir, 'index.mjs')); + assert.deepEqual( + { + injectedDirname: output.injectedDirname as unknown, + localDirname: output.localDirname as unknown, + }, + { + injectedDirname: await fs.realpath(outDir), + localDirname: 'declared in project', + }, + ); + }); + it('should bundle an ESM module that imports external modules', async () => { const id = crypto.randomUUID(); const moduleDir = path.join(os.tmpdir(), `in-dir-${id}`); @@ -301,6 +338,7 @@ describe('compile', () => { assert.deepEqual(await read(outDir), { 'index.mjs': `${commonJsCompatabilityBanner}\n\n` + + `${dirnameInject}\n\n` + `var hello = "world";\n` + `\n` + `import util from "node:util";\n` + @@ -334,6 +372,7 @@ describe('compile', () => { assert.deepEqual(convert(result.outputFiles), { 'index.mjs': `${commonJsCompatabilityBanner}\n\n` + + `${dirnameInject}\n\n` + `import { hello as test } from "test-esm-module";\n` + `import util from "node:util";\n` + `var hello = { test, message: util.format("hello %s", "world") };\n` + diff --git a/src/compile.ts b/src/compile.ts index cc8ea57..2ed4520 100644 --- a/src/compile.ts +++ b/src/compile.ts @@ -13,11 +13,14 @@ import tsConfigJson from '../tsconfig.json' with { type: 'json' }; const commonJsCompatabilityBanner = `import { createRequire as __createRequire } from "node:module"; import { fileURLToPath as __fileURLToPath } from "node:url"; -import { default as __path } from "node:path"; const __filename = __fileURLToPath(import.meta.url); -const __dirname = __path.dirname(__filename); const require = __createRequire(import.meta.url);`; +const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const dirNameJsCompatibilityInjection = `import path from 'node:path'; +export const __dirname = path.dirname(__filename);`; // relies on __filename from commonJsCompatabilityBanner +const dirNameJsCompatibilityInjectionURL = `data:text/javascript,${encodeURIComponent(dirNameJsCompatibilityInjection)}`; + export type ImportKind = | 'entry-point' | 'import-statement' @@ -274,7 +277,7 @@ export default async function ({ tsConfigJson, typescript.sys, // @checkdigit/typescript-config package root: - path.dirname(path.dirname(fileURLToPath(import.meta.url))), + packageRoot, ).options; const program = typescript.createProgram(productionSourceFiles, { ...compilerOptions, @@ -337,6 +340,7 @@ export default async function ({ metafile: outFile !== undefined, sourcesContent: false, logLevel: 'error', + inject: outFile === undefined ? [] : [dirNameJsCompatibilityInjectionURL], banner: outFile === undefined ? {}