From 223686314b011c54fd1930a2555eb6dab7ccd9b5 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Wed, 1 Mar 2023 13:52:21 +0100 Subject: [PATCH] Custom tsconfig path for the build command, default to `tsconfig.build.json` and fallback to `tsconfig.json` (#202) --- .changeset/proud-hairs-promise.md | 6 ++ src/commands/build.ts | 24 +++++- .../tsconfig-build-json/README.md | 1 + .../tsconfig-build-json/package.json | 46 +++++++++++ .../tsconfig-build-json/src/index.ts | 3 + .../tsconfig-build-json/tsconfig.build.json | 7 ++ .../tsconfig-build-json/tsconfig.json | 8 ++ test/integration.spec.ts | 81 +++++++++++++++++++ 8 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 .changeset/proud-hairs-promise.md create mode 100644 test/__fixtures__/tsconfig-build-json/README.md create mode 100644 test/__fixtures__/tsconfig-build-json/package.json create mode 100644 test/__fixtures__/tsconfig-build-json/src/index.ts create mode 100644 test/__fixtures__/tsconfig-build-json/tsconfig.build.json create mode 100644 test/__fixtures__/tsconfig-build-json/tsconfig.json diff --git a/.changeset/proud-hairs-promise.md b/.changeset/proud-hairs-promise.md new file mode 100644 index 00000000..335d1706 --- /dev/null +++ b/.changeset/proud-hairs-promise.md @@ -0,0 +1,6 @@ +--- +'bob-the-bundler': major +--- + +Custom tsconfig path for the build command, default to `tsconfig.build.json` and fallback to +`tsconfig.json`. diff --git a/src/commands/build.ts b/src/commands/build.ts index 866481dd..8b3feb2c 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -17,6 +17,8 @@ import { presetFields, presetFieldsESM } from './bootstrap.js'; export const DIST_DIR = 'dist'; +export const DEFAULT_TS_BUILD_CONFIG = 'tsconfig.build.json'; + interface PackageInfo { packagePath: string; cwd: string; @@ -65,10 +67,18 @@ function assertTypeScriptBuildResult(result: ExecaReturnValue) { } } -async function buildTypeScript(buildPath: string, options: { incremental?: boolean } = {}) { +async function buildTypeScript( + buildPath: string, + options: { cwd: string; tsconfig?: string; incremental?: boolean }, +) { + let tsconfig = options.tsconfig; + 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', @@ -79,6 +89,7 @@ async function buildTypeScript(buildPath: string, options: { incremental?: boole assertTypeScriptBuildResult( await execa('npx', [ 'tsc', + ...(tsconfig ? ['--project', tsconfig] : []), ...compilerOptionsToArgs(typeScriptCompilerOptions('cjs')), ...(options.incremental ? ['--incremental'] : []), '--outDir', @@ -90,6 +101,7 @@ async function buildTypeScript(buildPath: string, options: { incremental?: boole export const buildCommand = createCommand< {}, { + tsconfig?: string; incremental?: boolean; } >(api => { @@ -100,13 +112,17 @@ export const buildCommand = createCommand< describe: 'Build', builder(yargs) { return yargs.options({ + tsconfig: { + describe: `Which tsconfig file to use when building TypeScript. By default bob will use ${DEFAULT_TS_BUILD_CONFIG} if it exists, otherwise the TSC's default.`, + type: 'string', + }, incremental: { describe: 'Better performance by building only packages that had changes.', type: 'boolean', }, }); }, - async handler({ incremental }) { + async handler({ tsconfig, incremental }) { const cwd = process.cwd(); const rootPackageJSON = await getRootPackageJSON(); const workspaces = await getWorkspaces(rootPackageJSON); @@ -118,7 +134,7 @@ export const buildCommand = createCommand< if (!incremental) { await fse.remove(buildPath); } - await buildTypeScript(buildPath, { incremental }); + await buildTypeScript(buildPath, { cwd, tsconfig, incremental }); const pkg = await fse.readJSON(resolve(cwd, 'package.json')); const fullName: string = pkg.name; @@ -155,7 +171,7 @@ export const buildCommand = createCommand< if (!incremental) { await fse.remove(bobBuildPath); } - await buildTypeScript(bobBuildPath, { incremental }); + await buildTypeScript(bobBuildPath, { cwd, tsconfig, incremental }); await Promise.all( packageInfoList.map(({ cwd, pkg, fullName }) => diff --git a/test/__fixtures__/tsconfig-build-json/README.md b/test/__fixtures__/tsconfig-build-json/README.md new file mode 100644 index 00000000..10ddd6d2 --- /dev/null +++ b/test/__fixtures__/tsconfig-build-json/README.md @@ -0,0 +1 @@ +Hello! diff --git a/test/__fixtures__/tsconfig-build-json/package.json b/test/__fixtures__/tsconfig-build-json/package.json new file mode 100644 index 00000000..5d11592e --- /dev/null +++ b/test/__fixtures__/tsconfig-build-json/package.json @@ -0,0 +1,46 @@ +{ + "name": "tsconfig-build-json", + "type": "module", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "exports": { + ".": { + "require": { + "types": "./dist/typings/index.d.cts", + "default": "./dist/cjs/index.js" + }, + "import": { + "types": "./dist/typings/index.d.ts", + "default": "./dist/esm/index.js" + }, + "default": { + "types": "./dist/typings/index.d.ts", + "default": "./dist/esm/index.js" + } + }, + "./*": { + "require": { + "types": "./dist/typings/*.d.cts", + "default": "./dist/cjs/*.js" + }, + "import": { + "types": "./dist/typings/*.d.ts", + "default": "./dist/esm/*.js" + }, + "default": { + "types": "./dist/typings/*.d.ts", + "default": "./dist/esm/*.js" + } + }, + "./package.json": "./package.json", + "./style.css": "./dist/esm/style.css" + }, + "typings": "dist/typings/index.d.ts", + "publishConfig": { + "directory": "dist", + "access": "public" + }, + "typescript": { + "definition": "dist/typings/index.d.ts" + } +} diff --git a/test/__fixtures__/tsconfig-build-json/src/index.ts b/test/__fixtures__/tsconfig-build-json/src/index.ts new file mode 100644 index 00000000..2821defe --- /dev/null +++ b/test/__fixtures__/tsconfig-build-json/src/index.ts @@ -0,0 +1,3 @@ +export const hello = 1; + +export default 'there'; diff --git a/test/__fixtures__/tsconfig-build-json/tsconfig.build.json b/test/__fixtures__/tsconfig-build-json/tsconfig.build.json new file mode 100644 index 00000000..3c5baa8c --- /dev/null +++ b/test/__fixtures__/tsconfig-build-json/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "declaration": false + } +} diff --git a/test/__fixtures__/tsconfig-build-json/tsconfig.json b/test/__fixtures__/tsconfig-build-json/tsconfig.json new file mode 100644 index 00000000..bc793b4c --- /dev/null +++ b/test/__fixtures__/tsconfig-build-json/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "ESNext", + "declaration": true, + "skipLibCheck": true, + "outDir": "dist" + } +} diff --git a/test/integration.spec.ts b/test/integration.spec.ts index 17c27799..bc4d2132 100644 --- a/test/integration.spec.ts +++ b/test/integration.spec.ts @@ -636,3 +636,84 @@ it('can build a monorepo pnpm project', async () => { cwd: path.resolve(fixturesFolder, 'simple-monorepo-pnpm'), }); }); + +it('can bundle a tsconfig-build-json project', async () => { + await fse.remove(path.resolve(fixturesFolder, 'tsconfig-build-json', 'dist')); + + const result = await execa('node', [binaryFolder, 'build'], { + cwd: path.resolve(fixturesFolder, 'tsconfig-build-json'), + }); + expect(result.exitCode).toEqual(0); + + const baseDistPath = path.resolve(fixturesFolder, 'tsconfig-build-json', 'dist'); + await expect(fse.readFile(path.resolve(baseDistPath, 'package.json'), 'utf8')).resolves + .toMatchInlineSnapshot(` + { + "name": "tsconfig-build-json", + "main": "cjs/index.js", + "module": "esm/index.js", + "typings": "typings/index.d.ts", + "typescript": { + "definition": "typings/index.d.ts" + }, + "type": "module", + "exports": { + ".": { + "require": { + "types": "./typings/index.d.cts", + "default": "./cjs/index.js" + }, + "import": { + "types": "./typings/index.d.ts", + "default": "./esm/index.js" + }, + "default": { + "types": "./typings/index.d.ts", + "default": "./esm/index.js" + } + }, + "./*": { + "require": { + "types": "./typings/*.d.cts", + "default": "./cjs/*.js" + }, + "import": { + "types": "./typings/*.d.ts", + "default": "./esm/*.js" + }, + "default": { + "types": "./typings/*.d.ts", + "default": "./esm/*.js" + } + }, + "./package.json": "./package.json", + "./style.css": "./esm/style.css" + } + } + `); + await expect( + fse.readFile(path.resolve(baseDistPath, 'README.md'), 'utf8'), + ).resolves.toMatchInlineSnapshot('Hello!'); + await expect(fse.readFile(path.resolve(baseDistPath, 'cjs', 'index.js'), 'utf8')).resolves + .toMatchInlineSnapshot(` + "use strict"; + exports.__esModule = true; + exports.hello = void 0; + exports.hello = 1; + exports["default"] = 'there'; + `); + await expect(fse.readFile(path.resolve(baseDistPath, 'esm', 'index.js'), 'utf8')).resolves + .toMatchInlineSnapshot(` + export var hello = 1; + export default 'there'; + `); + + // because the tsconfig.build.json has `declaration: false` + await expect(fse.stat(path.resolve(baseDistPath, 'typings', 'index.d.ts'))).rejects.toThrowError( + 'ENOENT: no such file or directory', + ); + + await execa('node', [binaryFolder, 'check'], { + cwd: path.resolve(fixturesFolder, 'simple'), + }); +});