Skip to content

feat(vue-extractor): make the underlying extractor configurable - #2729

Merged
andrii-bodnar merged 3 commits into
lingui:mainfrom
ybai08:feat/vue-extractor-underlying-extractor
Oct 27, 2026
Merged

andrii-bodnar merged 3 commits into
lingui:mainfrom
ybai08:feat/vue-extractor-underlying-extractor

Conversation

@ybai08

@ybai08 ybai08 commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Description

This implements the API proposed in #2719. createVueExtractor() accepts an extractor option, which receives the code compiled from the .vue file instead of the hardcoded Babel extractor:

import { createVueExtractor } from "@lingui/extractor-vue"
import { createSwcExtractor } from "@lingui/native-tools"

const swc = createSwcExtractor()

export default defineConfig({
  extractors: [swc, createVueExtractor({ extractor: swc })],
})

The default is still the Babel extractor, so existing configs behave the same.

One extra change was needed for the SWC extractor. The Vue extractor passes each block's source map as ctx.sourceMaps, and createSwcExtractor() (native-tools 0.1.6) doesn't read it. It does read an inline //# sourceMappingURL=data:... comment, though. Without that, template messages ended up with positions inside the compiled render function (test.vue.ts:4, test.vue.ts:8) rather than in the .vue file. The Vue extractor now inlines the map into the code it passes on, in addition to ctx.sourceMaps. Babel still uses ctx.sourceMaps first, and the existing snapshots don't change. If you'd rather have createSwcExtractor read ctx.sourceMaps in native-tools, I can drop this part.

With the SWC extractor, file and line now match the Babel extractor for every message in the fixture. Columns still differ, because the two extractors count them differently (for example 21 vs 20 for the same call), so the test compares file and line only.

<script setup> messages report the wrong file and line no matter which extractor runs, because Vue doesn't generate a source map for that block. That fix is a separate PR (#2728). The two PRs touch the same lines in vue-extractor.ts, so whichever merges second needs a small rebase. With both changes applied together, the extractor-vue tests pass.

The vue/compiler-sfc peer dependency idea from the issue comment isn't part of this PR.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Examples update

Fixes #2719

Checklist

  • I have read the CONTRIBUTING and CODE_OF_CONDUCT docs
  • I have added tests that prove my fix is effective or that my feature works
  • I have added the necessary documentation (if appropriate)

Tests:

  • A fake extractor checks that the option is used. It receives the <script>, <script setup> and <template> code along with the source map.
  • createSwcExtractor() runs through the Vue extractor on the existing test.vue fixture. Its ids, messages, comments, files and lines must equal what the Babel path gives.

@lingui/native-tools is added as a devDependency of @lingui/extractor-vue for that test. Without the change in vue-extractor.ts, the first test fails. With the option but without the inlined map, the second one fails.

Docs: there is a new "Underlying Extractor" section in website/docs/ref/extractor-vue.md.

Commands run (Node 22.23, after yarn release:build):

  • yarn vitest --run packages/extractor-vue: 6 passed
  • yarn test: 94 files, 1612 passed, 2 skipped
  • yarn lint:eslint, yarn workspace @lingui/extractor-vue check-types, prettier --check on the package and the doc page: clean
  • an end-to-end lingui extract with extractors: [swc, createVueExtractor({ extractor: swc })] on test.vue: all 6 messages extracted, with App.vue:<line> references

Not run: yarn test:e2e, yarn test:types, and the docs site build (website/ isn't installed).

@vercel

vercel Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
js-lingui Ready Ready Preview Oct 26, 2026 5:12pm UTC

Request Review

@codecov

codecov Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.94%. Comparing base (6bb8983) to head (3f96bd0).
⚠️ Report is 456 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2729       +/-   ##
===========================================
+ Coverage   77.05%   89.94%   +12.89%     
===========================================
  Files          84      138       +54     
  Lines        2157     4474     +2317     
  Branches      555     1336      +781     
===========================================
+ Hits         1662     4024     +2362     
- Misses        382      401       +19     
+ Partials      113       49       -64     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ybai08

ybai08 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review.

  • __dirname → import.meta.dirname in the two tests this PR adds (e6e92b0). I left the three existing __dirname uses in that file as they were to keep the diff to this change; happy to switch them too.
  • Line 81: yes, correct. The inline map is only there because createSwcExtractor() ignores ctx.sourceMaps; fix(native-tools): honor ctx.sourceMaps in createSwcExtractor swc-plugin#287 makes it read ctx.sourceMaps, and ctx.sourceMaps is already passed here, so the withInlineSourceMap() call (and the helper) can go once @lingui/native-tools with feat: Add extractBabelOptions to configuration #287 is the version used here. The "should map positions back to the vue file with the SWC extractor" test covers that, so it would show whether dropping it is safe after the bump. I can remove it in this PR together with the dependency bump once feat: Add extractBabelOptions to configuration #287 is released, or leave it in and drop it in a follow-up, whichever you prefer.

yarn release:build then vitest run packages/extractor-vue: 6 tests pass.

@timofei-iatsenko

Copy link
Copy Markdown
Collaborator

i think it's worth merging/releasing this one first lingui/swc-plugin#287 (review)

Then bump a version of native-tools here and drop workarounds and related tests.

@ybai08

ybai08 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Sounds good. Once a @lingui/native-tools release with lingui/swc-plugin#287 is out, I'll bump it here and drop withInlineSourceMap and the tests that only cover the workaround.

@andrii-bodnar

Copy link
Copy Markdown
Contributor

Hi @ybai08, I just released the new version of @lingui/native-tools - https://github.com/lingui/swc-plugin/releases/tag/native-tools%400.2.0

@andrii-bodnar andrii-bodnar added Vue.js ↻ awaiting rebase Rebase is needed due to conflicts labels Oct 19, 2026
`createVueExtractor({ extractor })` passes the code compiled from .vue
files to the given extractor instead of the Babel one, so Vue projects
can use the SWC extractor from @lingui/native-tools.

The source maps are also inlined into that code, as the SWC extractor
reads inline source maps but not `ctx.sourceMaps`, so positions still
point into the .vue file.

Closes lingui#2719
…ools reads ctx.sourceMaps

@lingui/native-tools 0.2.0 (lingui/swc-plugin#287) makes the SWC
extractor read ctx.sourceMaps, so the inline copy of the map is no longer
needed. Bump the dev dependency to ^0.2.0, as cli and vite-plugin use.
@ybai08
ybai08 force-pushed the feat/vue-extractor-underlying-extractor branch from e6e92b0 to 3f96bd0 Compare October 26, 2026 17:12
@ybai08

ybai08 commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the release, @andrii-bodnar, and sorry this took a week.

Done in 3f96bd0:

  • @lingui/native-tools in extractor-vue is now ^0.2.0, as cli and vite-plugin already use. yarn.lock changes by that one line.
  • withInlineSourceMap() and its call are gone. The compiled code goes to the extractor as is, with the map in ctx.sourceMaps.
  • I kept "should map positions back to the vue file with the SWC extractor". It is the test that needed the inline map with 0.1.6. With 0.2.0 it now shows that the positions still map back to the .vue file without it. I can drop it if you see it as covering only the workaround.
  • website/docs/ref/extractor-vue.md now says that the SWC extractor reads those maps from 0.2.0 on.

I also rebased onto main (2527a65), because the branch conflicted with #2728 in vue-extractor.ts and extractor.test.ts. Both kept their changes: the <script setup> position callback from #2728, and this PR's extractor option and tests.

After yarn release:build:

  • vitest --run: 94 files, 1644 passed, 2 skipped.
  • extractor-vue: 7 tests.
  • eslint and prettier --check on the changed files: clean.

@andrii-bodnar andrii-bodnar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ybai08 thank you!

@andrii-bodnar
andrii-bodnar merged commit 1527e4e into lingui:main Oct 27, 2026
13 checks passed

This branch was successfully deployed

1 active deployment
Preview — 3f96bd08 Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make underlying extractor in Vue extractor configurable (to use a Rust Extractor)

3 participants