internal-docs/guides/development/build-constraints.md
This document explains the comprehensive build constraints, transformations, and code management strategies in Glimmer VM. It serves as a reference for understanding how code is transformed from development to production and as a starting point for further analysis of the build system.
Glimmer VM uses several categories of code that have different constraints on where they can appear:
What it is: A de facto standard created by Vite for build-time environment variables.
Usage in Glimmer VM:
import.meta.env.DEV - true in development builds, false in productionimport.meta.env.PROD - true in production builds, false in developmentimport.meta.env.VM_LOCAL_DEV - false in published builds, true in Vite dev serverConstraint: These references are replaced at build time with actual values. The string import.meta.env never appears in published builds.
What it is: A build-time flag for code that should only run during local Glimmer VM development.
Purpose: Enables expensive debugging features when working on the VM itself. These features never reach published packages (not even development builds).
Example Usage:
if (VM_LOCAL) {
// Expensive validation that helps VM developers
validateOpcodeSequence(opcodes);
}
Constraint: Code blocks guarded by VM_LOCAL are completely removed from all published builds. The condition and its contents are stripped out.
What they are: Runtime type checking and validation functions from @glimmer/debug:
check(value, checker) - Validates a value against a type checkerexpect(value, message) - Asserts a condition is truthylocalAssert(condition, message) - Development-only assertionunwrap(value) - Unwraps optional values, throwing if null/undefinedPurpose: Catch bugs during Glimmer VM development by validating assumptions about types and state.
Example Usage:
import { check } from '@glimmer/debug';
import { CheckReference } from './-debug-strip';
let definition = check(stack.pop(), CheckReference);
let capturedArgs = check(stack.pop(), CheckCapturedArguments);
Constraint: These function calls are stripped from ALL published builds (both development and production) using a Babel plugin during the build process.
What they are: Functions that create runtime type validators:
CheckInterface - Validates object shapeCheckOr - Union type validationCheckFunction - Function type validationCheckObject - Object/WeakMap key validationPurpose: Define the type constraints used by check() calls.
Example Usage:
export const CheckReference: Checker<Reference> = CheckInterface({
[REFERENCE]: CheckFunction,
});
export const CheckArguments = CheckOr(CheckObject, CheckFunction);
Constraint: These should never appear in published builds as they're only used by the stripped check() calls.
Three private packages contain development-only utilities:
Constraint: These packages are never published to npm. Import statements for them should never appear in published builds - their contents are either inlined or stripped during compilation.
The build process uses a Babel plugin (@glimmer/local-debug-babel-plugin) that:
@glimmer/debugcheck(value, checker) → valueexpect(...) → removed entirelyCheckInterface(...) → () => truerecordStackSize() → removed entirelyThe Rollup replace plugin performs these build-time replacements:
Production builds:
import.meta.env.MODE → "production"import.meta.env.DEV → falseimport.meta.env.PROD → trueimport.meta.env.VM_LOCAL_DEV → falseDevelopment builds:
import.meta.env.MODE → "development"import.meta.env.DEV → DEBUG (with import { DEBUG } from '@glimmer/env' injected)import.meta.env.PROD → !DEBUGimport.meta.env.VM_LOCAL_DEV → false (becomes true only in Vite dev server)The build system has specific rules for what gets inlined vs treated as external:
Always Inlined:
@glimmer/local-debug-flags@glimmer/constants@glimmer/debug@glimmer/debug-util., /, #)tslib)Always External:
@handlebars/parsersimple-html-tokenizerbabel-plugin-debug-macros@glimmer/* packages (to avoid duplication)@simple-dom/* packages@babel/* packagesnode:*)Every package produces multiple build artifacts:
Development Build (dist/dev/)
Production Build (dist/prod/)
debugger statements (for {{debugger}} helper)Type Definitions (dist/{dev,prod}/*.d.ts)
CommonJS Build (optional, *.cjs)
Glimmer VM uses a multi-tiered TypeScript configuration system:
tsconfig.base.json - Shared base configurationtsconfig.json - Development configuration (looser for better DX)tsconfig.dist.json - Distribution configuration (stricter for published code)Packages can declare their strictness level in package.json:
{
"repo-meta": {
"strictness": "strict" | "loose"
}
}
This affects which TypeScript compiler options are applied during type checking.
The build system uses Turbo for orchestration with these key relationships:
prepack must complete before any buildspnpm repo:prepack - Build all packages via Turbo (recommended)pnpm repo:lint:types - Type check all packagespnpm clean - Clean build artifactsPublished Package Structure:
dist/ directory is included in npm packagespublint validates package structure before publishingExport Configuration:
{
"exports": {
".": {
"development": "./dist/dev/index.js",
"default": "./dist/prod/index.js"
}
}
}
Note: Private packages (@glimmer/debug, @glimmer/constants, @glimmer/debug-util, and all @glimmer-workspace/*) are never published to npm.
dust utility for accurate measurementstsc)eslint)publint)import.meta.env.VM_LOCAL_DEV → true for local developmentTools in bin/fixes/:
apply-eslint-suggestions.js - Apply ESLint auto-fixesapply-ts-codefixes.js - Apply TypeScript code fixesapply-suggestions.js - Apply both types of fixes@glimmer/debug imports in VM codeThe Glimmer VM build system enables developers to write defensive, well-instrumented code during development while shipping minimal, performant code to production. Through multiple layers of transformations, validations, and constraints, it ensures debug code never reaches users while maintaining a fast and helpful development experience.