Skip to content

Commit

Permalink
No env specific bundle for ESM (#142)
Browse files Browse the repository at this point in the history
* No env specific bundles (except for CJS)

* CJS does have both builds

* Revert readme

* Enable terser toplevel for CJS

* Output format condition

* es -> esm

* Use backticks to format entry file
  • Loading branch information
FredyC authored and jaredpalmer committed Jun 18, 2019
1 parent 830e104 commit 0f9d964
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 79 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ exports.sum = (s, t) => s + t;

AS you can see, TSDX stripped out the development check from the production code. **This allows you to safely add development-only behavior (like more useful error messages) without any production bundle size impact.**

For ESM build, it's up to end-user to build environment specific build with NODE_ENV replace (done by Webpack 4 automatically).

#### Rollup Treeshaking

TSDX's rollup config [removes getters and setters on objects](https://github.com/palmerhq/tsdx/blob/1f6a1b6819bb17678aa417f0df5349bec12f59ac/src/createRollupConfig.ts#L73) so that property access has no side effects. Don't do it.
Expand Down Expand Up @@ -246,7 +248,7 @@ For brevity let's look at the ES module output.
<!-- prettier-ignore -->
```js
import o from"lodash-es/kebabCase";const e=e=>{console.log(o(e))};export{e as KebabLogger};
//# sourceMappingURL=test-react-tsdx.es.production.js.map
//# sourceMappingURL=test-react-tsdx.esm.production.js.map
```

TSDX will rewrite your `import kebabCase from 'lodash/kebabCase'` to `import o from 'lodash-es/kebabCase'`. This allows your library to be treeshakable to end consumers while allowing to you to use `@types/lodash` for free.
Expand Down Expand Up @@ -279,14 +281,14 @@ Options
-i, --entry Entry module(s)
--target Specify your target environment (default web)
--name Specify name exposed in UMD builds
--format Specify module format(s) (default cjs,es)
--format Specify module format(s) (default cjs,esm)
-h, --help Displays this message

Examples
$ tsdx watch --entry src/foo.tsx
$ tsdx watch --target node
$ tsdx watch --name Foo
$ tsdx watch --format cjs,es,umd
$ tsdx watch --format cjs,esm,umd
```

### `tsdx build`
Expand All @@ -302,14 +304,14 @@ Options
-i, --entry Entry module(s)
--target Specify your target environment (default web)
--name Specify name exposed in UMD builds
--format Specify module format(s) (default cjs,es)
--format Specify module format(s) (default cjs,esm)
-h, --help Displays this message

Examples
$ tsdx build --entry src/foo.tsx
$ tsdx build --target node
$ tsdx build --name Foo
$ tsdx build --format cjs,es,umd
$ tsdx build --format cjs,esm,umd
```

### `tsdx test`
Expand Down
35 changes: 24 additions & 11 deletions src/createRollupConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import shebangPlugin from '@jaredpalmer/rollup-plugin-preserve-shebang';
const replacements = [{ original: 'lodash', replacement: 'lodash-es' }];

const babelOptions = (
format: 'cjs' | 'es' | 'umd',
format: 'cjs' | 'esm' | 'umd',
target: 'node' | 'browser'
) => ({
exclude: 'node_modules/**',
Expand Down Expand Up @@ -52,15 +52,29 @@ const babelOptions = (
// shebang cache map thing because the transform only gets run once
let shebang: any = {};
export function createRollupConfig(
format: 'cjs' | 'umd' | 'es',
env: 'development' | 'production',
format: 'cjs' | 'umd' | 'esm',
opts: {
env?: 'development' | 'production';
minify?: boolean;
input: string;
name: string;
target: 'node' | 'browser';
tsconfig?: string;
}
) {
const shouldMinify =
opts.minify !== undefined ? opts.minify : opts.env === 'production';

const outputName = [
`${paths.appDist}/${safePackageName(opts.name)}`,
format,
opts.env,
shouldMinify ? 'min' : '',
'js',
]
.filter(Boolean)
.join('.');

return {
// Tell Rollup the entry point to the package
input: opts.input,
Expand All @@ -74,9 +88,7 @@ export function createRollupConfig(
// Establish Rollup output
output: {
// Set filenames of the consumer's package
file: `${paths.appDist}/${safePackageName(
opts.name
)}.${format}.${env}.js`,
file: outputName,
// Pass through the file format
format,
// Do not let Rollup call Object.freeze() on namespace import objects
Expand Down Expand Up @@ -161,14 +173,15 @@ export function createRollupConfig(
},
}),
babel(babelOptions(format, opts.target)),
replace({
'process.env.NODE_ENV': JSON.stringify(env),
}),
opts.env !== undefined &&
replace({
'process.env.NODE_ENV': JSON.stringify(opts.env),
}),
sourceMaps(),
// sizeSnapshot({
// printInfo: false,
// }),
env === 'production' &&
shouldMinify &&
terser({
sourcemap: true,
output: { comments: false },
Expand All @@ -178,7 +191,7 @@ export function createRollupConfig(
passes: 10,
},
ecma: 5,
toplevel: format === 'es' || format === 'cjs',
toplevel: format === 'cjs',
warnings: true,
}),
],
Expand Down
108 changes: 51 additions & 57 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ function createBuildConfigs(
return concatAllArray(
opts.input.map((input: string) => [
opts.format.includes('cjs') &&
createRollupConfig('cjs', 'development', { ...opts, input }),
createRollupConfig('cjs', { env: 'development', ...opts, input }),
opts.format.includes('cjs') &&
createRollupConfig('cjs', 'production', { ...opts, input }),
opts.format.includes('es') &&
createRollupConfig('es', 'production', { ...opts, input }),
createRollupConfig('cjs', { env: 'production', ...opts, input }),
opts.format.includes('esm') &&
createRollupConfig('esm', { ...opts, input }),
opts.format.includes('umd') &&
createRollupConfig('umd', 'development', { ...opts, input }),
createRollupConfig('umd', { env: 'development', ...opts, input }),
opts.format.includes('umd') &&
createRollupConfig('umd', 'production', { ...opts, input }),
createRollupConfig('umd', { env: 'production', ...opts, input }),
])
).filter(Boolean);
}
Expand Down Expand Up @@ -259,31 +259,16 @@ prog
.example('watch --target node')
.option('--name', 'Specify name exposed in UMD builds')
.example('watch --name Foo')
.option('--format', 'Specify module format(s)', 'cjs,es,umd')
.example('watch --format cjs,es')
.option('--format', 'Specify module format(s)', 'cjs,esm')
.example('watch --format cjs,esm')
.option('--tsconfig', 'Specify custom tsconfig path')
.example('build --tsconfig ./tsconfig.foo.json')
.action(async (opts: any) => {
opts.name = opts.name || appPackageJson.name;
opts.input = await getInputs(opts.entry, appPackageJson.source);
.action(async (dirtyOpts: any) => {
const opts = await normalizeOpts(dirtyOpts);
const buildConfigs = createBuildConfigs(opts);
await ensureDistFolder();
if (opts.format.includes('cjs')) {
await util.promisify(mkdirp)(resolveApp('dist'));
await fs.writeFile(
resolveApp('dist/index.js'),
`
'use strict'
if (process.env.NODE_ENV === 'production') {
module.exports = require('./${safePackageName(
opts.name
)}.cjs.production.js')
} else {
module.exports = require('./${safePackageName(
opts.name
)}.cjs.development.js')
}`
);
await writeCjsEntryFile(opts.name);
}
const spinner = ora().start();
await watch(
Expand Down Expand Up @@ -329,40 +314,17 @@ prog
.example('build --target node')
.option('--name', 'Specify name exposed in UMD builds')
.example('build --name Foo')
.option('--format', 'Specify module format(s)', 'cjs,es')
.example('build --format cjs,es')
.option('--format', 'Specify module format(s)', 'cjs,esm')
.example('build --format cjs,esm')
.option('--tsconfig', 'Specify custom tsconfig path')
.example('build --tsconfig ./tsconfig.foo.json')
.action(async (opts: any) => {
opts.name = opts.name || appPackageJson.name;
opts.input = await getInputs(opts.entry, appPackageJson.source);
.action(async (dirtyOpts: any) => {
const opts = await normalizeOpts(dirtyOpts);
const buildConfigs = createBuildConfigs(opts);
await ensureDistFolder();
if (opts.format.includes('cjs')) {
try {
await util.promisify(mkdirp)(resolveApp('./dist'));
const promise = fs
.writeFile(
resolveApp('./dist/index.js'),
`
'use strict'
if (process.env.NODE_ENV === 'production') {
module.exports = require('./${safePackageName(
opts.name
)}.cjs.production.js')
} else {
module.exports = require('./${safePackageName(
opts.name
)}.cjs.development.js')
}`
)
.catch(e => {
throw e;
});
logger(promise, 'Creating entry file');
} catch (e) {
logError(e);
}
const promise = writeCjsEntryFile(opts.name).catch(logError);
logger(promise, 'Creating entry file');
}
try {
const promise = asyncro
Expand All @@ -383,6 +345,38 @@ prog
}
});

async function normalizeOpts(opts: any) {
return {
...opts,
name: opts.name || appPackageJson.name,
input: await getInputs(opts.entry, appPackageJson.source),
format: opts.format.split(',').map((format: string) => {
if (format === 'es') {
return 'esm';
}
return format;
}),
};
}

function ensureDistFolder() {
return util.promisify(mkdirp)(resolveApp('dist'));
}

function writeCjsEntryFile(name: string) {
const baseLine = `module.exports = require('./${safePackageName(name)}`;
const contents = `
'use strict'
if (process.env.NODE_ENV === 'production') {
${baseLine}.cjs.production.min.js')
} else {
${baseLine}.cjs.development.js')
}
`;
return fs.writeFile(resolveApp(`./dist/index.js`), contents);
}

prog
.command('test')
.describe(
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/build-default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
},
"name": "build-default",
"license": "MIT"
}
}
8 changes: 3 additions & 5 deletions test/tests/tsdx-build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,16 @@ describe('tsdx build', () => {
it('should compile files into a dist directory', () => {
util.setupStageWithFixture(stageName, 'build-default');

const output = shell.exec('node ../dist/index.js build');
const output = shell.exec('node ../dist/index.js build --format esm,cjs');

expect(shell.test('-f', 'dist/index.js')).toBeTruthy();
expect(
shell.test('-f', 'dist/build-default.cjs.development.js')
).toBeTruthy();
expect(
shell.test('-f', 'dist/build-default.cjs.production.js')
).toBeTruthy();
expect(
shell.test('-f', 'dist/build-default.es.production.js')
shell.test('-f', 'dist/build-default.cjs.production.min.js')
).toBeTruthy();
expect(shell.test('-f', 'dist/build-default.esm.js')).toBeTruthy();

expect(shell.test('-f', 'dist/index.d.ts')).toBeTruthy();

Expand Down

0 comments on commit 0f9d964

Please sign in to comment.