Skip to content

PlayForm/Build

Repository files navigation

Build 🌀

Build is a powerful tool that compiles all your TypeScript files into JavaScript, leveraging the speed of ESBuild and the type-checking capabilities of the TypeScript compiler.

Feature 📦

  • Fast compilation using ESBuild
  • TypeScript support with type-checking (tsc & tsc-alias)
  • Watch mode for development (--Watch)
  • Customizable ESBuild configuration via object or function (--ESBuild)
  • Supports both CommonJS and ES modules
  • Glob pattern support for input files
  • Exclusion patterns for input files

Installation 🚀

Install the package as a development dependency:

# Using npm
npm install -D -E @playform/build

# Or using yarn
# yarn add -D -E @playform/build

# Or using pnpm
# pnpm add -D -E @playform/build

Usage 🛠️

Command Line

Run the build tool from the command line, providing file patterns:

npx @playform/build 'Source/**/*.ts' 'OtherSource/**/*.mts'

CLI Options

Usage: Build [options] <File...>

Arguments:
  File                      One or more glob patterns matching files to build 📝

Options:
  -V, --version             Output the version number
  -E, --ESBuild <File>      Path to a custom ESBuild configuration file (exports an object or a function) 📜
  -T, --TypeScript <File>   Path to a custom TypeScript configuration file (default: "tsconfig.json") 📜
  -W, --Watch               Enable watch mode: rebuild on file changes 👁️
  -h, --help                Display help information

NPM Scripts

Add Build to your package.json scripts:

{
	"scripts": {
		"Build": "Build 'Source/**/*.ts'",
		"Run": "Build 'Source/**/*.ts' --Watch",
		"prepublishOnly": "Build 'Source/**/*.ts'"
	}
}

Configuration ⚙️

ESBuild Configuration (--ESBuild) 📜

You can provide a custom configuration file to modify the default ESBuild options used by @playform/build. This file (e.g., ESBuild.js or esbuild.config.ts) should use export default with either a configuration object or a function.

1. Exporting an Object:

Provide an object with ESBuild options. These will be merged with the base configuration.

// ESBuild.js
/** @type {import('esbuild').BuildOptions} */
const config = {
	minify: true,
	sourcemap: "external",
	platform: "node",
	// Add other esbuild options here
};

export default config;

2. Exporting a Function:

Provide a function that receives the current ESBuild configuration object (BuildOptions from esbuild) as its argument. You can modify this object directly or return a new (partial) object containing options to be merged. This allows for dynamic configuration, such as filtering entry points.

// ESBuild.js
/**
 * @param {import('esbuild').BuildOptions} Current The configuration object before this function runs.
 * @returns {import('esbuild').BuildOptions | void} Return changes to merge, or modify Current directly.
 */
export default (Current) => {
	console.log("Applying custom ESBuild function...");

	// Example: Filter out test files from entry points
	if (Array.isArray(Current.entryPoints)) {
		Current.entryPoints = Current.entryPoints.filter(
			(entry) => !/\.(test|spec)\.[mc]?[jt]s$/.test(entry),
		);

		console.log("Filtered entry points:", Current.entryPoints);
	}

	// Example: Modify the config directly
	Current.define = {
		...(Current.define ?? {}), // Preserve existing defines
		"process.env.BUILD_TIME": `"${new Date().toISOString()}"`,
	};

	// Example: Return additional options to merge
	return {
		banner: {
			js: "// Generated by @playform/build",
		},
		logLevel: "info",
	};

	// If you only modify 'Current' directly, you can simply have no return:
	// return;

	// or return undefined;
};

Usage:

Specify the path to your configuration file using the --ESBuild option:

npx @playform/build 'Source/**/*.ts' --ESBuild ESBuild.ts

See the base configuration used internally in Source/Variable/ESBuild.ts (link might need adjustment based on your actual path).

TypeScript Configuration (--TypeScript) 📜

By default, Build looks for tsconfig.json in the current working directory. You can specify a different path using the --TypeScript option. This tsconfig.json is used by ESBuild for path resolution and by the tsc and tsc-alias commands run after the build for type checking and path alias replacement.

A typical tsconfig.json might look like this:

// tsconfig.json
{
	"compilerOptions": {
		// --- Essential for tsc/tsc-alias ---
		"outDir": "Target", // Must match esbuild's outdir/outfile directory
		"rootDir": "Source", // Or your source root
		"baseUrl": ".", // Often needed for path aliases
		"paths": {
			// Example alias - must match esbuild alias config if used
			"@/*": ["Source/*"]
		},
		// --- Type Checking Options ---
		"strict": true,
		"skipLibCheck": true,
		"forceConsistentCasingInFileNames": true,
		// --- Module Settings (match esbuild target/format) ---
		"target": "ES2020",
		"module": "ESNext", // or CommonJS
		"moduleResolution": "node", // or node16/nodenext
		// --- Interoperability ---
		"esModuleInterop": true,
		"allowSyntheticDefaultImports": true,
		// --- Other ---
		"resolveJsonModule": true,
		"isolatedModules": true, // Good practice with transpilers like esbuild
		"noEmit": true // Important: Set noEmit to true as esbuild handles file emission
	},
	// Use @playform/build's base config for sensible defaults (optional)
	// "extends": "@playform/build/tsconfig",
	"include": ["Source/**/*"], // Files to be type-checked
	"exclude": ["node_modules", "Target"] // Exclude output and dependencies
}

Important: Ensure compilerOptions.outDir in your tsconfig.json aligns with the output directory configured for ESBuild (either via the default Source/Variable/ESBuild.ts or your custom --ESBuild config). Also, set "noEmit": true as esbuild handles the file generation; tsc is only used for type checking here.

JSConfig Configuration (Optional) 📜

If you are working with JavaScript and JSDoc for type checking, you can use a jsconfig.json file instead of tsconfig.json. The principles are similar.

// jsconfig.json
{
	"compilerOptions": {
		"outDir": "Target",
		"rootDir": "Source",
		"checkJs": true, // Enable type checking for JS files
		"target": "ES2020",
		"module": "ESNext", // or CommonJS
		"moduleResolution": "node", // or node16/nodenext
		"baseUrl": ".",
		"paths": {
			"@/*": ["Source/*"]
		},
		"noEmit": true // Still important
	},
	"extends": "@playform/build/jsconfig", // If you provide a base jsconfig
	"include": ["Source/**/*"],
	"exclude": ["node_modules", "Target"]
}

Remember to pass the path to this file if it's not named tsconfig.json (even if it's a JS project, the default flag might still look for tsconfig.json unless you explicitly pass --TypeScript jsconfig.json).

Contributing 🤝

Contributions are welcome! Please see CONTRIBUTING.md for guidelines and feel free to submit a Pull Request.

Changelog

See CHANGELOG.md for a history of changes to this component.