177 lines
5.7 KiB
TypeScript
177 lines
5.7 KiB
TypeScript
/**
|
|
* Utilities for assembling the Rollup plugin pipeline used by the Effect
|
|
* bundle-size tooling.
|
|
*
|
|
* This module is responsible for the bundler-specific work that turns local
|
|
* fixture entrypoints into comparable ESM output: resolving Effect package
|
|
* imports against each package's built `dist` files, replacing production
|
|
* environment checks, lowering TypeScript with esbuild, minifying with terser,
|
|
* and optionally adding a bundle visualizer. It is primarily used by the
|
|
* Rollup service when measuring gzipped fixture sizes or opening a
|
|
* visualization for bundle inspection.
|
|
*
|
|
* Keep plugin ordering intentional when changing this module. Local package
|
|
* resolution must run before normal node resolution so workspace imports are
|
|
* measured from built artifacts, esbuild must emit ESM for Rollup to continue
|
|
* tree-shaking, and terser mangling is disabled while visualizing so reported
|
|
* module names stay readable.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
import { nodeResolve } from "@rollup/plugin-node-resolve"
|
|
import replace from "@rollup/plugin-replace"
|
|
import terser from "@rollup/plugin-terser"
|
|
import type * as Path from "effect/Path"
|
|
import * as Predicate from "effect/Predicate"
|
|
import type { Plugin } from "rollup"
|
|
import esbuild from "rollup-plugin-esbuild"
|
|
import { type PluginVisualizerOptions, visualizer } from "rollup-plugin-visualizer"
|
|
|
|
const EFFECT_PACKAGE_REGEX = /^(@effect\/[\w-]+|effect)(\/.*)?$/
|
|
const TYPE_SCRIPT_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts"])
|
|
|
|
const toLocalDistPath = (pathService: Path.Path, packageDir: string, resolvedId: string): string => {
|
|
const srcDir = pathService.join(packageDir, "src")
|
|
const relative = pathService.relative(srcDir, resolvedId)
|
|
if (relative === "" || relative.startsWith("..") || pathService.isAbsolute(relative)) {
|
|
return resolvedId
|
|
}
|
|
const extension = pathService.extname(relative)
|
|
if (!TYPE_SCRIPT_EXTENSIONS.has(extension)) {
|
|
return resolvedId
|
|
}
|
|
return pathService.join(packageDir, "dist", relative.slice(0, -extension.length) + ".js")
|
|
}
|
|
|
|
/**
|
|
* Options for configuring Rollup plugins.
|
|
*
|
|
* @category options
|
|
* @since 4.0.0
|
|
*/
|
|
export interface PluginOptions {
|
|
readonly nodeTarget?: string | undefined
|
|
readonly minify?: boolean | undefined
|
|
readonly mangle?: boolean | undefined
|
|
readonly visualize?: boolean | undefined
|
|
readonly visualizations?: ReadonlyArray<VisualizationOutput> | undefined
|
|
}
|
|
|
|
/**
|
|
* Output generated by the Rollup visualizer plugin.
|
|
*
|
|
* @category options
|
|
* @since 4.0.0
|
|
*/
|
|
export interface VisualizationOutput {
|
|
readonly filename: string
|
|
readonly template: NonNullable<PluginVisualizerOptions["template"]>
|
|
readonly title?: string | undefined
|
|
}
|
|
|
|
interface ResolvedPluginOptions {
|
|
readonly nodeTarget: string
|
|
readonly minify: boolean
|
|
readonly mangle: boolean
|
|
readonly visualize: boolean
|
|
readonly visualizations: ReadonlyArray<VisualizationOutput>
|
|
}
|
|
|
|
const defaultPluginOptions: ResolvedPluginOptions = {
|
|
nodeTarget: "node20",
|
|
minify: true,
|
|
mangle: true,
|
|
visualize: false,
|
|
visualizations: []
|
|
}
|
|
|
|
/**
|
|
* Merges provided options with defaults.
|
|
*/
|
|
const resolvePluginOptions = (options: PluginOptions): ResolvedPluginOptions => ({
|
|
nodeTarget: options.nodeTarget ?? defaultPluginOptions.nodeTarget,
|
|
minify: options.minify ?? defaultPluginOptions.minify,
|
|
mangle: options.mangle ?? defaultPluginOptions.mangle,
|
|
visualize: options.visualize ?? defaultPluginOptions.visualize,
|
|
visualizations: options.visualizations ?? defaultPluginOptions.visualizations
|
|
})
|
|
|
|
/**
|
|
* Creates a custom Rollup plugin that resolves Effect package imports to their
|
|
* local dist directories.
|
|
*
|
|
* @category constructors
|
|
* @since 4.0.0
|
|
*/
|
|
export const createResolveLocalPackageImports = (pathService: Path.Path): Plugin => ({
|
|
name: "rollup-plugin-resolve-imports",
|
|
async resolveId(source, importer) {
|
|
const match = source.match(EFFECT_PACKAGE_REGEX)
|
|
if (Predicate.isNotNull(match)) {
|
|
const packageName = match[1]
|
|
const packageJson = await this.resolve(`${packageName}/package.json`, importer, { skipSelf: true })
|
|
if (packageJson === null) return null
|
|
const resolved = await this.resolve(source, importer, { skipSelf: true })
|
|
if (resolved === null) return null
|
|
return {
|
|
...resolved,
|
|
id: toLocalDistPath(pathService, pathService.dirname(packageJson.id), resolved.id),
|
|
external: false
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
})
|
|
|
|
/**
|
|
* Creates the full Rollup plugin pipeline for bundling.
|
|
*
|
|
* @category constructors
|
|
* @since 4.0.0
|
|
*/
|
|
export const createPlugins = (pathService: Path.Path, options: PluginOptions = {}): Array<Plugin> => {
|
|
const resolved = resolvePluginOptions(options)
|
|
const plugins: Array<Plugin> = [
|
|
createResolveLocalPackageImports(pathService),
|
|
nodeResolve(),
|
|
// @ts-expect-error see https://github.com/rollup/plugins/issues/1662
|
|
replace({
|
|
"process.env.NODE_ENV": JSON.stringify("production"),
|
|
preventAssignment: true
|
|
}),
|
|
esbuild({
|
|
target: resolved.nodeTarget,
|
|
format: "esm",
|
|
treeShaking: true
|
|
}),
|
|
// @ts-expect-error see https://github.com/rollup/plugins/issues/1662
|
|
terser({
|
|
format: { comments: false },
|
|
compress: resolved.minify,
|
|
mangle: resolved.mangle && !resolved.visualize
|
|
})
|
|
]
|
|
|
|
if (resolved.visualizations.length > 0) {
|
|
for (const output of resolved.visualizations) {
|
|
const visualizerOptions: PluginVisualizerOptions = {
|
|
filename: output.filename,
|
|
gzipSize: true,
|
|
open: false,
|
|
template: output.template
|
|
}
|
|
if (output.title !== undefined) {
|
|
visualizerOptions.title = output.title
|
|
}
|
|
plugins.push(visualizer(visualizerOptions))
|
|
}
|
|
} else if (resolved.visualize) {
|
|
plugins.push(visualizer({
|
|
open: true,
|
|
gzipSize: true
|
|
}))
|
|
}
|
|
|
|
return plugins
|
|
}
|