cli

February 2022 - ESM, Rollup, and Vitest

The initial project setup followed oclif recommendation: CommonJS-based Javascript generated by the Typescript compiler. The dev executable generated by oclif would run the CLI through ts-node. That worked fine until we started adding some pure ESM dependencies. They cause the setup to break because the interoperability is only from ESM to CommonJS. In other words, ESM projects can import CommonJS code, but not the other way around. It wouldn’t be an issue if we used only CommonJS dependencies, but considering more dependencies are migrating to ESM, sticking with CommonJS might put us in a position where we won’t be able to use some dependencies that are necessary for the CLI. Motivated by aligning the CLI with the ecosystem’s future and ensuring that we can use CommonJS and ESM dependencies, we turned the project into an ESM project. Moreover, we took the opportunity to introduce Rollup as a build tool to make the CLI more lightweight and minimize the dependencies that need to be resolved and installed on the user side. Resolving and installing deep dependency graphs might lead to graph with incompabilities between nodes (i.e. the well-known delete node_modules to fix the problem solution).

@shopify/cli-kit builds a bundle under dist/index.js that includes the external dependencies. Internal teams and external developers building and extending the CLI can thus make @shopify/cli-kit a peerDependency of the project and have access to a set of tools to build an experience that’s consistent with the rest of the CLI.

@shopify/create-app builds a bundle under dist/commands/init.js that includes external dependencies including @shopify/cli-kit. We bundle @shopify/cli-kit instead of treating it as an external dependency that needs to be installed when creating apps to let Rollup tree-shake it and only include the elements that are necessary for creating apps.

@shopify/cli builds a bundle under dist/commands/*.js representing the commands from @shopify/app and @shopify/theme. Unlike @shopify/create-app, @shopify/cli-kit is a dependency so that external plugins that add additional functionality to the CLI can depend on it.

While working on enabling this setup, we encountered some Jest limitations because the framework’s support for ESM is still experimental. In particular, the mocking functionality that we use of from the CLI doesn’t work. To remove the blocker while the full support is being worked on, we set up a different test framework, Vitest, which was designed for ESM and has an API compatible with Jest’s.

Resources