From b0235196ee14ca7d4206ba9c1d1c444cf516ab0f Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:03:56 +0100 Subject: [PATCH 01/12] modern dual --- src/commands/build.ts | 101 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 12 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 2c04686d..a29dcf07 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -43,7 +43,7 @@ const filesToExcludeFromDist = [ const moduleMappings = { esm: 'es2022', - cjs: 'commonjs', + cjs: 'nodenext', } as const; function typeScriptCompilerOptions(target: 'esm' | 'cjs'): Record { @@ -89,17 +89,22 @@ async function buildTypeScript( reporter, ); - assertTypeScriptBuildResult( - await execa('npx', [ - 'tsc', - ...(tsconfig ? ['--project', tsconfig] : []), - ...compilerOptionsToArgs(typeScriptCompilerOptions('cjs')), - ...(options.incremental ? ['--incremental'] : []), - '--outDir', - join(buildPath, 'cjs'), - ]), - reporter, - ); + const revertPackageJsonsType = await setPackageJsonsType(options.cwd, 'commonjs'); + try { + assertTypeScriptBuildResult( + await execa('npx', [ + 'tsc', + ...(tsconfig ? ['--project', tsconfig] : []), + ...compilerOptionsToArgs(typeScriptCompilerOptions('cjs')), + ...(options.incremental ? ['--incremental'] : []), + '--outDir', + join(buildPath, 'cjs'), + ]), + reporter, + ); + } finally { + await revertPackageJsonsType(); + } } export const buildCommand = createCommand< @@ -479,6 +484,78 @@ export function validatePackageJson( } } +type PackageJsonType = 'module' | 'commonjs'; + +/** + * Sets the {@link filePath package.json} `"type"` field to the defined {@link type} + * returning a "revert" function which puts the original `"type"` back. + * + * @returns A revert function that reverts the original value of the `"type"` field. + */ +async function setPackageJsonsType( + cwd: string, + type: PackageJsonType, +): Promise<() => Promise> { + const rootPkgJsonPath = join(cwd, 'package.json'); + const rootContents = await fse.readFile(rootPkgJsonPath, 'utf8'); + const rootPkg = JSON.parse(rootContents); + + const reverts: (() => Promise)[] = []; + + if ('workspaces' in rootPkg) { + for (const pkgJsonPath of [ + // we also want to modify the root package.json + // TODO: do we? + rootPkgJsonPath, + // get all package.jsons from the defined workspaces + ...(await globby( + rootPkg.workspaces.map((w: string) => w + '/package.json'), + { cwd, absolute: true }, + )), + ]) { + const contents = + pkgJsonPath === rootPkgJsonPath + ? // no need to re-read the root package.json + rootContents + : await fse.readFile(pkgJsonPath, 'utf8'); + const endsWithNewline = contents.endsWith('\n'); + + const pkg = JSON.parse(contents); + if (pkg.type != null && pkg.type !== 'commonjs' && pkg.type !== 'module') { + throw new Error(`Invalid "type" property value "${pkg.type}" in ${pkgJsonPath}`); + } + + const originalType: PackageJsonType | undefined = pkg.type; + const differentType = + (pkg.type || + // default when the type is not defined + 'commonjs') !== type; + + // change only if the provided type is different + if (differentType) { + pkg.type = type; + await fse.writeFile( + pkgJsonPath, + JSON.stringify(pkg, null, ' ') + (endsWithNewline ? '\n' : ''), + ); + + // revert change, of course only if we changed something + reverts.push(async () => { + pkg.type = originalType; + await fse.writeFile( + pkgJsonPath, + JSON.stringify(pkg, null, ' ') + (endsWithNewline ? '\n' : ''), + ); + }); + } + } + } + + return async function revert() { + await Promise.all(reverts.map(r => r())); + }; +} + async function executeCopy(sourcePath: string, destPath: string) { await fse.mkdirp(dirname(destPath)); await fse.copyFile(sourcePath, destPath); From 223de44db9538232ecbbf79ba8a3d0c90645eb2a Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:07:39 +0100 Subject: [PATCH 02/12] implement modern commonjs --- src/commands/build.ts | 80 ++++++++++++++++++++-------------------- test/integration.spec.ts | 4 +- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index a29dcf07..32b82f25 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -499,55 +499,55 @@ async function setPackageJsonsType( const rootPkgJsonPath = join(cwd, 'package.json'); const rootContents = await fse.readFile(rootPkgJsonPath, 'utf8'); const rootPkg = JSON.parse(rootContents); + const workspaces = await getWorkspaces(rootPkg); + const isSinglePackage = workspaces === null; const reverts: (() => Promise)[] = []; - if ('workspaces' in rootPkg) { - for (const pkgJsonPath of [ - // we also want to modify the root package.json - // TODO: do we? - rootPkgJsonPath, - // get all package.jsons from the defined workspaces - ...(await globby( - rootPkg.workspaces.map((w: string) => w + '/package.json'), - { cwd, absolute: true }, - )), - ]) { - const contents = - pkgJsonPath === rootPkgJsonPath - ? // no need to re-read the root package.json - rootContents - : await fse.readFile(pkgJsonPath, 'utf8'); - const endsWithNewline = contents.endsWith('\n'); - - const pkg = JSON.parse(contents); - if (pkg.type != null && pkg.type !== 'commonjs' && pkg.type !== 'module') { - throw new Error(`Invalid "type" property value "${pkg.type}" in ${pkgJsonPath}`); - } + for (const pkgJsonPath of [ + // we also want to modify the root package.json TODO: do we? + rootPkgJsonPath, + ...(isSinglePackage + ? [] + : await globby( + workspaces.map((w: string) => w + '/package.json'), + { cwd, absolute: true }, + )), + ]) { + const contents = + pkgJsonPath === rootPkgJsonPath + ? // no need to re-read the root package.json + rootContents + : await fse.readFile(pkgJsonPath, 'utf8'); + const endsWithNewline = contents.endsWith('\n'); + + const pkg = JSON.parse(contents); + if (pkg.type != null && pkg.type !== 'commonjs' && pkg.type !== 'module') { + throw new Error(`Invalid "type" property value "${pkg.type}" in ${pkgJsonPath}`); + } - const originalType: PackageJsonType | undefined = pkg.type; - const differentType = - (pkg.type || - // default when the type is not defined - 'commonjs') !== type; + const originalType: PackageJsonType | undefined = pkg.type; + const differentType = + (pkg.type || + // default when the type is not defined + 'commonjs') !== type; + + // change only if the provided type is different + if (differentType) { + pkg.type = type; + await fse.writeFile( + pkgJsonPath, + JSON.stringify(pkg, null, ' ') + (endsWithNewline ? '\n' : ''), + ); - // change only if the provided type is different - if (differentType) { - pkg.type = type; + // revert change, of course only if we changed something + reverts.push(async () => { + pkg.type = originalType; await fse.writeFile( pkgJsonPath, JSON.stringify(pkg, null, ' ') + (endsWithNewline ? '\n' : ''), ); - - // revert change, of course only if we changed something - reverts.push(async () => { - pkg.type = originalType; - await fse.writeFile( - pkgJsonPath, - JSON.stringify(pkg, null, ' ') + (endsWithNewline ? '\n' : ''), - ); - }); - } + }); } } diff --git a/test/integration.spec.ts b/test/integration.spec.ts index db0fb352..7caf0b2d 100644 --- a/test/integration.spec.ts +++ b/test/integration.spec.ts @@ -204,7 +204,7 @@ it('can build a monorepo project', async () => { __exportStar(require("./foo.js"), exports); exports.b = 'SUP' + foo_js_1.b; function foo() { - return Promise.resolve().then(() => require('./foo.js')); + return import('./foo.js'); } `); expect(await fse.readFile(files.b['typings/index.d.ts'], 'utf8')).toMatchInlineSnapshot(` @@ -552,7 +552,7 @@ it('can build a monorepo pnpm project', async () => { __exportStar(require("./foo.js"), exports); exports.b = 'SUP' + foo_js_1.b; function foo() { - return Promise.resolve().then(() => require('./foo.js')); + return import('./foo.js'); } `); expect(await fse.readFile(files.b['typings/index.d.ts'], 'utf8')).toMatchInlineSnapshot(` From 3a04294750d64c2430221a98641592c18b996ba8 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:12:47 +0100 Subject: [PATCH 03/12] changeset --- .changeset/thin-mails-clap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thin-mails-clap.md diff --git a/.changeset/thin-mails-clap.md b/.changeset/thin-mails-clap.md new file mode 100644 index 00000000..d53a2682 --- /dev/null +++ b/.changeset/thin-mails-clap.md @@ -0,0 +1,5 @@ +--- +'bob-the-bundler': minor +--- + +Build modern CommonJS and support package.json exports From b8e8b7c7a526b8dcb5bb393832a7bdde39ffd733 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:22:31 +0100 Subject: [PATCH 04/12] fix comment --- src/commands/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 32b82f25..1231ff76 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -487,7 +487,7 @@ export function validatePackageJson( type PackageJsonType = 'module' | 'commonjs'; /** - * Sets the {@link filePath package.json} `"type"` field to the defined {@link type} + * Sets the {@link cwd workspaces} package.json(s) `"type"` field to the defined {@link type} * returning a "revert" function which puts the original `"type"` back. * * @returns A revert function that reverts the original value of the `"type"` field. From fc9c30f47103e5b4462706972e47fb2e6a3834bf Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:22:53 +0100 Subject: [PATCH 05/12] todo --- src/commands/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 1231ff76..c77c4874 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -505,7 +505,7 @@ async function setPackageJsonsType( const reverts: (() => Promise)[] = []; for (const pkgJsonPath of [ - // we also want to modify the root package.json TODO: do we? + // we also want to modify the root package.json TODO: do we in single package repos? rootPkgJsonPath, ...(isSinglePackage ? [] From 9b14535d5e3c3ddeafb0548e5134fad2253150f5 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:46:44 +0100 Subject: [PATCH 06/12] module node16 --- src/commands/build.ts | 49 +++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index c77c4874..574c713f 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -41,23 +41,6 @@ const filesToExcludeFromDist = [ '**/temp', ]; -const moduleMappings = { - esm: 'es2022', - cjs: 'nodenext', -} as const; - -function typeScriptCompilerOptions(target: 'esm' | 'cjs'): Record { - return { - module: moduleMappings[target], - sourceMap: false, - inlineSourceMap: false, - }; -} - -function compilerOptionsToArgs(options: Record): string[] { - return Object.entries(options).flatMap(([key, value]) => [`--${key}`, `${value}`]); -} - function assertTypeScriptBuildResult( result: Awaited>, reporter: ConsolaInstance, @@ -77,31 +60,33 @@ async function buildTypeScript( if (!tsconfig && (await fse.exists(join(options.cwd, DEFAULT_TS_BUILD_CONFIG)))) { tsconfig = join(options.cwd, DEFAULT_TS_BUILD_CONFIG); } - assertTypeScriptBuildResult( - await execa('npx', [ - 'tsc', - ...(tsconfig ? ['--project', tsconfig] : []), - ...compilerOptionsToArgs(typeScriptCompilerOptions('esm')), - ...(options.incremental ? ['--incremental'] : []), - '--outDir', - join(buildPath, 'esm'), - ]), - reporter, - ); - const revertPackageJsonsType = await setPackageJsonsType(options.cwd, 'commonjs'); - try { + async function build(outDir: string) { assertTypeScriptBuildResult( await execa('npx', [ 'tsc', ...(tsconfig ? ['--project', tsconfig] : []), - ...compilerOptionsToArgs(typeScriptCompilerOptions('cjs')), + '--module node16', // not nodenext because this keeps up with latest node and we dont want to break something suddenly + '--sourceMap false', + '--inlineSourceMap false', ...(options.incremental ? ['--incremental'] : []), '--outDir', - join(buildPath, 'cjs'), + outDir, ]), reporter, ); + } + + let revertPackageJsonsType = await setPackageJsonsType(options.cwd, 'module'); + try { + await build(join(buildPath, 'esm')); + } finally { + await revertPackageJsonsType(); + } + + revertPackageJsonsType = await setPackageJsonsType(options.cwd, 'commonjs'); + try { + await build(join(buildPath, 'cjs')); } finally { await revertPackageJsonsType(); } From f964c44629690df55a9eec4131fb749c38f564ff Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 19:51:12 +0100 Subject: [PATCH 07/12] node16 fixes --- src/commands/build.ts | 21 ++++++++++++++------- test/integration.spec.ts | 10 +++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 574c713f..70298b7e 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -41,6 +41,12 @@ const filesToExcludeFromDist = [ '**/temp', ]; +function compilerOptionsToArgs(options: Record): string[] { + return Object.entries(options) + .filter(([, value]) => !!value) + .flatMap(([key, value]) => [`--${key}`, `${value}`]); +} + function assertTypeScriptBuildResult( result: Awaited>, reporter: ConsolaInstance, @@ -65,13 +71,14 @@ async function buildTypeScript( assertTypeScriptBuildResult( await execa('npx', [ 'tsc', - ...(tsconfig ? ['--project', tsconfig] : []), - '--module node16', // not nodenext because this keeps up with latest node and we dont want to break something suddenly - '--sourceMap false', - '--inlineSourceMap false', - ...(options.incremental ? ['--incremental'] : []), - '--outDir', - outDir, + ...compilerOptionsToArgs({ + project: tsconfig, + module: 'node16', + sourceMap: false, + inlineSourceMap: false, + incremental: options.incremental, + outDir, + }), ]), reporter, ); diff --git a/test/integration.spec.ts b/test/integration.spec.ts index 7caf0b2d..0c16755b 100644 --- a/test/integration.spec.ts +++ b/test/integration.spec.ts @@ -33,7 +33,7 @@ it('can bundle a simple project', async () => { export default _default; `); expect(await fse.readFile(indexMjsFilePath, 'utf8')).toMatchInlineSnapshot(` - export var someNumber = 1; + export const someNumber = 1; export default 'kek'; `); expect(await fse.readFile(readmeFilePath, 'utf8')).toMatchInlineSnapshot('Hello!'); @@ -355,7 +355,7 @@ it('can build an esm only project', async () => { `); expect(await fse.readFile(indexJsFilePath, 'utf8')).toMatchInlineSnapshot( - 'export var someNumber = 1;', + `export const someNumber = 1;`, ); expect(await fse.readFile(indexDtsFilePath, 'utf8')).toMatchInlineSnapshot( 'export declare const someNumber = 1;', @@ -732,7 +732,7 @@ it('can bundle a tsconfig-build-json project', async () => { `); await expect(fse.readFile(path.resolve(baseDistPath, 'esm', 'index.js'), 'utf8')).resolves .toMatchInlineSnapshot(` - export var hello = 1; + export const hello = 1; export default 'there'; `); @@ -776,7 +776,7 @@ it('can bundle a simple project with additional exports', async () => { `); await expect(fse.readFile(path.join(dist, 'esm', 'index.js'), 'utf8')).resolves .toMatchInlineSnapshot(` - export var someLetter = 'a'; + export const someLetter = 'a'; export default { b: 'c' }; `); @@ -798,7 +798,7 @@ it('can bundle a simple project with additional exports', async () => { `); await expect(fse.readFile(path.join(dist, 'esm', 'sub', 'index.js'), 'utf8')).resolves .toMatchInlineSnapshot(` - export var someOtherLetter = 'd'; + export const someOtherLetter = 'd'; export default { e: 'f' }; `); From 39d9794ed85b0b947a4e254c11d08c741369df22 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 20:24:41 +0100 Subject: [PATCH 08/12] detect and use moduleresolution --- package.json | 1 + pnpm-lock.yaml | 15 +++++++++ src/commands/build.ts | 71 ++++++++++++++++++++++------------------ test/integration.spec.ts | 10 +++--- 4 files changed, 60 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 8d0e8361..dc33729c 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "consola": "^3.0.0", "execa": "^9.0.0", "fs-extra": "^11.1.0", + "get-tsconfig": "^4.8.1", "globby": "^14.0.0", "js-yaml": "^4.1.0", "lodash.get": "^4.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f9fed19..cd711e19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: fs-extra: specifier: ^11.1.0 version: 11.3.0 + get-tsconfig: + specifier: ^4.8.1 + version: 4.8.1 globby: specifier: ^14.0.0 version: 14.0.2 @@ -1160,6 +1163,9 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1781,6 +1787,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} @@ -3439,6 +3448,10 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.7 + get-tsconfig@4.8.1: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4033,6 +4046,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve.exports@2.0.3: {} resolve@1.22.10: diff --git a/src/commands/build.ts b/src/commands/build.ts index 70298b7e..4f07a2f2 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -3,6 +3,7 @@ import { dirname, join, resolve } from 'path'; import { type ConsolaInstance } from 'consola'; import { execa } from 'execa'; import fse from 'fs-extra'; +import { getTsconfig, parseTsconfig } from 'get-tsconfig'; import { globby } from 'globby'; import get from 'lodash.get'; import pLimit from 'p-limit'; @@ -59,44 +60,50 @@ function assertTypeScriptBuildResult( async function buildTypeScript( buildPath: string, - options: { cwd: string; tsconfig?: string; incremental?: boolean }, + options: { + cwd: string; + tsconfig?: string; + incremental?: boolean; + }, reporter: ConsolaInstance, ) { - let tsconfig = options.tsconfig; - if (!tsconfig && (await fse.exists(join(options.cwd, DEFAULT_TS_BUILD_CONFIG)))) { - tsconfig = join(options.cwd, DEFAULT_TS_BUILD_CONFIG); + let project = options.tsconfig; + if (!project && (await fse.exists(join(options.cwd, DEFAULT_TS_BUILD_CONFIG)))) { + project = join(options.cwd, DEFAULT_TS_BUILD_CONFIG); } - async function build(outDir: string) { - assertTypeScriptBuildResult( - await execa('npx', [ - 'tsc', - ...compilerOptionsToArgs({ - project: tsconfig, - module: 'node16', - sourceMap: false, - inlineSourceMap: false, - incremental: options.incremental, - outDir, - }), - ]), - reporter, - ); - } - - let revertPackageJsonsType = await setPackageJsonsType(options.cwd, 'module'); - try { - await build(join(buildPath, 'esm')); - } finally { - await revertPackageJsonsType(); + const tsconfig = project ? parseTsconfig(project) : getTsconfig(options.cwd)?.config; + const moduleResolution = tsconfig?.compilerOptions?.moduleResolution || ''; + const isModernNodeModuleResolution = ['node16', 'nodenext'].includes(moduleResolution); + + async function build(out: PackageJsonType) { + const revertPackageJsonsType = await setPackageJsonsType(options.cwd, out); + try { + assertTypeScriptBuildResult( + await execa('npx', [ + 'tsc', + ...compilerOptionsToArgs({ + project, + module: isModernNodeModuleResolution + ? moduleResolution // match module with moduleResolution for modern node (nodenext and node16) + : out === 'module' + ? 'es2022' + : 'node16', // modern commonjs + sourceMap: false, + inlineSourceMap: false, + incremental: options.incremental, + outDir: out === 'module' ? join(buildPath, 'esm') : join(buildPath, 'cjs'), + }), + ]), + reporter, + ); + } finally { + await revertPackageJsonsType(); + } } - revertPackageJsonsType = await setPackageJsonsType(options.cwd, 'commonjs'); - try { - await build(join(buildPath, 'cjs')); - } finally { - await revertPackageJsonsType(); - } + await build('module'); + await build('commonjs'); } export const buildCommand = createCommand< diff --git a/test/integration.spec.ts b/test/integration.spec.ts index 0c16755b..a8ff3324 100644 --- a/test/integration.spec.ts +++ b/test/integration.spec.ts @@ -33,7 +33,7 @@ it('can bundle a simple project', async () => { export default _default; `); expect(await fse.readFile(indexMjsFilePath, 'utf8')).toMatchInlineSnapshot(` - export const someNumber = 1; + export var someNumber = 1; export default 'kek'; `); expect(await fse.readFile(readmeFilePath, 'utf8')).toMatchInlineSnapshot('Hello!'); @@ -355,7 +355,7 @@ it('can build an esm only project', async () => { `); expect(await fse.readFile(indexJsFilePath, 'utf8')).toMatchInlineSnapshot( - `export const someNumber = 1;`, + `export var someNumber = 1;`, ); expect(await fse.readFile(indexDtsFilePath, 'utf8')).toMatchInlineSnapshot( 'export declare const someNumber = 1;', @@ -732,7 +732,7 @@ it('can bundle a tsconfig-build-json project', async () => { `); await expect(fse.readFile(path.resolve(baseDistPath, 'esm', 'index.js'), 'utf8')).resolves .toMatchInlineSnapshot(` - export const hello = 1; + export var hello = 1; export default 'there'; `); @@ -776,7 +776,7 @@ it('can bundle a simple project with additional exports', async () => { `); await expect(fse.readFile(path.join(dist, 'esm', 'index.js'), 'utf8')).resolves .toMatchInlineSnapshot(` - export const someLetter = 'a'; + export var someLetter = 'a'; export default { b: 'c' }; `); @@ -798,7 +798,7 @@ it('can bundle a simple project with additional exports', async () => { `); await expect(fse.readFile(path.join(dist, 'esm', 'sub', 'index.js'), 'utf8')).resolves .toMatchInlineSnapshot(` - export const someOtherLetter = 'd'; + export var someOtherLetter = 'd'; export default { e: 'f' }; `); From 07210ed639ad98f6e872555316d84869da80f06f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 16 Jan 2025 19:25:03 +0000 Subject: [PATCH 09/12] chore(dependencies): updated changesets for modified dependencies --- .changeset/bob-the-bundler-336-dependencies.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/bob-the-bundler-336-dependencies.md diff --git a/.changeset/bob-the-bundler-336-dependencies.md b/.changeset/bob-the-bundler-336-dependencies.md new file mode 100644 index 00000000..6a31e185 --- /dev/null +++ b/.changeset/bob-the-bundler-336-dependencies.md @@ -0,0 +1,5 @@ +--- +"bob-the-bundler": patch +--- +dependencies updates: + - Added dependency [`get-tsconfig@^4.8.1` ↗︎](https://www.npmjs.com/package/get-tsconfig/v/4.8.1) (to `dependencies`) From bd546e87187cd67c866363b8d90d2be9d95e86c9 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 20:42:40 +0100 Subject: [PATCH 10/12] old and modern node and stuff --- src/commands/build.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 4f07a2f2..5fd7c9a9 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -73,8 +73,15 @@ async function buildTypeScript( } const tsconfig = project ? parseTsconfig(project) : getTsconfig(options.cwd)?.config; - const moduleResolution = tsconfig?.compilerOptions?.moduleResolution || ''; + + const moduleResolution = (tsconfig?.compilerOptions?.moduleResolution || '').toLowerCase(); const isModernNodeModuleResolution = ['node16', 'nodenext'].includes(moduleResolution); + const isOldNodeModuleResolution = ['classic', 'node', 'node10'].includes(moduleResolution); + if (moduleResolution && !isOldNodeModuleResolution && !isModernNodeModuleResolution) { + throw new Error( + `'moduleResolution' option '${moduleResolution}' cannot be used to build CommonJS"`, + ); + } async function build(out: PackageJsonType) { const revertPackageJsonsType = await setPackageJsonsType(options.cwd, out); @@ -88,7 +95,9 @@ async function buildTypeScript( ? moduleResolution // match module with moduleResolution for modern node (nodenext and node16) : out === 'module' ? 'es2022' - : 'node16', // modern commonjs + : isOldNodeModuleResolution + ? 'commonjs' // old commonjs + : 'node16', // modern commonjs sourceMap: false, inlineSourceMap: false, incremental: options.incremental, @@ -525,7 +534,7 @@ async function setPackageJsonsType( throw new Error(`Invalid "type" property value "${pkg.type}" in ${pkgJsonPath}`); } - const originalType: PackageJsonType | undefined = pkg.type; + const originalPkg = { ...pkg }; const differentType = (pkg.type || // default when the type is not defined @@ -541,10 +550,9 @@ async function setPackageJsonsType( // revert change, of course only if we changed something reverts.push(async () => { - pkg.type = originalType; await fse.writeFile( pkgJsonPath, - JSON.stringify(pkg, null, ' ') + (endsWithNewline ? '\n' : ''), + JSON.stringify(originalPkg, null, ' ') + (endsWithNewline ? '\n' : ''), ); }); } From 7d68b1cc984c25eab3fd7bfffa255c8f6a3acc8e Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 20:54:14 +0100 Subject: [PATCH 11/12] yeah is a major change --- .changeset/thin-mails-clap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/thin-mails-clap.md b/.changeset/thin-mails-clap.md index d53a2682..2a0dbe56 100644 --- a/.changeset/thin-mails-clap.md +++ b/.changeset/thin-mails-clap.md @@ -1,5 +1,5 @@ --- -'bob-the-bundler': minor +'bob-the-bundler': major --- Build modern CommonJS and support package.json exports From 1c6814b11c3fec160bbf4b301ed13402ff664183 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 16 Jan 2025 21:17:58 +0100 Subject: [PATCH 12/12] ignore some paths when transforming pkgjson --- src/commands/build.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 5fd7c9a9..8433fe20 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -84,7 +84,10 @@ async function buildTypeScript( } async function build(out: PackageJsonType) { - const revertPackageJsonsType = await setPackageJsonsType(options.cwd, out); + const revertPackageJsonsType = await setPackageJsonsType( + { cwd: options.cwd, ignore: [...filesToExcludeFromDist, ...(tsconfig?.exclude || [])] }, + out, + ); try { assertTypeScriptBuildResult( await execa('npx', [ @@ -501,7 +504,7 @@ type PackageJsonType = 'module' | 'commonjs'; * @returns A revert function that reverts the original value of the `"type"` field. */ async function setPackageJsonsType( - cwd: string, + { cwd, ignore }: { cwd: string; ignore: string[] }, type: PackageJsonType, ): Promise<() => Promise> { const rootPkgJsonPath = join(cwd, 'package.json'); @@ -519,7 +522,7 @@ async function setPackageJsonsType( ? [] : await globby( workspaces.map((w: string) => w + '/package.json'), - { cwd, absolute: true }, + { cwd, absolute: true, ignore }, )), ]) { const contents =