Skip to content

Preserve native destructuring for CommonJS exports to keep iterator semantics#3705

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-array-destructuring-transpilation
Draft

Preserve native destructuring for CommonJS exports to keep iterator semantics#3705
Copilot wants to merge 3 commits intomainfrom
copilot/fix-array-destructuring-transpilation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 4, 2026

With module=commonjs, exported array destructuring was always flattened to index access, silently dropping iterator semantics:

declare function foo(): any;
export const [A, V] = foo();
// before
_a = foo(), exports.A = _a[0], exports.V = _a[1];

// after
const [A, V] = foo();
exports.A = A, exports.V = V;

Since downLevelIteration was removed (and the __read helper is not available), preserving native destructuring is the only way to keep iteration semantics for exported array patterns. Emitting the export references as separate exports.X = X assignments (rather than e.g. [exports.A, exports.V] = foo()) keeps the output recognizable to tools like cjs-module-lexer.

Changes (internal/transformers/moduletransforms/commonjsmodule.go)

  • visitTopLevelVariableStatement: for exported binding-pattern variable declarations, the variable declaration is now preserved as-is (so array destructuring keeps native iterator semantics) and a new helper emits exports.X = X for each declared identifier.
  • appendExportAssignmentsForBindingPattern (new): walks the binding pattern and pushes an exports.X = X assignment for each declared identifier, recursing through nested patterns, skipping omitted elements, and handling rest and default-value elements. Comments and source-map ranges are suppressed on the synthetic export references so JSDoc on binding elements isn't duplicated on the export assignment.

Side effects on existing baselines

All resulting baseline changes are improvements in the same direction: object/array patterns, defaults, and rest elements are now emitted natively, and __rest is no longer pulled in when the target supports native rest. The multi-export path (destructuringAssignmentWithExportedName) is unchanged.

A new test exportDestructuringIterator.ts covers identifiers, object patterns, defaults, and rest spread under module=commonjs.

Copilot AI changed the title [WIP] Fix array destructuring transpilation with module=commonjs Preserve native destructuring for CommonJS exports to keep iterator semantics May 4, 2026
Copilot AI requested a review from jakebailey May 4, 2026 22:04
@jakebailey
Copy link
Copy Markdown
Member

This does what it says on the tin, but, I'm not sure what affect this has on cjs-module-lexer's scanning.

@blickly Is this the change you were hoping to get?

@blickly
Copy link
Copy Markdown

blickly commented May 5, 2026

Honestly, I was expecting something more like:

const [A, V] = foo();
exports.A = A;
exports.V = V;

but this version is technically not wrong, and is definitely and improvement over the old behavior.

@jakebailey
Copy link
Copy Markdown
Member

Yeah. I think what you've suggested would be better just because it avoids the lexer problem.

@copilot please try

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 5, 2026

Yeah. I think what you've suggested would be better just because it avoids the lexer problem.

@copilot please try

Reworked in 4a6c647 to emit the lexer-friendly form. For export const [A, V] = foo() the output is now:

const [A, V] = foo();
exports.A = A, exports.V = V;

The variable declaration is preserved as-is (so array destructuring keeps native iterator semantics), and exports.X = X is emitted for each declared identifier — so cjs-module-lexer can pick them up.

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.

When using module=commonjs, Array destructuring is transpiled with wrong (non-iterator) behavior

3 participants