From d1251ad32e418b021700c1c669e48cc8035cb307 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Thu, 12 Dec 2019 01:43:21 -0500 Subject: [PATCH 01/10] (fix): multiple entries should output multiple bundles - previously, while rollup was passed multiple entries, they all had the same output.file, and so only the first ended up being output - now, give each entry its own output.file (everything after src/) - also handle UMD names by converting subdir '/' to '__' - and handle creating multiple CJS entry files - clarify that these are CJS in the log output too (before it just said "entry file", which needs some disambiguation now) --- README.md | 4 +++ src/createBuildConfigs.ts | 36 +++++++++++--------- src/createRollupConfig.ts | 4 +-- src/index.ts | 70 ++++++++++++++++++++++++++++++++++----- src/types.ts | 9 ++++- 5 files changed, 96 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 03bf8a38b..fabdc0cbb 100644 --- a/README.md +++ b/README.md @@ -326,6 +326,10 @@ export interface TsdxOptions { input: string; // Name of package name: string; + // output path + output: { + file: string; + }; // JS target target: 'node' | 'browser'; // Module format diff --git a/src/createBuildConfigs.ts b/src/createBuildConfigs.ts index b53bdee54..caf0bbcbc 100644 --- a/src/createBuildConfigs.ts +++ b/src/createBuildConfigs.ts @@ -22,8 +22,8 @@ export async function createBuildConfigs( opts: NormalizedOpts ): Promise> { const allInputs = concatAllArray( - opts.input.map((input: string) => - createAllFormats(opts, input).map( + opts.input.map((input: string, index: number) => + createAllFormats(opts, input, index).map( (options: TsdxOptions, index: number) => ({ ...options, // We want to know if this is the first run for each entryfile @@ -45,45 +45,49 @@ export async function createBuildConfigs( function createAllFormats( opts: NormalizedOpts, - input: string + input: string, + index: number ): [TsdxOptions, ...TsdxOptions[]] { + const sharedOpts = { + ...opts, + input, + name: opts.name[index], + output: { + file: opts.output.file[index], + }, + }; + return [ opts.format.includes('cjs') && { - ...opts, + ...sharedOpts, format: 'cjs', env: 'development', - input, }, opts.format.includes('cjs') && { - ...opts, + ...sharedOpts, format: 'cjs', env: 'production', - input, }, - opts.format.includes('esm') && { ...opts, format: 'esm', input }, + opts.format.includes('esm') && { ...sharedOpts, format: 'esm' }, opts.format.includes('umd') && { - ...opts, + ...sharedOpts, format: 'umd', env: 'development', - input, }, opts.format.includes('umd') && { - ...opts, + ...sharedOpts, format: 'umd', env: 'production', - input, }, opts.format.includes('system') && { - ...opts, + ...sharedOpts, format: 'system', env: 'development', - input, }, opts.format.includes('system') && { - ...opts, + ...sharedOpts, format: 'system', env: 'production', - input, }, ].filter(Boolean) as [TsdxOptions, ...TsdxOptions[]]; } diff --git a/src/createRollupConfig.ts b/src/createRollupConfig.ts index 0092baf28..d9b3bd417 100644 --- a/src/createRollupConfig.ts +++ b/src/createRollupConfig.ts @@ -1,4 +1,4 @@ -import { safeVariableName, safePackageName, external } from './utils'; +import { safeVariableName, external } from './utils'; import { paths } from './constants'; import { RollupOptions } from 'rollup'; import { terser } from 'rollup-plugin-terser'; @@ -34,7 +34,7 @@ export async function createRollupConfig( opts.minify !== undefined ? opts.minify : opts.env === 'production'; const outputName = [ - `${paths.appDist}/${safePackageName(opts.name)}`, + `${paths.appDist}/${opts.output.file}`, opts.format, opts.env, shouldMinify ? 'min' : '', diff --git a/src/index.ts b/src/index.ts index 58d8d6cf4..472e22257 100755 --- a/src/index.ts +++ b/src/index.ts @@ -100,6 +100,42 @@ async function getInputs( return concatAllArray(inputs); } +function getNamesAndFiles( + inputs: string[], + name?: string +): { names: string[]; files: string[] } { + if (inputs.length === 1) { + const singleName = name || appPackageJson.name; + return { + names: [singleName], + files: [safePackageName(singleName)], + }; + } + // if multiple entries, each entry should retain its filename + const names: string[] = []; + const files: string[] = []; + inputs.forEach(input => { + // remove leading src/ directory + let filename = input; + const srcVars = ['src/', './src/']; + if (input.startsWith(srcVars[0])) + filename = input.substring(srcVars[0].length); + else if (input.startsWith(srcVars[1])) + filename = input.substring(srcVars[1].length); + + // remove file extension + const noExt = filename + .split('.') + .slice(0, -1) + .join('.'); + + // UMD name shouldn't contain slashes, replace with __ + names.push(noExt.replace('/', '__')); + files.push(noExt); + }); + return { names, files }; +} + prog .version(pkg.version) .command('create ') @@ -294,7 +330,11 @@ prog opts.name = opts.name || appPackageJson.name; opts.input = await getInputs(opts.entry, appPackageJson.source); if (opts.format.includes('cjs')) { - await writeCjsEntryFile(opts.name); + await Promise.all( + opts.output.file.map((file: string) => + writeCjsEntryFile(file, opts.input.length) + ) + ); } type Killer = execa.ExecaChildProcess | null; @@ -399,8 +439,15 @@ prog await cleanDistFolder(); const logger = await createProgressEstimator(); if (opts.format.includes('cjs')) { - const promise = writeCjsEntryFile(opts.name).catch(logError); - logger(promise, 'Creating entry file'); + const promise = Promise.all( + opts.output.file.map((file: string) => + writeCjsEntryFile(file, opts.input.length).catch(logError) + ) + ); + logger( + promise, + `Creating CJS entry file${opts.input.length > 1 ? 's' : ''}` + ); } try { const promise = asyncro @@ -424,16 +471,22 @@ prog }); async function normalizeOpts(opts: WatchOpts): Promise { + const inputs = await getInputs(opts.entry, appPackageJson.source); + const { names, files } = getNamesAndFiles(inputs, opts.name); + return { ...opts, - name: opts.name || appPackageJson.name, - input: await getInputs(opts.entry, appPackageJson.source), + name: names, + input: inputs, format: opts.format.split(',').map((format: string) => { if (format === 'es') { return 'esm'; } return format; }) as [ModuleFormat, ...ModuleFormat[]], + output: { + file: files, + }, }; } @@ -441,8 +494,8 @@ async function cleanDistFolder() { await fs.remove(paths.appDist); } -function writeCjsEntryFile(name: string) { - const baseLine = `module.exports = require('./${safePackageName(name)}`; +function writeCjsEntryFile(file: string, numEntries: number) { + const baseLine = `module.exports = require('./${file}`; const contents = ` 'use strict' @@ -452,7 +505,8 @@ if (process.env.NODE_ENV === 'production') { ${baseLine}.cjs.development.js') } `; - return fs.outputFile(path.join(paths.appDist, 'index.js'), contents); + const filename = numEntries === 1 ? 'index.js' : `${file}.js`; + return fs.outputFile(path.join(paths.appDist, filename), contents); } function getAuthorName() { diff --git a/src/types.ts b/src/types.ts index e6878897e..8e98a95ec 100644 --- a/src/types.ts +++ b/src/types.ts @@ -27,9 +27,12 @@ export interface WatchOpts extends BuildOpts { export interface NormalizedOpts extends Omit { - name: string; + name: string[]; input: string[]; format: [ModuleFormat, ...ModuleFormat[]]; + output: { + file: string[]; + }; } export interface TsdxOptions extends SharedOpts { @@ -37,6 +40,10 @@ export interface TsdxOptions extends SharedOpts { name: string; // path to file input: string; + // output path + output: { + file: string; + }; // Environment env: 'development' | 'production'; // Module format From 501c32f76cdb45069416acb273df2ba4cf12afbf Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Thu, 12 Dec 2019 02:55:13 -0500 Subject: [PATCH 02/10] (test): ensure multiple entries output multiple bundles - ensure that both index.ts _and_ foo.ts have their respective files output --- test/tests/tsdx-build.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/tests/tsdx-build.test.js b/test/tests/tsdx-build.test.js index b91bc9808..07fff7ed1 100644 --- a/test/tests/tsdx-build.test.js +++ b/test/tests/tsdx-build.test.js @@ -33,6 +33,30 @@ describe('tsdx build', () => { expect(output.code).toBe(0); }); + it('should compile multiple entries into a dist directory', () => { + util.setupStageWithFixture(stageName, 'build-default'); + + const output = shell.exec( + 'node ../dist/index.js build --entry src/index.ts --entry src/foo.ts --format esm,cjs' + ); + + // index output + expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/index.cjs.development.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/index.cjs.production.min.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/index.esm.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); + + // foo output + expect(shell.test('-f', 'dist/foo.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/foo.cjs.development.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/foo.cjs.production.min.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/foo.esm.js')).toBeTruthy(); + expect(shell.test('-f', 'dist/foo.d.ts')).toBeTruthy(); + + expect(output.code).toBe(0); + }); + it('should create the library correctly', () => { util.setupStageWithFixture(stageName, 'build-default'); From 9eab4e8233473500bd1c5fd1605b18b66015f9f8 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Thu, 12 Dec 2019 03:11:48 -0500 Subject: [PATCH 03/10] (docs): document how to configure multiple entry files - use the same example as in the tests --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index fabdc0cbb..0cf29df85 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Despite all the recent hype, setting up a new TypeScript (x React) library can b - [`tsdx build`](#tsdx-build) - [`tsdx test`](#tsdx-test) - [`tsdx lint`](#tsdx-lint) + - [Multiple Entry Files](#multiple-entry-files) - [Contributing](#contributing) - [Author](#author) - [License](#license) @@ -496,6 +497,17 @@ Examples $ tsdx lint src --report-file report.json ``` +### Multiple Entry Files + +You can run `tsdx watch` or `tsdx build` with multiple entry files, for example: + +```shell +tsdx build --entry src/index.ts --entry src/foo.ts +# outputs dist/index.js dist/foo.js and their respective formats and declarations +``` + +When given multiple entries, TSDX will output separate bundles for each file for each format, as well as their declarations. Each file will be output to `dist/` with the same name it has in the `src/` directory. + ## Contributing Please see the [Contributing Guidelines](./CONTRIBUTING.md). From 09a35fbf22d8ca70bd86ca56f44ced665952c458 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Thu, 12 Dec 2019 21:22:02 -0500 Subject: [PATCH 04/10] (test): ensure multi-entry w/ subdirs output subdir bundles - add some subdirs to the fixture so they can be tested - refactor multi-entry expects into a function called multiple times - reformat shell.exec call to be pseduo-multi-line --- .../src/subdir1/subdir1-2/index.ts | 1 + test/tests/tsdx-build.test.js | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 test/fixtures/build-default/src/subdir1/subdir1-2/index.ts diff --git a/test/fixtures/build-default/src/subdir1/subdir1-2/index.ts b/test/fixtures/build-default/src/subdir1/subdir1-2/index.ts new file mode 100644 index 000000000..1e7c4d70c --- /dev/null +++ b/test/fixtures/build-default/src/subdir1/subdir1-2/index.ts @@ -0,0 +1 @@ +export const bar = () => 'foo'; diff --git a/test/tests/tsdx-build.test.js b/test/tests/tsdx-build.test.js index 07fff7ed1..e6161334b 100644 --- a/test/tests/tsdx-build.test.js +++ b/test/tests/tsdx-build.test.js @@ -37,22 +37,30 @@ describe('tsdx build', () => { util.setupStageWithFixture(stageName, 'build-default'); const output = shell.exec( - 'node ../dist/index.js build --entry src/index.ts --entry src/foo.ts --format esm,cjs' + [ + 'node ../dist/index.js build', + '--entry src/index.ts', + '--entry src/foo.ts', + '--entry src/subdir1/subdir1-2/index.ts', + '--format esm,cjs', + ].join(' ') ); - // index output - expect(shell.test('-f', 'dist/index.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/index.cjs.development.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/index.cjs.production.min.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/index.esm.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy(); - - // foo output - expect(shell.test('-f', 'dist/foo.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/foo.cjs.development.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/foo.cjs.production.min.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/foo.esm.js')).toBeTruthy(); - expect(shell.test('-f', 'dist/foo.d.ts')).toBeTruthy(); + function testEntryOutput(filename) { + expect(shell.test('-f', `dist/${filename}.js`)).toBeTruthy(); + expect( + shell.test('-f', `dist/${filename}.cjs.development.js`) + ).toBeTruthy(); + expect( + shell.test('-f', `dist/${filename}.cjs.production.min.js`) + ).toBeTruthy(); + expect(shell.test('-f', `dist/${filename}.esm.js`)).toBeTruthy(); + expect(shell.test('-f', `dist/${filename}.d.ts`)).toBeTruthy(); + } + + testEntryOutput('index'); + testEntryOutput('foo'); + testEntryOutput('subdir1/subdir1-2/index'); expect(output.code).toBe(0); }); From 14b539763ef21019bf2dd70747a9b09ed1ec0287 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Thu, 12 Dec 2019 22:33:40 -0500 Subject: [PATCH 05/10] (fix/optim): only emit type declarations once - every other emission is just a duplicate -- no need to spend compute to duplicate - this also fixes a bug with multi-entry where if an entry in a subdir of src/ were added, e.g. src/foo/bar, the entire tree of type declarations would get output into dist/foo/src/*.d.ts - alternatively, could call `moveTypes()` with an arg for each entry, but there's no need since all declarations get produced the first time around anyway --- src/createBuildConfigs.ts | 4 ++-- src/createRollupConfig.ts | 5 ++++- src/index.ts | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/createBuildConfigs.ts b/src/createBuildConfigs.ts index caf0bbcbc..84e4975a4 100644 --- a/src/createBuildConfigs.ts +++ b/src/createBuildConfigs.ts @@ -35,9 +35,9 @@ export async function createBuildConfigs( ); return await Promise.all( - allInputs.map(async (options: TsdxOptions) => { + allInputs.map(async (options: TsdxOptions, index: number) => { // pass the full rollup config to tsdx.config.js override - const config = await createRollupConfig(options); + const config = await createRollupConfig(options, index); return tsdxConfig.rollup(config, options); }) ); diff --git a/src/createRollupConfig.ts b/src/createRollupConfig.ts index d9b3bd417..9a328969b 100644 --- a/src/createRollupConfig.ts +++ b/src/createRollupConfig.ts @@ -23,7 +23,8 @@ const errorCodeOpts = { let shebang: any = {}; export async function createRollupConfig( - opts: TsdxOptions + opts: TsdxOptions, + outputNum: number ): Promise { const findAndRecordErrorCodes = await extractErrors({ ...errorCodeOpts, @@ -162,6 +163,8 @@ export async function createRollupConfig( compilerOptions: { // TS -> esnext, then leave the rest to babel-preset-env target: 'esnext', + // only output declarations once + declaration: outputNum !== 0 ? false : undefined, }, }, check: !opts.transpileOnly, diff --git a/src/index.ts b/src/index.ts index 472e22257..666f0c73c 100755 --- a/src/index.ts +++ b/src/index.ts @@ -456,7 +456,6 @@ prog async (inputOptions: RollupOptions & { output: OutputOptions }) => { let bundle = await rollup(inputOptions); await bundle.write(inputOptions.output); - await deprecated.moveTypes(); } ) .catch((e: any) => { @@ -464,6 +463,7 @@ prog }); logger(promise, 'Building modules'); await promise; + await deprecated.moveTypes(); } catch (error) { logError(error); process.exit(1); From 059d572e44070f657cf45cf93d5dba8db32053b3 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Sat, 14 Dec 2019 03:53:11 -0500 Subject: [PATCH 06/10] (test): ensure globs work with multi-entry --- test/fixtures/build-default/src/subdir1/glob.ts | 1 + test/tests/tsdx-build.test.js | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 test/fixtures/build-default/src/subdir1/glob.ts diff --git a/test/fixtures/build-default/src/subdir1/glob.ts b/test/fixtures/build-default/src/subdir1/glob.ts new file mode 100644 index 000000000..43746e7ec --- /dev/null +++ b/test/fixtures/build-default/src/subdir1/glob.ts @@ -0,0 +1 @@ +export const glob = 'find me with a glob pattern!'; diff --git a/test/tests/tsdx-build.test.js b/test/tests/tsdx-build.test.js index e6161334b..8710513b7 100644 --- a/test/tests/tsdx-build.test.js +++ b/test/tests/tsdx-build.test.js @@ -42,6 +42,7 @@ describe('tsdx build', () => { '--entry src/index.ts', '--entry src/foo.ts', '--entry src/subdir1/subdir1-2/index.ts', + '--entry src/**/*.ts', '--format esm,cjs', ].join(' ') ); @@ -61,6 +62,7 @@ describe('tsdx build', () => { testEntryOutput('index'); testEntryOutput('foo'); testEntryOutput('subdir1/subdir1-2/index'); + testEntryOutput('subdir1/glob'); expect(output.code).toBe(0); }); From 8a70e997e57538b8903d3102ca7ea9cd2ab4f11d Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Sat, 14 Dec 2019 04:35:11 -0500 Subject: [PATCH 07/10] (docs): document usage of multi-entry w/ subdirs + globs --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0cf29df85..c668c42fa 100644 --- a/README.md +++ b/README.md @@ -502,11 +502,16 @@ Examples You can run `tsdx watch` or `tsdx build` with multiple entry files, for example: ```shell -tsdx build --entry src/index.ts --entry src/foo.ts -# outputs dist/index.js dist/foo.js and their respective formats and declarations +tsdx build \ + --entry src/index.ts \ + --entry src/foo.ts \ + --entry src/subdir/index.ts \ + --entry src/globdir/**/*.ts; +# outputs dist/index.js, dist/foo.js, dist/subdir/index.js, and dist/globdir/**/*.js +# as well as their respective formats and declarations ``` -When given multiple entries, TSDX will output separate bundles for each file for each format, as well as their declarations. Each file will be output to `dist/` with the same name it has in the `src/` directory. +When given multiple entries, TSDX will output separate bundles for each file for each format, as well as their declarations. Each file will be output to `dist/` with the same name it has in the `src/` directory. Entries in subdirectories of `src/` will be mapped to equivalently named subdirectories in `dist/`. TSDX will also expand any globs. ## Contributing From 4e3a5929e667a5b92c08be0990e1a4f45026d15d Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Tue, 31 Dec 2019 15:23:07 -0500 Subject: [PATCH 08/10] (refactor): better multi-entry test errors w/ shell.ls and toContain - test errors now explicitly state which file is missing, and which files are present, like this: Expected value: "foo.js" Received array: ["foo.d.ts", "index.cjs.development.js", "index.cjs.production.min.js", "index.d.ts", "index.esm.js", ...] --- test/tests/tsdx-build.test.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/test/tests/tsdx-build.test.js b/test/tests/tsdx-build.test.js index 8710513b7..2d69b8ab3 100644 --- a/test/tests/tsdx-build.test.js +++ b/test/tests/tsdx-build.test.js @@ -47,23 +47,17 @@ describe('tsdx build', () => { ].join(' ') ); - function testEntryOutput(filename) { - expect(shell.test('-f', `dist/${filename}.js`)).toBeTruthy(); - expect( - shell.test('-f', `dist/${filename}.cjs.development.js`) - ).toBeTruthy(); - expect( - shell.test('-f', `dist/${filename}.cjs.production.min.js`) - ).toBeTruthy(); - expect(shell.test('-f', `dist/${filename}.esm.js`)).toBeTruthy(); - expect(shell.test('-f', `dist/${filename}.d.ts`)).toBeTruthy(); + const entries = ['index', 'foo', 'subdir1/subdir1-2/index', 'subdir1/glob']; + const outputFiles = shell.ls('-R', 'dist/'); + + for (const entry of entries) { + expect(outputFiles).toContain(`${entry}.js`); + expect(outputFiles).toContain(`${entry}.cjs.development.js`); + expect(outputFiles).toContain(`${entry}.cjs.production.min.js`); + expect(outputFiles).toContain(`${entry}.esm.js`); + expect(outputFiles).toContain(`${entry}.d.ts`); } - testEntryOutput('index'); - testEntryOutput('foo'); - testEntryOutput('subdir1/subdir1-2/index'); - testEntryOutput('subdir1/glob'); - expect(output.code).toBe(0); }); From ca3c8a9dcdcc7ecd08dcea36eeeb2cdffccd85e5 Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Tue, 31 Dec 2019 15:23:07 -0500 Subject: [PATCH 09/10] (refactor): even better multi-entry test errors w/ toMatchObject - output is now like: Object { - "foo.cjs.development.js": true, - "foo.cjs.production.min.js": true, "foo.d.ts": true, - "foo.esm.js": true, - "foo.js": true, "index.cjs.development.js": true, "index.cjs.production.min.js": true, "index.d.ts": true, "index.esm.js": true, "index.js": true, } - super readable!! - and no extraneous files listed (e.g. .map, other directories, etc) --- test/tests/tsdx-build.test.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/test/tests/tsdx-build.test.js b/test/tests/tsdx-build.test.js index 2d69b8ab3..325a7d0db 100644 --- a/test/tests/tsdx-build.test.js +++ b/test/tests/tsdx-build.test.js @@ -46,17 +46,27 @@ describe('tsdx build', () => { '--format esm,cjs', ].join(' ') ); - - const entries = ['index', 'foo', 'subdir1/subdir1-2/index', 'subdir1/glob']; const outputFiles = shell.ls('-R', 'dist/'); - for (const entry of entries) { - expect(outputFiles).toContain(`${entry}.js`); - expect(outputFiles).toContain(`${entry}.cjs.development.js`); - expect(outputFiles).toContain(`${entry}.cjs.production.min.js`); - expect(outputFiles).toContain(`${entry}.esm.js`); - expect(outputFiles).toContain(`${entry}.d.ts`); + function arrToDict(arr) { + return arr.reduce((dict, elem) => { + dict[elem] = true; + return dict; + }, {}); } + const outputDict = arrToDict(outputFiles); + + const entries = ['index', 'foo', 'subdir1/subdir1-2/index', 'subdir1/glob']; + const expected = entries.reduce((dict, entry) => { + dict[`${entry}.js`] = true; + dict[`${entry}.cjs.development.js`] = true; + dict[`${entry}.cjs.production.min.js`] = true; + dict[`${entry}.esm.js`] = true; + dict[`${entry}.d.ts`] = true; + return dict; + }, {}); + + expect(outputDict).toMatchObject(expected); expect(output.code).toBe(0); }); From a1097eaf2ea43f2dde04b72ceb1050e1f2bcfe5d Mon Sep 17 00:00:00 2001 From: Anton Gilgur Date: Sat, 7 Mar 2020 05:57:51 -0500 Subject: [PATCH 10/10] (feat/optim): code-split multiple entries - use Rollup's multiple inputs in order to code-split and significantly speed up multi-entry builds - fixup some type errors - the only caveat is that "multi-entry" for UMD can't have different names... but UMD isn't meant to be multi-entry, so that's probably best left as a separate use case - i.e. running `tsdx build --noClean` after creating the ESM, CJS, etc bundles - that's the way it seems to be done in Rollup guides too - UMD is kind of the exception in general --- README.md | 8 ++------ src/createBuildConfigs.ts | 43 ++++++++++++++++++--------------------- src/createRollupConfig.ts | 19 +++++++++-------- src/types.ts | 8 ++------ 4 files changed, 35 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index c668c42fa..0d28b6131 100644 --- a/README.md +++ b/README.md @@ -323,14 +323,10 @@ The `options` object contains the following: ```tsx export interface TsdxOptions { - // path to file - input: string; + // map: dist path -> entry path + input: { [entryAlias: string]: string }; // Name of package name: string; - // output path - output: { - file: string; - }; // JS target target: 'node' | 'browser'; // Module format diff --git a/src/createBuildConfigs.ts b/src/createBuildConfigs.ts index 84e4975a4..604e1e446 100644 --- a/src/createBuildConfigs.ts +++ b/src/createBuildConfigs.ts @@ -1,6 +1,5 @@ -import { RollupOptions, OutputOptions } from 'rollup'; +import { RollupOptions } from 'rollup'; import * as fs from 'fs-extra'; -import { concatAllArray } from 'jpjs'; import { paths } from './constants'; import { TsdxOptions, NormalizedOpts } from './types'; @@ -20,18 +19,14 @@ if (fs.existsSync(paths.appConfig)) { export async function createBuildConfigs( opts: NormalizedOpts -): Promise> { - const allInputs = concatAllArray( - opts.input.map((input: string, index: number) => - createAllFormats(opts, input, index).map( - (options: TsdxOptions, index: number) => ({ - ...options, - // We want to know if this is the first run for each entryfile - // for certain plugins (e.g. css) - writeMeta: index === 0, - }) - ) - ) +): Promise { + const allInputs = createAllFormats(opts).map( + (options: TsdxOptions, index: number) => ({ + ...options, + // We want to know if this is the first run for each entryfile + // for certain plugins (e.g. css) + writeMeta: index === 0, + }) ); return await Promise.all( @@ -44,17 +39,19 @@ export async function createBuildConfigs( } function createAllFormats( - opts: NormalizedOpts, - input: string, - index: number + opts: NormalizedOpts ): [TsdxOptions, ...TsdxOptions[]] { - const sharedOpts = { + const sharedOpts: Omit = { ...opts, - input, - name: opts.name[index], - output: { - file: opts.output.file[index], - }, + // for multi-entry, we use an input object to specify where to put each + // file instead of output.file + input: opts.input.reduce((dict: TsdxOptions['input'], input, index) => { + dict[`${opts.output.file[index]}`] = input; + return dict; + }, {}), + // multiple UMD names aren't currently supported for multi-entry + // (can't code-split UMD anyway) + name: opts.name[0], }; return [ diff --git a/src/createRollupConfig.ts b/src/createRollupConfig.ts index 9a328969b..9d6073fca 100644 --- a/src/createRollupConfig.ts +++ b/src/createRollupConfig.ts @@ -34,15 +34,16 @@ export async function createRollupConfig( const shouldMinify = opts.minify !== undefined ? opts.minify : opts.env === 'production'; - const outputName = [ - `${paths.appDist}/${opts.output.file}`, - opts.format, - opts.env, - shouldMinify ? 'min' : '', - 'js', - ] + const outputSuffix = [opts.format, opts.env, shouldMinify ? 'min' : '', 'js'] .filter(Boolean) .join('.'); + let entryFileNames = `[name].${outputSuffix}`; + + // if there's only one input, uses the package name instead of the filename + const inputKeys = Object.keys(opts.input); + if (inputKeys.length === 1) { + entryFileNames = `${inputKeys[0]}.${outputSuffix}`; + } let tsconfigJSON; try { @@ -82,8 +83,10 @@ export async function createRollupConfig( }, // Establish Rollup output output: { + // Set dir to output to + dir: paths.appDist, // Set filenames of the consumer's package - file: outputName, + entryFileNames, // Pass through the file format format: opts.format, // Do not let Rollup call Object.freeze() on namespace import objects diff --git a/src/types.ts b/src/types.ts index 8e98a95ec..2e97fd9f7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -38,12 +38,8 @@ export interface NormalizedOpts export interface TsdxOptions extends SharedOpts { // Name of package name: string; - // path to file - input: string; - // output path - output: { - file: string; - }; + // map: dist path -> entry path + input: { [entryAlias: string]: string }; // Environment env: 'development' | 'production'; // Module format