A build tool for apps and packages, static and server-rendered sites. Built on Vite, Rollup and esbuild.
- Compile TypeScript to JavaScript and generate bundles for CJS and ESM
- Generates type declaration files (
.d.ts
) for entry points - Supports multiple (nested) entry points
- Generate stub entry points for local development
- ESM reconciliation
- Handles side-effects for generated bundles
- Compile Vanilla Extract stylesheets to JavaScript
- 🚧 Start an HTTP server (with hot reloading) to preview your site (WIP)
- 🚧 Build a static version of your site (e.g. for deploying to S3) (WIP)
- Features
- Contents
- Getting started
- Commands
- Side-effects
- ESM reconciliation
- DTS bundles
- Backwards-compatible entry points
- Contributing
pnpm add --save-dev @crackle/cli
Crackle will look for a crackle.config.ts
file in the project directory, but if one doesn't exist, it will use the default configuration.
Here's how to specify a custom configuration file:
// crackle.config.ts
import { defineConfig } from '@crackle/cli/config';
export default defineConfig({
// ...
});
or
// crackle.config.ts
import type { UserConfig } from '@crackle/cli/config';
export default {
// ...
} satisfies UserConfig;
(the default config values are documented in the type UserConfig
)
You can find the full list of commands by running crackle --help
.
The most common ones are listed below.
Compile the package for publishing.
This will compile TypeScript files to CJS- and ESM-compatible files and generate TypeScript declaration files (.d.ts
files) for entry points.
Passing the --fix
parameter will also run crackle fix
.
The default entry point is src/index.ts
.
Multiple (nested) entry points are supported, by adding .ts
files to src/entries/
.
Given this directory structure:
src
├── entries
│ ├── components.ts
│ └── themes
│ └── apac.ts
└── index.ts
Crackle will generate these entry points:
my-project (main entry point; mapped to src/index.ts)
my-project/components (mapped to src/entries/components.ts)
my-project/themes/apac (mapped to src/entries/themes/apac.ts)
If a dependency is present in devDependencies
(but not in peerDependencies
) it is bundled along with the project's source code.
dependencies
, peerDependencies
and optionalDependencies
are marked as external and not bundled.
crackle package --mode=bundle
crackle package --mode=preserve
This controls how Crackle generates output files.
bundle
rolls up output files into as few chunks as possible (default behaviour)preserve
creates separate files for all modules using the original module names as file names. This is similar to Rollup'soutput.preserveModules
, but allows more fine-grained control because we hook intooutput.manualChunks
.
The mode can also be configured via crackle.config.ts
:
// crackle.config.ts
import { defineConfig } from '@crackle/cli/config';
export default defineConfig({
package: {
mode: 'preserve',
},
});
Some libraries declare namespaces, which are hard/impossible to bundle.
For such cases, Crackle has an option to preserve the file structure of the generated .d.ts
files.
// crackle.config.ts
import { defineConfig } from '@crackle/cli/config';
export default defineConfig({
dts: {
mode: 'preserve',
},
});
bundle
rolls up output files into as few chunks as possible (default behaviour)preserve
creates separate files for all modules using the original module names as file names. This is similar to Rollup'soutput.preserveModules
.
Generate entry points for local development. This will generate stub entry points for local development. Stub entry points import the source files directly instead of the compiled files.
By default, Crackle generates a require
shim which enables the loading of TypeScript files in Node.js using a require hook.
There are situations where this is not required, such as when using a bundler that supports TypeScript natively, e.g. esbuild or Vite.
In these cases, the --shim
option can be set to none
.
crackle dev --shim=require
crackle dev --shim=none
require
generates a shim for use in Node.js (default behaviour)none
doesn't generate arequire
shim
This can also be configured via crackle.config.ts
:
// crackle.config.ts
import { defineConfig } from '@crackle/cli/config';
export default defineConfig({
dev: {
shim: 'none',
},
});
Updates package.json
exports, files, sideEffects
field and more:
package.json
:"main"
,"module"
and"types"
are updated to point to the generated files indist
package.json
:"exports"
and"files"
are updated to include generated entry pointspackage.json
:"sideEffects"
flag is updated to match the generated files indist
package.json
: keys are sorted usingsort-package-json
.gitignore
is updated to ignore backwards-compatible entry points
Crackle updates the sideEffects
flag if needed.
If an entry point has side-effects (as defined via package.json
's "sideEffects"
key), Crackle will update the "sideEffects"
key to match the output path in dist
.
Based on what's in src
and the value of the flag, when running crackle package
these things will happen:
.css.ts
files (and files importing them) will be placed indist/styles/<same-path-as-in-src>
- files with side-effects (matching the globs defined in
"sideEffects"
) will be placed indist/side-effects/<same-path-as-in-src>
- if an entry has side-effects, the
"sideEffects"
key will be updated to match the output path indist
In the ESM build, Crackle will automatically reconcile import specifiers in the output code to point to an actual file when the imported package doesn't have an "exports"
field.
More details in ESM reconciliation.
Crackle will generate type declaration files (.d.ts
) for entry points.
The same rules for dependencies apply here, meaning types for devDependencies
(but not peerDependencies
) are bundled with the project's types.
Crackle generates backwards-compatible entry points for tools which don't support the "exports"
field in package.json
.
Although there is good support in bundlers and Node.js, there are still some tools that don't support it, mainly TypeScript versions below 5.0.
- Clone this repository
- Set up Node.js using Volta
- Enable Corepack using
corepack enable
- Install dependencies using
pnpm install
- Run bootstrap script using
pnpm bootstrap
- Run
pnpm dev
which runscrackle dev
in every package or runpnpm build
to build packages usingcrackle package
Crackle is self-hosting: it is built and tested with itself.
There's a bootstrap
package that uses a published version of Crackle to build the packages in the monorepo.
This is done by running pnpm bootstrap
at the root of the monorepo.
Once the bootstrap script is done, the current version of Crackle can be used to build itself.
If anything goes wrong and the bootstrap script fails, there's a fallback script to build @crackle/core
using unbuild by running pnpm unbuild
in the packages/core
directory.