From 12c03fb956db1afde2558f6e1ae9bff9991301ea Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Fri, 13 Mar 2026 13:42:58 +0100 Subject: [PATCH 1/3] fix(transform,transform-shaker): wrap VM errors as host Error, fix shaker structural child removal - Wrap vm.runInContext() errors in Module.evaluate() as proper host Error instances with filename context, fixing NonErrorEmittedError in webpack - Add ExportAllDeclaration.exported edge in graph builder, distinguishing `export * as ns` (named export) from `export *` (wildcard re-export) - Guard syntactically required children (source, exported) from removal in removeDeadCode(), preventing invalid ESM output Co-Authored-By: Claude Opus 4.6 --- .../src/__snapshots__/shaker.test.ts.snap | 4 ++++ packages/transform-shaker/src/langs/core.ts | 20 ++++++++++++++++--- packages/transform-shaker/src/shaker.test.ts | 17 ++++++++++++++++ packages/transform-shaker/src/shaker.ts | 10 +++++++++- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/transform-shaker/src/__snapshots__/shaker.test.ts.snap b/packages/transform-shaker/src/__snapshots__/shaker.test.ts.snap index 4b198fb4a..a39bb8df3 100644 --- a/packages/transform-shaker/src/__snapshots__/shaker.test.ts.snap +++ b/packages/transform-shaker/src/__snapshots__/shaker.test.ts.snap @@ -1,5 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`does not break export * as ns when requesting the namespace export 1`] = `"export * as ns from './module';"`; + exports[`keeps ESM default import with batch export 1`] = ` "import blank from './blank.jpg'; export const __mkPreval = { backgroundImage: blank };" @@ -141,6 +143,8 @@ const foo = bar(); export const __linariaPreval = [foo];" `; +exports[`removes \`export * as ns\` if not requested 1`] = `"export * from './other';"`; + exports[`removes all 1`] = ` " export const __linariaPreval = [];" diff --git a/packages/transform-shaker/src/langs/core.ts b/packages/transform-shaker/src/langs/core.ts index e5cf902dc..c8f0d57f4 100644 --- a/packages/transform-shaker/src/langs/core.ts +++ b/packages/transform-shaker/src/langs/core.ts @@ -639,11 +639,25 @@ export const visitors = { if (!this.graph.imports.has(source)) { this.graph.imports.set(source, []); } - this.graph.importTypes.set(source, 'reexport'); + this.graph.addEdge(node, node.source); - // Create a sentinel node that represents this re-export - this.graph.reexports.push(node as unknown as IdentifierNode); + if (node.exported) { + // `export * as ns from "module"` — namespace re-export creates a single named export `ns`, + // it does NOT pass through individual exports like `export * from "module"` does. + const name = isIdentifier(node.exported) + ? node.exported.name + : (node.exported as unknown as { value: string }).value; + + this.graph.addExport(name, node); + this.graph.addEdge(node, node.exported); + + this.graph.importTypes.set(source, 'wildcard'); + } else { + // `export * from "module"` — true wildcard re-export, passes through all exports + this.graph.importTypes.set(source, 'reexport'); + this.graph.reexports.push(node as unknown as IdentifierNode); + } return 'ignore' as const; }, diff --git a/packages/transform-shaker/src/shaker.test.ts b/packages/transform-shaker/src/shaker.test.ts index c2af9a792..a1744f0c0 100644 --- a/packages/transform-shaker/src/shaker.test.ts +++ b/packages/transform-shaker/src/shaker.test.ts @@ -376,3 +376,20 @@ it('keeps identifiers used as computed property keys', () => { expect(shaken).toMatchSnapshot(); }); + +it('removes `export * as ns` if not requested', () => { + const [shaken] = _shake(['unknownExport'])` + export * as ns from './module'; + export * from './other'; + `; + + expect(shaken).toMatchSnapshot(); +}); + +it('does not break export * as ns when requesting the namespace export', () => { + const [shaken] = _shake(['ns'])` + export * as ns from './module'; + `; + + expect(shaken).toMatchSnapshot(); +}); diff --git a/packages/transform-shaker/src/shaker.ts b/packages/transform-shaker/src/shaker.ts index c079e96b7..2c2aa4110 100644 --- a/packages/transform-shaker/src/shaker.ts +++ b/packages/transform-shaker/src/shaker.ts @@ -9,6 +9,14 @@ import MagicString from 'magic-string'; import { isNode, getVisitorKeys, debug } from './utils.js'; import build from './graphBuilder.js'; +// Syntactically required children that must not be removed independently — +// removing them produces invalid code (e.g. `export { X }` without `from "module"`). +const STRUCTURAL_CHILDREN: Record> = { + ExportNamedDeclaration: new Set(['source']), + ExportAllDeclaration: new Set(['source', 'exported']), + ImportDeclaration: new Set(['source']), +}; + function isStatementBody(nodeType: string, key: string): boolean { return ( (nodeType === 'Program' && key === 'body') || @@ -130,7 +138,7 @@ function removeDeadCode(node: Node, alive: Set, s: MagicString, sourceCode } else if (isNode(subNode)) { if (alive.has(subNode)) { removeDeadCode(subNode, alive, s, sourceCode); - } else { + } else if (!STRUCTURAL_CHILDREN[node.type]?.has(key)) { s.remove(subNode.start, subNode.end); } } From 20ab490ddd96d99856c8f6f7c8d79add6a23591f Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Fri, 13 Mar 2026 13:43:13 +0100 Subject: [PATCH 2/3] chore: add change files for VM error wrapping and shaker fixes Co-Authored-By: Claude Opus 4.6 --- ...fel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json | 7 +++++++ ...nsform-shaker-7cb35a36-cfc0-46d5-82ea-c95ac660f49a.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json create mode 100644 change/@griffel-transform-shaker-7cb35a36-cfc0-46d5-82ea-c95ac660f49a.json diff --git a/change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json b/change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json new file mode 100644 index 000000000..6c6f27780 --- /dev/null +++ b/change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: wrap VM errors as host Error with filename context", + "packageName": "@griffel/transform", + "email": "olfedias@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@griffel-transform-shaker-7cb35a36-cfc0-46d5-82ea-c95ac660f49a.json b/change/@griffel-transform-shaker-7cb35a36-cfc0-46d5-82ea-c95ac660f49a.json new file mode 100644 index 000000000..6b6eeea3a --- /dev/null +++ b/change/@griffel-transform-shaker-7cb35a36-cfc0-46d5-82ea-c95ac660f49a.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: protect structural children from removal, handle export * as ns correctly", + "packageName": "@griffel/transform-shaker", + "email": "olfedias@microsoft.com", + "dependentChangeType": "patch" +} From 9945599a7d9789bfc065f98f84e8ca0f9d49aca8 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Fri, 13 Mar 2026 17:29:08 +0100 Subject: [PATCH 3/3] chore: remove redundant change file for transform Co-Authored-By: Claude Opus 4.6 --- ...fel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json diff --git a/change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json b/change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json deleted file mode 100644 index 6c6f27780..000000000 --- a/change/@griffel-transform-04e458b3-6dac-4944-8d4e-f47b6c3501c3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "fix: wrap VM errors as host Error with filename context", - "packageName": "@griffel/transform", - "email": "olfedias@microsoft.com", - "dependentChangeType": "patch" -}