npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@tramvai/build

v5.0.6

Published

Rollup wrapper, build multiple bundles for different module systems

Downloads

1,970

Readme

@tramvai/build

Library for building production ready bundles for packages written in TypeScript targetting next environments:

  • NodeJS
  • Bundlers (Webpack, etc.)
  • Browsers

Installation

Install @tramvai/build first:

npm install --save-dev @tramvai/build

Get started

Add necessary fields to package.json:

{
  "main": "lib/index.js",
  "module": "lib/index.es.js",
  "typings": "lib/index.d.ts",
  "sideEffects": false,
  "files": [
    "lib"
  ]
}

"main": "lib/index.js" based on that field lib calculates entry point for the build and it will be "src/index.ts" in this case

Create tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "module": "ESNext",
    "target": "ES2015",
    "allowJs": true,
    "declaration": true,
    "sourceMap": true,
    "importHelpers": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "jsx": "react-jsx",
    "rootDir": "./src",
    "outDir": "./lib",
    "declarationDir": "./lib",
    "types": ["node"],
    "lib": [
      "es2015",
      "es2016",
      "es2017",
      "es2018",
      "dom"
    ]
  },
  "include": ["./src"],
  "exclude": [
    "**/*.spec.ts",
    "**/*.spec.tsx",
    "**/*.test.ts",
    "**/*.test.tsx"
  ]
}

Add to dependencies library tslib:

npm install tslib

Build package with command tramvai-build:

tramvai-build --preserveModules --forPublish

with flag --preserveModules tramvai-build wil preserve file structure of library modules for better tree-shaking

with flag --forPublish tramvai-build replaces some fields in package.json in order to make built package usable in the end apps, for example "browser" field with object value can be updated`

Explanation

The main purpose for the lib is the effective production build for TypeScript package using rollup, with watch mode support.

Such builds, especially for monorepositories with big number of packages, can take a long time and are not very comfortable to work. Thats why, for the development environment it is preferred to use tsc with project references and incremental build.

Recommended and automatically generated package.json for @tramvai/build allows apps to use packages that were built either with tsc, or with @tramvai/build without any additional steps.

All of the built bundles will contain ES2019 standard code, it is expected that they will be bundled to ES5 using bundler (Webpack, etc.) with configured transpilation through babel for packages inside node_modules, written in modern JS.

NodeJS bundle in CommonJs format

NodeJS before 12 version hasn't supported ES modules or supported it only behind special flag. @tramvai/build generates bundle in ES2019 standard in CommonJS format automatically. Name of the result bundle is taken from field main in package.json, e.g. lib/index.js.

When bundling package in the app using webpack with option target: 'node' this CommonJS bundle probably will not be used as webpack will prefer to use module field while resolving source code.

It is expected that bundle from field "main" will be resolved only by NodeJS itself while bundlers will use bundle from field "module"

Bundle for bundlers (Webpack, etc.) in ES modules format

Modern bundlers support ES modules and non-standard field "module" in package.json. @tramvai/build generates bundle in ES2019 standard in ES modules format automatically. Name of the result bundle is calculates from field main in package.json by adding postfix .es e.g. lib/index.es.js.

If build was called with flag --forPublish to package.json will be added new field "module": "lib/index.es.js".

When bundling package in the app through webpack with option target: 'node' bundle from field module will have higher priority over bundle from main.

ES2019 code standard is generated as it is expected that bundle from field "module" will be resolved by bundler with configured transpilation through babel for packages inside node_modules, written in modern JS. Why we still prefer to use ES5 code over ES2019? Apparently, code in ES5 is still notably faster on NodeJS server. In the same time output bundle size is not important on server.

Bundle for browsers

Modern bundlers support ES modules and non-standard field "browser" in package.json. When field browser in specified in package.json, @tramvai/build will generate bundle in ES2019 standard in ES modules format.

If field browser in package.json is defined as a string then this string determines entry point to browser bundle and its name. E.g. when "browser": "lib/browser.js" entry point will be src/browser.ts and bundle will have a name lib/browser.js.

Otherwise, if field browser is defined as an object and build was called with flag --forPublish then name is defined by the field main in package.json with adding postfix .browser e.g. lib/index.browser.js. After that to field browser new property will be added as pointer for bundlers to bundle for the browser, instead of the field module:

{
  "browser": {
    ...,
    "./index.es.js": "./index.browser.js"
  }
}

Specification for the field browser

ES2019 code standard is generated as it is expected that bundle from "browser" field will be resolved by bundler with configured transpilation through babel for packages inside node_modules written in modern JS to the code according to the browserslist config.

When building our package in the app with webpack with option target: 'web' bundle from field browser will be prioritized over field module.

Copy static assets

For every build, all of the non JS/TS/JSON files (e.g. CSS, fonts, images) are copied to the output bundle preserving their relative paths (e.g. src/css/style.css -> lib/css/style.css). You can disable such copying by using flag copyStaticAssets:

tramvai-build --copyStaticAssets false

Build and copy migrations

When directory migrations has any code files they are considered as migration files. These files will be compiled to .js and copied to directory __migrations__.

CLI

Single build

tramvai-build

Build in watch mode

tramvai-build --watch

Copy static assets

tramvai-copy

Available flags

tramvai-build --help

JavaScript API

TramvaiBuild

TramvaiBuild is used to configure build process for following usage.

import { TramvaiBuild } from '@tramvai/build';

new TramvaiBuild(options);

Available options:

@inline src/options.h.ts

Build

Method TramvaiBuild.start builds package either single time or in watch mode depending on configuration of TramvaiBuild:

import { TramvaiBuild } from '@tramvai/build';

new TramvaiBuild(options).start();

Copy static files

Method TramvaiBuild.copy copies static assets to the output directory:

import { TramvaiBuild } from '@tramvai/build';

new TramvaiBuild(options).copy();

How to

Build separate bundle for browsers

Let's say we have to entry points. One is for the server - src/server.ts and for the client - src/browser.ts. In this case we should set field browser in package.json the next way:

{
  "main": "lib/server.js",
  "browser": "lib/browser.js"
}

After build for publication we will get next package.json:

{
  "main": "lib/server.js",
  "browser": "lib/browser.js",
  "typings": "lib/server.d.ts",
  "module": "lib/server.es.js"
}

Replace specific module for browser bundle

Let's say we have one entry point - src/index.ts and a module src/external.ts we want to replace by src/external.browser.ts. In this case we should set field browser in package.json the next way:

{
  "main": "lib/index.js",
  "browser": {
    "./lib/external.js": "./lib/external.browser.js"
  }
}

After build for publication we will get next package.json:

{
  "main": "lib/index.js",
  "browser": {
    "./lib/external.js": "./lib/external.browser.js",
    "./lib/index.es.js": "./lib/index.browser.js"
  },
  "typings": "lib/index.d.ts",
  "module": "lib/index.es.js"
}

Build all of the packages in monorepo in watch mode

@TODO + link to @tinkoff/fix-ts-references

Import module only under some circumstances or put module to separate chunk

Instead of static imports you can use dynamic import or require. In this case imported module will be build in the separate chunk. Later this chunk can be added by bundler to the generated bundle and if dynamic import was used it will be separate chunk as well after bundlers build, but when using require separate chunk will not be generated.

let func = noop;

if (process.env.NODE_ENV !== 'production') {
  func = require('./realFunc').func;
}

export { func };

Use JSON in package

By default in root tsconfig.json option resolveJsonModule is enabled. It is allows to import json-files the same way as usual source code using import, moreover typecheck and tree-shaking will work to json as well when publishing package. To disable ts errors for json imports add to tsconfig.json of the package new entry to field includes:

{
  "includes": ["./src", "./src/**/*.json"]
}

Use assets file in the package (e.g. css, svg)

These files are not used in bundle or source code and ts will ignore them. For proper package usage additional setup should be done. Add script tramvai-copy to package.json:

{
  "scripts": {
    "copy-static-assets": "tramvai-copy"
  }
}

This script will copy not related files to source code to the output directory. Copying itself happens either on dependencies install in the repository root or on package publishing. As for some reasons output directory might be deleted it may be needed to rerun tramvai-copy command for package.

Use css-modules

In order to disable typescript errors for css-modules imports add new file typings.d.ts to the src folder with the next content:

declare module '*.css' {
  const value: any;
  export default value;
}

To copy css while deb-build change next command:

"watch": "tramvai-copy && tsc -w"

Such imports are not compiled. To use it properly you can use @tramvai/cli for building app or any other solution for the css-modules.

When building correctness of imports for the css is not checking so check your package manually before publication.

Build only migrations or tests

If you need to build only migrations or tests files then you can specify option --only with either migrations or tests value.

tramvai-build --only migrations
tramvai-build --only tests