@shopify/loom-plugin-build-library
TypeScript icon, indicating that this package has built-in type declarations

1.0.3 • Public • Published

@shopify/loom-plugin-build-library

This package is your entrypoint into building libraries with loom. It configures a core series of tools for building libraries:

This package handles JavaScript and TypeScript files only. If you need support for other file types, such as styles, images and graphql files then check out @shopify/loom-plugin-build-library-extended.

Ite exposes two plugins - buildLibrary and buildLibraryWorkspace that should be added to your loom plugins.

Installation

$ yarn add @shopify/loom-plugin-build-library --dev

Usage

Add buildLibrary and buildLibraryWorkspace to your loom plugins.

By default all build types - commonjs, esmodules and esnext are disabled. You should enable the builds that that you need by setting those options to true, along with specifying the required targets option based upon what browsers / node versions you wish to support.

import {createPackage} from '@shopify/loom';
import {
  buildLibrary,
  buildLibraryWorkspace,
} from '@shopify/loom-plugin-build-library';

export default createPackage((pkg) => {
  pkg.entry({root: './src/index.js'});
  pkg.use(
    buildLibrary({
      // Required. A browserslist string for specifying your target output.
      // Use browser targets (e.g. `'defaults'`) if your package targets the browser,
      // node targets (e.g. `'node 12.22'`) if your package targets node
      // or both (e.g.`'defaults, node 12.22'`) if your package targets both
      targets: 'defaults, node 12.22',
      // Optional. Defaults to false. Defines if commonjs outputs should be generated.
      commonjs: false,
      // Optional. Defaults to false. Defines if esmodules outputs should be generated.
      esmodules: false,
      // Optional. Defaults to false. Defines if esnext outputs should be generated.
      esnext: false,
      // Optional. Defaults to true. Defines if entrypoints should be written at
      // the root of the repository. You can disable this if you have a single
      // entrypoint or if your package uses the `exports` key in package.json
      rootEntrypoints: true,
      // Optional. Defaults to 'node'. Defines if the jest environment should be 'node' or 'jsdom'.
      jestTestEnvironment: 'node',
    }),
    buildLibraryWorkspace(),
  );
});

Targets

buildLibrary has single required option - targets - which is a browserslist string that controls the level of transpilation that is required.

What targets you specify shall depend upon the type of package you are creating.

  • If you are targeting a node-only package you can use 'maintained node versions' or an explicit minimum version number like 'node 12.22.0'.
  • If you are targeting browsers only you can specify browserlist defaults like 'defaults', recent versions like last 3 chrome versions, or use the @shopify/browserslist-config preset like 'extends @shopify/browserslist-config'.
  • If you are targeting both node and browser usage use a comma separated string to join both of the above: 'extends @shopify/browserslist-config, node 12.22.0'.

You can see what environments these values correspond to by running e.g. npx browserslist 'defaults'. If you find these values unclear or are concerned that changes to them may be non-obvious, you can use explicit version numbers instead.

Outputs

This plugin exposes 3 ouptput options. They are all disabled by default and you must opt into enabling them.

  • commmonjs controls creating commonjs output in the build/cjs folder. This uses require() and exports = to handle imports and exports, in the format supported by node. You should enable this format and set it as your main entrypoint (see below) when creating packages that are ran in node.
  • esmodules controls creating esmodule output in the build/esm folder. This uses import and export to handle imports and exports, in the format handled by modern bundlers and node 12.22+. You should enable this format and set it as your module entrypoint when creating packages that are meant to be bundled for use in the browser.
  • esnext controls creating "esnext" output in the build/esnext folder. This uses import and export to handle imports and exports and forces the compile target to be the latest chrome version, instead of whatever was specified in the targets option. Setting this as the esnext entrypoint will allow apps configured using sewing-kit to use this build to produce a minimally compiled version of the library as part of its application build in the event that the app targets a smaller browserlist than what you specified in your library's targets. The consuming app's webpack config must treat this package being as not external - i.e. it must be processed by the consuming app's webpack config.

Entrypoints

By default, entrypoint files are written to the root of your package that correspond the package's entrys defined in its config file. This is to support packages with multiple entrypoints in a world where package.json's exports support is not pervasive as Webpack 4, TypeScript and Jest support is currently absent at time of writing (September 2021).

Single entrypoint

When creating a package with a single entrypoint, you can set rootEntrypoints: false to not write any root entrypoints, and point fields in your package.json to the contents of the build folder.

Given a loom.config.js file that contains:

export default createPackage((pkg) => {
  pkg.entry({root: './src/index.js'});
  pkg.use(
    buildLibrary({
      targets: 'defaults, node 12.22',
      commonjs: true,
      esmodules: true,
      esnext: true,
      rootEntrypoints: false,
    });
  );
});

In the package.json add the following main (for commonjs output), module (for esmodules output), esnext (for esnext output) and types (for TypeScript types) keys. You can omit a given key if you are not generating a particular output type.

{
  "main": "build/cjs/index.js" /* commonjs output */,
  "module": "build/esm/index.mjs" /* esmodules output */,
  "esnext": "build/esnext/index.esnext" /* esnext output */,
  "types": "build/ts/index.d.ts" /* typescript output - this path depends upon your tsconfig.json */
}

Multiple entrypoints

If you have multiple entrypoints then you should leave rootEntrypoints: true to generate entrypoints at the root. This is a slightly flawed approach as only commonjs content for the additional entrypoints is supported, but is the best that we can do give current support.

Given a loom.config.js file that contains:

export default createPackage((pkg) => {
  pkg.entry({root: './src/index.js'});
  pkg.entry({root: './src/second-entry.js', name: 'second-entry'});
  pkg.use(
    buildLibrary({
    targets: 'defaults, node 12.22',
    commonjs: true,
    esmodules: true,
    esnext: true,
    rootEntrypoints: true,
    });
  );
});

Then this shall write sets of outputs for each entrypoint at the root of your package:

  • index.js that reexports content from build/cjs/index.js
  • index.mjs that reexports content from build/esm/index.mjs
  • index.esnext that reexports content from build/esnext/index.esnext
  • second-entry.js that reexports content from build/cjs/second-entry.js
  • second-entry.mjs that reexports content from build/esm/second-entry.mjs
  • second-entry.esnext that reexports content from build/esnext/second-entry.esnext

In the package.json add the following main (for commonjs output), module (for esmodules output), esnext (for esnext output) and types(for TypeScript types) keys. You can omit a given key if you are not generating a particular output type. If you specify a files array, remember to add these root entrypoints to it.

{
  "main": "index.js" /* commonjs output */,
  "module": "index.mjs" /* esmodules output */,
  "esnext": "index.esnext" /* esnext output */,
  "types": "build/ts/index.d.ts" /* typescript output - this path depends upon your tsconfig.json */,
  "typesVersions": {
    "*": {
      /* typescript types for the second-entry entrypoint */
      "second-entry": ["./build/ts/second-entry.d.ts"]
    }
  }
}

If a consuming app imports your-package/second-entry then it shall load second-entry.js from the root of your package.

Overriding Babel config

We provide an initial babel config that supports typescript and react, using @shopify/babel-plugin. If you need to adjust this config, you can call the babel() plugin after buildLibrary to override the defaults.

import {createPackage} from '@shopify/loom';
import {
  buildLibrary,
  buildLibraryWorkspace,
  babel,
} from '@shopify/loom-plugin-build-library';

export default createPackage((pkg) => {
  pkg.entry({root: './src/index.js'});
  pkg.use(
    buildLibrary({targets: 'node 12.22.0', commonjs: true}),
    buildLibraryWorkspace(),
    // Override initial babel options.
    // Return a new object, instead of mutating the argument object.
    babel({
      config(babelConfig) {
        return {
          ...babelConfig,
          plugins: [...(babelConfig.plugins || []), 'my-custom-babel-plugin'],
        };
      },
    }),
  );
});

Examples

Configuring a nodejs-only package

When targeting nodejs only, create commonjs output and target a node version.

import {createPackage} from '@shopify/loom';
import {
  buildLibrary,
  buildLibraryWorkspace,
} from '@shopify/loom-plugin-build-library';

export default createPackage((pkg) => {
  pkg.entry({root: './src/index.js'});
  pkg.use(
    buildLibrary({targets: 'node 12.22.0', commonjs: true}),
    buildLibraryWorkspace(),
  );
});

Configuring a nodejs and browser package for use in sewing-kit powered apps

When targeting nodejs and the browser, create commonjs, esmodules and esnext output and target a node version, and extend from Shopify's browserlist config.

import {createPackage} from '@shopify/loom';
import {
  buildLibrary,
  buildLibraryWorkspace,
} from '@shopify/loom-plugin-build-library';

export default createPackage((pkg) => {
  pkg.entry({root: './src/index.js'});
  pkg.use(
    buildLibrary({
      targets: 'extends @shopify/browserslist-config, node 12.22.0',
      commonjs: true,
      esmodules: true,
      esnext: true,
    }),
    buildLibraryWorkspace(),
  );
});

Linting

This package does not offer any explicit opinion on linting configuration. This is because we want to keep the cadences for updating linting configs and build tooling separate. Combining them means attempting to update build tooling but first having to make a load of otherwise unrelated linter suggested changes, and we've found that really annoying in the past.

We recommend using the following:

Readme

Keywords

none

Package Sidebar

Install

npm i @shopify/loom-plugin-build-library

Weekly Downloads

1,247

Version

1.0.3

License

MIT

Unpacked Size

34.2 kB

Total Files

21

Last publish

Collaborators

  • jaimie.rockburn
  • blittle
  • shopify-admin
  • maryharte
  • crisfmb
  • pmoloney89
  • netlohan
  • st999999
  • justin-irl
  • megswim
  • wcandillon
  • nathanpjf
  • shopify-dep
  • goodforonefare
  • lemonmade
  • vsumner
  • wizardlyhel
  • antoine.grant
  • tsov
  • andyw8-shopify
  • henrytao
  • hannachen
  • vividviolet
  • bpscott