tachyon-package-builder1

32.0.0 • Public • Published

Tachyon Package Builder

Builds TypeScript packages into module and dist/bundle output with accompanying types, while also ensuring that code will run in both browser and node contexts while being fully tree-shakeable for webpack/parcel/etc builders to minimize client-side bundle size.

Install

yarn add -D tachyon-package-builder

CLI Usage

The base command is simply:

build-package

If everything is properly configured (as listed below), this should just work and you'll be ready to publish your package!

Flags

There are a couple of flags you can use to modify the build behavior:

  • default option (technically --pkgDir/-p for obscure use-cases) This defines the directory of the package to be built. Useful for CI or automation situations in which you might want to run the builder from outside of the package directory. Since this is the default argument you should not normally need the flag:
build-package path/to/package
  • --watch/-w This will cause the builder to run in watch mode, in which case it will incrementally rebuild any change files, greatly reducing subsequent build times. Useful for when you developing the package while it is linked to another application or in a monorepo.

  • --notify/-n This will give you an OS notification when the build is done. Useful for large projects with longer build-times.

  • --serverOnly/-S This will eliminate the module build from the output, for when you know that the package will never be run in the browser. See config notes below regarding this build method.

  • --webpackOnly/-W This is a special mode that will skip TypeScript as well and only run webpack, outputting only a single bundle file. This is only for very specific use-cases like transpiling a vendored dependency and should not be generally used.

  • --help/-h This shows the available options.

Node Usage

You can use this package inside another JavaScript file by importing it.

const buildPackage = require('tachyon-package-builder');
...
// Set the config values you want. Note that usage in node requires providing
// the pkgDir parameter.
const opts = { help, notify, pkgDir, serverOnly, watch, webpackOnly };
buildPackage(opts);

How it Works

This package works by combining TypeScript, Babel, and Webpack to generate the appropriate build artifacts. It uses TypeScript only to strip types and emit .d.ts files, leaving the actual JS transpilation to Babel in order to be able to share @babel/runtime with community packages (instead of getting tied to ts.lib). Webpack is left to do the bundling for the dist output.

Package Configuration

As a result of the above pipeline, there are a few requirements with regards to package structure:

  • tsconfig.build.json in the root of the package. This will probably extend a tsconfig.json, and can be useful to exclude things like tests, mocks, and set-up scripts from the actual package distribution contents (using the top-level excludes key). There are a few mandatory compilerOptions settings (you can change the rest as needed):
"compilerOptions": {
  "declaration": true,
  "declarationDir": "types",
  "declarationMap": true,
  "jsx": "preserve",
  "module": "esnext",
  "moduleResolution": "node",
  "target": "esnext",
  "outDir": "esnext"
}

Note that by default, tsc will still emit output even if there are type errors in the code. This is relevant for rebuilds within a --watch setting, because after the first successful build, tsc will continue to emit transpiled JS (which will be picked up by babel and webpack) regardless of any type errors; the type errors will still be shown in the console. This can be useful for exploratory coding and similar use-cases (and is the recommended configuration for good developer experience), but you can opt-out of this behavior by adding the noEmitOnError option to the compilerOptions. Normal (non-watch) builds will always fail on type errors with or without this flag.

  • babel.config.js in the root of the package. In order to ensure that ES module are transpiled for the dist build but left intact for the module build, there is a base configuration you will need (you can add transforms as needed):
module.exports = (api) => {
  api.cache.using(() => process.env.NODE_ENV);

  return {
    presets: [
      ['@babel/preset-env', {
        loose: true,
        modules: !api.env('module') && 'auto',
      }],
      '@babel/preset-react',
      '@babel/preset-typescript',
    ],
    plugins: [
      '@babel/plugin-transform-runtime',
    ],
  };
};
  • webpack.config.js in the root of the package. We'll be re-using the Babel config here via babel-loader, and here is the resulting minimal configuration you will need:
const { resolve } = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');

const { name: library } = require('./package.json');

module.exports = function(env = {}) {
  return {
    mode: 'production',
    entry: resolve(__dirname, 'esnext', 'index'),
    resolve: {
      extensions: ['.js', '.jsx'],
    },
    externals: [nodeExternals(nodeExternalsOpts)],
    module: {
      rules: [
        {
          test: /\.jsx?$/,
          use: 'babel-loader',
        },
      ],
    },
    output: {
      filename: 'index.js',
      path: resolve(__dirname, 'dist'),
      libraryTarget: 'commonjs2',
      library,
    },
  };
};

If you are only supporting server builds, then you should add target: "node" to your config.

  • src directory should contain all code for packaging. As mentioned above, to prevent any unwanted files in src from being included in the final distributable package, use the tsconfig exclude key to keep files from entering the build process.

  • package.json will need a few entries after this is all done, making your package ready for publishing/distribution:

"main": "dist/index.js",
"module": "module/index.js",
"types": "types/index.d.ts",
"sideEffects": false,
"files": [
  "dist/",
  "module/",
  "types/"
],
"browserslist": [
  ...
],

Use the browserslist values to control which features you transpile and which you allow through (consumed by Babel's preset-env).

If you are only supporting server builds, then you should omit the module key.

It is also common to include:

"scripts": {
  "build": "build-package",
  "build:watch": "build-package -w",
}

Readme

Keywords

none

Package Sidebar

Install

npm i tachyon-package-builder1

Weekly Downloads

0

Version

32.0.0

License

UNLICENSED

Unpacked Size

20.4 kB

Total Files

13

Last publish

Collaborators

  • frgjdhskxjogu