diff --git a/package-lock.json b/package-lock.json index c19fcb3..f62f2c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@knighted/module", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@knighted/module", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT", "dependencies": { "magic-string": "^0.30.21", diff --git a/package.json b/package.json index 7a794db..361e31e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@knighted/module", - "version": "1.2.0", + "version": "1.2.1", "description": "Bidirectional transform for ES modules and CommonJS.", "type": "module", "main": "dist/module.js", diff --git a/src/format.ts b/src/format.ts index c15971f..12fbe89 100644 --- a/src/format.ts +++ b/src/format.ts @@ -805,7 +805,10 @@ const format = async (src: string, ast: ParseResult, opts: FormatterOptions) => seen.add(propName) if (rhs.type === 'Identifier') { const rhsId = rhsSourceFor(rhs) - if (rhsId === rhs.name) { + const rhsName = rhs.name + if (rhsId === rhsName && rhsName === propName) { + exportsOut.push(`export { ${propName} };`) + } else if (rhsId === rhsName) { exportsOut.push(`export { ${rhsId} as ${propName} };`) } else { exportsOut.push(`export const ${propName} = ${rhsId};`) @@ -815,7 +818,12 @@ const format = async (src: string, ast: ParseResult, opts: FormatterOptions) => } } - replacements.push({ start: write.start, end: write.end }) + // Trim trailing whitespace and one optional semicolon so the idiomatic export + // replacement does not leave the original `;` behind (avoids emitting `;;`). + let end = write.end + while (end < src.length && (src[end] === ' ' || src[end] === '\t')) end++ + if (end < src.length && src[end] === ';') end++ + replacements.push({ start: write.start, end }) } if (!seen.size) return { ok: false, reason: 'no-seen' } diff --git a/test/fixtures/idiomaticShorthand.cjs b/test/fixtures/idiomaticShorthand.cjs new file mode 100644 index 0000000..b45e061 --- /dev/null +++ b/test/fixtures/idiomaticShorthand.cjs @@ -0,0 +1,5 @@ +function example() { + return 'ok' +} + +exports.example = example diff --git a/test/module.ts b/test/module.ts index 768d42d..00ee8a5 100644 --- a/test/module.ts +++ b/test/module.ts @@ -1179,6 +1179,26 @@ describe('@knighted/module', () => { assert.equal((mod as any).bar(), 'bar') }) + it('emits shorthand exports when names match', async t => { + const fixturePath = join(fixtures, 'idiomaticShorthand.cjs') + const outFile = join(fixtures, 'idiomaticShorthand.mjs') + + t.after(() => rm(outFile, { force: true })) + + const result = await transform(fixturePath, { target: 'module' }) + await writeFile(outFile, result) + + assert.equal(result.includes('__exports'), false) + assert.ok(result.includes('export { example };')) + assert.equal(result.includes(';;'), false) + + const { status } = spawnSync('node', [outFile], { stdio: 'inherit' }) + assert.equal(status, 0) + + const mod = await import(pathToFileURL(outFile).href) + assert.equal((mod as any).example(), 'ok') + }) + it('respects idiomaticExports: off and keeps helper bag', async t => { const fixturePath = join(fixtures, 'idiomaticSafe.cjs') const outFile = join(fixtures, 'idiomaticOff.mjs')