From e71870ad14304b026cf8a33f67b75916f60c6e56 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 14 Dec 2022 20:23:30 +0100 Subject: [PATCH] make the bundle script a bit less repetitive, make it have a done indicator --- scripts/package.json | 1 + scripts/prepare/bundle.ts | 238 +++++++++++++++++++++++--------------- scripts/yarn.lock | 8 ++ 3 files changed, 154 insertions(+), 93 deletions(-) diff --git a/scripts/package.json b/scripts/package.json index d8d7b7437c1b..642c241a2d51 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -168,6 +168,7 @@ "ts-dedent": "^2.0.0", "ts-node": "^10.4.0", "tsup": "^6.2.2", + "type-fest": "^3.4.0", "typescript": "~4.9.3", "util": "^0.12.4", "wait-on": "^5.2.1", diff --git a/scripts/prepare/bundle.ts b/scripts/prepare/bundle.ts index 7a15a2028129..fd40f2b5ba49 100755 --- a/scripts/prepare/bundle.ts +++ b/scripts/prepare/bundle.ts @@ -2,21 +2,39 @@ import fs from 'fs-extra'; import path, { dirname, join, relative } from 'path'; +import type { Options } from 'tsup'; +import type { PackageJson } from 'type-fest'; import { build } from 'tsup'; import aliasPlugin from 'esbuild-plugin-alias'; import dedent from 'ts-dedent'; import slash from 'slash'; import { exec } from '../utils/exec'; -const hasFlag = (flags: string[], name: string) => !!flags.find((s) => s.startsWith(`--${name}`)); +/* TYPES */ + +type Formats = 'esm' | 'cjs'; +type BundlerConfig = { + entries: string[]; + untypedEntries: string[]; + platform: Options['platform']; + pre: string; + post: string; + formats: Formats[]; +}; +type PackageJsonWithBundlerConfig = PackageJson & { + bundler: BundlerConfig; +}; +type DtsConfigSection = Pick; + +/* MAIN */ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { const { name, dependencies, peerDependencies, - bundler: { entries = [], untypedEntries = [], platform, pre, post }, - } = await fs.readJson(join(cwd, 'package.json')); + bundler: { entries = [], untypedEntries = [], platform, pre, post, formats = ['esm', 'cjs'] }, + } = (await fs.readJson(join(cwd, 'package.json'))) as PackageJsonWithBundlerConfig; if (pre) { await exec(`node -r ${__dirname}/../node_modules/esbuild-register/register.js ${pre}`, { cwd }); @@ -30,97 +48,78 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { await fs.emptyDir(join(process.cwd(), 'dist')); } - const tsConfigPath = join(cwd, 'tsconfig.json'); - const tsConfigExists = await fs.pathExists(tsConfigPath); + const tasks: Promise[] = []; - await Promise.all([ - // SHIM DTS FILES (only for development) - ...(optimized - ? [] - : entries.map(async (file: string) => { - const { name: entryName, dir } = path.parse(file); - - const pathName = join(process.cwd(), dir.replace('./src', 'dist'), `${entryName}.d.ts`); - const srcName = join(process.cwd(), file); - - const rel = relative(dirname(pathName), dirname(srcName)) - .split(path.sep) - .join(path.posix.sep); - - await fs.ensureFile(pathName); - await fs.writeFile( - pathName, - dedent` - // dev-mode - export * from '${rel}/${entryName}'; - ` - ); - })), - - // BROWSER EMS - build({ - silent: true, - entry: [...entries, ...untypedEntries].map((e: string) => slash(join(cwd, e))), - watch, - ...(tsConfigExists ? { tsconfig: tsConfigPath } : {}), - outDir: join(process.cwd(), 'dist'), - format: ['esm'], - target: 'chrome100', - clean: !watch, - platform: platform || 'browser', - esbuildPlugins: [ - aliasPlugin({ - process: path.resolve('../node_modules/process/browser.js'), - util: path.resolve('../node_modules/util/util.js'), - }), - ], - external: [name, ...Object.keys(dependencies || {}), ...Object.keys(peerDependencies || {})], - - dts: - optimized && tsConfigExists - ? { - entry: entries, - resolve: true, - } - : false, - esbuildOptions: (c) => { - /* eslint-disable no-param-reassign */ - c.conditions = ['module']; - c.logLevel = 'error'; - c.platform = platform || 'browser'; - c.legalComments = 'none'; - c.minifyWhitespace = optimized; - c.minifyIdentifiers = false; - c.minifySyntax = optimized; - /* eslint-enable no-param-reassign */ - }, - }), - - // NODE CJS - build({ - silent: true, - entry: [...entries, ...untypedEntries].map((e: string) => slash(join(cwd, e))), - watch, - outDir: join(process.cwd(), 'dist'), - ...(tsConfigExists ? { tsconfig: tsConfigPath } : {}), - format: ['cjs'], - target: 'node16', - platform: 'node', - clean: !watch, - external: [name, ...Object.keys(dependencies || {}), ...Object.keys(peerDependencies || {})], - - esbuildOptions: (c) => { - /* eslint-disable no-param-reassign */ - c.logLevel = 'error'; - c.platform = 'node'; - c.legalComments = 'none'; - c.minifyWhitespace = optimized; - c.minifyIdentifiers = optimized; - c.minifySyntax = optimized; - /* eslint-enable no-param-reassign */ - }, - }), - ]); + const outDir = join(process.cwd(), 'dist'); + const externals = [ + name, + ...Object.keys(dependencies || {}), + ...Object.keys(peerDependencies || {}), + ]; + const allEntries = [...entries, ...untypedEntries].map((e: string) => slash(join(cwd, e))); + + const { dtsBuild, dtsConfig, tsConfigExists } = await getDTSConfigs({ formats, entries }); + + if (formats.includes('esm')) { + tasks.push( + build({ + silent: true, + entry: allEntries, + watch, + outDir, + format: ['esm'], + target: 'chrome100', + clean: !watch, + ...(dtsBuild === 'esm' ? dtsConfig : {}), + platform: platform || 'browser', + esbuildPlugins: [ + aliasPlugin({ + process: path.resolve('../node_modules/process/browser.js'), + util: path.resolve('../node_modules/util/util.js'), + }), + ], + external: externals, + + esbuildOptions: (c) => { + /* eslint-disable no-param-reassign */ + c.conditions = ['module']; + c.platform = platform || 'browser'; + Object.assign(c, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }) + ); + } + + if (formats.includes('cjs')) { + tasks.push( + build({ + silent: true, + entry: allEntries, + watch, + outDir, + format: ['cjs'], + target: 'node16', + ...(dtsBuild === 'cjs' ? dtsConfig : {}), + platform: 'node', + clean: !watch, + external: externals, + + esbuildOptions: (c) => { + /* eslint-disable no-param-reassign */ + c.platform = 'node'; + Object.assign(c, getESBuildOptions(optimized)); + /* eslint-enable no-param-reassign */ + }, + }) + ); + } + + if (tsConfigExists && !optimized) { + tasks.push(...entries.map(generateDTSMapperFile)); + } + + await Promise.all(tasks); if (post) { await exec( @@ -129,8 +128,61 @@ const run = async ({ cwd, flags }: { cwd: string; flags: string[] }) => { { debug: true } ); } + + console.log('done'); }; +/* UTILS */ + +async function getDTSConfigs({ formats, entries }: { formats: Formats[]; entries: string[] }) { + const tsConfigPath = join(cwd, 'tsconfig.json'); + const tsConfigExists = await fs.pathExists(tsConfigPath); + + const dtsBuild = formats[0] && tsConfigExists ? formats[0] : undefined; + + const dtsConfig: DtsConfigSection = { + tsconfig: tsConfigPath, + dts: { + entry: entries, + resolve: true, + }, + }; + + return { dtsBuild, dtsConfig, tsConfigExists }; +} + +function getESBuildOptions(optimized: boolean) { + return { + logLevel: 'error', + legalComments: 'none', + minifyWhitespace: optimized, + minifyIdentifiers: false, + minifySyntax: optimized, + }; +} + +async function generateDTSMapperFile(file: string) { + const { name: entryName, dir } = path.parse(file); + + const pathName = join(process.cwd(), dir.replace('./src', 'dist'), `${entryName}.d.ts`); + const srcName = join(process.cwd(), file); + + const rel = relative(dirname(pathName), dirname(srcName)).split(path.sep).join(path.posix.sep); + + await fs.ensureFile(pathName); + await fs.writeFile( + pathName, + dedent` + // dev-mode + export * from '${rel}/${entryName}'; + ` + ); +} + +const hasFlag = (flags: string[], name: string) => !!flags.find((s) => s.startsWith(`--${name}`)); + +/* SELF EXECUTION */ + const flags = process.argv.slice(2); const cwd = process.cwd(); diff --git a/scripts/yarn.lock b/scripts/yarn.lock index 79a1aadd67f8..5875ecd49622 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -3474,6 +3474,7 @@ __metadata: ts-loader: ^9.2.8 ts-node: ^10.4.0 tsup: ^6.2.2 + type-fest: ^3.4.0 typescript: ~4.9.3 util: ^0.12.4 verdaccio: ^4.10.0 @@ -17350,6 +17351,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^3.4.0": + version: 3.4.0 + resolution: "type-fest@npm:3.4.0" + checksum: 68e9c9db8df9e7310f6f4a4711bf743905522fb741cfe2746e3d412671e9b9e11e47ceb01973f765d5ee2774ce2655792562e1678e5d341f93f404a64660f943 + languageName: node + linkType: hard + "type-is@npm:~1.6.17, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18"