Skip to content

Commit 52bb539

Browse files
Merge pull request #1753 from glimmerjs/fix/strip-debug-calls-from-builds
Fix build verification by stripping debug calls from all builds
2 parents db73010 + cea7cf0 commit 52bb539

File tree

10 files changed

+402
-65
lines changed

10 files changed

+402
-65
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ jobs:
2929
node-version: 22.13.0
3030
repo-token: ${{ secrets.GITHUB_TOKEN }}
3131
- run: pnpm turbo prepack
32-
- run: node ./bin/build-verify.mjs
3332

3433
lint:
3534
name: Linting

bin/build-verify.mjs

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
# Build Constraints and Transformations
2+
3+
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.
4+
5+
## Overview
6+
7+
Glimmer VM uses several categories of code that have different constraints on where they can appear:
8+
9+
1. **Production Code** - Ships to end users in production builds
10+
2. **Development Code** - Available in development builds for end users
11+
3. **Local Development Code** - Only for Glimmer VM developers, never ships
12+
4. **Build-Time Code** - Used during compilation but not at runtime
13+
14+
## Code Categories and Constraints
15+
16+
### 1. import.meta.env
17+
18+
**What it is**: A de facto standard created by Vite for build-time environment variables.
19+
20+
**Usage in Glimmer VM**:
21+
- `import.meta.env.DEV` - `true` in development builds, `false` in production
22+
- `import.meta.env.PROD` - `true` in production builds, `false` in development
23+
- `import.meta.env.VM_LOCAL_DEV` - `false` in published builds, `true` in Vite dev server
24+
25+
**Constraint**: These references are replaced at build time with actual values. The string `import.meta.env` never appears in published builds.
26+
27+
### 2. VM_LOCAL Flag
28+
29+
**What it is**: A build-time flag for code that should only run during local Glimmer VM development.
30+
31+
**Purpose**: Enables expensive debugging features when working on the VM itself. These features never reach published packages (not even development builds).
32+
33+
**Example Usage**:
34+
```typescript
35+
if (VM_LOCAL) {
36+
// Expensive validation that helps VM developers
37+
validateOpcodeSequence(opcodes);
38+
}
39+
```
40+
41+
**Constraint**: Code blocks guarded by `VM_LOCAL` are completely removed from all published builds. The condition and its contents are stripped out.
42+
43+
### 3. Debug Assertion Functions
44+
45+
**What they are**: Runtime type checking and validation functions from `@glimmer/debug`:
46+
47+
- `check(value, checker)` - Validates a value against a type checker
48+
- `expect(value, message)` - Asserts a condition is truthy
49+
- `localAssert(condition, message)` - Development-only assertion
50+
- `unwrap(value)` - Unwraps optional values, throwing if null/undefined
51+
52+
**Purpose**: Catch bugs during Glimmer VM development by validating assumptions about types and state.
53+
54+
**Example Usage**:
55+
```typescript
56+
import { check } from '@glimmer/debug';
57+
import { CheckReference } from './-debug-strip';
58+
59+
let definition = check(stack.pop(), CheckReference);
60+
let capturedArgs = check(stack.pop(), CheckCapturedArguments);
61+
```
62+
63+
**Constraint**: These function calls are stripped from ALL published builds (both development and production) using a Babel plugin during the build process.
64+
65+
### 4. Type Checker Functions
66+
67+
**What they are**: Functions that create runtime type validators:
68+
69+
- `CheckInterface` - Validates object shape
70+
- `CheckOr` - Union type validation
71+
- `CheckFunction` - Function type validation
72+
- `CheckObject` - Object/WeakMap key validation
73+
74+
**Purpose**: Define the type constraints used by `check()` calls.
75+
76+
**Example Usage**:
77+
```typescript
78+
export const CheckReference: Checker<Reference> = CheckInterface({
79+
[REFERENCE]: CheckFunction,
80+
});
81+
82+
export const CheckArguments = CheckOr(CheckObject, CheckFunction);
83+
```
84+
85+
**Constraint**: These should never appear in published builds as they're only used by the stripped `check()` calls.
86+
87+
### 5. Debug-Only Packages
88+
89+
Three private packages contain development-only utilities:
90+
91+
- **@glimmer/debug** - Type checkers, validation utilities, debugging tools
92+
- **@glimmer/constants** - VM opcodes, DOM constants (inlined during build)
93+
- **@glimmer/debug-util** - Debug assertions, platform-specific logging
94+
95+
**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.
96+
97+
## Build Process and Transformations
98+
99+
### Debug Code Stripping
100+
101+
The build process uses a Babel plugin (`@glimmer/local-debug-babel-plugin`) that:
102+
103+
1. Identifies imports from `@glimmer/debug`
104+
2. Tracks which debug functions are imported
105+
3. Strips or transforms the function calls:
106+
- `check(value, checker)``value`
107+
- `expect(...)` → removed entirely
108+
- `CheckInterface(...)``() => true`
109+
- `recordStackSize()` → removed entirely
110+
111+
### Environment Variable Replacements
112+
113+
The Rollup replace plugin performs these build-time replacements:
114+
115+
**Production builds:**
116+
- `import.meta.env.MODE``"production"`
117+
- `import.meta.env.DEV``false`
118+
- `import.meta.env.PROD``true`
119+
- `import.meta.env.VM_LOCAL_DEV``false`
120+
121+
**Development builds:**
122+
- `import.meta.env.MODE``"development"`
123+
- `import.meta.env.DEV``DEBUG` (with `import { DEBUG } from '@glimmer/env'` injected)
124+
- `import.meta.env.PROD``!DEBUG`
125+
- `import.meta.env.VM_LOCAL_DEV``false` (becomes `true` only in Vite dev server)
126+
127+
### Module Resolution and Bundling
128+
129+
The build system has specific rules for what gets inlined vs treated as external:
130+
131+
**Always Inlined:**
132+
- `@glimmer/local-debug-flags`
133+
- `@glimmer/constants`
134+
- `@glimmer/debug`
135+
- `@glimmer/debug-util`
136+
- Relative imports (`.`, `/`, `#`)
137+
- TypeScript helper library (`tslib`)
138+
139+
**Always External:**
140+
- `@handlebars/parser`
141+
- `simple-html-tokenizer`
142+
- `babel-plugin-debug-macros`
143+
- Other `@glimmer/*` packages (to avoid duplication)
144+
- `@simple-dom/*` packages
145+
- `@babel/*` packages
146+
- Node.js built-ins (`node:*`)
147+
148+
### Build Output Structure
149+
150+
Every package produces multiple build artifacts:
151+
152+
1. **Development Build** (`dist/dev/`)
153+
- Readable, formatted code
154+
- Preserves comments
155+
- No variable name mangling
156+
- Includes source maps
157+
158+
2. **Production Build** (`dist/prod/`)
159+
- Minified with Terser (3 passes)
160+
- Aggressive optimizations
161+
- Preserves `debugger` statements (for `{{debugger}}` helper)
162+
- Includes source maps
163+
164+
3. **Type Definitions** (`dist/{dev,prod}/*.d.ts`)
165+
- Generated from TypeScript source
166+
- Rolled up into single files per entry point
167+
168+
4. **CommonJS Build** (optional, `*.cjs`)
169+
- Only generated if package.json includes CommonJS exports
170+
- Follows same dev/prod split
171+
172+
## TypeScript Configuration and Strictness
173+
174+
Glimmer VM uses a multi-tiered TypeScript configuration system:
175+
176+
### Configuration Files
177+
- `tsconfig.base.json` - Shared base configuration
178+
- `tsconfig.json` - Development configuration (looser for better DX)
179+
- `tsconfig.dist.json` - Distribution configuration (stricter for published code)
180+
181+
### Per-Package Strictness Levels
182+
183+
Packages can declare their strictness level in `package.json`:
184+
```json
185+
{
186+
"repo-meta": {
187+
"strictness": "strict" | "loose"
188+
}
189+
}
190+
```
191+
192+
This affects which TypeScript compiler options are applied during type checking.
193+
194+
### Key Compiler Constraints
195+
- **Target**: ES2022
196+
- **Module Resolution**: "bundler" mode
197+
- **Isolated Modules**: Required for build performance
198+
- **Exact Optional Properties**: Enforced in distribution builds
199+
- **No Unchecked Indexed Access**: Enforced in distribution builds
200+
201+
## Build Orchestration
202+
203+
### Turbo Pipeline
204+
205+
The build system uses Turbo for orchestration with these key relationships:
206+
- `prepack` must complete before any builds
207+
- Type checking runs in parallel with builds
208+
- Cache keys include TypeScript configs, source files, and lock files
209+
210+
### Build Commands
211+
- `pnpm build:control` - Build all packages using Rollup
212+
- `pnpm repo:prepack` - Prepare packages for publishing
213+
- `pnpm repo:lint:types` - Type check all packages
214+
215+
### Package Publishing
216+
217+
**Published Package Structure**:
218+
- Only `dist/` directory is included in npm packages
219+
- Conditional exports for dev/prod builds
220+
- `publint` validates package structure before publishing
221+
222+
**Export Configuration**:
223+
```json
224+
{
225+
"exports": {
226+
".": {
227+
"development": "./dist/dev/index.js",
228+
"default": "./dist/prod/index.js"
229+
}
230+
}
231+
}
232+
```
233+
234+
Note: Private packages (`@glimmer/debug`, `@glimmer/constants`, `@glimmer/debug-util`, and all `@glimmer-workspace/*`) are never published to npm.
235+
236+
## Continuous Integration Constraints
237+
238+
### Bundle Size Monitoring
239+
- Automated size tracking via GitHub Actions
240+
- Compares dev/prod sizes against main branch
241+
- Reports size changes in PR comments
242+
- Uses `dust` utility for accurate measurements
243+
244+
### Test Environment Constraints
245+
- **Browser Tests**: Puppeteer with specific Chrome flags
246+
- **Smoke Tests**: 300s timeout (vs 30s for regular tests)
247+
- **BrowserStack**: Cross-browser testing for releases
248+
- **Floating Dependencies**: Special CI job tests against latest deps
249+
250+
### Validation Steps
251+
1. Type checking (`tsc`)
252+
2. Linting (`eslint`)
253+
3. Unit tests (QUnit/Vitest)
254+
4. Smoke tests
255+
5. Bundle size analysis
256+
6. Package structure validation (`publint`)
257+
258+
## Development Environment
259+
260+
### Vite Development Server
261+
- Transforms `import.meta.env.VM_LOCAL_DEV``true` for local development
262+
- Pre-bundles test dependencies for performance
263+
- Custom extension resolution order
264+
265+
### ESLint Configuration
266+
- Environment-aware rules (console vs non-console packages)
267+
- Strictness based on package metadata
268+
- Test-specific rules for QUnit
269+
- Custom rules for Glimmer-specific patterns
270+
271+
### Automated Code Fixes
272+
Tools in `bin/fixes/`:
273+
- `apply-eslint-suggestions.js` - Apply ESLint auto-fixes
274+
- `apply-ts-codefixes.js` - Apply TypeScript code fixes
275+
- `apply-suggestions.js` - Apply both types of fixes
276+
277+
## Guidelines for Developers
278+
279+
1. **Use debug assertions liberally** - They help catch bugs and document assumptions
280+
2. **Don't wrap debug code in conditions** - The build process handles removal
281+
3. **Import from the right place** - Use `@glimmer/debug` imports in VM code
282+
4. **Trust the build process** - Write clear development code; the build makes it production-ready
283+
5. **Respect package boundaries** - Don't import from private packages in public ones
284+
6. **Follow strictness levels** - Adhere to the TypeScript strictness of your package
285+
286+
## Summary
287+
288+
The 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.

0 commit comments

Comments
 (0)