Describe the bug
/* #__PURE__*/ annotations in the published ESM/ES5 bundles are positioned inside the wrapping parentheses, between the open paren and the annotated expression. Bundlers that follow the conventional rules for pure annotations (most notably Rolldown, used by Vite 8) cannot associate the annotation with the expression that follows it, so the annotation is silently ignored and dead-code elimination does not kick in for the affected values.
The pattern in the published artifact looks like this:
// node_modules/@nevware21/ts-utils/dist/es5/mod/ts-utils.js
var isNode = ( /*#__PURE__*/_getGlobalInstFn(function () {
return !!( /*#__PURE__*/safe(function () { return (process && (process.versions || {}).node); }).v);
}));
var isWebWorker = ( /*#__PURE__*/_getGlobalInstFn(function () {
return !!( /*#__PURE__*/safe(function () { return self && self instanceof WorkerGlobalScope; }).v);
}));
The expected pattern (as documented at https://rolldown.rs/in-depth/dead-code-elimination#pure) is that the annotation must come immediately before the call expression, with no intervening open paren:
var isNode = /*#__PURE__*/_getGlobalInstFn(function () {
return !!(/*#__PURE__*/safe(function () { return (process && (process.versions || {}).node); }).v);
});
When Vite 8 (Rolldown) bundles a downstream app that pulls in @nevware21/ts-utils either directly or transitively (e.g. via @microsoft/applicationinsights-web, which is how I hit this), the build emits a series of [INVALID_ANNOTATION] warnings like:
[INVALID_ANNOTATION] A comment "/*#__PURE__*/" in
"node_modules/@nevware21/ts-utils/dist/es5/mod/ts-utils.js" contains
an annotation that Rolldown cannot interpret due to the position of the comment.
╭─[ node_modules/@nevware21/ts-utils/dist/es5/mod/ts-utils.js:920:16 ]
│
920 │ return !!( /*#__PURE__*/safe(function () { return (process && (process.versions || {}).node); }).v);
│ ──────┬──────
│ ╰──────── comment ignored due to position
grep -c "/\*[ ]*#__PURE__\*/" dist/es5/mod/ts-utils.js dist/es6/mod/ts-utils.js shows 159 occurrences in each build, so the issue is repo-wide rather than a single hand-written hotspot — it looks like a build-step / wrapper that prepends ( /*#__PURE__*/...) around each annotated value.
The build still produces working output; the symptom is silently lost dead-code elimination opportunities (slightly larger bundles and longer-than-needed module evaluation chains in downstream apps).
Runtime (please provide the browser and version, delete other browsers):
- Node: 24 (downstream consumer); affects bundles targeting any browser/Node downstream
Desktop (please complete the following information):
- OS: macOS 15 (Darwin 25.5.0)
- Browser: n/a — issue surfaces at bundle time, not at runtime
- Node: 24
To Reproduce
Steps to reproduce the behavior:
- Create a fresh project:
npm create vite@latest pure-repro -- --template react-ts
cd pure-repro && npm install
npm install @nevware21/ts-utils@0.14.0
- Add a single import in
src/main.tsx: import { isNode } from "@nevware21/ts-utils"; console.log(isNode());
- Run
npm run build
- Observe multiple
[INVALID_ANNOTATION] warnings in the build output pointing into node_modules/@nevware21/ts-utils/dist/es5/mod/ts-utils.js (the specific line/column varies based on which exports are pulled in).
The two warnings I see in my real-world build (Vite 8.0.14, Rolldown):
dist/es5/mod/ts-utils.js:920:16 — inside isNode
dist/es5/mod/ts-utils.js:923:16 — inside isWebWorker
@microsoft/applicationinsights-web (which depends on this package) triggers the same warnings via re-export, so any downstream that uses App Insights also hits the issue.
Expected behavior
/* #__PURE__*/ annotations in the published artifacts are placed immediately before the call expression they annotate, with no open paren between the annotation and the expression. Rolldown (and any other bundler that follows the conventional pure-annotation rules) is then able to mark the call as side-effect-free and tree-shake it when the result is unused.
Concretely: emit
var isNode = /*#__PURE__*/_getGlobalInstFn(function () { /* ... */ });
instead of
var isNode = ( /*#__PURE__*/_getGlobalInstFn(function () { /* ... */ }));
If the surrounding parens are required for some reason (e.g. a TypeScript downlevel artifact), move the annotation to sit between the open paren and the call without a leading space:
var isNode = (/*#__PURE__*/_getGlobalInstFn(function () { /* ... */ }));
In Rolldown's view, the key property is that the comment must be immediately adjacent to the call expression token; any whitespace/token in between (including the open paren) disqualifies the annotation.
Screenshots
n/a — terminal warnings only.
Additional context
- Affected package version:
@nevware21/ts-utils@0.14.0
- Reproducer bundler:
vite@8.0.14 (which uses Rolldown). Rollup tolerated the misplaced annotations silently, which is likely why this hasn't surfaced before.
- Both the
dist/es5/mod/ts-utils.js and dist/es6/mod/ts-utils.js artifacts are affected (159 occurrences in each). The pattern is uniform across the file, suggesting the upstream cause is a build-step wrapper rather than the source files themselves.
- Rolldown documentation describing the placement rule: https://rolldown.rs/in-depth/dead-code-elimination#pure
- Companion / downstream impact:
@microsoft/applicationinsights-web and @microsoft/applicationinsights-core-js re-package some of these annotations and produce the same warnings via their own builds; happy to file a sibling issue on the ApplicationInsights-JS repo once this one has been confirmed.
Describe the bug
/* #__PURE__*/annotations in the published ESM/ES5 bundles are positioned inside the wrapping parentheses, between the open paren and the annotated expression. Bundlers that follow the conventional rules for pure annotations (most notably Rolldown, used by Vite 8) cannot associate the annotation with the expression that follows it, so the annotation is silently ignored and dead-code elimination does not kick in for the affected values.The pattern in the published artifact looks like this:
The expected pattern (as documented at https://rolldown.rs/in-depth/dead-code-elimination#pure) is that the annotation must come immediately before the call expression, with no intervening open paren:
When Vite 8 (Rolldown) bundles a downstream app that pulls in
@nevware21/ts-utilseither directly or transitively (e.g. via@microsoft/applicationinsights-web, which is how I hit this), the build emits a series of[INVALID_ANNOTATION]warnings like:grep -c "/\*[ ]*#__PURE__\*/" dist/es5/mod/ts-utils.js dist/es6/mod/ts-utils.jsshows 159 occurrences in each build, so the issue is repo-wide rather than a single hand-written hotspot — it looks like a build-step / wrapper that prepends( /*#__PURE__*/...)around each annotated value.The build still produces working output; the symptom is silently lost dead-code elimination opportunities (slightly larger bundles and longer-than-needed module evaluation chains in downstream apps).
Runtime (please provide the browser and version, delete other browsers):
Desktop (please complete the following information):
To Reproduce
Steps to reproduce the behavior:
npm create vite@latest pure-repro -- --template react-tscd pure-repro && npm installnpm install @nevware21/ts-utils@0.14.0src/main.tsx:import { isNode } from "@nevware21/ts-utils"; console.log(isNode());npm run build[INVALID_ANNOTATION]warnings in the build output pointing intonode_modules/@nevware21/ts-utils/dist/es5/mod/ts-utils.js(the specific line/column varies based on which exports are pulled in).The two warnings I see in my real-world build (Vite 8.0.14, Rolldown):
dist/es5/mod/ts-utils.js:920:16— insideisNodedist/es5/mod/ts-utils.js:923:16— insideisWebWorker@microsoft/applicationinsights-web(which depends on this package) triggers the same warnings via re-export, so any downstream that uses App Insights also hits the issue.Expected behavior
/* #__PURE__*/annotations in the published artifacts are placed immediately before the call expression they annotate, with no open paren between the annotation and the expression. Rolldown (and any other bundler that follows the conventional pure-annotation rules) is then able to mark the call as side-effect-free and tree-shake it when the result is unused.Concretely: emit
instead of
If the surrounding parens are required for some reason (e.g. a TypeScript downlevel artifact), move the annotation to sit between the open paren and the call without a leading space:
In Rolldown's view, the key property is that the comment must be immediately adjacent to the call expression token; any whitespace/token in between (including the open paren) disqualifies the annotation.
Screenshots
n/a — terminal warnings only.
Additional context
@nevware21/ts-utils@0.14.0vite@8.0.14(which uses Rolldown). Rollup tolerated the misplaced annotations silently, which is likely why this hasn't surfaced before.dist/es5/mod/ts-utils.jsanddist/es6/mod/ts-utils.jsartifacts are affected (159 occurrences in each). The pattern is uniform across the file, suggesting the upstream cause is a build-step wrapper rather than the source files themselves.@microsoft/applicationinsights-weband@microsoft/applicationinsights-core-jsre-package some of these annotations and produce the same warnings via their own builds; happy to file a sibling issue on the ApplicationInsights-JS repo once this one has been confirmed.