Skip to content

feat(napi/parser)!: represent empty optional fields on JS side as null#16411

Merged
graphite-app[bot] merged 1 commit intomainfrom
12-02-feat_napi_parser_represent_empty_optional_fields_on_js_side_as_null_
Dec 2, 2025
Merged

feat(napi/parser)!: represent empty optional fields on JS side as null#16411
graphite-app[bot] merged 1 commit intomainfrom
12-02-feat_napi_parser_represent_empty_optional_fields_on_js_side_as_null_

Conversation

@overlookmotel
Copy link
Member

@overlookmotel overlookmotel commented Dec 2, 2025

Follow-on after #16383 and #16403.

While working on those I discovered an option NAPI-RS has to represent Option::None as null, instead of omitting the field entirely.

Use that option on the structs used for transferring errors and module record over to JS.

I think this is likely more performant because:

  1. NAPI-RS can create all properties of the objects using only the faster node_api_create_object_with_properties API.
  2. It produces consistent object shapes, so JS engine can better optimize code using these objects.

It also brings the shape of the data perfectly into line between standard transfer and raw transfer, and is consistent with how empty fields in the AST are represented as null.

I would have used this option before if I'd known it existed.

Breaking change

I've marked this as a breaking change because code consuming these objects would now need to check for empty fields with value === null instead of value === undefined.

However in practice, most people probably use !value or value ?? ..., so it's unlikely to affect many users. This only affects module record and errors anyway, not the AST itself, as we transfer that as JSON, not via NAPI.

@github-actions github-actions bot added A-parser Area - Parser C-enhancement Category - New feature or request labels Dec 2, 2025
Copy link
Member Author

overlookmotel commented Dec 2, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@overlookmotel overlookmotel marked this pull request as ready for review December 2, 2025 23:08
Copilot AI review requested due to automatic review settings December 2, 2025 23:08
@overlookmotel overlookmotel marked this pull request as draft December 2, 2025 23:10
Copy link
Contributor

Copilot AI left a comment

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 enhances NAPI-RS data transfer by representing empty optional fields as null instead of omitting them entirely. This aligns the representation with how AST fields are handled and improves JS engine optimization through consistent object shapes.

Key Changes:

  • Added use_nullable = true to NAPI object macros for module record and error structs
  • Updated TypeScript type definitions to reflect | null instead of optional fields (?)
  • Removed the removeNullProperties helper function that was previously needed to normalize outputs
  • Updated all test snapshots to expect null values for empty optional fields

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
napi/parser/src/types.rs Added use_nullable = true to ImportName, StaticExportEntry, ExportImportName, ExportExportName, and ExportLocalName structs
crates/oxc_napi/src/error.rs Added use_nullable = true to OxcError and ErrorLabel structs
napi/parser/src-js/index.d.ts Updated TypeScript type definitions to use `
napi/parser/test/parse.test.ts Updated test expectations to include null values for empty optional fields
napi/parser/test/parse-raw-worker.ts Removed removeNullProperties helper function and related field reordering logic
napi/parser/test/__snapshots__/esm.test.ts.snap Updated all test snapshots to include null values for empty optional fields in module records

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@overlookmotel overlookmotel force-pushed the 12-02-feat_napi_parser_represent_empty_optional_fields_on_js_side_as_null_ branch from 248b924 to 022bf48 Compare December 2, 2025 23:14
@overlookmotel overlookmotel marked this pull request as ready for review December 2, 2025 23:14
@github-actions github-actions bot added A-minifier Area - Minifier A-transformer Area - Transformer / Transpiler labels Dec 2, 2025
@overlookmotel overlookmotel force-pushed the 12-02-feat_napi_parser_represent_empty_optional_fields_on_js_side_as_null_ branch from 022bf48 to 9c8d65e Compare December 2, 2025 23:23
@overlookmotel overlookmotel self-assigned this Dec 2, 2025
@graphite-app graphite-app bot added the 0-merge Merge with Graphite Merge Queue label Dec 2, 2025
@graphite-app
Copy link
Contributor

graphite-app bot commented Dec 2, 2025

Merge activity

…ll` (#16411)

Follow-on after #16383 and #16403.

While working on those I discovered an option NAPI-RS has to represent `Option::None` as `null`, instead of omitting the field entirely.

Use that option on the structs used for transferring errors and module record over to JS.

I think this is likely more performant because:

1. NAPI-RS can create all properties of the objects using only the faster `node_api_create_object_with_properties` API.
2. It produces consistent object shapes, so JS engine can better optimize code using these objects.

It also brings the shape of the data perfectly into line between standard transfer and raw transfer, and is consistent with how empty fields in the AST are represented as `null`.

I would have used this option before if I'd known it existed.

### Breaking change

I've marked this as a breaking change because code consuming these objects would now need to check for empty fields with `value === null` instead of `value === undefined`.

However in practice, most people probably use `!value` or `value ?? ...`, so it's unlikely to affect many users. This only affects module record and errors anyway, not the AST itself, as we transfer that as JSON, not via NAPI.
@graphite-app graphite-app bot force-pushed the 12-02-feat_napi_parser_represent_empty_optional_fields_on_js_side_as_null_ branch from 9c8d65e to 083fea9 Compare December 2, 2025 23:30
graphite-app bot pushed a commit that referenced this pull request Dec 2, 2025
…16412)

Revert the change made in #16403. It's no longer necessary after #16411, because NAPI-RS no longer re-orders the fields.
@graphite-app graphite-app bot merged commit 083fea9 into main Dec 2, 2025
20 checks passed
@graphite-app graphite-app bot deleted the 12-02-feat_napi_parser_represent_empty_optional_fields_on_js_side_as_null_ branch December 2, 2025 23:36
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Dec 2, 2025
Afsoon pushed a commit to Afsoon/oxc that referenced this pull request Dec 3, 2025
…ll` (oxc-project#16411)

Follow-on after oxc-project#16383 and oxc-project#16403.

While working on those I discovered an option NAPI-RS has to represent `Option::None` as `null`, instead of omitting the field entirely.

Use that option on the structs used for transferring errors and module record over to JS.

I think this is likely more performant because:

1. NAPI-RS can create all properties of the objects using only the faster `node_api_create_object_with_properties` API.
2. It produces consistent object shapes, so JS engine can better optimize code using these objects.

It also brings the shape of the data perfectly into line between standard transfer and raw transfer, and is consistent with how empty fields in the AST are represented as `null`.

I would have used this option before if I'd known it existed.

### Breaking change

I've marked this as a breaking change because code consuming these objects would now need to check for empty fields with `value === null` instead of `value === undefined`.

However in practice, most people probably use `!value` or `value ?? ...`, so it's unlikely to affect many users. This only affects module record and errors anyway, not the AST itself, as we transfer that as JSON, not via NAPI.
Afsoon pushed a commit to Afsoon/oxc that referenced this pull request Dec 3, 2025
…xc-project#16412)

Revert the change made in oxc-project#16403. It's no longer necessary after oxc-project#16411, because NAPI-RS no longer re-orders the fields.
overlookmotel pushed a commit that referenced this pull request Dec 8, 2025
### 💥 BREAKING CHANGES

- 083fea9 napi/parser: [**BREAKING**] Represent empty optional fields on
JS side as `null` (#16411) (overlookmotel)

### 🚀 Features

- 7a2afee parser: Add TS1174 error for classes extending multiple base
classes (#15993) (sapphi-red)
- da87812 semantic: Add TS2309 error for export assignment with other
exports (#15992) (sapphi-red)
- d6d2bcd minifier: Remove unused function calls that are marked by
`manual_pure_functions` (#16534) (sapphi-red)
- c90f053 minifier: Support `.` separated values for
`compress.treeshake.manualPureFunctions` (#16529) (sapphi-red)
- a607cc4 codegen: Preserve comments between CatchClause's param and
body (#16167) (copilot-swe-agent)
- 8c10694 semantic: Expose get_comment_at method (#16439) (camc314)
- 3981e7a ast: Add get_comment_at to lookup a comment by span (#16438)
(camc314)

### 🐛 Bug Fixes

- 699406a napi/parser: Move `ExportEntry::module_request` field to first
(#16412) (overlookmotel)
- 12bd794 napi/parser: Move `ExportEntry::module_request` field to last
(#16403) (overlookmotel)

### ⚡ Performance

- 790beeb napi/parser: Do not remove extraneous options on JS side
(#16447) (overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Copilot AI pushed a commit that referenced this pull request Dec 10, 2025
### 💥 BREAKING CHANGES

- 083fea9 napi/parser: [**BREAKING**] Represent empty optional fields on
JS side as `null` (#16411) (overlookmotel)

### 🚀 Features

- 7a2afee parser: Add TS1174 error for classes extending multiple base
classes (#15993) (sapphi-red)
- da87812 semantic: Add TS2309 error for export assignment with other
exports (#15992) (sapphi-red)
- d6d2bcd minifier: Remove unused function calls that are marked by
`manual_pure_functions` (#16534) (sapphi-red)
- c90f053 minifier: Support `.` separated values for
`compress.treeshake.manualPureFunctions` (#16529) (sapphi-red)
- a607cc4 codegen: Preserve comments between CatchClause's param and
body (#16167) (copilot-swe-agent)
- 8c10694 semantic: Expose get_comment_at method (#16439) (camc314)
- 3981e7a ast: Add get_comment_at to lookup a comment by span (#16438)
(camc314)

### 🐛 Bug Fixes

- 699406a napi/parser: Move `ExportEntry::module_request` field to first
(#16412) (overlookmotel)
- 12bd794 napi/parser: Move `ExportEntry::module_request` field to last
(#16403) (overlookmotel)

### ⚡ Performance

- 790beeb napi/parser: Do not remove extraneous options on JS side
(#16447) (overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
taearls pushed a commit to taearls/oxc that referenced this pull request Dec 11, 2025
…ll` (oxc-project#16411)

Follow-on after oxc-project#16383 and oxc-project#16403.

While working on those I discovered an option NAPI-RS has to represent `Option::None` as `null`, instead of omitting the field entirely.

Use that option on the structs used for transferring errors and module record over to JS.

I think this is likely more performant because:

1. NAPI-RS can create all properties of the objects using only the faster `node_api_create_object_with_properties` API.
2. It produces consistent object shapes, so JS engine can better optimize code using these objects.

It also brings the shape of the data perfectly into line between standard transfer and raw transfer, and is consistent with how empty fields in the AST are represented as `null`.

I would have used this option before if I'd known it existed.

### Breaking change

I've marked this as a breaking change because code consuming these objects would now need to check for empty fields with `value === null` instead of `value === undefined`.

However in practice, most people probably use `!value` or `value ?? ...`, so it's unlikely to affect many users. This only affects module record and errors anyway, not the AST itself, as we transfer that as JSON, not via NAPI.
taearls pushed a commit to taearls/oxc that referenced this pull request Dec 11, 2025
…xc-project#16412)

Revert the change made in oxc-project#16403. It's no longer necessary after oxc-project#16411, because NAPI-RS no longer re-orders the fields.
taearls pushed a commit to taearls/oxc that referenced this pull request Dec 11, 2025
### 💥 BREAKING CHANGES

- 083fea9 napi/parser: [**BREAKING**] Represent empty optional fields on
JS side as `null` (oxc-project#16411) (overlookmotel)

### 🚀 Features

- 7a2afee parser: Add TS1174 error for classes extending multiple base
classes (oxc-project#15993) (sapphi-red)
- da87812 semantic: Add TS2309 error for export assignment with other
exports (oxc-project#15992) (sapphi-red)
- d6d2bcd minifier: Remove unused function calls that are marked by
`manual_pure_functions` (oxc-project#16534) (sapphi-red)
- c90f053 minifier: Support `.` separated values for
`compress.treeshake.manualPureFunctions` (oxc-project#16529) (sapphi-red)
- a607cc4 codegen: Preserve comments between CatchClause's param and
body (oxc-project#16167) (copilot-swe-agent)
- 8c10694 semantic: Expose get_comment_at method (oxc-project#16439) (camc314)
- 3981e7a ast: Add get_comment_at to lookup a comment by span (oxc-project#16438)
(camc314)

### 🐛 Bug Fixes

- 699406a napi/parser: Move `ExportEntry::module_request` field to first
(oxc-project#16412) (overlookmotel)
- 12bd794 napi/parser: Move `ExportEntry::module_request` field to last
(oxc-project#16403) (overlookmotel)

### ⚡ Performance

- 790beeb napi/parser: Do not remove extraneous options on JS side
(oxc-project#16447) (overlookmotel)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-minifier Area - Minifier A-parser Area - Parser A-transformer Area - Transformer / Transpiler C-enhancement Category - New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants