Skip to content

fix: createView() eager target resolution stripped by production minifiers #7375

@aangriawan

Description

@aangriawan

🐛 Bug Report

The createView method in the template compiler triggers lazy target node locators via bare property access (targets[id]), which JavaScript minifiers like Terser remove as dead code when pure_getters is enabled.

💻 Repro or Code Sample

  1. Build @microsoft/fast-element with a bundler that uses Terser with pure_getters: true (or "strict").
  2. Inspect the minified output of CompilationContext.createView.
  3. The for (const id of this.nodeIds) { targets[id]; } loop body is removed entirely.
// Source (compiler.ts – createView)
for (const id of this.nodeIds) {
    targets[id]; // trigger locator — DROPPED by Terser
}

The getter on each target ID lazily resolves and caches a DOM node reference via childNodes[targetIndex]. When the access is removed, target nodes are never resolved and view behavior factories receive undefined targets at runtime.

🤔 Expected Behavior

The bare property access should execute at runtime so that all target descriptors are eagerly resolved and cached before the view is returned. Templates should render correctly regardless of minifier settings.

😯 Current Behavior

When the consuming application's bundler enables Terser's pure_getters optimization (common in production builds), the getter-triggering loop is eliminated as a side-effect-free statement. This causes view factories to receive undefined target nodes, leading to runtime errors or silently broken template bindings.

Using void targets[id] does not help — Terser also drops void expressions when the inner expression is classified as pure.

💁 Possible Solution

Replace the bare property access with Reflect.get(targets, id). Terser treats function calls as side-effectful by default (unless annotated with /*#__PURE__*/), so Reflect.get survives minification while producing the identical getter invocation:

for (const id of this.nodeIds) {
    Reflect.get(targets, id); // trigger locator
}

A fix for this has already been implemented and tests pass.

🔦 Context

Any consumer bundling @microsoft/fast-element with Terser (directly or via Webpack/Vite/Rollup with the terser plugin) and enabling pure_getters will hit this issue in production builds. Development builds are unaffected because minification is typically disabled. This makes the bug particularly insidious — templates work in dev but silently break in production.

🌍 Your Environment

  • OS & Device: All platforms
  • Browser: All browsers
  • Version: @microsoft/fast-element v2.x (current main branch)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions