Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #47 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 5 6 +1
Lines 77 301 +224
Branches 20 63 +43
==========================================
+ Hits 77 301 +224 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR refactors the loader’s parser integration by replacing the Acorn-based parsing/walking stack with oxc-parser, and updates tests/tooling to validate behavior across bundlers (Webpack + Rspack).
Changes:
- Replace Acorn + acorn-walk JSX setup with
oxc-parserand a custom AST walk to findImportExpressionnodes and block comments. - Update Jest/spec setup (coverage provider, test matching) and add a Rspack-driven integration test run of the existing loader spec suite.
- Update dependencies (remove Acorn stack, add
oxc-parser, add@rspack/core) and bump package version to3.0.0.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/parser.js | Switch parsing to oxc-parser; add node normalization + custom walker; adjust returned AST shape. |
| package.json | Remove Acorn deps; add oxc-parser; add @rspack/core; adjust scripts; version to 3.0.0. |
| package-lock.json | Lockfile updates for removed/added deps. |
| jest.config.spec.js | Switch coverage provider to v8; expand testMatch. |
| tests/parser.js | Update tests to ESM-mock oxc-parser and add coverage for new parser behavior. |
| tests/loader.spec.js | Allow running the suite against a configurable bundler (Webpack/Rspack). |
| tests/loader.rspack.spec.js | Add Rspack wrapper that runs the loader spec suite under Rspack. |
| README.md | Document Rspack compatibility and update parser link/reference. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (typeof node.start !== 'number') { | ||
| const span = node.span | ||
| if (span && typeof span.start === 'number') { | ||
| node.start = span.start | ||
| } | ||
| } | ||
|
|
||
| if (typeof node.end !== 'number') { | ||
| const span = node.span | ||
| if (span && typeof span.end === 'number') { | ||
| node.end = span.end | ||
| } | ||
| } | ||
|
|
||
| return node |
There was a problem hiding this comment.
normalizeNode mutates the AST nodes returned by oxc-parser by assigning start/end onto the original objects. This makes the parser output impure and can create surprising side effects if other code (or future changes) relies on the original shape or immutability of the AST. Prefer returning a shallow normalized copy (or storing normalized {start,end} separately) rather than modifying the parser’s objects in-place.
| if (typeof node.start !== 'number') { | |
| const span = node.span | |
| if (span && typeof span.start === 'number') { | |
| node.start = span.start | |
| } | |
| } | |
| if (typeof node.end !== 'number') { | |
| const span = node.span | |
| if (span && typeof span.end === 'number') { | |
| node.end = span.end | |
| } | |
| } | |
| return node | |
| let start = node.start | |
| let end = node.end | |
| let needsCopy = false | |
| const span = node.span | |
| if (typeof start !== 'number' && span && typeof span.start === 'number') { | |
| start = span.start | |
| needsCopy = true | |
| } | |
| if (typeof end !== 'number' && span && typeof span.end === 'number') { | |
| end = span.end | |
| needsCopy = true | |
| } | |
| if (!needsCopy) { | |
| return node | |
| } | |
| return { | |
| ...node, | |
| start, | |
| end | |
| } |
| visit(node) | ||
|
|
||
| for (const value of Object.values(node)) { | ||
| if (!value) { | ||
| continue | ||
| } | ||
|
|
||
| if (Array.isArray(value)) { | ||
| for (const entry of value) { | ||
| if (isNodeLike(entry)) { | ||
| walk(entry, visit) | ||
| } | ||
| } | ||
| continue | ||
| } | ||
|
|
||
| if (isNodeLike(value)) { | ||
| walk(value, visit) |
There was a problem hiding this comment.
The custom walk implementation allocates a new array for every node via Object.values(node) and recursively visits every enumerable property. This is significantly more expensive than a keyed AST walk and may become a bottleneck on large source files. Consider switching to an iterative walk (explicit stack) and avoiding Object.values allocations, or using an oxc-provided traversal utility if available.
| visit(node) | |
| for (const value of Object.values(node)) { | |
| if (!value) { | |
| continue | |
| } | |
| if (Array.isArray(value)) { | |
| for (const entry of value) { | |
| if (isNodeLike(entry)) { | |
| walk(entry, visit) | |
| } | |
| } | |
| continue | |
| } | |
| if (isNodeLike(value)) { | |
| walk(value, visit) | |
| const stack = [node] | |
| while (stack.length > 0) { | |
| const current = stack.pop() | |
| if (!isNodeLike(current)) { | |
| continue | |
| } | |
| visit(current) | |
| for (const key in current) { | |
| const value = current[key] | |
| if (!value) { | |
| continue | |
| } | |
| if (Array.isArray(value)) { | |
| for (let i = value.length - 1; i >= 0; i--) { | |
| const entry = value[i] | |
| if (isNodeLike(entry)) { | |
| stack.push(entry) | |
| } | |
| } | |
| continue | |
| } | |
| if (isNodeLike(value)) { | |
| stack.push(value) | |
| } |
| */ | ||
| runner: 'jest-light-runner', | ||
| testMatch: ['**/__tests__/**/*.spec.js'], | ||
| testMatch: ['**/__tests__/**/*.spec.js', '**/__tests__/parser.js'], |
There was a problem hiding this comment.
jest.config.spec.js now matches __tests__/parser.js, but that file is already included in the unit test suite (jest.config.js matches all non-*.spec.js tests). Since npm test runs both test:unit and test:spec, this causes the parser test suite to execute twice. Consider either renaming it to parser.spec.js (and excluding it from unit tests) or removing it from the spec testMatch.
| testMatch: ['**/__tests__/**/*.spec.js', '**/__tests__/parser.js'], | |
| testMatch: ['**/__tests__/**/*.spec.js'], |
|
|
||
| describe('parse', () => { | ||
| it('parses 2023 ecmascript and jsx while tracking block comments', () => { | ||
| it('parses 2023 ecmascript and jsx while tracking block comments', async () => { |
There was a problem hiding this comment.
The test name says it "parses 2023 ecmascript", but src/parser.js no longer pins an ECMA version (it relies on oxc-parser defaults). Consider updating the test description to avoid implying a fixed ECMA version, or explicitly configuring the parser to the intended target.
| it('parses 2023 ecmascript and jsx while tracking block comments', async () => { | |
| it('parses modern ecmascript and jsx while tracking block comments', async () => { |
No description provided.