Skip to content

fix: remove invalid PURE literal annotations and add bundle validation tests#2737

Merged
hectorhdzg merged 22 commits into
microsoft:mainfrom
hectorhdzg:fix/rolldown-pure-annotation-validation
Jun 18, 2026
Merged

fix: remove invalid PURE literal annotations and add bundle validation tests#2737
hectorhdzg merged 22 commits into
microsoft:mainfrom
hectorhdzg:fix/rolldown-pure-annotation-validation

Conversation

@hectorhdzg

Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings May 26, 2026 21:00
@hectorhdzg hectorhdzg requested a review from a team as a code owner May 26, 2026 21:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses build/bundler compatibility by removing invalid /*#__PURE__*/ annotations placed on non-call/non-new expressions (e.g., string literals / null) and adds automated bundle validations to prevent regressions.

Changes:

  • Remove invalid /*#__PURE__*/ annotations applied to literals / null in source constants.
  • Switch ExtVersion back to the build-time #extVersion# placeholder.
  • Add unit “size” tests that scan built dist bundles for invalid PURE annotation placement (Rolldown-focused validation).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCoreSize.Tests.ts Adds a bundle scan test to detect invalid PURE annotation placement in the AppInsightsCore dist output.
shared/AppInsightsCore/src/ext/extUtils.ts Removes PURE-on-null and restores ExtVersion to the #extVersion# build placeholder.
AISKU/Tests/Unit/src/AISKUSize.Tests.ts Adds a bundle scan test to detect invalid PURE annotation placement in the AISKU dist output.
AISKU/src/internal/trace/spanUtils.ts Removes PURE annotations incorrectly applied to string literal constants.

Comment thread shared/AppInsightsCore/Tests/Unit/src/ai/AppInsightsCoreSize.Tests.ts Outdated
Comment thread AISKU/Tests/Unit/src/AISKUSize.Tests.ts Outdated
Keep legacy parenthesized PURE markers in source and canonicalize emitted bundle annotations as a final rollup plugin pass. Update AISKU and AppInsightsCore size tests to validate canonical PURE spacing in dist output.
@hectorhdzg hectorhdzg force-pushed the fix/rolldown-pure-annotation-validation branch from 37d850c to ad95fbd Compare May 27, 2026 23:21
@hectorhdzg hectorhdzg force-pushed the fix/rolldown-pure-annotation-validation branch from 56aa4f6 to 2075b26 Compare June 11, 2026 00:43
Set fail-fast: false so a failure on one Node.js version no longer cancels
the other matrix jobs, allowing us to see whether failures reproduce across
all versions or only some.

Also revert channels/offline-channel-js offlinebatchhandler.tests.ts back to
main's version, removing the unstable refactor that was causing the
'test1 with unload events' IndexedDB tests to time out in CI.
The branch had switched qunit to a system-installed Chrome (gruntfile
_getPuppeteerExecutablePath) combined with PUPPETEER_SKIP_DOWNLOAD=true.
On the CI Linux runners the system Chrome is version-mismatched with the
pinned puppeteer API, causing IndexedDB operations in headless 'new' mode
to hang. Every async IndexedDB test (e.g. 'send Next Batch with IndexedDB
provider test1 with unload events') then timed out at 30000ms, producing
9 failures and a ~195s offline-channel run.

main is green because it uses puppeteer's bundled, version-matched Chrome.
Revert gruntfile.js to main's puppeteer config and drop PUPPETEER_SKIP_DOWNLOAD
so the offline-channel tests run against the same Chrome as main. Keep
fail-fast: false for matrix diagnostics.
Both prior approaches failed in CI:
- Bundled auto-download (main): npm install crashes because the combined
  chrome + chrome-headless-shell postinstall download is flaky on runners
  ('folder exists but executable is missing' = partial extraction).
- Skip download + system Chrome: IndexedDB hangs in headless 'new' mode
  (version-mismatched Chrome) -> offline-channel tests time out at 30s.

Combine the safe parts of both: keep PUPPETEER_SKIP_DOWNLOAD=true so the
flaky postinstall never hard-fails npm install, then install the
version-matched Chrome explicitly in a dedicated step that clears the cache
and retries up to 3 times to recover from partial downloads. The reverted
gruntfile no longer overrides executablePath, so puppeteer auto-discovers
this cached, version-matched Chrome (which does not hang on IndexedDB).

Validated ci.yml YAML and the retry-loop bash syntax locally.
The shims build failed on CI Node 24 because tools/shims/Tests/UnitTests.html
had a branch-only edit (modules.run() require errback changed to 'false') that
breaks grunt shims (build:esm runs the unit test page during build). main's
version is green on Node 24.

Revert all remaining branch-only build/tooling churn back to main, since main
is green on Node 24 without any of it:
- tools/shims/Tests/UnitTests.html (build failure cause)
- tools/updateDistEsm/updateDistEsm.js (dynamicproto path; main uses short path)
- common/Tests/Framework/rollup.config.js (tslib remap arg)
- tools/grunt-tasks/minifyNames.js (retry sleep; no-op on Linux CI)

The branch now contains only the real PR (PURE annotation removal/normalization
in rollup.base.config.js + extUtils.ts, new bundle validation size tests),
the coupled size-threshold bumps, and the working puppeteer chrome CI fix.
Root cause of the shims build failure: grunt shims (build:esm) runs a
qunit BROWSER test as part of the build (registerTask('shims') concats
tsTestActions). My branch's ci.yml used PUPPETEER_SKIP_DOWNLOAD + a manual
'npx puppeteer browsers install chrome chrome-headless-shell', which installs
the LATEST Chrome rather than the version the repo's pinned puppeteer expects.
When grunt qunit launched during the shims build it could not find puppeteer's
expected Chrome build and failed immediately (~6s) -> build:esm exit 1 -> shims
build failed.

main's ci.yml is green because its plain 'npm install' lets puppeteer's
postinstall download the exact version-matched Chrome that grunt qunit uses.

Revert ci.yml entirely to main's known-green baseline. The branch now contains
ONLY the real PR: PURE annotation removal/normalization (rollup.base.config.js,
extUtils.ts) and the coupled bundle-size validation tests/threshold bumps.
Root cause of BOTH CI failures, now confirmed:

1) Download crash on 'npm install grunt-cli': puppeteer's postinstall download
   of chrome-headless-shell is flaky on the runners (the runner image carries a
   partial/mismatched puppeteer cache -> 'folder exists but executable is
   missing' -> install hard-fails).

2) Shims build failure (~6s): my earlier fix ran
   'npx puppeteer browsers install chrome chrome-headless-shell' as ONE command.
   The puppeteer CLI 'browsers install [browser]' takes a SINGLE browser
   positional, so only 'chrome' was installed and 'chrome-headless-shell' was
   silently ignored. The grunt qunit task launches puppeteer with headless:'new'
   which resolves to chrome-headless-shell in puppeteer 24; with SKIP_DOWNLOAD
   blocking auto-download and chrome-headless-shell missing, the browser tests
   that run DURING the build (grunt shims concats qunit) failed to launch.

Fix: keep PUPPETEER_SKIP_DOWNLOAD to avoid the flaky postinstall, then run
'npx puppeteer browsers install' (no arg = install ALL pinned browsers, both
chrome and chrome-headless-shell, at the exact pinned versions) with a
clean-cache + retry loop to recover from partial/corrupt downloads.

Branch still contains only the real PR plus this single ci.yml fix.
…packages)

THE actual root cause of the recurring shims build failure:

registerTask('shims') was the ONLY package whose BUILD task concatenated
tsTestActions, which appends 'qunit:shims' - i.e. it launched a headless
browser DURING the build. Every other package keeps build (tsBuildActions)
and test (tsTestActions) separate. That is why only @microsoft/applicationinsights-shims
failed during 'rush rebuild' (npm run build), at the ~8s browser-launch step,
identically on Node 18 and Node 24 - it had nothing to do with the source.

shims already has a separate 'shimstest' task (tsTestActions) used by
'npm run test' / rush test, so deferring the qunit there loses no coverage.

Change: registerTask('shims', tsBuildActions('shims')) - build only.

Verified locally:
- npm run build:esm (grunt shims): completes with NO browser launch, exit 0.
- npm run test (grunt shimstest): runs all 8 qunit tests, 0 failed, exit 0.

This removes the browser dependency from the build entirely, so the shims
build can no longer fail on runner Chrome flakiness.
…er SKIP_DOWNLOAD)

Confirmed locally (puppeteer 24.43, pinned Chrome 148.0.7778.97):
- 'npx puppeteer browsers install'        (no arg) WITH PUPPETEER_SKIP_DOWNLOAD=true
  => exits 0, prints nothing, installs NOTHING (no-op).
- 'npx puppeteer browsers install chrome' (single arg) WITH the same env
  => installs chrome 148.0.7778.97 (exit 0).
- 'npx puppeteer browsers install chrome-headless-shell' likewise installs.

The job-level PUPPETEER_SKIP_DOWNLOAD=true (needed to gate the flaky postinstall
download) ALSO applied to the install step, so the no-arg form silently installed
nothing. The retry loop 'succeeded' on attempt 1 having installed nothing, so
Chrome was never in ~/.cache/puppeteer and 'rush test' failed:
  'Fatal error: Could not find Chrome (ver. 148.0.7778.97)'.

Fix: install each browser explicitly (chrome AND chrome-headless-shell), which
works regardless of PUPPETEER_SKIP_DOWNLOAD, and log 'puppeteer browsers list'
for visibility.

Note: the prior shims build-only fix worked - this run got past the build into
'rush test', which is where this Chrome-not-found surfaced.
…nnotation-validation

# Conflicts:
#	.github/workflows/ci.yml
#	AISKU/Tests/Unit/src/AISKUSize.Tests.ts
#	AISKULight/Tests/Unit/src/AISKULightSize.Tests.ts
#	channels/1ds-post-js/test/Unit/src/FileSizeCheckTest.ts

@JacksonWeber JacksonWeber left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hectorhdzg hectorhdzg merged commit 8b8f7cb into microsoft:main Jun 18, 2026
9 checks passed
hectorhdzg added a commit that referenced this pull request Jun 18, 2026
…ies (#2733)

* fix: Migrate from npm to pnpm and resolve all dependency vulnerabilities

- Migrate Rush package manager from npm 9.9.4 to pnpm 9.15.9 to eliminate bundled tar 6.2.x CVEs
- Upgrade Rush from 5.172.1 to 5.175.1 for pnpm support
- Add pnpm-config.json with global overrides for minimatch, tar, glob, lodash, and postcss
- Remove unused autoprefixer dependency (source of postcss CVEs)
- Add 177 missing phantom dependencies across 27 packages for pnpm strict module isolation
- Fix tsconfig lib settings across 26+ packages for @nevware21/ts-utils ES2015+ type compatibility
- Fix eventemitter2 export structure change in tools/grunt-tasks/qunit.js
- Fix cross-package script resolution in tools/subResourceIntegrity and tools/release-tools
- Add Node 24 ESLint 8 compatibility guard in gruntfile.js
- Delete npm-shrinkwrap.json, add pnpm-lock.yaml

Resolves: 6 tar CVEs (critical/high), 2 postcss CVEs (moderate) — 0 vulnerabilities remaining

* fix: use explicit dynamicproto rollup plugin path

* fix: update @nevware21/ts-utils version constraint to 0.14.0+ for consistency

* fix: apply build/CI improvements from PR 2737

- Add PUPPETEER_SKIP_DOWNLOAD env var in CI
- Add puppeteer executable path detection for Linux/CI environments
- Fix cross-platform sleep in minifyNames using Atomics.wait
- Fix updateDistEsm dynamicproto import path
- Fix test module loading in shims tests

* fix: resolve grunt task references for pnpm workspace isolation

These packages need explicit paths to the root gruntfile since pnpm
uses strict dependency isolation. Direct grunt task calls fail without
specifying the gruntfile location.

* fix(deps): add pnpm overrides to resolve transitive vulnerabilities

Regenerated pnpm-lock.yaml after merging upstream/main and added global
overrides for 4 advisories surfaced by 'pnpm audit':
- ws            >=8.21.0        (GHSA-96hv-2xvq-fx4p, high)  via puppeteer
- form-data     >=2.5.6 <3.0.0  (GHSA-hmw2-7cc7-3qxx, high)
- js-yaml       >=4.2.0         (GHSA-h67p-54hq-rp68, moderate) via eslint/grunt
- markdown-it   >=14.2.0        (GHSA-6v5v-wf23-fmfq, moderate) via typedoc

form-data pinned to the 2.x line (2.5.6 exists) to stay API-compatible with
its single 2.x consumer; js-yaml already resolved uniformly to 4.x so 4.2.0 is
safe. 'pnpm audit' (prod and full) now reports 0 vulnerabilities.

* fix(build): repair malformed perf qunit puppeteer config in gruntfile

A prior refactor extracted the inline perf puppeteer options into the
'perfPuppeteerOptions' variable and replaced the inline object with
'puppeteer: perfPuppeteerOptions', but left behind the old object's
trailing ']' and an extra '}'. This produced 'SyntaxError: Unexpected
token ]' when grunt loaded the gruntfile, which broke the very first
build operation (@microsoft/ai-test-framework -> 'grunt tst-framework')
and cascaded to block all 27 downstream package builds in CI.

Removed the stray ']' and extra '}'. Verified: gruntfile now parses
(node require) and 'rush build' completes successfully (exit 0).

* ci: temporarily run Node.js CI on push for the pnpm migration branch

Adds fix/migrate-pnpm-resolve-dependency-vulnerabilities to the push
trigger so the fork runs the full build+test matrix on each push and we
can watch the result directly (the pull_request trigger only runs in the
upstream PR context). Revert before merge.

* fix(deps): align ts-async constraint in properties-js to < 0.6.0

'rush check' failed: @microsoft/applicationinsights-properties-js still
declared '@nevware21/ts-async: >= 0.5.5 < 2.x' while every other package
was narrowed to '>= 0.5.5 < 0.6.0' during the upstream/main merge. This
file was not part of the merge conflict set so it was missed.

Narrowed it to match. 'rush check' now reports no mis-matching deps (exit 0).

* ci: revert temporary push-trigger for pnpm migration branch

* revert: remove PR #2737 build/CI scope creep from pnpm migration branch

Reverts commit 63c6ed7 ("apply build/CI improvements from PR 2737")
which was unrelated to the pnpm migration + vulnerability remediation.
Restored to upstream/main state:
- gruntfile.js: removed _getPuppeteerExecutablePath() + the unit/perf
  puppeteer-options refactor (CI relies on puppeteer's native
  PUPPETEER_EXECUTABLE_PATH handling, same as upstream). Kept the
  pnpm-migration content (a52dd9f).
- tools/grunt-tasks/minifyNames.js: removed sleepForRetry()
- shared/AppInsightsCore/src/utils/DataCacheHelper.ts: #version# -> 3.4.1
- common/Tests/Framework/rollup.config.js: dropped extra updateDistEsmFiles arg
- tools/shims/Tests/UnitTests.html: dropped extra modules.run arg

Also reverted e021de0 (the perf-qunit syntax fix) implicitly, since the
malformed block it fixed only existed because of 63c6ed7.

extUtils.ts already matched upstream (#extVersion#) and was left as-is.
hectorhdzg added a commit that referenced this pull request Jun 18, 2026
…#2744)

* fix(#2736): canonicalize PURE annotations in dist-es5 (module) output

Modern bundlers (Rolldown / Vite 8) prefer the package "module" entry,
which resolves to the per-module dist-es5 tsc output. TypeScript emits
parenthesized PURE annotations with whitespace after the opening paren
(e.g. `( /*#__PURE__*/"http.")`), which Rolldown rejects with
[INVALID_ANNOTATION]. The existing rollup fixPureAnnotations() pass only
covers the bundled dist/es5 ("main") output, so the ESM consumer path
stayed broken (#2737 only fixed the bundle).

Changes:
- Add shared tools/pureAnnotations.js (single source of truth for the
  canonicalization regex), reused by both rollup.base.config.js and the
  new grunt task.
- Add "fix-pure" grunt multitask that canonicalizes dist-es5/**/*.js after
  the tsc compile; wired into the shared tsBuildActions pipeline for all
  packages.
- Extract shared getDistPackageRoots() in gruntfile.js (reused by
  validate-es5 and fix-pure).
- Keep the wrapping parens (required for older Rollup/Webpack/Terser to
  tree-shake the constants), per maintainer guidance.
- Extend AISKU/AppInsightsCore size tests to assert canonical PURE form in
  the dist-es5 module output.
- Document the fix in RELEASES.md (3.4.2).

* fix: load shared PURE helper as ESM (.mjs) so rollup config bundling resolves it

rollup's --bundleConfigAsCjs writes the bundled config into the subpackage dir and does not run the commonjs plugin, so a relative require()/CJS import of the shared helper failed to resolve. Convert the shared helper to an ES module and import it directly in rollup.base.config.js (rollup inlines it); the CommonJS grunt task now loads it via async dynamic import().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants