diff --git a/.cargo/config.toml b/.cargo/config.toml index 2dd5e85..b1fd65a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,3 +8,6 @@ build-wasm32 = "build --target wasm32-unknown-unknown" rustflags = [ "--cfg=swc_ast_unknown" ] + +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/.github/workflows/ci-lingui-swc.yml b/.github/workflows/ci-lingui-swc.yml new file mode 100644 index 0000000..b41c97f --- /dev/null +++ b/.github/workflows/ci-lingui-swc.yml @@ -0,0 +1,349 @@ +name: CI - Lingui SWC +env: + DEBUG: napi:* + APP_NAME: lingui-swc + MACOSX_DEPLOYMENT_TARGET: '10.13' + CARGO_INCREMENTAL: '1' +on: + # push: + # branches: + # - main + # tags-ignore: + # - '**' + # paths-ignore: + # - '**/*.md' + # - LICENSE + # - '**/*.gitignore' + # - .editorconfig + # - docs/** + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + working-directory: ./packages/lingui-swc + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + - run: corepack enable + - name: Install Rust Toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy, rustfmt + - name: Install dependencies + run: yarn install + working-directory: . + - name: Oxlint + run: yarn lint + - name: Cargo fmt + run: cargo fmt -- --check + - name: Clippy + run: cargo clippy + build: + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + build: yarn build --target x86_64-apple-darwin + - host: windows-latest + build: yarn build --target x86_64-pc-windows-msvc + target: x86_64-pc-windows-msvc + # - host: windows-latest + # build: yarn build --target i686-pc-windows-msvc + # target: i686-pc-windows-msvc# + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + build: yarn build --target x86_64-unknown-linux-gnu --use-napi-cross + - host: ubuntu-latest + target: x86_64-unknown-linux-musl + build: yarn build --target x86_64-unknown-linux-musl -x + - host: macos-latest + target: aarch64-apple-darwin + build: yarn build --target aarch64-apple-darwin + - host: ubuntu-latest + target: aarch64-unknown-linux-gnu + build: yarn build --target aarch64-unknown-linux-gnu --use-napi-cross + - host: ubuntu-latest + target: aarch64-linux-android + build: yarn build --target aarch64-linux-android + - host: ubuntu-latest + target: aarch64-unknown-linux-musl + build: yarn build --target aarch64-unknown-linux-musl -x + - host: windows-latest + target: aarch64-pc-windows-msvc + build: yarn build --target aarch64-pc-windows-msvc + name: stable - ${{ matrix.settings.target }} - node@24 + runs-on: ${{ matrix.settings.host }} + env: + RUSTUP_TOOLCHAIN: stable + steps: + - uses: actions/checkout@v6 + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + - run: corepack enable + - name: Install Rust Toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + - name: Cache cargo + uses: actions/cache@v5 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + ~/.napi-rs + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + - uses: mlugg/setup-zig@v2 + if: ${{ contains(matrix.settings.target, 'musl') }} + with: + version: 0.14.1 + - name: Install cargo-zigbuild + uses: taiki-e/install-action@v2 + if: ${{ contains(matrix.settings.target, 'musl') }} + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tool: cargo-zigbuild + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + - name: Install dependencies + run: yarn install + working-directory: . + - name: Build + run: ${{ matrix.settings.build }} + shell: bash + - name: Upload artifact + uses: actions/upload-artifact@v6 + with: + name: bindings-${{ matrix.settings.target }} + + path: | + ./packages/lingui-swc/${{ env.APP_NAME }}.*.node + ./packages/lingui-swc/${{ env.APP_NAME }}.*.wasm + if-no-files-found: error + # build-freebsd: + # runs-on: ubuntu-latest + # name: Build FreeBSD + # steps: + # - uses: actions/checkout@v6 + # - name: Build + # id: build + # uses: cross-platform-actions/action@v0.32.0 + # env: + # DEBUG: napi:* + # RUSTUP_IO_THREADS: 1 + # with: + # operating_system: freebsd + # version: '15.0' + # memory: 8G + # cpu_count: 3 + # environment_variables: DEBUG RUSTUP_IO_THREADS + # shell: bash + # # language=bash + # run: | + # sudo pkg install -y -f curl node libnghttp2 npm cmake + # corepack enable + # # sudo npm install -g yarn --ignore-scripts + # curl https://sh.rustup.rs -sSf --output rustup.sh + # sh rustup.sh -y --profile minimal --default-toolchain stable + # source "$HOME/.cargo/env" + # echo "~~~~ rustc --version ~~~~" + # rustc --version + # echo "~~~~ node -v ~~~~" + # node -v + # echo "~~~~ yarn --version ~~~~" + # yarn --version + # pwd + # ls -lah + # whoami + # env + # freebsd-version + # yarn install + # yarn build + # - name: Upload artifact + # uses: actions/upload-artifact@v6 + # with: + # name: bindings-freebsd + # path: ./packages/lingui-swc/${{ env.APP_NAME }}.*.node + # if-no-files-found: error + test-macOS-windows-binding: + name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + settings: + - host: windows-latest + target: x86_64-pc-windows-msvc + architecture: x64 + - host: macos-latest + target: aarch64-apple-darwin + architecture: arm64 + - host: macos-latest + target: x86_64-apple-darwin + architecture: x64 + node: + - '22' + - '24' + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v6 + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + architecture: ${{ matrix.settings.architecture }} + - run: corepack enable + - name: Install dependencies + run: yarn install + working-directory: . + - name: Download artifacts + uses: actions/download-artifact@v7 + with: + name: bindings-${{ matrix.settings.target }} + path: ./packages/lingui-swc + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: yarn test + test-linux-binding: + name: Test ${{ matrix.target }} - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl + node: + - '22' + - '24' + runs-on: ${{ contains(matrix.target, 'aarch64') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v6 + # - name: Setup node + # uses: actions/setup-node@v6 + # with: + # node-version: ${{ matrix.node }} + - run: corepack enable + - name: Output docker params + uses: actions/github-script@v8 + id: docker + with: + # language=javascript + script: | + const target = '${{ matrix.target }}'; + const node = '${{ matrix.node }}'; + + if (target.startsWith('aarch64')) { + core.setOutput('PLATFORM', 'linux/arm64'); + } else if (target.startsWith('armv7')) { + core.setOutput('PLATFORM', 'linux/arm/v7'); + } else { + core.setOutput('PLATFORM', 'linux/amd64'); + } + + const suffix = target.endsWith('-musl') ? 'alpine' : 'slim'; + core.setOutput('IMAGE', `node:${node}-${suffix}`) + + - name: Download artifacts + uses: actions/download-artifact@v7 + with: + name: bindings-${{ matrix.target }} + path: ./packages/lingui-swc + - name: List packages + run: ls -R . + shell: bash + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + if: ${{ contains(matrix.target, 'armv7') }} + with: + platforms: all + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + if: ${{ contains(matrix.target, 'armv7') }} + - name: Test bindings + run: | + docker run --rm \ + -v ${{ github.workspace }}:/repo \ + -w /repo \ + --platform ${{ steps.docker.outputs.PLATFORM }} \ + ${{ steps.docker.outputs.IMAGE }} \ + sh -c "corepack enable && yarn install && cd packages/lingui-swc && yarn test" + publish: + name: Publish + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + needs: + - lint + # - build-freebsd + - test-macOS-windows-binding + - test-linux-binding + steps: + - uses: actions/checkout@v6 + - name: Setup node + uses: actions/setup-node@v6 + with: + node-version: 24 + - run: corepack enable + - name: Install dependencies + run: yarn install + working-directory: . + - name: create npm dirs + run: yarn napi create-npm-dirs + - name: Download all artifacts + uses: actions/download-artifact@v7 + with: + path: ./packages/lingui-swc/artifacts + - name: Move artifacts + run: yarn artifacts + - name: List packages + run: ls -R ./npm + shell: bash + - name: Publish + if: github.event_name == 'workflow_dispatch' + # language=bash + run: | + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --provenance --access public + # + # if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$"; + # then + # npm publish --provenance --access public + # elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+"; + # then + # npm publish --access public --tag next + # else + # echo "Not a release, skipping publish" + # fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/ci-macro.yml b/.github/workflows/ci-macro.yml index c90a6a8..1e7dacc 100644 --- a/.github/workflows/ci-macro.yml +++ b/.github/workflows/ci-macro.yml @@ -31,7 +31,7 @@ jobs: run: cargo fmt --check - name: Run clippy - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy -p lingui_macro --all-targets --all-features -- -D warnings - name: Install cargo-llvm-cov uses: taiki-e/install-action@v2 @@ -39,7 +39,7 @@ jobs: tool: cargo-llvm-cov - name: Generate code coverage - run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info + run: cargo llvm-cov -p lingui_macro --all-features --lcov --output-path lcov.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 11f04d3..6722a77 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release on: release: - types: [released, prereleased] + types: [ released, prereleased ] jobs: release: diff --git a/.yarnrc.yml b/.yarnrc.yml index 3186f3f..c6e883c 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,2 @@ nodeLinker: node-modules + diff --git a/CLAUDE.md b/CLAUDE.md index 217e936..a6f12b0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,23 +4,37 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Monorepo for [LinguiJS](https://lingui.dev) Rust/SWC-based tooling. The primary crate is an SWC plugin that transforms `@lingui/macro` and `@lingui/react/macro` calls into optimized i18n runtime code. Compiles to WebAssembly (wasm32-wasip1) and runs inside SWC/Next.js build pipelines. +Monorepo for [LinguiJS](https://lingui.dev) Rust/SWC-based tooling. Contains two main tools: -## Repository Layout +1. **AST Macro Transform** (`crates/lingui_macro`) — SWC plugin that transforms `@lingui/macro` and `@lingui/react/macro` calls into optimized i18n runtime code. Compiles to WebAssembly (wasm32-wasip1) and runs inside SWC/Next.js build pipelines. +2. **Message Extractor** (`crates/lingui_extractor`) — AST visitor (built on SWC) that extracts translatable messages from source files. + +Published npm packages: +- `packages/lingui-macro` (`@lingui/swc-plugin`) — ships the compiled WASM binary for the macro transform. +- `packages/lingui-swc` (`lingui-swc`) — NAPI-RS native Node.js binding for the extractor. + +## Repository Structure ``` -crates/lingui_macro/ — SWC plugin (Rust, compiles to WASM) -packages/lingui-macro/ — npm package (@lingui/swc-plugin) +├── crates/ +│ ├── lingui_macro/ # SWC plugin (wasm32-wasip1 target) +│ └── lingui_extractor/ # Message extractor library (depends on lingui_macro) +├── packages/ +│ ├── lingui-macro/ # npm package wrapping the WASM binary +│ └── lingui-swc/ # NAPI-RS Node.js binding for the extractor +├── Cargo.toml # Workspace root (members: crates/*, packages/lingui-swc) +└── package.json # Yarn workspaces root (packages/*) ``` ## Build & Test Commands ```bash -# Build WASM (primary target) -cargo build-wasi --release - -# Run all tests -cargo test +# === Rust (whole workspace) === +cargo test # Run all Rust tests +cargo test -p lingui_macro # Tests for the macro crate only +cargo test -p lingui_extractor # Tests for the extractor crate only +cargo fmt --check +cargo clippy --all-targets --all-features -- -D warnings # Run a single test by name cargo test js_choices_may_contain_expressions @@ -28,29 +42,36 @@ cargo test js_choices_may_contain_expressions # Run tests matching a prefix cargo test jsx_ -# Update snapshots interactively (requires: cargo install cargo-insta) +# Update insta snapshots (lingui_macro) interactively cargo insta test --review -# Bulk-accept all snapshot changes -INSTA_UPDATE=always cargo test +# Bulk-accept all insta snapshot changes (lingui_macro) +INSTA_UPDATE=always cargo test -p lingui_macro -# Format / lint -cargo fmt --check -cargo clippy --all-targets --all-features -- -D warnings +# Update extractor snapshots +UPDATE=1 cargo test -p lingui_extractor + +# === WASM build (lingui_macro plugin) === +cargo build-wasi --release # alias defined in .cargo/config.toml -# E2E tests (requires WASM build + Node v22 + yarn) +# === NAPI-RS build (lingui-swc) === +cd packages/lingui-swc && yarn build + +# === E2E tests (lingui-macro npm package) === cd packages/lingui-macro && yarn test:e2e -# Build TypeScript (options helper) -cd packages/lingui-macro && yarn build:ts +# === lingui-swc tests === +cd packages/lingui-swc && yarn test ``` ## Architecture +### crates/lingui_macro + The plugin follows SWC's AST visitor pattern using the `Fold` trait for recursive descent transformation. **Core transformation pipeline:** -1. `crates/lingui_macro/src/lib.rs` — Entry point (`#[plugin_transform]`). Parses config, creates `LinguiMacroFolder` which implements `Fold`. +1. `lib.rs` — Entry point (`#[plugin_transform]`). Parses config, creates `LinguiMacroFolder` which implements `Fold`. 2. `macro_utils.rs` — `MacroCtx` tracks imports from `@lingui/macro` and `@lingui/react/macro`, maps symbol names to local identifiers. 3. `js_macro_folder.rs` — Transforms JS macro calls (`t()`, `defineMessage()`, `msg()`) into `MsgToken` streams. 4. `jsx_visitor.rs` — `TransJSXVisitor` transforms JSX elements (``, ``, ` {}} /> diff --git a/crates/lingui_extractor/tests/message_extractor.rs b/crates/lingui_extractor/tests/message_extractor.rs new file mode 100644 index 0000000..247e1a9 --- /dev/null +++ b/crates/lingui_extractor/tests/message_extractor.rs @@ -0,0 +1,652 @@ +use lingui_extractor::ExtractedMessage; +use lingui_extractor::{extract_messages, ExtractorOptions}; +use lingui_macro::LinguiJsOptions; + +fn extract_and_sort(source_code: &str, filename: &str) -> (Vec, Vec) { + let options = ExtractorOptions { + parser: None, + macro_options: None, + }; + + let result = + extract_messages(source_code, filename, &options).expect("Failed to extract messages"); + (result.messages, result.warnings) +} + +fn assert_no_warnings(warnings: &[String]) { + if !warnings.is_empty() { + panic!("Expected no warnings but got: {warnings:?}"); + } +} + +#[test] +fn test_ignore_files_without_lingui_import() { + let code = r#" +const message = "Hello World"; +console.log(message); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.jsx"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 0); +} + +#[test] +fn test_extract_from_jsx_trans_component() { + let code = r#" +import { Trans } from "@lingui/react"; + +; +; +; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.jsx"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 3); + + assert_eq!(messages[0].id, "msg.hello"); + assert_eq!(messages[0].comment, Some("Description".to_string())); + + assert_eq!(messages[1].id, "msg.context"); + assert_eq!(messages[1].context, Some("Context1".to_string())); + + assert_eq!(messages[2].id, "msg.default"); + assert_eq!(messages[2].message, Some("Hello World".to_string())); +} + +#[test] +fn test_jsx_trans_no_warning_when_id_from_variable() { + let code = r#" +import { Trans } from "@lingui/react"; + +; +; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.jsx"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 0); +} + +#[test] +fn test_jsx_trans_warning_when_missing_id() { + let code = r#" +import { Trans } from "@lingui/react"; + +; +; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.jsx"); + assert!(messages.is_empty()); + assert!(!warnings.is_empty()); + assert!(warnings.iter().any(|w| w.contains("Missing message ID"))); +} + +#[test] +fn test_call_expression_i18n_underscore() { + let code = r#" +const msg = i18n._("Message"); +const withDescription = i18n._("Description", {}, { comment: "description" }); +const withId = i18n._("ID", {}, { message: "Message with id" }); +const withValues = i18n._("Values {param}", { param: param }); +const withContext = i18n._("Some id", {}, { context: "Context1" }); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 5); + + assert_eq!(messages[0].id, "Message"); + + assert_eq!(messages[1].id, "Description"); + assert_eq!(messages[1].comment, Some("description".to_string())); + + assert_eq!(messages[2].id, "ID"); + assert_eq!(messages[2].message, Some("Message with id".to_string())); + + assert_eq!(messages[3].id, "Values {param}"); + assert_eq!( + messages[3].placeholders.get("param"), + Some(&"param".to_string()) + ); + + assert_eq!(messages[4].id, "Some id"); + assert_eq!(messages[4].context, Some("Context1".to_string())); +} + +#[test] +fn test_call_expression_object_descriptor() { + let code = r#" +i18n._({ + id: "my.id", + message: "My Id Message", + comment: "My comment", +}); + +// support alias +i18n.t("Aliased Message"); + +i18n.t({ + id: "my.id", + message: "My Id Message", + comment: "My comment", +}); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 3); + + assert_eq!(messages[0].id, "my.id"); + assert_eq!(messages[0].message, Some("My Id Message".to_string())); + assert_eq!(messages[0].comment, Some("My comment".to_string())); + + assert_eq!(messages[1].id, "Aliased Message"); + + assert_eq!(messages[2].id, "my.id"); +} + +#[test] +fn test_call_expression_member_access() { + let code = r#" +// member access +ctx.i18n._("Message1"); + +// member access any depth +ctx.req.i18n._("Message2"); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].id, "Message1"); + assert_eq!(messages[1].id, "Message2"); +} + +#[test] +fn test_call_expression_ignore_non_i18n_members() { + let code = r#" +i18n.load("Message"); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 0); +} + +#[test] +fn test_call_expression_ignore_with_annotation() { + let code = r#" +/* lingui-extract-ignore */ +i18n._("Message1"); + +/* lingui-extract-ignore */ +ctx.i18n._("Message2"); + +/* lingui-extract-ignore */ +ctx.req.i18n._("Message3"); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 0); +} + +#[test] +fn test_call_expression_no_warning_for_variables() { + let code = r#" +i18n._(message); +// member expression +i18n._(foo.bar); +// function call +i18n._(getMessage()); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 0); +} + +#[test] +fn test_call_expression_template_literal_and_concatenation() { + let code = r#" +const msg = i18n._(`message.id`); +const msg2 = i18n._("second" + '.' + "id"); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].id, "message.id"); + assert_eq!(messages[1].id, "second.id"); +} + +#[test] +fn test_call_expression_string_concatenation_in_comment() { + let code = r#" +const msg = i18n._('message.id', {}, {comment: "first " + "second " + "third"}); + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].comment, Some("first second third".to_string())); +} + +#[test] +fn test_string_literal_with_i18n_comment() { + let code = r#" +const t = /*i18n*/'Message'; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].id, "Message"); +} + +#[test] +fn test_string_literal_empty_warning() { + let code = r#" +const t = /*i18n*/''; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_eq!(messages.len(), 0); + assert!(!warnings.is_empty()); + assert!(warnings.iter().any(|w| w.contains("Empty StringLiteral"))); +} + +#[test] +fn test_message_descriptor_with_i18n_comment() { + let code = r#" +const msg = /*i18n*/{id: "Message"}; +const withDesc = /*i18n*/{id: "Description", comment: "description"}; +const withId = /*i18n*/{id: "ID", message: "Message with id"}; +const withContext = /*i18n*/{id: "Some id", context: "Context1"}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 4); + + assert_eq!(messages[0].id, "Message"); + assert_eq!(messages[1].id, "Description"); + assert_eq!(messages[1].comment, Some("description".to_string())); + assert_eq!(messages[2].id, "ID"); + assert_eq!(messages[2].message, Some("Message with id".to_string())); + assert_eq!(messages[3].id, "Some id"); + assert_eq!(messages[3].context, Some("Context1".to_string())); +} + +#[test] +fn test_string_literal_with_jsdoc_i18n_comment() { + let code = r#" +const a = /**i18n*/'Message1'; +const b = /** i18n */'Message2'; +const c = /** i18n */'Message3'; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 3); + assert_eq!(messages[0].id, "Message1"); + assert_eq!(messages[1].id, "Message2"); + assert_eq!(messages[2].id, "Message3"); +} + +#[test] +fn test_message_descriptor_with_jsdoc_i18n_comment() { + let code = r#" +const a = /**i18n*/{id: "Message1"}; +const b = /** i18n */{id: "Message2"}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].id, "Message1"); + assert_eq!(messages[1].id, "Message2"); +} + +#[test] +fn test_i18n_comment_with_extra_spaces() { + let code = r#" +const a = /* i18n */'Message1'; +const b = /* i18n */'Message2'; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 2); + assert_eq!(messages[0].id, "Message1"); + assert_eq!(messages[1].id, "Message2"); +} + +#[test] +fn test_message_descriptor_template_literal() { + let code = r#" +const msg = /*i18n*/{id: `Message`}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].id, "Message"); +} + +#[test] +fn test_message_descriptor_template_with_expressions_warning() { + let code = r#" +const msg = /*i18n*/{id: `Hello ${name}`}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert!(messages.is_empty()); + assert!(!warnings.is_empty()); + assert!(warnings + .iter() + .any(|w| w.contains("Could not extract from template literal with expressions"))); +} + +#[test] +fn test_message_descriptor_missing_id_warning() { + let code = r#" +const msg = /*i18n*/ {message: `Hello ${name}`}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_eq!(messages.len(), 0); + assert!(!warnings.is_empty()); + assert!(warnings.iter().any(|w| w.contains("Missing message ID"))); +} + +#[test] +fn test_message_descriptor_string_concatenation() { + let code = r#" +const msg = /*i18n*/ {id: "msg.id", comment: "first " + "second " + "third"}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 1); + assert_eq!(messages[0].comment, Some("first second third".to_string())); +} + +#[test] +fn test_placeholders_extraction() { + let code = r#" +const msg = /*i18n*/{ + id: "Values {param} {name}", + values: { + param: param, + name: "foo" + } +}; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.js"); + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 1); + assert_eq!( + messages[0].placeholders.get("param"), + Some(&"param".to_string()) + ); + assert_eq!( + messages[0].placeholders.get("name"), + Some(&"\"foo\"".to_string()) + ); +} + +// #[test] +// fn test_origin_information() { +// let code = r#" +// const msg = i18n._("Message"); +// "#; +// +// let (messages, _) = extract_and_sort(code, "test.js"); +// assert_eq!(messages.len(), 1); +// +// let origin = messages[0].origin.as_ref().unwrap(); +// assert_eq!(origin.0, "test.js"); +// assert_eq!(origin.1, 2); // Line 2 because of the blank line at the start +// assert_eq!(origin.2, Some(13)); // Column where i18n._ starts +// } + +// ============================================================================ +// Macro Options Tests +// ============================================================================ + +#[test] +fn test_macro_options_jsx_placeholder_attribute() { + let code = r#" +import { Trans } from "@lingui/react/macro"; +Hello world!; + "#; + + let options = ExtractorOptions { + parser: None, + macro_options: Some(LinguiJsOptions { + jsx_placeholder_attribute: Some("_t".into()), + ..Default::default() + }), + }; + + let result = extract_messages(code, "test.tsx", &options).expect("Failed to extract messages"); + assert_eq!(result.messages.len(), 1); + assert_eq!( + result.messages[0].message, + Some("Hello world!".to_string()) + ); +} + +#[test] +fn test_macro_options_jsx_placeholder_attribute_not_set() { + let code = r#" +import { Trans } from "@lingui/react/macro"; +Hello world!; + "#; + + let (messages, _) = extract_and_sort(code, "test.tsx"); + assert_eq!(messages.len(), 1); + // Without jsxPlaceholderAttribute, the _t attribute is ignored and index-based placeholder is used + assert_eq!(messages[0].message, Some("Hello <0>world!".to_string())); +} + +#[test] +fn test_extracts_messages_after_ts_module_declarations() { + let code = r#" +import { t } from "@lingui/core/macro"; +import { Trans } from "@lingui/react/macro"; + +declare module "x" { + interface I { + a: string; + } +} + +const afterModule = after module; + +declare global { + interface Window { + b: string; + } +} + +const afterGlobal = t`after global`; + +declare namespace Foo { + interface I { + c: string; + } +} + +const afterDeclareNamespace = after declare namespace; + +namespace Bar { + export interface I { + d: string; + } +} + +const afterNamespace = t`after namespace`; + "#; + + let (messages, warnings) = extract_and_sort(code, "test.tsx"); + + assert_no_warnings(&warnings); + assert_eq!(messages.len(), 4); + assert_eq!( + messages + .iter() + .filter_map(|message| message.message.as_deref()) + .collect::>(), + vec![ + "after module", + "after global", + "after declare namespace", + "after namespace" + ] + ); +} + +// ============================================================================ +// Snapshot Testing Framework +// ============================================================================ + +use std::fs; +use std::path::{Path, PathBuf}; + +/// Load fixture file from tests/fixtures/ +fn load_fixture(filename: &str) -> String { + let fixture_path = PathBuf::from("tests/fixtures").join(filename); + fs::read_to_string(&fixture_path) + .unwrap_or_else(|e| panic!("Failed to read fixture {}: {}", fixture_path.display(), e)) +} + +/// Get snapshot path for a fixture +fn get_snapshot_path(fixture_name: &str) -> PathBuf { + let snapshot_name = format!("{}.json", fixture_name); + PathBuf::from("tests/__snapshots__").join(snapshot_name) +} + +/// Load snapshot from disk +fn load_snapshot(path: &Path) -> Option { + fs::read_to_string(path).ok() +} + +/// Save snapshot to disk +fn save_snapshot(path: &Path, content: &str) { + fs::create_dir_all(path.parent().unwrap()) + .unwrap_or_else(|e| panic!("Failed to create snapshots directory: {}", e)); + fs::write(path, content) + .unwrap_or_else(|e| panic!("Failed to write snapshot {}: {}", path.display(), e)); +} + +/// Check if UPDATE=1 environment variable is set +fn should_update_snapshots() -> bool { + std::env::var("UPDATE").unwrap_or_default() == "1" +} + +/// Serialize messages to JSON +fn serialize_to_json(messages: &[ExtractedMessage]) -> String { + serde_json::to_string_pretty(messages).expect("Failed to serialize messages to JSON") +} + +/// Perform snapshot test +fn snapshot_test(fixture_name: &str) { + // Load fixture + let source_code = load_fixture(fixture_name); + + // Extract messages + let (messages, warnings) = extract_and_sort(&source_code, fixture_name); + + // Fail if there are warnings (optional - you can remove this if warnings are expected) + if !warnings.is_empty() { + eprintln!("Warnings during extraction from {}:", fixture_name); + for warning in &warnings { + eprintln!(" - {}", warning); + } + } + + // Serialize to JSON + let actual_json = serialize_to_json(&messages); + + // Get snapshot path + let snapshot_path = get_snapshot_path(fixture_name); + + if should_update_snapshots() { + // Update mode: save the snapshot + save_snapshot(&snapshot_path, &actual_json); + println!("Updated snapshot: {}", snapshot_path.display()); + } else { + // Compare mode: check against existing snapshot + let expected_json = load_snapshot(&snapshot_path).unwrap_or_else(|| { + panic!( + "Snapshot file not found: {}\n\nTo create snapshots, run:\n UPDATE=1 cargo test", + snapshot_path.display() + ) + }); + + // Compare JSON + if actual_json != expected_json { + // Pretty print the difference + eprintln!("\n❌ Snapshot mismatch for {}\n", fixture_name); + eprintln!("Expected ({}):", snapshot_path.display()); + eprintln!("{}\n", expected_json); + eprintln!("Actual:"); + eprintln!("{}\n", actual_json); + eprintln!( + "To update the snapshot, run:\n UPDATE=1 cargo test {}", + fixture_name.replace(".js", "").replace("-", "_") + ); + panic!("Snapshot mismatch"); + } + } +} + +// ============================================================================ +// Snapshot Tests +// ============================================================================ + +#[test] +fn test_snapshot_js_call_expression() { + snapshot_test("js-call-expression.js"); +} + +#[test] +fn test_snapshot_js_message_descriptor() { + snapshot_test("js-message-descriptor.js"); +} + +#[test] +fn test_snapshot_js_with_macros() { + snapshot_test("js-with-macros.js"); +} + +#[test] +fn test_snapshot_jsx_with_macros() { + snapshot_test("jsx-with-macros.jsx"); +} + +#[test] +fn test_snapshot_jsx_without_macros() { + snapshot_test("jsx-without-macros.jsx"); +} + +#[test] +fn test_snapshot_jsx_without_trans() { + snapshot_test("jsx-without-trans.jsx"); +} + +#[test] +fn test_snapshot_without_lingui() { + snapshot_test("without-lingui.jsx"); +} + +#[test] +fn test_snapshot_with_sourcemaps() { + snapshot_test("with-sourcemaps.js"); +} diff --git a/crates/lingui_extractor/tests/sourcemapped/a.jsx b/crates/lingui_extractor/tests/sourcemapped/a.jsx new file mode 100644 index 0000000..d656317 --- /dev/null +++ b/crates/lingui_extractor/tests/sourcemapped/a.jsx @@ -0,0 +1 @@ +i18n.t('Message from file a.jsx') diff --git a/crates/lingui_extractor/tests/sourcemapped/b.jsx b/crates/lingui_extractor/tests/sourcemapped/b.jsx new file mode 100644 index 0000000..f4fbcaf --- /dev/null +++ b/crates/lingui_extractor/tests/sourcemapped/b.jsx @@ -0,0 +1 @@ +i18n.t('Message from file b.jsx') diff --git a/crates/lingui_extractor/tests/sourcemapped/c.jsx b/crates/lingui_extractor/tests/sourcemapped/c.jsx new file mode 100644 index 0000000..445dc18 --- /dev/null +++ b/crates/lingui_extractor/tests/sourcemapped/c.jsx @@ -0,0 +1 @@ +i18n.t('Message from file c.jsx') diff --git a/crates/lingui_extractor/tests/sourcemapped/index.jsx b/crates/lingui_extractor/tests/sourcemapped/index.jsx new file mode 100644 index 0000000..df9bc0f --- /dev/null +++ b/crates/lingui_extractor/tests/sourcemapped/index.jsx @@ -0,0 +1,3 @@ +import './a.jsx' +import './b.jsx' +import './c.jsx' diff --git a/crates/lingui_extractor/tests/sourcemapped/out/index.js b/crates/lingui_extractor/tests/sourcemapped/out/index.js new file mode 100644 index 0000000..e2f060e --- /dev/null +++ b/crates/lingui_extractor/tests/sourcemapped/out/index.js @@ -0,0 +1,12 @@ +"use strict"; +(() => { + // a.jsx + i18n.t("Message from file a.jsx"); + + // b.jsx + i18n.t("Message from file b.jsx"); + + // c.jsx + i18n.t("Message from file c.jsx"); +})(); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vYS5qc3giLCAiLi4vYi5qc3giLCAiLi4vYy5qc3giXSwKICAic291cmNlc0NvbnRlbnQiOiBbImkxOG4udCgnTWVzc2FnZSBmcm9tIGZpbGUgYS5qc3gnKVxuIiwgImkxOG4udCgnTWVzc2FnZSBmcm9tIGZpbGUgYi5qc3gnKVxuIiwgImkxOG4udCgnTWVzc2FnZSBmcm9tIGZpbGUgYy5qc3gnKVxuIl0sCiAgIm1hcHBpbmdzIjogIjs7O0FBQUEsT0FBSyxFQUFFLHlCQUF5Qjs7O0FDQWhDLE9BQUssRUFBRSx5QkFBeUI7OztBQ0FoQyxPQUFLLEVBQUUseUJBQXlCOyIsCiAgIm5hbWVzIjogW10KfQo= diff --git a/packages/lingui-swc/.editorconfig b/packages/lingui-swc/.editorconfig new file mode 100644 index 0000000..182fda2 --- /dev/null +++ b/packages/lingui-swc/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors or IDEs +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/lingui-swc/.gitattributes b/packages/lingui-swc/.gitattributes new file mode 100644 index 0000000..9a6a0d4 --- /dev/null +++ b/packages/lingui-swc/.gitattributes @@ -0,0 +1,18 @@ +# Auto detect text files and perform LF normalization +* text=auto + + +*.ts text eol=lf merge=union +*.tsx text eol=lf merge=union +*.rs text eol=lf merge=union +*.js text eol=lf merge=union +*.json text eol=lf merge=union +*.debug text eol=lf merge=union + +# Generated codes +index.js linguist-detectable=false +index.d.ts linguist-detectable=false +lingui-swc.wasi-browser.js linguist-detectable=false +lingui-swc.wasi.cjs linguist-detectable=false +wasi-worker-browser.mjs linguist-detectable=false +wasi-worker.mjs linguist-detectable=false diff --git a/packages/lingui-swc/.gitignore b/packages/lingui-swc/.gitignore new file mode 100644 index 0000000..60b522b --- /dev/null +++ b/packages/lingui-swc/.gitignore @@ -0,0 +1,130 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# End of https://www.toptal.com/developers/gitignore/api/node + + +#Added by cargo + +/target +Cargo.lock + +*.node +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +/npm \ No newline at end of file diff --git a/packages/lingui-swc/.prettierignore b/packages/lingui-swc/.prettierignore new file mode 100644 index 0000000..901e6b6 --- /dev/null +++ b/packages/lingui-swc/.prettierignore @@ -0,0 +1,8 @@ +target +.yarn +index.js +package-template.wasi-browser.js +package-template.wasi.cjs +wasi-worker-browser.mjs +wasi-worker.mjs +.yarnrc.yml \ No newline at end of file diff --git a/packages/lingui-swc/.taplo.toml b/packages/lingui-swc/.taplo.toml new file mode 100644 index 0000000..b2d27a7 --- /dev/null +++ b/packages/lingui-swc/.taplo.toml @@ -0,0 +1,7 @@ +exclude = ["node_modules/**/*.toml"] + +# https://taplo.tamasfe.dev/configuration/formatter-options.html +[formatting] +align_entries = true +indent_tables = true +reorder_keys = true diff --git a/packages/lingui-swc/Cargo.toml b/packages/lingui-swc/Cargo.toml new file mode 100644 index 0000000..5e5f57c --- /dev/null +++ b/packages/lingui-swc/Cargo.toml @@ -0,0 +1,24 @@ +[package] +authors = ["Timofei Iatsenko"] +edition = "2021" +name = "lingui_swc" +version = "0.1.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +napi = "3.0.0" +napi-derive = "3.0.0" +lingui_extractor = { path = "../../crates/lingui_extractor" } +lingui_macro = { path = "../../crates/lingui_macro" } +swc_core = { workspace = true, features = ["base_node", "swc_config", "ecma_codegen", "ecma_transforms"] } +swc_sourcemap = "10.0.2" +data-encoding = "2.3.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +anyhow = "1.0.100" +rayon = "1.10" + +[build-dependencies] +napi-build = "2" diff --git a/packages/lingui-swc/README.md b/packages/lingui-swc/README.md new file mode 100644 index 0000000..a6e478f --- /dev/null +++ b/packages/lingui-swc/README.md @@ -0,0 +1,118 @@ +# `lingui-swc` + +LinguiJS utils based on SWC Platform + +## Low level extraction methods + +- `extractMessagesFromFiles` +- `extractMessages` + +Check TypeScript types for more info. + +## Lingui Extractor Plugin + +This will add rust based extractor implementation to your existing lingui setup. + +:::note +To achieve better performance you need to disable multithreading support on the lingui cli side using `--workers 1`. +::: + +```ts +import {createSwcExtractor} from 'lingui-swc' + +// lingui.config.ts +defineConfig({ + extractors: [createSwcExtractor()], +}) +``` + +`createSwcExtractor()` accepts extractor options: + +```ts +export type ExtractorOptions = { + /** + * The same options as in `jsc.parser` in `.swcrc` + * https://swc.rs/docs/configuration/compilation#jscparser + * + * The syntax (ecmascript/typescript) and jsx support is automatically inferred from the filename, + * you don't need to specify it manually + */ + parser?: ParserConfig + /** + * Options for Lingui Macro + * + * Except of `descriptorFields` property which is always set to `All` in extraction + */ + macro?: Omit +} +``` + +In most of the cases you don't need to specify anything, unless you use some non-standard parser features or has a custom +configuration for macro itself. The macro options automatically inferred from your Lingui Config. + +## Transform + +A native Lingui macro transformer that can be used as a standalone alternative to a full SWC or Babel setup. + +It is a minimal SWC setup with the Lingui macro transform baked into a single native binary. It skips the SWC plugin system overhead and omits all other SWC transforms — only Lingui macros are processed, everything else is emitted as-is. + +This is useful when you have a custom build pipeline (e.g. esbuild, Rollup, or a dev server) and only need to transform Lingui macros without pulling in the full SWC or Babel toolchain. + +```ts +import { transform } from 'lingui-swc' + +const result = await transform( + `import { t } from '@lingui/core/macro'; +const msg = t\`Hello world\`;`, + 'app.tsx' +) + +console.log(result.code) +// => transformed code with Lingui macros compiled to runtime calls +console.log(result.map) +// => source map JSON string +``` + +The `transform` function accepts an optional third argument with options: + +```ts +import { transform, type TransformOptions } from 'lingui-swc' + +const options: TransformOptions = { + // SWC parser config (auto-inferred from filename by default) + parser: { syntax: 'typescript', tsx: true }, + // Lingui macro options + macro: { + runtimeModules: { + i18n: ['@lingui/core', 'i18n'], + Trans: ['@lingui/react', 'Trans'], + useLingui: ['@lingui/react', 'useLingui'], + }, + }, + // External source map JSON string (inline source maps are used if not provided) + sourceMap: '...', +} + +const result = await transform(code, 'app.tsx', options) +``` + +Here is a benchmark results for native transformer (lower value - better): + +``` +══════════════════════════════════════════════════════════════ + Lingui Benchmark — Preset: medium + 1000 files · 10.0k messages · 5 locales + Node v24.13.1 · darwin arm64 + Apple M3 Max (16 cores) +══════════════════════════════════════════════════════════════ + +Running: Macro Transform... + +Babel █████████████████████████ 1.57s 636 files/s ±10.9% +SWC ██░░░░░░░░░░░░░░░░░░░░░░░ 143ms 6996 files/s +native transformer █░░░░░░░░░░░░░░░░░░░░░░░░ 54ms 18.6k files/s ⚡ + +Summary: +native transformer is 29.3x faster than Babel +native transformer is 2.7x faster than SWC +``` diff --git a/packages/lingui-swc/__test__/__snapshots__/index.spec.ts.snap b/packages/lingui-swc/__test__/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000..802e4d2 --- /dev/null +++ b/packages/lingui-swc/__test__/__snapshots__/index.spec.ts.snap @@ -0,0 +1,70 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`extractMessagesFromFiles > should extract from multiple files 1`] = ` +[ + { + "id": "1nGWAC", + "message": "Hello world", + "origin": { + "column": 19, + "filename": "file1.js", + "line": 3, + }, + "placeholders": {}, + }, + { + "id": "cs9Rfg", + "message": "Goodbye, {name}!", + "origin": { + "column": 19, + "filename": "file1.js", + "line": 4, + }, + "placeholders": { + "name": "name", + }, + }, + { + "id": "user-greeting", + "message": "Hello {userName}", + "origin": { + "column": 33, + "filename": "file2.tsx", + "line": 7, + }, + "placeholders": { + "userName": "userName", + }, + }, + { + "id": "Vw8l6h", + "message": "An error occurred", + "origin": { + "column": 11, + "filename": "file3.ts", + "line": 9, + }, + "placeholders": {}, + }, + { + "id": "welcome", + "message": "Welcome to our app", + "origin": { + "column": 27, + "filename": "file2.tsx", + "line": 6, + }, + "placeholders": {}, + }, + { + "id": "x5qT5N", + "message": "Operation completed successfully", + "origin": { + "column": 13, + "filename": "file3.ts", + "line": 10, + }, + "placeholders": {}, + }, +] +`; diff --git a/packages/lingui-swc/__test__/fixtures/file1.js b/packages/lingui-swc/__test__/fixtures/file1.js new file mode 100644 index 0000000..6a81930 --- /dev/null +++ b/packages/lingui-swc/__test__/fixtures/file1.js @@ -0,0 +1,4 @@ +import {t} from '@lingui/core/macro'; + +const greeting = t`Hello world`; +const farewell = t`Goodbye, ${name}!`; diff --git a/packages/lingui-swc/__test__/fixtures/file2.tsx b/packages/lingui-swc/__test__/fixtures/file2.tsx new file mode 100644 index 0000000..a5edefe --- /dev/null +++ b/packages/lingui-swc/__test__/fixtures/file2.tsx @@ -0,0 +1,10 @@ +import {Trans} from '@lingui/react/macro'; + +function Component() { + return ( +
+ Welcome to our app + Hello {userName} +
+ ); +} diff --git a/packages/lingui-swc/__test__/fixtures/file3.ts b/packages/lingui-swc/__test__/fixtures/file3.ts new file mode 100644 index 0000000..1e62aa0 --- /dev/null +++ b/packages/lingui-swc/__test__/fixtures/file3.ts @@ -0,0 +1,11 @@ +import {t} from '@lingui/core/macro'; + +type User = { + name: string; + age: number; +}; + +const messages = { + error: t`An error occurred`, + success: t`Operation completed successfully`, +}; diff --git a/packages/lingui-swc/__test__/index.spec.ts b/packages/lingui-swc/__test__/index.spec.ts new file mode 100644 index 0000000..7049105 --- /dev/null +++ b/packages/lingui-swc/__test__/index.spec.ts @@ -0,0 +1,147 @@ +import {extractMessages, extractMessagesFromFiles} from '../src-js/index' +import {describe, expect, test} from 'vitest' +import path from 'path' + +test('test bindings', async () => { + const code = ` + import {t} from '@lingui/core/macro'; + t\`Hello world\`; + ` + + const result = await extractMessages(code, 'test.js') + + // Check that we have no warnings + // t.is(result.warnings.length, 0, `Expected no warnings, got: ${JSON.stringify(result.warnings)}`) + + expect(result).toMatchInlineSnapshot(` + { + "messages": [ + { + "id": "1nGWAC", + "message": "Hello world", + "origin": { + "column": 4, + "filename": "test.js", + "line": 3, + }, + "placeholders": {}, + }, + ], + "warnings": [], + } + `); +}) + + +test('handle extraction errors gracefully', async () => { + const invalidCode = 'const x = {' + await expect(async () => await extractMessages(invalidCode, 'invalid.js')) + .rejects + .toThrowError("Parse error") +}) + + +test('parser options: should parse jsx', async () => { + const code = `const t =
Hello
` + + await extractMessages(code, 'test.js', { + parser: {jsx: true, syntax: "ecmascript"} + }) +}) + +test('parser options: should parse typescript', async () => { + const code = `type Test = number; const t: Test = 5;` + + await extractMessages(code, 'test.js', { + parser: {tsx: false, syntax: "typescript",} + }) + +}) + +describe('extractMessagesFromFiles', () => { + test('should extract from multiple files', async () => { + const filePaths = [ + path.join(import.meta.dirname, 'fixtures/file1.js'), + path.join(import.meta.dirname, 'fixtures/file2.tsx'), + path.join(import.meta.dirname, 'fixtures/file3.ts'), + ] + + const result = await extractMessagesFromFiles(filePaths, { + parser: {tsx: true, syntax: "typescript"} + }) + + // Sort messages by id for deterministic results + result.messages.sort((a, b) => a.id.localeCompare(b.id)) + + result.messages.forEach((msg) => { + msg.origin!.filename = path.relative(path.join(import.meta.dirname, 'fixtures'), msg.origin?.filename!) + }) + + expect(result.warnings).toHaveLength(0) + expect(result.messages).toHaveLength(6) + expect(result.messages).toMatchSnapshot() + }) + + test('should handle non-existent files gracefully', async () => { + const filePaths = [ + path.join(import.meta.dirname, 'fixtures/file1.js'), + path.join(import.meta.dirname, 'fixtures/non-existent.js'), + path.join(import.meta.dirname, 'fixtures/file3.ts'), + ] + + const result = await extractMessagesFromFiles(filePaths, { + parser: {tsx: true, syntax: "typescript"} + }) + + // Sort messages by id for deterministic results + result.messages.sort((a, b) => a.id.localeCompare(b.id)) + + expect(result.warnings.length).toBeGreaterThan(0) + expect(result.warnings.some(w => w.includes('non-existent.js'))).toBe(true) + expect(result.messages.length).toBeGreaterThan(0) // Should still have messages from valid files + }) + + test('should handle empty array', async () => { + const result = await extractMessagesFromFiles([]) + + expect(result.messages).toHaveLength(0) + expect(result.warnings).toHaveLength(0) + }) + + test('should work with different parser options', async () => { + const filePaths = [ + path.join(import.meta.dirname, 'fixtures/file1.js'), + ] + + const result = await extractMessagesFromFiles(filePaths, { + parser: {syntax: "ecmascript"} + }) + + expect(result.messages.length).toBeGreaterThan(0) + expect(result.warnings).toHaveLength(0) + }) + + test('messages should include origin with filename', async () => { + const filePaths = [ + path.join(import.meta.dirname, 'fixtures/file1.js'), + path.join(import.meta.dirname, 'fixtures/file3.ts'), + ] + + const result = await extractMessagesFromFiles(filePaths, { + parser: {syntax: "typescript"} + }) + + // Sort messages by id for deterministic results + result.messages.sort((a, b) => a.id.localeCompare(b.id)) + + expect(result.messages.length).toBeGreaterThan(0) + + // Check that all messages have origin with filename + result.messages.forEach(msg => { + expect(msg.origin).toBeDefined() + expect(msg.origin?.filename).toBeDefined() + expect(msg.origin?.filename).toMatch(/file[13]\.(js|ts)$/) + }) + }) +}) + diff --git a/packages/lingui-swc/__test__/package.json b/packages/lingui-swc/__test__/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/packages/lingui-swc/__test__/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/lingui-swc/__test__/transform.spec.ts b/packages/lingui-swc/__test__/transform.spec.ts new file mode 100644 index 0000000..486a8b7 --- /dev/null +++ b/packages/lingui-swc/__test__/transform.spec.ts @@ -0,0 +1,182 @@ +import {transform} from '../src-js/index' +import {describe, expect, test} from 'vitest' + +describe('transform', () => { + test('transforms t`` macro', async () => { + const code = ` +import { t } from '@lingui/core/macro'; +const msg = t\`Hello world\`; +` + const result = await transform(code, 'test.ts') + + expect(result.code).toMatchInlineSnapshot(` + "import { i18n as $_i18n } from "@lingui/core"; + const msg = $_i18n._(/*i18n*/ { + id: "1nGWAC", + message: "Hello world" + }); + " + `) + expect(result.map).toBeDefined() + }) + + test('transforms Trans JSX component', async () => { + const code = ` +import { Trans } from '@lingui/react/macro'; +const App = () => Hello world; +` + const result = await transform(code, 'app.tsx') + + expect(result.code).toMatchInlineSnapshot(` + "import { Trans as Trans_ } from "@lingui/react"; + const App = ()=>; + " + `) + expect(result.map).toBeDefined() + }) + + test('keeps non-lingui code unchanged', async () => { + const code = ` +import { useState } from 'react'; +const App = () => { + const [count, setCount] = useState(0); + return
{count}
; +}; +` + const result = await transform(code, 'app.tsx') + + expect(result.code).toMatchInlineSnapshot(` + "import { useState } from 'react'; + const App = ()=>{ + const [count, setCount] = useState(0); + return
{count}
; + }; + " + `) + expect(result.map).toBeDefined() + }) + + test('infers parser from .tsx filename', async () => { + const code = ` +import { Trans } from '@lingui/react/macro'; +type Props = { name: string }; +const Greet = (props: Props) => Hello {props.name}; +` + const result = await transform(code, 'Greet.tsx') + + expect(result.code).toMatchInlineSnapshot(` + "import { Trans as Trans_ } from "@lingui/react"; + type Props = { + name: string; + }; + const Greet = (props: Props)=>; + " + `) + expect(result.code).not.toContain('@lingui/react/macro') + expect(result.code).toContain('props.name') + }) + + test('infers parser from .js filename (no type annotations)', async () => { + const code = ` +import { t } from '@lingui/core/macro'; +const msg = t\`Hello\`; +` + const result = await transform(code, 'app.js') + + expect(result.code).not.toContain('@lingui/core/macro') + }) + + test('returns valid source map JSON', async () => { + const code = ` +import { t } from '@lingui/core/macro'; +const msg = t\`Hello\`; +` + const result = await transform(code, 'test.ts') + + expect(result.map).toBeDefined() + const map = JSON.parse(result.map!) + expect(map.version).toBe(3) + expect(map.sources).toContain('test.ts') + expect(map.sourcesContent).toBeDefined() + expect(map.sourcesContent[0]).toBe(code) + }) + + test('handles inline source maps', async () => { + const originalCode = `import { t } from '@lingui/core/macro';\nconst msg = t\`Hi\`;\n` + const fakeMap = JSON.stringify({ + version: 3, + sources: ['original.ts'], + names: [], + mappings: 'AAAA;AACA', + sourcesContent: ['// original source'] + }) + const base64Map = Buffer.from(fakeMap).toString('base64') + const codeWithInlineMap = originalCode + `//# sourceMappingURL=data:application/json;base64,${base64Map}\n` + + const result = await transform(codeWithInlineMap, 'test.ts') + + expect(result.code).not.toContain('@lingui/core/macro') + expect(result.map).toBeDefined() + const outputMap = JSON.parse(result.map!) + expect(outputMap.sources).toContain('original.ts') + }) + + + test('sourceMaps: true returns map in result', async () => { + const code = ` +import { t } from '@lingui/core/macro'; +const msg = t\`Hello\`; +` + const result = await transform(code, 'test.ts', {sourceMaps: true}) + + expect(result.map).toBeDefined() + const map = JSON.parse(result.map!) + expect(map.version).toBe(3) + expect(result.code).not.toContain('sourceMappingURL') + }) + + test('sourceMaps: false disables source maps', async () => { + const code = ` +import { t } from '@lingui/core/macro'; +const msg = t\`Hello\`; +` + const result = await transform(code, 'test.ts', {sourceMaps: false}) + + expect(result.map).toBeUndefined() + expect(result.code).not.toContain('sourceMappingURL') + }) + + test('sourceMaps: "inline" appends source map to code', async () => { + const code = ` +import { t } from '@lingui/core/macro'; +const msg = t\`Hello\`; +` + const result = await transform(code, 'test.ts', {sourceMaps: "inline"}) + + expect(result.map).toBeUndefined() + expect(result.code).toContain('//# sourceMappingURL=data:application/json;charset=utf-8;base64,') + + // extract and verify the inline map + const match = result.code.match(/sourceMappingURL=data:application\/json;charset=utf-8;base64,(.+)/) + expect(match).toBeTruthy() + const decoded = JSON.parse(Buffer.from(match![1], 'base64').toString()) + expect(decoded.version).toBe(3) + expect(decoded.sources).toContain('test.ts') + }) + + test('throws on parse errors', async () => { + const code = 'const x = {' + + await expect(transform(code, 'broken.ts')) + .rejects.toThrowError('Parse error') + }) +}) diff --git a/packages/lingui-swc/__test__/tsconfig.json b/packages/lingui-swc/__test__/tsconfig.json new file mode 100644 index 0000000..f36973a --- /dev/null +++ b/packages/lingui-swc/__test__/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "outDir": "lib", + "rootDir": "." + }, + "include": ["*.ts"], + "exclude": ["lib"] +} diff --git a/packages/lingui-swc/binding.d.ts b/packages/lingui-swc/binding.d.ts new file mode 100644 index 0000000..6cc25af --- /dev/null +++ b/packages/lingui-swc/binding.d.ts @@ -0,0 +1,89 @@ +/* auto-generated by NAPI-RS */ +/* eslint-disable */ +/** A message extracted from source code */ +export interface ExtractedMessage { + id: string + message?: string + context?: string + comment?: string + placeholders: Record + origin?: Origin +} + +/** Result of message extraction containing messages and any warnings */ +export interface ExtractionResult { + messages: Array + warnings: Array +} + +/** Represents the location where a message was found */ +export interface Origin { + filename: string + line: number + column?: number +} +/** + * Extract messages from source code + * + * This function parses JavaScript/TypeScript code and extracts internationalization + * messages from lingui macro calls and components. + * + * # Arguments + * + * * `source_code` - The source code to analyze + * * `filename` - The filename (used for error reporting and source maps) + * + * # Returns + * + * A Promise that resolves to an ExtractionResult containing: + * * `messages` - Array of extracted messages + * * `warnings` - Array of warning messages encountered during extraction + * + * # Example + * + * ```javascript + * const result = await extractMessages(sourceCode, 'app.tsx'); + * console.log(result.messages); + * ``` + */ +export declare function extractMessages(sourceCode: string, filename: string, options: Buffer): Promise + +/** + * Extract messages from multiple files in parallel + * + * This function reads multiple files and extracts internationalization + * messages from all of them in parallel using all available CPU cores. + * + * # Arguments + * + * * `file_paths` - Array of file paths to process + * * `options` - Extraction options (parser configuration) + * + * # Returns + * + * A Promise that resolves to an ExtractionResult containing: + * * `messages` - Array of all extracted messages from all files + * * `warnings` - Array of warning messages (including file read errors) + * + * # Example + * + * ```javascript + * const result = await extractMessagesFromFiles(['app.tsx', 'components/Header.tsx']); + * console.log(result.messages); + * ``` + */ +export declare function extractMessagesFromFiles(filePaths: Array, options: Buffer): Promise + +export declare function transform(code: string, filename: string, options?: Buffer | undefined | null): Promise + +export interface TransformResult { + code: string + map?: string +} +export interface TransformOutput { + code: string + map?: string + output?: string + extractedComments?: Array + diagnostics: Array +} diff --git a/packages/lingui-swc/binding.js b/packages/lingui-swc/binding.js new file mode 100644 index 0000000..a0c6078 --- /dev/null +++ b/packages/lingui-swc/binding.js @@ -0,0 +1,581 @@ +// prettier-ignore +/* eslint-disable */ +// @ts-nocheck +/* auto-generated by NAPI-RS */ + +const { readFileSync } = require('node:fs') +let nativeBinding = null +const loadErrors = [] + +const isMusl = () => { + let musl = false + if (process.platform === 'linux') { + musl = isMuslFromFilesystem() + if (musl === null) { + musl = isMuslFromReport() + } + if (musl === null) { + musl = isMuslFromChildProcess() + } + } + return musl +} + +const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-') + +const isMuslFromFilesystem = () => { + try { + return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl') + } catch { + return null + } +} + +const isMuslFromReport = () => { + let report = null + if (typeof process.report?.getReport === 'function') { + process.report.excludeNetwork = true + report = process.report.getReport() + } + if (!report) { + return null + } + if (report.header && report.header.glibcVersionRuntime) { + return false + } + if (Array.isArray(report.sharedObjects)) { + if (report.sharedObjects.some(isFileMusl)) { + return true + } + } + return false +} + +const isMuslFromChildProcess = () => { + try { + return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl') + } catch (e) { + // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false + return false + } +} + +function requireNative() { + if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) { + try { + return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH); + } catch (err) { + loadErrors.push(err) + } + } else if (process.platform === 'android') { + if (process.arch === 'arm64') { + try { + return require('./lingui-swc.android-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-android-arm64') + const bindingPackageVersion = require('lingui-swc-android-arm64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 'arm') { + try { + return require('./lingui-swc.android-arm-eabi.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-android-arm-eabi') + const bindingPackageVersion = require('lingui-swc-android-arm-eabi/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`)) + } + } else if (process.platform === 'win32') { + if (process.arch === 'x64') { + if (process.config?.variables?.shlib_suffix === 'dll.a' || process.config?.variables?.node_target_type === 'shared_library') { + try { + return require('./lingui-swc.win32-x64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-win32-x64-gnu') + const bindingPackageVersion = require('lingui-swc-win32-x64-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + try { + return require('./lingui-swc.win32-x64-msvc.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-win32-x64-msvc') + const bindingPackageVersion = require('lingui-swc-win32-x64-msvc/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } + } else if (process.arch === 'ia32') { + try { + return require('./lingui-swc.win32-ia32-msvc.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-win32-ia32-msvc') + const bindingPackageVersion = require('lingui-swc-win32-ia32-msvc/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 'arm64') { + try { + return require('./lingui-swc.win32-arm64-msvc.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-win32-arm64-msvc') + const bindingPackageVersion = require('lingui-swc-win32-arm64-msvc/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`)) + } + } else if (process.platform === 'darwin') { + try { + return require('./lingui-swc.darwin-universal.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-darwin-universal') + const bindingPackageVersion = require('lingui-swc-darwin-universal/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + if (process.arch === 'x64') { + try { + return require('./lingui-swc.darwin-x64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-darwin-x64') + const bindingPackageVersion = require('lingui-swc-darwin-x64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 'arm64') { + try { + return require('./lingui-swc.darwin-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-darwin-arm64') + const bindingPackageVersion = require('lingui-swc-darwin-arm64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`)) + } + } else if (process.platform === 'freebsd') { + if (process.arch === 'x64') { + try { + return require('./lingui-swc.freebsd-x64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-freebsd-x64') + const bindingPackageVersion = require('lingui-swc-freebsd-x64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 'arm64') { + try { + return require('./lingui-swc.freebsd-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-freebsd-arm64') + const bindingPackageVersion = require('lingui-swc-freebsd-arm64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`)) + } + } else if (process.platform === 'linux') { + if (process.arch === 'x64') { + if (isMusl()) { + try { + return require('./lingui-swc.linux-x64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-x64-musl') + const bindingPackageVersion = require('lingui-swc-linux-x64-musl/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + try { + return require('./lingui-swc.linux-x64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-x64-gnu') + const bindingPackageVersion = require('lingui-swc-linux-x64-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } + } else if (process.arch === 'arm64') { + if (isMusl()) { + try { + return require('./lingui-swc.linux-arm64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-arm64-musl') + const bindingPackageVersion = require('lingui-swc-linux-arm64-musl/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + try { + return require('./lingui-swc.linux-arm64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-arm64-gnu') + const bindingPackageVersion = require('lingui-swc-linux-arm64-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } + } else if (process.arch === 'arm') { + if (isMusl()) { + try { + return require('./lingui-swc.linux-arm-musleabihf.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-arm-musleabihf') + const bindingPackageVersion = require('lingui-swc-linux-arm-musleabihf/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + try { + return require('./lingui-swc.linux-arm-gnueabihf.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-arm-gnueabihf') + const bindingPackageVersion = require('lingui-swc-linux-arm-gnueabihf/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } + } else if (process.arch === 'loong64') { + if (isMusl()) { + try { + return require('./lingui-swc.linux-loong64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-loong64-musl') + const bindingPackageVersion = require('lingui-swc-linux-loong64-musl/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + try { + return require('./lingui-swc.linux-loong64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-loong64-gnu') + const bindingPackageVersion = require('lingui-swc-linux-loong64-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } + } else if (process.arch === 'riscv64') { + if (isMusl()) { + try { + return require('./lingui-swc.linux-riscv64-musl.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-riscv64-musl') + const bindingPackageVersion = require('lingui-swc-linux-riscv64-musl/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + try { + return require('./lingui-swc.linux-riscv64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-riscv64-gnu') + const bindingPackageVersion = require('lingui-swc-linux-riscv64-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } + } else if (process.arch === 'ppc64') { + try { + return require('./lingui-swc.linux-ppc64-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-ppc64-gnu') + const bindingPackageVersion = require('lingui-swc-linux-ppc64-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 's390x') { + try { + return require('./lingui-swc.linux-s390x-gnu.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-linux-s390x-gnu') + const bindingPackageVersion = require('lingui-swc-linux-s390x-gnu/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`)) + } + } else if (process.platform === 'openharmony') { + if (process.arch === 'arm64') { + try { + return require('./lingui-swc.openharmony-arm64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-openharmony-arm64') + const bindingPackageVersion = require('lingui-swc-openharmony-arm64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 'x64') { + try { + return require('./lingui-swc.openharmony-x64.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-openharmony-x64') + const bindingPackageVersion = require('lingui-swc-openharmony-x64/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else if (process.arch === 'arm') { + try { + return require('./lingui-swc.openharmony-arm.node') + } catch (e) { + loadErrors.push(e) + } + try { + const binding = require('lingui-swc-openharmony-arm') + const bindingPackageVersion = require('lingui-swc-openharmony-arm/package.json').version + if (bindingPackageVersion !== '0.9.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.9.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + } + return binding + } catch (e) { + loadErrors.push(e) + } + } else { + loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`)) + } + } else { + loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`)) + } +} + +nativeBinding = requireNative() + +if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { + let wasiBinding = null + let wasiBindingError = null + try { + wasiBinding = require('./lingui-swc.wasi.cjs') + nativeBinding = wasiBinding + } catch (err) { + if (process.env.NAPI_RS_FORCE_WASI) { + wasiBindingError = err + } + } + if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { + try { + wasiBinding = require('lingui-swc-wasm32-wasi') + nativeBinding = wasiBinding + } catch (err) { + if (process.env.NAPI_RS_FORCE_WASI) { + if (!wasiBindingError) { + wasiBindingError = err + } else { + wasiBindingError.cause = err + } + loadErrors.push(err) + } + } + } + if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) { + const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error') + error.cause = wasiBindingError + throw error + } +} + +if (!nativeBinding) { + if (loadErrors.length > 0) { + throw new Error( + `Cannot find native binding. ` + + `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` + + 'Please try `npm i` again after removing both package-lock.json and node_modules directory.', + { + cause: loadErrors.reduce((err, cur) => { + cur.cause = err + return cur + }), + }, + ) + } + throw new Error(`Failed to load native binding`) +} + +module.exports = nativeBinding +module.exports.extractMessages = nativeBinding.extractMessages +module.exports.extractMessagesFromFiles = nativeBinding.extractMessagesFromFiles +module.exports.transform = nativeBinding.transform diff --git a/packages/lingui-swc/build.rs b/packages/lingui-swc/build.rs new file mode 100644 index 0000000..bbfc9e4 --- /dev/null +++ b/packages/lingui-swc/build.rs @@ -0,0 +1,3 @@ +fn main() { + napi_build::setup(); +} diff --git a/packages/lingui-swc/package.json b/packages/lingui-swc/package.json new file mode 100644 index 0000000..2cf24bb --- /dev/null +++ b/packages/lingui-swc/package.json @@ -0,0 +1,81 @@ +{ + "name": "lingui-swc", + "version": "0.10.0", + "description": "Lingui Extractor Implementation using Rust and SWC", + "license": "MIT", + "repository": { + "url": "https://github.com/timofei-iatsenko/lingui-rust-tools.git", + "directory": "packages/lingui-swc" + }, + "keywords": [ + "napi-rs", + "NAPI", + "N-API", + "Rust", + "node-addon", + "node-addon-api" + ], + "files": [ + "binding.d.ts", + "binding.js", + "dist" + ], + "exports": { + ".": "./dist/index.js" + }, + "napi": { + "binaryName": "lingui-swc", + "targets": [ + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "aarch64-pc-windows-msvc", + "aarch64-linux-android" + ] + }, + "engines": { + "node": ">= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "access": "public" + }, + "scripts": { + "artifacts": "napi artifacts", + "bench": "node --import @oxc-node/core/register benchmark/bench.ts", + "build": "napi build --platform --release --js ./binding.js --dts ./binding.d.ts", + "build:ts": "tsc", + "build:debug": "napi build --platform", + "format": "run-p format:prettier format:rs format:toml", + "format:prettier": "prettier . -w", + "format:toml": "taplo format", + "format:rs": "cargo fmt", + "lint": "oxlint .", + "prepublishOnly": "yarn build:ts && napi prepublish -t npm --no-gh-release", + "test": "vitest run", + "preversion": "napi build --platform && git add .", + "version": "napi version" + }, + "dependencies": { + "@lingui/conf": "^6.0.1", + "@swc/types": "^0.1.25" + }, + "devDependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@napi-rs/cli": "^3.5.1", + "@oxc-node/core": "^0.0.35", + "@taplo/cli": "^0.7.0", + "@tybys/wasm-util": "^0.10.0", + "@types/node": "^24.1.0", + "oxlint": "^1.14.0", + "prettier": "^3.6.2", + "tinybench": "^6.0.0", + "typescript": "^5.9.3", + "vitest": "^4.0.18" + } +} diff --git a/packages/lingui-swc/rustfmt.toml b/packages/lingui-swc/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/packages/lingui-swc/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/packages/lingui-swc/src-js/index.ts b/packages/lingui-swc/src-js/index.ts new file mode 100644 index 0000000..708d8e8 --- /dev/null +++ b/packages/lingui-swc/src-js/index.ts @@ -0,0 +1,156 @@ +import binding = require('../binding') +import type {ParserConfig} from "@swc/types" +import type {ExtractedMessage, ExtractorCtx, ExtractorType} from "@lingui/conf" +import {LinguiMacroOptions, mapOptions} from "./macro-src/map-options" + +export type {LinguiMacroOptions}; + +export type TransformOptions = { + /** + * The same options as in `jsc.parser` in `.swcrc` + * https://swc.rs/docs/configuration/compilation#jscparser + * + * The syntax (ecmascript/typescript) and jsx support is automatically inferred from the filename, + * you don't need to specify it manually + */ + parser?: ParserConfig + /** + * Options for Lingui Macro + */ + macro?: LinguiMacroOptions + /** + * Controls source map generation: + * - `true` (default) — source map returned in `result.map` + * - `"inline"` — source map appended to code as a base64 data URL, `result.map` is undefined + * - `false` — no source map generated, `result.map` is undefined + */ + sourceMaps?: "inline" | boolean +} + +export type TransformResult = { + code: string + map?: string +} + +/** + * Transform source code by applying the Lingui macro transformation. + * + * This is a minimal SWC + Lingui transformer built as a single native library + * for optimal performance. It only transforms Lingui macros and keeps everything + * else as-is. + * + * Parser options are automatically inferred from the filename (.ts, .tsx, .js, .jsx, etc.) + * + * @param code - The source code to transform + * @param filename - The filename (used for parser inference and source maps) + * @param options - Optional transform options + * @returns Promise resolving to transformed code and source map + */ +export function transform(code: string, filename: string, options?: TransformOptions): Promise { + return binding.transform(code, filename, options ? toBuffer(options) : undefined) +} + +export type ExtractorOptions = { + /** + * The same options as in `jsc.parser` in `.swcrc` + * https://swc.rs/docs/configuration/compilation#jscparser + * + * The syntax (ecmascript/typescript) and jsx support is automatically inferred from the filename, + * you don't need to specify it manually + */ + parser?: ParserConfig + /** + * Options for Lingui Macro + * + * Except of `descriptorFields` property which is always set to `All` in extraction + */ + macro?: Omit +} + +function toBuffer(t: any): Buffer { + return Buffer.from(JSON.stringify(t)); +} + +export function extractMessages(sourceCode: string, filename: string, options?: ExtractorOptions) { + return binding.extractMessages(sourceCode, filename, toBuffer(options || {})) +} + +export function extractMessagesFromFiles(filePaths: string[], options?: ExtractorOptions) { + return binding.extractMessagesFromFiles(filePaths, toBuffer(options || {})) +} + +const mapMessage = (msg: binding.ExtractedMessage): ExtractedMessage => { + return { + id: msg.id, + origin: msg.origin + ? [msg.origin.filename, msg.origin.line, msg.origin.column] + : undefined, + placeholders: msg.placeholders, + context: msg.context, + comment: msg.comment, + message: msg.message, + }; +} + +/** + * Creates pluggable SWC Lingui Extractor implementation. + * + * Example: + * + * ```ts + * // lingui.config.ts + * defineConfig({ + * extractors: [createSwcExtractor()], + * }) + * ``` + * + * Macro options automatically inherited from the Lingui Config. + */ +export function createSwcExtractor(options: ExtractorOptions = {}): ExtractorType & { + extractFromFiles: ( + filenames: string[], + onMessageExtracted: (msg: ExtractedMessage) => void, + ctx: ExtractorCtx, + ) => Promise +} { + const matchRe = new RegExp( + "\\.(" + + [".ts", ".mts", ".cts", ".tsx", ".js", ".mjs", ".cjs", ".jsx"] + .map((ext) => ext.slice(1)) + .join("|") + + ")$", + "i", + ) + + return { + match(filename) { + return matchRe.test(filename) + }, + + async extract(filename, code, onMessageExtracted, ctx) { + const {messages} = await extractMessages(code, filename, { + ...options, + macro: mapOptions(ctx.linguiConfig) + }) + + messages.forEach((msg) => { + onMessageExtracted(mapMessage(msg)) + }) + }, + + async extractFromFiles(filenames: string[], + onMessageExtracted: (msg: ExtractedMessage) => void, + ctx: ExtractorCtx) { + const {messages} = await extractMessagesFromFiles(filenames, { + ...options, + macro: mapOptions(ctx.linguiConfig) + }) + + messages.forEach((msg) => { + onMessageExtracted(mapMessage(msg)) + }) + } + } +} + + diff --git a/packages/lingui-swc/src-js/macro-src b/packages/lingui-swc/src-js/macro-src new file mode 120000 index 0000000..55a6aca --- /dev/null +++ b/packages/lingui-swc/src-js/macro-src @@ -0,0 +1 @@ +../../lingui-macro/src-js \ No newline at end of file diff --git a/packages/lingui-swc/src/lib.rs b/packages/lingui-swc/src/lib.rs new file mode 100644 index 0000000..7b79507 --- /dev/null +++ b/packages/lingui-swc/src/lib.rs @@ -0,0 +1,165 @@ +#![deny(clippy::all)] + +use anyhow::Result as AnyhowResult; +use lingui_extractor::{ExtractionResult, ExtractorOptions}; +use napi::bindgen_prelude::*; +use napi_derive::napi; +use rayon::prelude::*; +use std::fs; + +use swc_core::node::get_deserialized; + +mod transform; + +/// Task for extracting messages asynchronously +pub struct ExtractMessagesTask { + source_code: String, + filename: String, + pub options: String, +} + +impl Task for ExtractMessagesTask { + type Output = ExtractionResult; + type JsValue = ExtractionResult; + + fn compute(&mut self) -> Result { + let options: ExtractorOptions = get_deserialized(&self.options)?; + + let result = lingui_extractor::extract_messages(&self.source_code, &self.filename, &options) + .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?; + + Ok(result) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +/// Extract messages from source code +/// +/// This function parses JavaScript/TypeScript code and extracts internationalization +/// messages from lingui macro calls and components. +/// +/// # Arguments +/// +/// * `source_code` - The source code to analyze +/// * `filename` - The filename (used for error reporting and source maps) +/// +/// # Returns +/// +/// A Promise that resolves to an ExtractionResult containing: +/// * `messages` - Array of extracted messages +/// * `warnings` - Array of warning messages encountered during extraction +/// +/// # Example +/// +/// ```javascript +/// const result = await extractMessages(sourceCode, 'app.tsx'); +/// console.log(result.messages); +/// ``` +#[napi(ts_return_type = "Promise")] +pub fn extract_messages( + source_code: String, + filename: String, + options: Buffer, +) -> AsyncTask { + let options = String::from_utf8_lossy(options.as_ref()).into_owned(); + + AsyncTask::new(ExtractMessagesTask { + source_code, + filename, + options, + }) +} + +/// Task for extracting messages from multiple files in parallel +pub struct ExtractMessagesFromFilesTask { + file_paths: Vec, + pub options: String, +} + +impl Task for ExtractMessagesFromFilesTask { + type Output = ExtractionResult; + type JsValue = ExtractionResult; + + fn compute(&mut self) -> Result { + let options: ExtractorOptions = get_deserialized(&self.options)?; + + // Process files in parallel using rayon + let results: Vec<(String, AnyhowResult)> = self + .file_paths + .par_iter() + .map(|file_path| { + let result = fs::read_to_string(file_path) + .map_err(|e| anyhow::anyhow!("Failed to read file '{file_path}': {e}")) + .and_then(|source_code| { + lingui_extractor::extract_messages(&source_code, file_path, &options) + .map_err(|e| anyhow::anyhow!("Failed to extract from '{file_path}': {e}")) + }); + + (file_path.clone(), result) + }) + .collect(); + + // Aggregate all messages and warnings + let mut all_messages = Vec::new(); + let mut all_warnings = Vec::new(); + + for (_file_path, result) in results { + match result { + Ok(extraction_result) => { + all_messages.extend(extraction_result.messages); + all_warnings.extend(extraction_result.warnings); + } + Err(err) => { + all_warnings.push(err.to_string()); + } + } + } + + Ok(ExtractionResult { + messages: all_messages, + warnings: all_warnings, + }) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +/// Extract messages from multiple files in parallel +/// +/// This function reads multiple files and extracts internationalization +/// messages from all of them in parallel using all available CPU cores. +/// +/// # Arguments +/// +/// * `file_paths` - Array of file paths to process +/// * `options` - Extraction options (parser configuration) +/// +/// # Returns +/// +/// A Promise that resolves to an ExtractionResult containing: +/// * `messages` - Array of all extracted messages from all files +/// * `warnings` - Array of warning messages (including file read errors) +/// +/// # Example +/// +/// ```javascript +/// const result = await extractMessagesFromFiles(['app.tsx', 'components/Header.tsx']); +/// console.log(result.messages); +/// ``` +#[napi(ts_return_type = "Promise")] +pub fn extract_messages_from_files( + file_paths: Vec, + options: Buffer, +) -> AsyncTask { + let options = String::from_utf8_lossy(options.as_ref()).into_owned(); + + AsyncTask::new(ExtractMessagesFromFilesTask { + file_paths, + options, + }) +} diff --git a/packages/lingui-swc/src/transform.rs b/packages/lingui-swc/src/transform.rs new file mode 100644 index 0000000..cb03f23 --- /dev/null +++ b/packages/lingui-swc/src/transform.rs @@ -0,0 +1,298 @@ +use data_encoding::BASE64; +use lingui_extractor::detect_parser_config; +use lingui_macro::{LinguiJsOptions, LinguiMacroFolder, LinguiOptions}; +use napi::bindgen_prelude::*; +use napi_derive::napi; +use serde::Deserialize; +use swc_core::common::comments::SingleThreadedComments; +use swc_core::common::errors::{DiagnosticBuilder, Handler, HandlerFlags}; +use swc_core::common::{sync::Lrc, BytePos, LineCol}; +use swc_core::common::{FileName, Globals, Mark, SourceMap, GLOBALS}; +use swc_core::ecma::codegen::text_writer::JsWriter; +use swc_core::ecma::codegen::Emitter; +use swc_core::ecma::parser::{Parser, StringInput, Syntax}; +use swc_core::ecma::transforms::base::{fixer, hygiene, resolver}; +use swc_core::ecma::visit::fold_pass; +use swc_sourcemap as sourcemap; + +use std::sync::{Arc, Mutex}; + +struct StringEmitter { + buffer: Arc>, +} + +impl swc_core::common::errors::Emitter for StringEmitter { + fn emit(&mut self, db: &mut DiagnosticBuilder<'_>) { + let msg: String = db + .message + .iter() + .map(|m| m.0.as_str()) + .collect::>() + .join(""); + let mut buf = self.buffer.lock().unwrap(); + if !buf.is_empty() { + buf.push('\n'); + } + buf.push_str(&msg); + } +} + +#[napi(object)] +pub struct TransformResult { + pub code: String, + pub map: Option, +} + +#[derive(Clone, Deserialize)] +#[serde(untagged)] +enum SourceMapsOption { + Bool(bool), + Str(String), +} + +impl Default for SourceMapsOption { + fn default() -> Self { + SourceMapsOption::Bool(true) + } +} + +#[derive(Default, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TransformOptionsInternal { + pub parser: Option, + #[serde(default, rename = "macro")] + pub macro_options: Option, + #[serde(default)] + pub source_maps: SourceMapsOption, +} + +fn extract_inline_sourcemap(source_code: &str) -> Option { + let source_mapping_prefix = "sourceMappingURL="; + + if let Some(idx) = source_code.rfind(source_mapping_prefix) { + let url_part = &source_code[idx + source_mapping_prefix.len()..]; + let url = url_part.lines().next().unwrap_or(url_part).trim(); + + if url.starts_with("data:application/json;base64,") { + if let Some(base64_start) = url.find("base64,") { + let base64_content = &url[base64_start + "base64,".len()..].trim(); + + if let Ok(decoded) = BASE64.decode(base64_content.as_bytes()) { + if let Ok(source_map) = sourcemap::SourceMap::from_slice(&decoded) { + return Some(source_map); + } + } + } + } + } + + None +} + +fn do_transform( + code: &str, + filename: &str, + parser_syntax: Option, + macro_options: Option, + input_source_map: Option, + source_maps: &SourceMapsOption, +) -> std::result::Result { + let error_buffer = Arc::new(Mutex::new(String::new())); + let cm: Lrc = Default::default(); + let comments = SingleThreadedComments::default(); + + let handler = Handler::with_emitter_and_flags( + Box::new(StringEmitter { + buffer: error_buffer.clone(), + }), + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let syntax = detect_parser_config(filename, parser_syntax); + + let source_file = cm.new_source_file(Lrc::new(FileName::Real(filename.into())), code.to_string()); + + let mut parser = Parser::new(syntax, StringInput::from(&*source_file), Some(&comments)); + + let module = parser + .parse_module() + .map_err(|e| format!("Parse error: {e:?}"))?; + + let program = swc_core::ecma::ast::Program::Module(module); + + let lingui_options = match macro_options { + Some(opts) => opts.into_options(""), + None => LinguiOptions::default(), + }; + + let globals = Globals::default(); + + let result = GLOBALS.set(&globals, || { + swc_core::common::errors::HANDLER.set(&handler, || { + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + + let lingui_macro = LinguiMacroFolder::new(lingui_options, Some(&comments), cm.clone()); + + let program = program + .apply(&mut resolver( + unresolved_mark, + top_level_mark, + syntax.typescript(), + )) + .apply(fold_pass(lingui_macro)) + .apply(hygiene::hygiene()) + .apply(fixer::fixer(Some(&comments))); + + let generate_maps = !matches!(source_maps, SourceMapsOption::Bool(false)); + let inline_maps = matches!(source_maps, SourceMapsOption::Str(ref s) if s == "inline"); + + let mut buf = Vec::new(); + let mut src_map_buf: Vec<(BytePos, LineCol)> = Vec::new(); + + { + let mut emitter = Emitter { + cfg: Default::default(), + cm: cm.clone(), + comments: Some(&comments), + wr: JsWriter::new( + cm.clone(), + "\n", + &mut buf, + if generate_maps { + Some(&mut src_map_buf) + } else { + None + }, + ), + }; + emitter + .emit_program(&program) + .map_err(|e| format!("Emit error: {e:?}"))?; + } + + let mut output_code = String::from_utf8(buf).map_err(|e| format!("UTF-8 error: {e:?}"))?; + + if !generate_maps { + return Ok(TransformResult { + code: output_code, + map: None, + }); + } + + let output_source_map = cm.build_source_map( + &src_map_buf, + input_source_map, + CustomSourceMapConfig { filename }, + ); + + if inline_maps { + let data_url = output_source_map + .to_data_url() + .map_err(|e| format!("Source map encode error: {e:?}"))?; + output_code.push_str("\n//# sourceMappingURL="); + output_code.push_str(&data_url); + output_code.push('\n'); + + Ok(TransformResult { + code: output_code, + map: None, + }) + } else { + let mut map_buf = Vec::new(); + output_source_map + .to_writer(&mut map_buf) + .map_err(|e| format!("Source map write error: {e:?}"))?; + + let map_string = + String::from_utf8(map_buf).map_err(|e| format!("Source map UTF-8 error: {e:?}"))?; + + Ok(TransformResult { + code: output_code, + map: Some(map_string), + }) + } + }) + }); + + let errors = error_buffer.lock().unwrap().clone(); + if !errors.is_empty() { + return Err(errors); + } + + result +} + +struct CustomSourceMapConfig<'a> { + filename: &'a str, +} + +impl swc_core::common::source_map::SourceMapGenConfig for CustomSourceMapConfig<'_> { + fn file_name_to_source(&self, _f: &FileName) -> String { + self.filename.to_string() + } + + fn inline_sources_content(&self, _f: &FileName) -> bool { + true + } +} + +pub struct TransformTask { + code: String, + filename: String, + options: String, +} + +impl Task for TransformTask { + type Output = TransformResult; + type JsValue = TransformResult; + + fn compute(&mut self) -> Result { + let options: TransformOptionsInternal = if self.options.is_empty() { + TransformOptionsInternal::default() + } else { + serde_json::from_str(&self.options) + .map_err(|e| Error::new(Status::InvalidArg, format!("Invalid options: {e}")))? + }; + + let input_source_map = if matches!(options.source_maps, SourceMapsOption::Bool(false)) { + None + } else { + extract_inline_sourcemap(&self.code) + }; + + do_transform( + &self.code, + &self.filename, + options.parser, + options.macro_options, + input_source_map, + &options.source_maps, + ) + .map_err(|e| Error::new(Status::GenericFailure, e)) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +#[napi(ts_return_type = "Promise")] +pub fn transform( + code: String, + filename: String, + options: Option, +) -> AsyncTask { + let options = options + .map(|buf| String::from_utf8_lossy(buf.as_ref()).into_owned()) + .unwrap_or_default(); + + AsyncTask::new(TransformTask { + code, + filename, + options, + }) +} diff --git a/packages/lingui-swc/tsconfig.json b/packages/lingui-swc/tsconfig.json new file mode 100644 index 0000000..7de0946 --- /dev/null +++ b/packages/lingui-swc/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "strict": true, + "module": "node20", + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "declaration": true, + "rootDir": "./src-js", + "outDir": "./dist", + "types": [ + "node" + ] + }, + "files": [ + "./src-js/index.ts" + ] +} diff --git a/packages/lingui-swc/vitest.config.ts b/packages/lingui-swc/vitest.config.ts new file mode 100644 index 0000000..e7e10ae --- /dev/null +++ b/packages/lingui-swc/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'node', + globals: false, + }, +}) diff --git a/packages/lingui-swc/wasi-worker-browser.mjs b/packages/lingui-swc/wasi-worker-browser.mjs new file mode 100644 index 0000000..8b1b172 --- /dev/null +++ b/packages/lingui-swc/wasi-worker-browser.mjs @@ -0,0 +1,32 @@ +import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime' + +const handler = new MessageHandler({ + onLoad({ wasmModule, wasmMemory }) { + const wasi = new WASI({ + print: function () { + // eslint-disable-next-line no-console + console.log.apply(console, arguments) + }, + printErr: function() { + // eslint-disable-next-line no-console + console.error.apply(console, arguments) + }, + }) + return instantiateNapiModuleSync(wasmModule, { + childThread: true, + wasi, + overwriteImports(importObject) { + importObject.env = { + ...importObject.env, + ...importObject.napi, + ...importObject.emnapi, + memory: wasmMemory, + } + }, + }) + }, +}) + +globalThis.onmessage = function (e) { + handler.handle(e) +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ab31454..42826a2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.85" -targets = ["wasm32-wasip1"] +channel = "nightly-2025-05-06" +profile = "minimal" components = ["rustfmt", "clippy"] diff --git a/yarn.lock b/yarn.lock index a36d1d2..aaff41e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,7 +5,7 @@ __metadata: version: 8 cacheKey: 10c0 -"@emnapi/core@npm:1.10.0": +"@emnapi/core@npm:1.10.0, @emnapi/core@npm:^1.8.1": version: 1.10.0 resolution: "@emnapi/core@npm:1.10.0" dependencies: @@ -15,7 +15,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/runtime@npm:1.10.0": +"@emnapi/runtime@npm:1.10.0, @emnapi/runtime@npm:^1.8.1": version: 1.10.0 resolution: "@emnapi/runtime@npm:1.10.0" dependencies: @@ -33,19 +33,244 @@ __metadata: languageName: node linkType: hard -"@isaacs/balanced-match@npm:^4.0.1": - version: 4.0.1 - resolution: "@isaacs/balanced-match@npm:4.0.1" - checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 +"@inquirer/ansi@npm:^2.0.5": + version: 2.0.5 + resolution: "@inquirer/ansi@npm:2.0.5" + checksum: 10c0/ad61532e5bb47473e3d987c32d4015499a8ce5f4f86e46467e8e672fc52670beb303905d6b324e453935a61671f59f3b9b1b6a1edbbe1f64085e2bb87735e295 + languageName: node + linkType: hard + +"@inquirer/checkbox@npm:^5.1.5": + version: 5.1.5 + resolution: "@inquirer/checkbox@npm:5.1.5" + dependencies: + "@inquirer/ansi": "npm:^2.0.5" + "@inquirer/core": "npm:^11.1.10" + "@inquirer/figures": "npm:^2.0.5" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/6cf3bfbc0e39b80b8a37b69e49231c22616877b31b77507a55be5363d14b33e91cd3c1eb4ebf0ba89435ab5ef8204ce634579a23ab7b186ee8c71d54a7c959ee + languageName: node + linkType: hard + +"@inquirer/confirm@npm:^6.0.13": + version: 6.0.13 + resolution: "@inquirer/confirm@npm:6.0.13" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/59f3c484f405b3ffe2e97a9e4927111d71d317ea84fa14dc0ffd2d7c902f934ad31f48b380765937f5ff85722ece475edcde8a64b8018c21f2ee3e33082cee8b + languageName: node + linkType: hard + +"@inquirer/core@npm:^11.1.10": + version: 11.1.10 + resolution: "@inquirer/core@npm:11.1.10" + dependencies: + "@inquirer/ansi": "npm:^2.0.5" + "@inquirer/figures": "npm:^2.0.5" + "@inquirer/type": "npm:^4.0.5" + cli-width: "npm:^4.1.0" + fast-wrap-ansi: "npm:^0.2.0" + mute-stream: "npm:^3.0.0" + signal-exit: "npm:^4.1.0" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/d1d4081cbb0bd3dc15a3c95c58560a4836079a14161c407bc20f9a1b0fdb93c2228daf61aa60acab7023bc78df1b85875fde94308dcac2c6bd0ffd24b5f05190 + languageName: node + linkType: hard + +"@inquirer/editor@npm:^5.1.2": + version: 5.1.2 + resolution: "@inquirer/editor@npm:5.1.2" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/external-editor": "npm:^3.0.0" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/5b24700b8d4339d4b9b5fe5991d4c35843c1977595bfc0ab242e95a2bd8c2463c9039050f3b7821e8f49786926c5b68ba4cb20d6d887292fc0b12ccb03bd3ca2 + languageName: node + linkType: hard + +"@inquirer/expand@npm:^5.0.14": + version: 5.0.14 + resolution: "@inquirer/expand@npm:5.0.14" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/efdc9a93d57397f415529ed2b969de6fa78c2ab98901a89efd73f1cfc79eba3ea899c621a63a458c530ca4142c89fd61cfeb873384e906f9cc33c022a5a3f5be + languageName: node + linkType: hard + +"@inquirer/external-editor@npm:^3.0.0": + version: 3.0.0 + resolution: "@inquirer/external-editor@npm:3.0.0" + dependencies: + chardet: "npm:^2.1.1" + iconv-lite: "npm:^0.7.2" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/120910c954869c73c54aee825abef6f4c4aa8620cafa831d56b218586b1ee02c12ad0a17b8d701784fb9d33fa8fd63ee155d9c718c90533ff4d8086b99986e1d + languageName: node + linkType: hard + +"@inquirer/figures@npm:^2.0.5": + version: 2.0.5 + resolution: "@inquirer/figures@npm:2.0.5" + checksum: 10c0/139671b88f33f059aec85ed3fdf464999115573350c6dea61141adc1cfd43d14742b6cb68150c2ca9baf5a1bae618f990ed89b4430ae768d415bbd19944c56df + languageName: node + linkType: hard + +"@inquirer/input@npm:^5.0.13": + version: 5.0.13 + resolution: "@inquirer/input@npm:5.0.13" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/df2d67a6f0a1b4cc22dfdc1e78f833560f613647bd75374d4664601b7947106dd977aceb8440a19a17204b58c11611e6084096eae3eb1873260f1f1d029a8a0a + languageName: node + linkType: hard + +"@inquirer/number@npm:^4.0.13": + version: 4.0.13 + resolution: "@inquirer/number@npm:4.0.13" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/9185d15c8b0ab820dc0456e7db6f189ae84bf8d5f5bce19398f3a858fca0276e66a53922a4ab118dbae65f1f4f6a29f06f3d34c6436ae9e095a98e9862590e2b + languageName: node + linkType: hard + +"@inquirer/password@npm:^5.0.13": + version: 5.0.13 + resolution: "@inquirer/password@npm:5.0.13" + dependencies: + "@inquirer/ansi": "npm:^2.0.5" + "@inquirer/core": "npm:^11.1.10" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/e06aa6ae4344e3e37630655c443001bcf4421b1e4821c15987fc747b8d0362d536a52a115d25d02b023bb7091fc88630a65d7b0ac02e15a2f70d933f6a863da3 + languageName: node + linkType: hard + +"@inquirer/prompts@npm:^8.0.0": + version: 8.4.3 + resolution: "@inquirer/prompts@npm:8.4.3" + dependencies: + "@inquirer/checkbox": "npm:^5.1.5" + "@inquirer/confirm": "npm:^6.0.13" + "@inquirer/editor": "npm:^5.1.2" + "@inquirer/expand": "npm:^5.0.14" + "@inquirer/input": "npm:^5.0.13" + "@inquirer/number": "npm:^4.0.13" + "@inquirer/password": "npm:^5.0.13" + "@inquirer/rawlist": "npm:^5.2.9" + "@inquirer/search": "npm:^4.1.9" + "@inquirer/select": "npm:^5.1.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/fd77efb0b12a9293d6533cf332a1af10f69fefa97202c3790c2a7695a06c443ed5d4877d05efe512803e4a98cce0fa46fed9d372149512c2eccbfcf23f1c44bd + languageName: node + linkType: hard + +"@inquirer/rawlist@npm:^5.2.9": + version: 5.2.9 + resolution: "@inquirer/rawlist@npm:5.2.9" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/10f1a23e5222a932d9965490beb8b37551b2c68dcb281290d8f0099dc0778b49d4ca671ad8b346c802cbff29924faffd01b62dc88f297020e3d1061eb00b7f78 + languageName: node + linkType: hard + +"@inquirer/search@npm:^4.1.9": + version: 4.1.9 + resolution: "@inquirer/search@npm:4.1.9" + dependencies: + "@inquirer/core": "npm:^11.1.10" + "@inquirer/figures": "npm:^2.0.5" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/0e0cd6f2f312cecfa02f7d5556a7ccf5cbffff442fbdd0828f320e0a0239b63d24db5e31e05ec77d2a969900ad40b2d115f6496b2c9c47561a15dc504298d9dc languageName: node linkType: hard -"@isaacs/brace-expansion@npm:^5.0.1": - version: 5.0.1 - resolution: "@isaacs/brace-expansion@npm:5.0.1" +"@inquirer/select@npm:^5.1.5": + version: 5.1.5 + resolution: "@inquirer/select@npm:5.1.5" dependencies: - "@isaacs/balanced-match": "npm:^4.0.1" - checksum: 10c0/e5d67c7bbf1f17b88132a35bc638af306d48acbb72810d48fa6e6edd8ab375854773108e8bf70f021f7ef6a8273455a6d1f0c3b5aa2aff06ce7894049ab77fb8 + "@inquirer/ansi": "npm:^2.0.5" + "@inquirer/core": "npm:^11.1.10" + "@inquirer/figures": "npm:^2.0.5" + "@inquirer/type": "npm:^4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/871e05266c00151031798dc659acaed3d9c76d0040a25204cbcc99f7104559db1a8bc2a73ee04318a01f06f879a3d7e5986db50f563aeca23ae4cd5e8cff6057 + languageName: node + linkType: hard + +"@inquirer/type@npm:^4.0.5": + version: 4.0.5 + resolution: "@inquirer/type@npm:4.0.5" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/390edb0fd1f027f9c8dc26bac28486d38bbde6c19974ef1588ea187f54a2cb58db639ebca31fa81a8fe4a4e84c2f0953ab3f5a6768ba86649368c5e806148a6f languageName: node linkType: hard @@ -89,14 +314,26 @@ __metadata: linkType: hard "@lingui/conf@npm:5 || 6": - version: 6.2.0 - resolution: "@lingui/conf@npm:6.2.0" + version: 6.3.0 + resolution: "@lingui/conf@npm:6.3.0" + dependencies: + jest-validate: "npm:^29.4.3" + jiti: "npm:^2.5.1" + lilconfig: "npm:^3.1.3" + normalize-path: "npm:^3.0.0" + checksum: 10c0/5c9fbbfab9248094097e1b68ec7cc29833f2d1086ef94bfa59031a7733c7b244e8c774833e0ef946de905afa2a0a3b52e7c28ddcf18084090f68898fb9925a9f + languageName: node + linkType: hard + +"@lingui/conf@npm:^6.0.1": + version: 6.1.0 + resolution: "@lingui/conf@npm:6.1.0" dependencies: jest-validate: "npm:^29.4.3" jiti: "npm:^2.5.1" lilconfig: "npm:^3.1.3" normalize-path: "npm:^3.0.0" - checksum: 10c0/a388b6b7ea76346ae520132fc38e67ea302dc4a97bcc23ebce0d53cf7d5592dd23e674023c1aa9aed80e323ea0cd2ad40fb54e3c67b4219555b33208a7fe6f06 + checksum: 10c0/e0dfd28206f8c846e92392075164f87da349a8739e22776ff216e8bf4eadfe59bdbeb161530772bc1dd23d7c2ad59e699ee65d47a55a4a1e24f9bc6a70bade42 languageName: node linkType: hard @@ -119,44 +356,1027 @@ __metadata: languageName: unknown linkType: soft -"@napi-rs/wasm-runtime@npm:^1.1.4": - version: 1.1.4 - resolution: "@napi-rs/wasm-runtime@npm:1.1.4" - dependencies: - "@tybys/wasm-util": "npm:^0.10.1" +"@napi-rs/cli@npm:^3.5.1": + version: 3.6.2 + resolution: "@napi-rs/cli@npm:3.6.2" + dependencies: + "@inquirer/prompts": "npm:^8.0.0" + "@napi-rs/cross-toolchain": "npm:^1.0.3" + "@napi-rs/wasm-tools": "npm:^1.0.1" + "@octokit/rest": "npm:^22.0.1" + clipanion: "npm:^4.0.0-rc.4" + colorette: "npm:^2.0.20" + emnapi: "npm:^1.9.1" + es-toolkit: "npm:^1.41.0" + js-yaml: "npm:^4.1.0" + obug: "npm:^2.0.0" + semver: "npm:^7.7.3" + typanion: "npm:^3.14.0" peerDependencies: - "@emnapi/core": ^1.7.1 "@emnapi/runtime": ^1.7.1 - checksum: 10c0/2e88e1955258949ccf2d18c79975821ad38071b465ef126a5e14110977b97868867b016c1ad046e963cccc42c0bd9db6c8ff5fd1ebb61b87bb3487f339041658 + peerDependenciesMeta: + "@emnapi/runtime": + optional: true + bin: + napi: dist/cli.js + napi-raw: cli.mjs + checksum: 10c0/3164711ed904c45247f9589c9cdf62f843c30e1b1b03bc770b9bb4d7c76bd134c42266ba85bc11d50f77df97eea56594a8151a749f2bca014c2b45cbfcf96a41 languageName: node linkType: hard -"@npmcli/agent@npm:^4.0.0": - version: 4.0.0 - resolution: "@npmcli/agent@npm:4.0.0" +"@napi-rs/cross-toolchain@npm:^1.0.3": + version: 1.0.3 + resolution: "@napi-rs/cross-toolchain@npm:1.0.3" + dependencies: + "@napi-rs/lzma": "npm:^1.4.5" + "@napi-rs/tar": "npm:^1.1.0" + debug: "npm:^4.4.1" + peerDependencies: + "@napi-rs/cross-toolchain-arm64-target-aarch64": ^1.0.3 + "@napi-rs/cross-toolchain-arm64-target-armv7": ^1.0.3 + "@napi-rs/cross-toolchain-arm64-target-ppc64le": ^1.0.3 + "@napi-rs/cross-toolchain-arm64-target-s390x": ^1.0.3 + "@napi-rs/cross-toolchain-arm64-target-x86_64": ^1.0.3 + "@napi-rs/cross-toolchain-x64-target-aarch64": ^1.0.3 + "@napi-rs/cross-toolchain-x64-target-armv7": ^1.0.3 + "@napi-rs/cross-toolchain-x64-target-ppc64le": ^1.0.3 + "@napi-rs/cross-toolchain-x64-target-s390x": ^1.0.3 + "@napi-rs/cross-toolchain-x64-target-x86_64": ^1.0.3 + peerDependenciesMeta: + "@napi-rs/cross-toolchain-arm64-target-aarch64": + optional: true + "@napi-rs/cross-toolchain-arm64-target-armv7": + optional: true + "@napi-rs/cross-toolchain-arm64-target-ppc64le": + optional: true + "@napi-rs/cross-toolchain-arm64-target-s390x": + optional: true + "@napi-rs/cross-toolchain-arm64-target-x86_64": + optional: true + "@napi-rs/cross-toolchain-x64-target-aarch64": + optional: true + "@napi-rs/cross-toolchain-x64-target-armv7": + optional: true + "@napi-rs/cross-toolchain-x64-target-ppc64le": + optional: true + "@napi-rs/cross-toolchain-x64-target-s390x": + optional: true + "@napi-rs/cross-toolchain-x64-target-x86_64": + optional: true + checksum: 10c0/fcc7877c1e47ba6bf4801a4154240d3130703524b1fed17e736126ce58b53960872b9933f8434e3e86b4635e4c7fe881228be0d237210dd96c2087244523750f + languageName: node + linkType: hard + +"@napi-rs/lzma-android-arm-eabi@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-android-arm-eabi@npm:1.4.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-android-arm64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-android-arm64@npm:1.4.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-arm64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-darwin-arm64@npm:1.4.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-x64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-darwin-x64@npm:1.4.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-freebsd-x64@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-freebsd-x64@npm:1.4.5" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm-gnueabihf@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-arm-gnueabihf@npm:1.4.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-arm64-gnu@npm:1.4.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-musl@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-arm64-musl@npm:1.4.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-ppc64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-ppc64-gnu@npm:1.4.5" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-riscv64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-riscv64-gnu@npm:1.4.5" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-s390x-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-s390x-gnu@npm:1.4.5" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-gnu@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.4.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-musl@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-linux-x64-musl@npm:1.4.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-wasm32-wasi@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-wasm32-wasi@npm:1.4.5" dependencies: - agent-base: "npm:^7.1.0" - http-proxy-agent: "npm:^7.0.0" - https-proxy-agent: "npm:^7.0.1" - lru-cache: "npm:^11.2.1" - socks-proxy-agent: "npm:^8.0.3" - checksum: 10c0/f7b5ce0f3dd42c3f8c6546e8433573d8049f67ef11ec22aa4704bc41483122f68bf97752e06302c455ead667af5cb753e6a09bff06632bc465c1cfd4c4b75a53 + "@napi-rs/wasm-runtime": "npm:^1.0.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-arm64-msvc@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-win32-arm64-msvc@npm:1.4.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-ia32-msvc@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-win32-ia32-msvc@npm:1.4.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-x64-msvc@npm:1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma-win32-x64-msvc@npm:1.4.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma@npm:^1.4.5": + version: 1.4.5 + resolution: "@napi-rs/lzma@npm:1.4.5" + dependencies: + "@napi-rs/lzma-android-arm-eabi": "npm:1.4.5" + "@napi-rs/lzma-android-arm64": "npm:1.4.5" + "@napi-rs/lzma-darwin-arm64": "npm:1.4.5" + "@napi-rs/lzma-darwin-x64": "npm:1.4.5" + "@napi-rs/lzma-freebsd-x64": "npm:1.4.5" + "@napi-rs/lzma-linux-arm-gnueabihf": "npm:1.4.5" + "@napi-rs/lzma-linux-arm64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-arm64-musl": "npm:1.4.5" + "@napi-rs/lzma-linux-ppc64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-riscv64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-s390x-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-x64-gnu": "npm:1.4.5" + "@napi-rs/lzma-linux-x64-musl": "npm:1.4.5" + "@napi-rs/lzma-wasm32-wasi": "npm:1.4.5" + "@napi-rs/lzma-win32-arm64-msvc": "npm:1.4.5" + "@napi-rs/lzma-win32-ia32-msvc": "npm:1.4.5" + "@napi-rs/lzma-win32-x64-msvc": "npm:1.4.5" + dependenciesMeta: + "@napi-rs/lzma-android-arm-eabi": + optional: true + "@napi-rs/lzma-android-arm64": + optional: true + "@napi-rs/lzma-darwin-arm64": + optional: true + "@napi-rs/lzma-darwin-x64": + optional: true + "@napi-rs/lzma-freebsd-x64": + optional: true + "@napi-rs/lzma-linux-arm-gnueabihf": + optional: true + "@napi-rs/lzma-linux-arm64-gnu": + optional: true + "@napi-rs/lzma-linux-arm64-musl": + optional: true + "@napi-rs/lzma-linux-ppc64-gnu": + optional: true + "@napi-rs/lzma-linux-riscv64-gnu": + optional: true + "@napi-rs/lzma-linux-s390x-gnu": + optional: true + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@napi-rs/lzma-linux-x64-musl": + optional: true + "@napi-rs/lzma-wasm32-wasi": + optional: true + "@napi-rs/lzma-win32-arm64-msvc": + optional: true + "@napi-rs/lzma-win32-ia32-msvc": + optional: true + "@napi-rs/lzma-win32-x64-msvc": + optional: true + checksum: 10c0/df098c99f904b54541e3a34feeb5878c98a4abf1ababf1d301d903b99d98402fff5eda49e1dd103bb4bf44a9217a5e1b17fb30b74044416561f8fe02ca098ee3 + languageName: node + linkType: hard + +"@napi-rs/tar-android-arm-eabi@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-android-arm-eabi@npm:1.1.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-android-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-android-arm64@npm:1.1.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-arm64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-darwin-arm64@npm:1.1.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-darwin-x64@npm:1.1.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-freebsd-x64@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-freebsd-x64@npm:1.1.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm-gnueabihf@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-arm-gnueabihf@npm:1.1.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-arm64-gnu@npm:1.1.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-musl@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-arm64-musl@npm:1.1.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-linux-ppc64-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-ppc64-gnu@npm:1.1.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-s390x-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-s390x-gnu@npm:1.1.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-gnu@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-x64-gnu@npm:1.1.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-musl@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-linux-x64-musl@npm:1.1.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-wasm32-wasi@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-wasm32-wasi@npm:1.1.0" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-arm64-msvc@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-win32-arm64-msvc@npm:1.1.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-ia32-msvc@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-win32-ia32-msvc@npm:1.1.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-x64-msvc@npm:1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar-win32-x64-msvc@npm:1.1.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar@npm:^1.1.0": + version: 1.1.0 + resolution: "@napi-rs/tar@npm:1.1.0" + dependencies: + "@napi-rs/tar-android-arm-eabi": "npm:1.1.0" + "@napi-rs/tar-android-arm64": "npm:1.1.0" + "@napi-rs/tar-darwin-arm64": "npm:1.1.0" + "@napi-rs/tar-darwin-x64": "npm:1.1.0" + "@napi-rs/tar-freebsd-x64": "npm:1.1.0" + "@napi-rs/tar-linux-arm-gnueabihf": "npm:1.1.0" + "@napi-rs/tar-linux-arm64-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-arm64-musl": "npm:1.1.0" + "@napi-rs/tar-linux-ppc64-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-s390x-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-x64-gnu": "npm:1.1.0" + "@napi-rs/tar-linux-x64-musl": "npm:1.1.0" + "@napi-rs/tar-wasm32-wasi": "npm:1.1.0" + "@napi-rs/tar-win32-arm64-msvc": "npm:1.1.0" + "@napi-rs/tar-win32-ia32-msvc": "npm:1.1.0" + "@napi-rs/tar-win32-x64-msvc": "npm:1.1.0" + dependenciesMeta: + "@napi-rs/tar-android-arm-eabi": + optional: true + "@napi-rs/tar-android-arm64": + optional: true + "@napi-rs/tar-darwin-arm64": + optional: true + "@napi-rs/tar-darwin-x64": + optional: true + "@napi-rs/tar-freebsd-x64": + optional: true + "@napi-rs/tar-linux-arm-gnueabihf": + optional: true + "@napi-rs/tar-linux-arm64-gnu": + optional: true + "@napi-rs/tar-linux-arm64-musl": + optional: true + "@napi-rs/tar-linux-ppc64-gnu": + optional: true + "@napi-rs/tar-linux-s390x-gnu": + optional: true + "@napi-rs/tar-linux-x64-gnu": + optional: true + "@napi-rs/tar-linux-x64-musl": + optional: true + "@napi-rs/tar-wasm32-wasi": + optional: true + "@napi-rs/tar-win32-arm64-msvc": + optional: true + "@napi-rs/tar-win32-ia32-msvc": + optional: true + "@napi-rs/tar-win32-x64-msvc": + optional: true + checksum: 10c0/88a0ab081eacfa235266f14a0bc408b7581058b1f7e18b118c6f8e7012cca0dd91c5baf5de84e1d2eb8070386a7380aa4d8dedfc6f81e24ae9d0287ff50ae153 + languageName: node + linkType: hard + +"@napi-rs/wasm-runtime@npm:^1.0.3, @napi-rs/wasm-runtime@npm:^1.0.7, @napi-rs/wasm-runtime@npm:^1.1.4": + version: 1.1.4 + resolution: "@napi-rs/wasm-runtime@npm:1.1.4" + dependencies: + "@tybys/wasm-util": "npm:^0.10.1" + peerDependencies: + "@emnapi/core": ^1.7.1 + "@emnapi/runtime": ^1.7.1 + checksum: 10c0/2e88e1955258949ccf2d18c79975821ad38071b465ef126a5e14110977b97868867b016c1ad046e963cccc42c0bd9db6c8ff5fd1ebb61b87bb3487f339041658 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-android-arm-eabi@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-android-arm-eabi@npm:1.0.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-android-arm64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-android-arm64@npm:1.0.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-darwin-arm64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-darwin-arm64@npm:1.0.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-darwin-x64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-darwin-x64@npm:1.0.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-freebsd-x64@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-freebsd-x64@npm:1.0.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-arm64-gnu@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-arm64-gnu@npm:1.0.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-arm64-musl@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-arm64-musl@npm:1.0.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-x64-gnu@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-x64-gnu@npm:1.0.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-linux-x64-musl@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-linux-x64-musl@npm:1.0.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-wasm32-wasi@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-wasm32-wasi@npm:1.0.1" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.3" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-win32-arm64-msvc@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-win32-arm64-msvc@npm:1.0.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-win32-ia32-msvc@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-win32-ia32-msvc@npm:1.0.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools-win32-x64-msvc@npm:1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools-win32-x64-msvc@npm:1.0.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/wasm-tools@npm:^1.0.1": + version: 1.0.1 + resolution: "@napi-rs/wasm-tools@npm:1.0.1" + dependencies: + "@napi-rs/wasm-tools-android-arm-eabi": "npm:1.0.1" + "@napi-rs/wasm-tools-android-arm64": "npm:1.0.1" + "@napi-rs/wasm-tools-darwin-arm64": "npm:1.0.1" + "@napi-rs/wasm-tools-darwin-x64": "npm:1.0.1" + "@napi-rs/wasm-tools-freebsd-x64": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-arm64-gnu": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-arm64-musl": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-x64-gnu": "npm:1.0.1" + "@napi-rs/wasm-tools-linux-x64-musl": "npm:1.0.1" + "@napi-rs/wasm-tools-wasm32-wasi": "npm:1.0.1" + "@napi-rs/wasm-tools-win32-arm64-msvc": "npm:1.0.1" + "@napi-rs/wasm-tools-win32-ia32-msvc": "npm:1.0.1" + "@napi-rs/wasm-tools-win32-x64-msvc": "npm:1.0.1" + dependenciesMeta: + "@napi-rs/wasm-tools-android-arm-eabi": + optional: true + "@napi-rs/wasm-tools-android-arm64": + optional: true + "@napi-rs/wasm-tools-darwin-arm64": + optional: true + "@napi-rs/wasm-tools-darwin-x64": + optional: true + "@napi-rs/wasm-tools-freebsd-x64": + optional: true + "@napi-rs/wasm-tools-linux-arm64-gnu": + optional: true + "@napi-rs/wasm-tools-linux-arm64-musl": + optional: true + "@napi-rs/wasm-tools-linux-x64-gnu": + optional: true + "@napi-rs/wasm-tools-linux-x64-musl": + optional: true + "@napi-rs/wasm-tools-wasm32-wasi": + optional: true + "@napi-rs/wasm-tools-win32-arm64-msvc": + optional: true + "@napi-rs/wasm-tools-win32-ia32-msvc": + optional: true + "@napi-rs/wasm-tools-win32-x64-msvc": + optional: true + checksum: 10c0/bee9258e0b16a2415acc57d9aa281fa50402b38f631aea28e3a8ecd16415cdfffade63313bca777e85c3a73b80cce899ac3dd35eba9104951d7da1eb28913122 + languageName: node + linkType: hard + +"@octokit/auth-token@npm:^6.0.0": + version: 6.0.0 + resolution: "@octokit/auth-token@npm:6.0.0" + checksum: 10c0/32ecc904c5f6f4e5d090bfcc679d70318690c0a0b5040cd9a25811ad9dcd44c33f2cf96b6dbee1cd56cf58fde28fb1819c01b58718aa5c971f79c822357cb5c0 + languageName: node + linkType: hard + +"@octokit/core@npm:^7.0.6": + version: 7.0.6 + resolution: "@octokit/core@npm:7.0.6" + dependencies: + "@octokit/auth-token": "npm:^6.0.0" + "@octokit/graphql": "npm:^9.0.3" + "@octokit/request": "npm:^10.0.6" + "@octokit/request-error": "npm:^7.0.2" + "@octokit/types": "npm:^16.0.0" + before-after-hook: "npm:^4.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/95a328ff7c7223d9eb4aa778c63171828514ae0e0f588d33beb81a4dc03bbeae055382f6060ce23c979ab46272409942ff2cf3172109999e48429c47055b1fbe + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^11.0.3": + version: 11.0.3 + resolution: "@octokit/endpoint@npm:11.0.3" + dependencies: + "@octokit/types": "npm:^16.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/3f9b67e6923ece5009aebb0dcbae5837fb574bc422561424049a43ead7fea6f132234edb72239d6ec067cf734937a608e4081af81c109de2cb754528f0d00520 + languageName: node + linkType: hard + +"@octokit/graphql@npm:^9.0.3": + version: 9.0.3 + resolution: "@octokit/graphql@npm:9.0.3" + dependencies: + "@octokit/request": "npm:^10.0.6" + "@octokit/types": "npm:^16.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/58588d3fb2834f64244fa5376ca7922a30117b001b621e141fab0d52806370803ab0c046ac99b120fa5f45b770f52a815157fb6ffc147fc6c1da4047c1f1af49 + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^27.0.0": + version: 27.0.0 + resolution: "@octokit/openapi-types@npm:27.0.0" + checksum: 10c0/602d1de033da180a2e982cdbd3646bd5b2e16ecf36b9955a0f23e37ae9e6cb086abb48ff2ae6f2de000fce03e8ae9051794611ae4a95a8f5f6fb63276e7b8e31 + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^14.0.0": + version: 14.0.0 + resolution: "@octokit/plugin-paginate-rest@npm:14.0.0" + dependencies: + "@octokit/types": "npm:^16.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/841d79d4ccfe18fc809a4a67529b75c1dcdda13399bf4bf5b48ce7559c8b4b2cd422e3204bad4cbdea31c0cf0943521067415268e5bcfc615a3b813e058cad6b + languageName: node + linkType: hard + +"@octokit/plugin-request-log@npm:^6.0.0": + version: 6.0.0 + resolution: "@octokit/plugin-request-log@npm:6.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/40e46ad0c77235742d0bf698ab4e17df1ae06e0d7824ffc5867ed71e27de860875adb73d89629b823fe8647459a8f262c26ed1aa6ee374873fa94095f37df0bb + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^17.0.0": + version: 17.0.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:17.0.0" + dependencies: + "@octokit/types": "npm:^16.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/cf9984d7cf6a36ff7ff1b86078ae45fe246e3df10fcef0bccf20c8cfd27bf5e7d98dcb9cf5a7b56332b9c6fa30be28d159c2987d272a4758f77056903d94402f + languageName: node + linkType: hard + +"@octokit/request-error@npm:^7.0.2": + version: 7.1.0 + resolution: "@octokit/request-error@npm:7.1.0" + dependencies: + "@octokit/types": "npm:^16.0.0" + checksum: 10c0/62b90a54545c36a30b5ffdda42e302c751be184d85b68ffc7f1242c51d7ca54dbd185b7d0027b491991776923a910c85c9c51269fe0d86111bac187507a5abc4 + languageName: node + linkType: hard + +"@octokit/request@npm:^10.0.6": + version: 10.0.9 + resolution: "@octokit/request@npm:10.0.9" + dependencies: + "@octokit/endpoint": "npm:^11.0.3" + "@octokit/request-error": "npm:^7.0.2" + "@octokit/types": "npm:^16.0.0" + content-type: "npm:^2.0.0" + fast-content-type-parse: "npm:^3.0.0" + json-with-bigint: "npm:^3.5.3" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/2a642870202117c8cf0021b7c01a92e18acdec4826c103f7749a942537a265251e033c0ac5178807f0c63b9759e149ce9701015454167d4c1627d30218ddf9f2 + languageName: node + linkType: hard + +"@octokit/rest@npm:^22.0.1": + version: 22.0.1 + resolution: "@octokit/rest@npm:22.0.1" + dependencies: + "@octokit/core": "npm:^7.0.6" + "@octokit/plugin-paginate-rest": "npm:^14.0.0" + "@octokit/plugin-request-log": "npm:^6.0.0" + "@octokit/plugin-rest-endpoint-methods": "npm:^17.0.0" + checksum: 10c0/f3abd84e887cc837973214ce70720a9bba53f5575f40601c6122aa25206e9055d859c0388437f0a137f6cd0e4ff405e1b46b903475b0db32a17bada0c6513d5b + languageName: node + linkType: hard + +"@octokit/types@npm:^16.0.0": + version: 16.0.0 + resolution: "@octokit/types@npm:16.0.0" + dependencies: + "@octokit/openapi-types": "npm:^27.0.0" + checksum: 10c0/b8d41098ba6fc194d13d641f9441347e3a3b96c0efabac0e14f57319340a2d4d1c8676e4cb37ab3062c5c323c617e790b0126916e9bf7b201b0cced0826f8ae2 + languageName: node + linkType: hard + +"@oxc-node/core-android-arm-eabi@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-android-arm-eabi@npm:0.0.35" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@oxc-node/core-android-arm64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-android-arm64@npm:0.0.35" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-darwin-arm64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-darwin-arm64@npm:0.0.35" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-darwin-x64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-darwin-x64@npm:0.0.35" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxc-node/core-freebsd-x64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-freebsd-x64@npm:0.0.35" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@oxc-node/core-linux-arm-gnueabihf@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-arm-gnueabihf@npm:0.0.35" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@oxc-node/core-linux-arm64-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-arm64-gnu@npm:0.0.35" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-arm64-musl@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-arm64-musl@npm:0.0.35" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@oxc-node/core-linux-ppc64-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-ppc64-gnu@npm:0.0.35" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-s390x-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-s390x-gnu@npm:0.0.35" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-x64-gnu@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-x64-gnu@npm:0.0.35" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@oxc-node/core-linux-x64-musl@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-linux-x64-musl@npm:0.0.35" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@oxc-node/core-openharmony-arm64@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-openharmony-arm64@npm:0.0.35" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-wasm32-wasi@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-wasm32-wasi@npm:0.0.35" + dependencies: + "@napi-rs/wasm-runtime": "npm:^1.0.7" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@oxc-node/core-win32-arm64-msvc@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-win32-arm64-msvc@npm:0.0.35" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@oxc-node/core-win32-ia32-msvc@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-win32-ia32-msvc@npm:0.0.35" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@oxc-node/core-win32-x64-msvc@npm:0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core-win32-x64-msvc@npm:0.0.35" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@oxc-node/core@npm:^0.0.35": + version: 0.0.35 + resolution: "@oxc-node/core@npm:0.0.35" + dependencies: + "@oxc-node/core-android-arm-eabi": "npm:0.0.35" + "@oxc-node/core-android-arm64": "npm:0.0.35" + "@oxc-node/core-darwin-arm64": "npm:0.0.35" + "@oxc-node/core-darwin-x64": "npm:0.0.35" + "@oxc-node/core-freebsd-x64": "npm:0.0.35" + "@oxc-node/core-linux-arm-gnueabihf": "npm:0.0.35" + "@oxc-node/core-linux-arm64-gnu": "npm:0.0.35" + "@oxc-node/core-linux-arm64-musl": "npm:0.0.35" + "@oxc-node/core-linux-ppc64-gnu": "npm:0.0.35" + "@oxc-node/core-linux-s390x-gnu": "npm:0.0.35" + "@oxc-node/core-linux-x64-gnu": "npm:0.0.35" + "@oxc-node/core-linux-x64-musl": "npm:0.0.35" + "@oxc-node/core-openharmony-arm64": "npm:0.0.35" + "@oxc-node/core-wasm32-wasi": "npm:0.0.35" + "@oxc-node/core-win32-arm64-msvc": "npm:0.0.35" + "@oxc-node/core-win32-ia32-msvc": "npm:0.0.35" + "@oxc-node/core-win32-x64-msvc": "npm:0.0.35" + pirates: "npm:^4.0.7" + dependenciesMeta: + "@oxc-node/core-android-arm-eabi": + optional: true + "@oxc-node/core-android-arm64": + optional: true + "@oxc-node/core-darwin-arm64": + optional: true + "@oxc-node/core-darwin-x64": + optional: true + "@oxc-node/core-freebsd-x64": + optional: true + "@oxc-node/core-linux-arm-gnueabihf": + optional: true + "@oxc-node/core-linux-arm64-gnu": + optional: true + "@oxc-node/core-linux-arm64-musl": + optional: true + "@oxc-node/core-linux-ppc64-gnu": + optional: true + "@oxc-node/core-linux-s390x-gnu": + optional: true + "@oxc-node/core-linux-x64-gnu": + optional: true + "@oxc-node/core-linux-x64-musl": + optional: true + "@oxc-node/core-openharmony-arm64": + optional: true + "@oxc-node/core-wasm32-wasi": + optional: true + "@oxc-node/core-win32-arm64-msvc": + optional: true + "@oxc-node/core-win32-ia32-msvc": + optional: true + "@oxc-node/core-win32-x64-msvc": + optional: true + checksum: 10c0/6328427d833c99b498f41b8dc58ed3503d17aabe66172a5aacce06c7e85377649aa6ac602dfa5dd8f8f01167b5fe428a65d751c40386eced495acbe9eb414e10 + languageName: node + linkType: hard + +"@oxc-project/types@npm:=0.132.0": + version: 0.132.0 + resolution: "@oxc-project/types@npm:0.132.0" + checksum: 10c0/d0ca5e98be0b873d69e4f0f743eb35026833603dac11db9d55f2b5438251b381b886dc556fe3175a17b673f8e2073c49bde88d7e6e702aa09298c22b8b5504e1 + languageName: node + linkType: hard + +"@oxlint/binding-android-arm-eabi@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-android-arm-eabi@npm:1.66.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@oxlint/binding-android-arm64@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-android-arm64@npm:1.66.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/binding-darwin-arm64@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-darwin-arm64@npm:1.66.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/binding-darwin-x64@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-darwin-x64@npm:1.66.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@oxlint/binding-freebsd-x64@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-freebsd-x64@npm:1.66.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@oxlint/binding-linux-arm-gnueabihf@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-arm-gnueabihf@npm:1.66.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@oxlint/binding-linux-arm-musleabihf@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-arm-musleabihf@npm:1.66.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@oxlint/binding-linux-arm64-gnu@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-arm64-gnu@npm:1.66.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/binding-linux-arm64-musl@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-arm64-musl@npm:1.66.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/binding-linux-ppc64-gnu@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-ppc64-gnu@npm:1.66.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/binding-linux-riscv64-gnu@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-riscv64-gnu@npm:1.66.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/binding-linux-riscv64-musl@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-riscv64-musl@npm:1.66.0" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/binding-linux-s390x-gnu@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-s390x-gnu@npm:1.66.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@oxlint/binding-linux-x64-gnu@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-x64-gnu@npm:1.66.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@oxlint/binding-linux-x64-musl@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-linux-x64-musl@npm:1.66.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@oxlint/binding-openharmony-arm64@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-openharmony-arm64@npm:1.66.0" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@oxlint/binding-win32-arm64-msvc@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-win32-arm64-msvc@npm:1.66.0" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@npmcli/fs@npm:^5.0.0": - version: 5.0.0 - resolution: "@npmcli/fs@npm:5.0.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/26e376d780f60ff16e874a0ac9bc3399186846baae0b6e1352286385ac134d900cc5dafaded77f38d77f86898fc923ae1cee9d7399f0275b1aa24878915d722b +"@oxlint/binding-win32-ia32-msvc@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-win32-ia32-msvc@npm:1.66.0" + conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@oxc-project/types@npm:=0.132.0": - version: 0.132.0 - resolution: "@oxc-project/types@npm:0.132.0" - checksum: 10c0/d0ca5e98be0b873d69e4f0f743eb35026833603dac11db9d55f2b5438251b381b886dc556fe3175a17b673f8e2073c49bde88d7e6e702aa09298c22b8b5504e1 +"@oxlint/binding-win32-x64-msvc@npm:1.66.0": + version: 1.66.0 + resolution: "@oxlint/binding-win32-x64-msvc@npm:1.66.0" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -433,7 +1653,7 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.26": +"@swc/types@npm:^0.1.25, @swc/types@npm:^0.1.26": version: 0.1.26 resolution: "@swc/types@npm:0.1.26" dependencies: @@ -442,7 +1662,16 @@ __metadata: languageName: node linkType: hard -"@tybys/wasm-util@npm:^0.10.1": +"@taplo/cli@npm:^0.7.0": + version: 0.7.0 + resolution: "@taplo/cli@npm:0.7.0" + bin: + taplo: dist/cli.js + checksum: 10c0/c5a9a34502335731e405547fe24ba1a2f867fccdf8abe4d13e261e30fad6c18a2773a961981206166fde0c85b19accef40381c189c8c6ed4952c5b082c1d4996 + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.10.0, @tybys/wasm-util@npm:^0.10.1": version: 0.10.2 resolution: "@tybys/wasm-util@npm:0.10.2" dependencies: @@ -469,9 +1698,9 @@ __metadata: linkType: hard "@types/estree@npm:^1.0.0": - version: 1.0.8 - resolution: "@types/estree@npm:1.0.8" - checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 languageName: node linkType: hard @@ -518,6 +1747,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^24.1.0": + version: 24.12.4 + resolution: "@types/node@npm:24.12.4" + dependencies: + undici-types: "npm:~7.16.0" + checksum: 10c0/d2c36b78b6050d8677769fa05a32243061675e81ddc2bb43955d91a671af3465506ef2731a24c0c9ab42b6b679bd5c1513de45bbe9ea278c2c07ee63b564b61b + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.3 resolution: "@types/yargs-parser@npm:21.0.3" @@ -623,13 +1861,6 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": - version: 7.1.4 - resolution: "agent-base@npm:7.1.4" - checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe - languageName: node - linkType: hard - "ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" @@ -646,6 +1877,13 @@ __metadata: languageName: node linkType: hard +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + "assertion-error@npm:^2.0.1": version: 2.0.1 resolution: "assertion-error@npm:2.0.1" @@ -653,22 +1891,10 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^20.0.1": - version: 20.0.3 - resolution: "cacache@npm:20.0.3" - dependencies: - "@npmcli/fs": "npm:^5.0.0" - fs-minipass: "npm:^3.0.0" - glob: "npm:^13.0.0" - lru-cache: "npm:^11.1.0" - minipass: "npm:^7.0.3" - minipass-collect: "npm:^2.0.1" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - p-map: "npm:^7.0.2" - ssri: "npm:^13.0.0" - unique-filename: "npm:^5.0.0" - checksum: 10c0/c7da1ca694d20e8f8aedabd21dc11518f809a7d2b59aa76a1fc655db5a9e62379e465c157ddd2afe34b19230808882288effa6911b2de26a088a6d5645123462 +"before-after-hook@npm:^4.0.0": + version: 4.0.0 + resolution: "before-after-hook@npm:4.0.0" + checksum: 10c0/9f8ae8d1b06142bcfb9ef6625226b5e50348bb11210f266660eddcf9734e0db6f9afc4cb48397ee3f5ac0a3728f3ae401cdeea88413f7bed748a71db84657be2 languageName: node linkType: hard @@ -696,6 +1922,13 @@ __metadata: languageName: node linkType: hard +"chardet@npm:^2.1.1": + version: 2.1.1 + resolution: "chardet@npm:2.1.1" + checksum: 10c0/d8391dd412338442b3de0d3a488aa9327f8bcf74b62b8723d6bd0b85c4084d50b731320e0a7c710edb1d44de75969995d2784b80e4c13b004a6c7a0db4c6e793 + languageName: node + linkType: hard + "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -703,6 +1936,24 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f + languageName: node + linkType: hard + +"clipanion@npm:^4.0.0-rc.4": + version: 4.0.0-rc.4 + resolution: "clipanion@npm:4.0.0-rc.4" + dependencies: + typanion: "npm:^3.8.0" + peerDependencies: + typanion: "*" + checksum: 10c0/047b415b59a5e9777d00690fba563ccc850eca6bf27790a88d1deea3ecc8a89840ae9aed554ff284cc698a9f3f20256e43c25ff4a7c4c90a71e5e7d9dca61dd1 + languageName: node + linkType: hard + "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -719,6 +1970,20 @@ __metadata: languageName: node linkType: hard +"colorette@npm:^2.0.20": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 + languageName: node + linkType: hard + +"content-type@npm:^2.0.0": + version: 2.0.0 + resolution: "content-type@npm:2.0.0" + checksum: 10c0/491539fff707d7594b0ca4fabcc084bef2a31ffa754ff0a4f80c4377e3963cff0394317f9271c24087596c97fa675bc123d61fa34ffe65b4904e7d3d3098de72 + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -726,7 +1991,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.4": +"debug@npm:^4.4.1": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -745,12 +2010,15 @@ __metadata: languageName: node linkType: hard -"encoding@npm:^0.1.13": - version: 0.1.13 - resolution: "encoding@npm:0.1.13" - dependencies: - iconv-lite: "npm:^0.6.2" - checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 +"emnapi@npm:^1.9.1": + version: 1.10.0 + resolution: "emnapi@npm:1.10.0" + peerDependencies: + node-addon-api: ">= 6.1.0" + peerDependenciesMeta: + node-addon-api: + optional: true + checksum: 10c0/7e8ea3bd03e682db993709b623a30289d9dd667a22088122bc5f80faf3e6d1c592b6cc33284d839aab39abf762fb5032cca4459bf8f803be711e517d02403fa0 languageName: node linkType: hard @@ -761,13 +2029,6 @@ __metadata: languageName: node linkType: hard -"err-code@npm:^2.0.2": - version: 2.0.3 - resolution: "err-code@npm:2.0.3" - checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 - languageName: node - linkType: hard - "es-module-lexer@npm:^2.0.0": version: 2.1.0 resolution: "es-module-lexer@npm:2.1.0" @@ -775,6 +2036,18 @@ __metadata: languageName: node linkType: hard +"es-toolkit@npm:^1.41.0": + version: 1.46.1 + resolution: "es-toolkit@npm:1.46.1" + dependenciesMeta: + "@trivago/prettier-plugin-sort-imports@4.3.0": + unplugged: true + prettier-plugin-sort-re-exports@0.0.1: + unplugged: true + checksum: 10c0/6a4c3dd0ddc2138fa13d983d93ebaf8061759c52c2cf7c3101315139fc1fca826d253daa67fe85ad880c03cc4c092bd53a83a7cbf58b4721c251479122ba5303 + languageName: node + linkType: hard + "estree-walker@npm:^3.0.3": version: 3.0.3 resolution: "estree-walker@npm:3.0.3" @@ -798,6 +2071,38 @@ __metadata: languageName: node linkType: hard +"fast-content-type-parse@npm:^3.0.0": + version: 3.0.0 + resolution: "fast-content-type-parse@npm:3.0.0" + checksum: 10c0/06251880c83b7118af3a5e66e8bcee60d44f48b39396fc60acc2b4630bd5f3e77552b999b5c8e943d45a818854360e5e97164c374ec4b562b4df96a2cdf2e188 + languageName: node + linkType: hard + +"fast-string-truncated-width@npm:^3.0.2": + version: 3.0.3 + resolution: "fast-string-truncated-width@npm:3.0.3" + checksum: 10c0/043b8663397d14a3880ce4f3407bcda60b40db9bbeafe62863a35d1f9c69ea17c8da3fcd72de235553e6c9cd053128cde9e24ca0d4a7463208f48db3cd23d981 + languageName: node + linkType: hard + +"fast-string-width@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-string-width@npm:3.0.2" + dependencies: + fast-string-truncated-width: "npm:^3.0.2" + checksum: 10c0/c8822d175315bb353ebe782b65214ac53b13e3bf704e03b132ea7bdfa8de6a636375b3ab7a4097545393d109381c37c4f387c72a462c90b61412dbc4632f39a7 + languageName: node + linkType: hard + +"fast-wrap-ansi@npm:^0.2.0": + version: 0.2.2 + resolution: "fast-wrap-ansi@npm:0.2.2" + dependencies: + fast-string-width: "npm:^3.0.2" + checksum: 10c0/1aa7be4f7cb86f4bdb14691cb6bcc0b8df8b3b89df142ade3ae1602332dcf6f990cd750a923cd581ca0847808cb4ec1aa5afaafa7a72f849e87a2a62c98fa370 + languageName: node + linkType: hard + "fdir@npm:^6.5.0": version: 6.5.0 resolution: "fdir@npm:6.5.0" @@ -810,15 +2115,6 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 - languageName: node - linkType: hard - "fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -838,17 +2134,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^13.0.0": - version: 13.0.1 - resolution: "glob@npm:13.0.1" - dependencies: - minimatch: "npm:^10.1.2" - minipass: "npm:^7.1.2" - path-scurry: "npm:^2.0.0" - checksum: 10c0/af7b863dec8dff74f61d7d6e53104e1f6bbdd482157a196cade8ed857481e876ec35181b38a059b2a7b93ea3b08248f4ff0792fef6dc91814fd5097a716f48e4 - languageName: node - linkType: hard - "graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -863,60 +2148,19 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.1.1": - version: 4.2.0 - resolution: "http-cache-semantics@npm:4.2.0" - checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37 - languageName: node - linkType: hard - -"http-proxy-agent@npm:^7.0.0": - version: 7.0.2 - resolution: "http-proxy-agent@npm:7.0.2" - dependencies: - agent-base: "npm:^7.1.0" - debug: "npm:^4.3.4" - checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.1": - version: 7.0.6 - resolution: "https-proxy-agent@npm:7.0.6" - dependencies: - agent-base: "npm:^7.1.2" - debug: "npm:4" - checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac - languageName: node - linkType: hard - -"iconv-lite@npm:^0.6.2": - version: 0.6.3 - resolution: "iconv-lite@npm:0.6.3" +"iconv-lite@npm:^0.7.2": + version: 0.7.2 + resolution: "iconv-lite@npm:0.7.2" dependencies: safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + checksum: 10c0/3c228920f3bd307f56bf8363706a776f4a060eb042f131cd23855ceca962951b264d0997ab38a1ad340e1c5df8499ed26e1f4f0db6b2a2ad9befaff22f14b722 languageName: node linkType: hard -"ip-address@npm:^10.0.1": - version: 10.1.0 - resolution: "ip-address@npm:10.1.0" - checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 - languageName: node - linkType: hard - -"isexe@npm:^3.1.1": - version: 3.1.4 - resolution: "isexe@npm:3.1.4" - checksum: 10c0/6e1f78dd794118d263319c27c26a339eda6855b43e11e1a9013cb68578ce0396b74f11ea635e52b9466fb48a60abe70f3c9faf682f67988be092cf89b7c0dc1c +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce languageName: node linkType: hard @@ -950,6 +2194,24 @@ __metadata: languageName: node linkType: hard +"js-yaml@npm:^4.1.0": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 + languageName: node + linkType: hard + +"json-with-bigint@npm:^3.5.3": + version: 3.5.8 + resolution: "json-with-bigint@npm:3.5.8" + checksum: 10c0/a0c4e37626d74a9a493539f9f9a94855933fa15ea2f028859a787229a42c5f11803db6f94f1ce7b1d89756c1e80a7c1f11006bac266ec7ce819b75701765ca0a + languageName: node + linkType: hard + "leven@npm:^3.1.0": version: 3.1.0 resolution: "leven@npm:3.1.0" @@ -1084,12 +2346,26 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": - version: 11.2.5 - resolution: "lru-cache@npm:11.2.5" - checksum: 10c0/cc98958d25dddf1c8a8cbdc49588bd3b24450e8dfa78f32168fd188a20d4a0331c7406d0f3250c86a46619ee288056fd7a1195e8df56dc8a9592397f4fbd8e1d - languageName: node - linkType: hard +"lingui-swc@workspace:packages/lingui-swc": + version: 0.0.0-use.local + resolution: "lingui-swc@workspace:packages/lingui-swc" + dependencies: + "@emnapi/core": "npm:^1.8.1" + "@emnapi/runtime": "npm:^1.8.1" + "@lingui/conf": "npm:^6.0.1" + "@napi-rs/cli": "npm:^3.5.1" + "@oxc-node/core": "npm:^0.0.35" + "@swc/types": "npm:^0.1.25" + "@taplo/cli": "npm:^0.7.0" + "@tybys/wasm-util": "npm:^0.10.0" + "@types/node": "npm:^24.1.0" + oxlint: "npm:^1.14.0" + prettier: "npm:^3.6.2" + tinybench: "npm:^6.0.0" + typescript: "npm:^5.9.3" + vitest: "npm:^4.0.18" + languageName: unknown + linkType: soft "magic-string@npm:^0.30.21": version: 0.30.21 @@ -1100,102 +2376,14 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^15.0.0": - version: 15.0.3 - resolution: "make-fetch-happen@npm:15.0.3" - dependencies: - "@npmcli/agent": "npm:^4.0.0" - cacache: "npm:^20.0.1" - http-cache-semantics: "npm:^4.1.1" - minipass: "npm:^7.0.2" - minipass-fetch: "npm:^5.0.0" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - negotiator: "npm:^1.0.0" - proc-log: "npm:^6.0.0" - promise-retry: "npm:^2.0.1" - ssri: "npm:^13.0.0" - checksum: 10c0/525f74915660be60b616bcbd267c4a5b59481b073ba125e45c9c3a041bb1a47a2bd0ae79d028eb6f5f95bf9851a4158423f5068539c3093621abb64027e8e461 - languageName: node - linkType: hard - -"minimatch@npm:^10.1.2": - version: 10.1.2 - resolution: "minimatch@npm:10.1.2" - dependencies: - "@isaacs/brace-expansion": "npm:^5.0.1" - checksum: 10c0/0cccef3622201703de6ecf9d772c0be1d5513dcc038ed9feb866c20cf798243e678ac35605dac3f1a054650c28037486713fe9e9a34b184b9097959114daf086 - languageName: node - linkType: hard - -"minipass-collect@npm:^2.0.1": - version: 2.0.1 - resolution: "minipass-collect@npm:2.0.1" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e - languageName: node - linkType: hard - -"minipass-fetch@npm:^5.0.0": - version: 5.0.1 - resolution: "minipass-fetch@npm:5.0.1" - dependencies: - encoding: "npm:^0.1.13" - minipass: "npm:^7.0.3" - minipass-sized: "npm:^2.0.0" - minizlib: "npm:^3.0.1" - dependenciesMeta: - encoding: - optional: true - checksum: 10c0/50bcf48c9841ebb25e29a2817468595219c72cfffc7c175a1d7327843c8bef9b72cb01778f46df7eca695dfe47ab98e6167af4cb026ddd80f660842919a5193c - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 - languageName: node - linkType: hard - -"minipass-sized@npm:^2.0.0": - version: 2.0.0 - resolution: "minipass-sized@npm:2.0.0" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10c0/f9201696a6f6d68610d04c9c83e3d2e5cb9c026aae1c8cbf7e17f386105cb79c1bb088dbc21bf0b1eb4f3fb5df384fd1e7aa3bf1f33868c416ae8c8a92679db8 - languageName: node - linkType: hard - -"minipass@npm:^3.0.0": - version: 3.3.6 - resolution: "minipass@npm:3.3.6" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c - languageName: node - linkType: hard - -"minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": - version: 7.1.2 - resolution: "minipass@npm:7.1.2" - checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb languageName: node linkType: hard -"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": +"minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" dependencies: @@ -1211,6 +2399,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "mute-stream@npm:3.0.0" + checksum: 10c0/12cdb36a101694c7a6b296632e6d93a30b74401873cf7507c88861441a090c71c77a58f213acadad03bc0c8fa186639dec99d68a14497773a8744320c136e701 + languageName: node + linkType: hard + "nanoid@npm:^3.3.12": version: 3.3.12 resolution: "nanoid@npm:3.3.12" @@ -1220,30 +2415,23 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:^1.0.0": - version: 1.0.0 - resolution: "negotiator@npm:1.0.0" - checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b - languageName: node - linkType: hard - "node-gyp@npm:latest": - version: 12.2.0 - resolution: "node-gyp@npm:12.2.0" + version: 12.3.0 + resolution: "node-gyp@npm:12.3.0" dependencies: env-paths: "npm:^2.2.0" exponential-backoff: "npm:^3.1.1" graceful-fs: "npm:^4.2.6" - make-fetch-happen: "npm:^15.0.0" nopt: "npm:^9.0.0" proc-log: "npm:^6.0.0" semver: "npm:^7.3.5" tar: "npm:^7.5.4" tinyglobby: "npm:^0.2.12" + undici: "npm:^6.25.0" which: "npm:^6.0.0" bin: node-gyp: bin/node-gyp.js - checksum: 10c0/3ed046746a5a7d90950cd8b0547332b06598443f31fe213ef4332a7174c7b7d259e1704835feda79b87d3f02e59d7791842aac60642ede4396ab25fdf0f8f759 + checksum: 10c0/9d9032b405cbe42f72a105259d9eb679376470c102df4a2dbaa51e07d59bf741dcffb85897087ea9d8318b9cabb824a8978af51508ae142f0239ae1e6a3c2329 languageName: node linkType: hard @@ -1265,27 +2453,83 @@ __metadata: languageName: node linkType: hard -"obug@npm:^2.1.1": +"obug@npm:^2.0.0, obug@npm:^2.1.1": version: 2.1.1 resolution: "obug@npm:2.1.1" checksum: 10c0/59dccd7de72a047e08f8649e94c1015ec72f94eefb6ddb57fb4812c4b425a813bc7e7cd30c9aca20db3c59abc3c85cc7a62bb656a968741d770f4e8e02bc2e78 languageName: node linkType: hard -"p-map@npm:^7.0.2": - version: 7.0.4 - resolution: "p-map@npm:7.0.4" - checksum: 10c0/a5030935d3cb2919d7e89454d1ce82141e6f9955413658b8c9403cfe379283770ed3048146b44cde168aa9e8c716505f196d5689db0ae3ce9a71521a2fef3abd - languageName: node - linkType: hard - -"path-scurry@npm:^2.0.0": - version: 2.0.1 - resolution: "path-scurry@npm:2.0.1" - dependencies: - lru-cache: "npm:^11.0.0" - minipass: "npm:^7.1.2" - checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 +"oxlint@npm:^1.14.0": + version: 1.66.0 + resolution: "oxlint@npm:1.66.0" + dependencies: + "@oxlint/binding-android-arm-eabi": "npm:1.66.0" + "@oxlint/binding-android-arm64": "npm:1.66.0" + "@oxlint/binding-darwin-arm64": "npm:1.66.0" + "@oxlint/binding-darwin-x64": "npm:1.66.0" + "@oxlint/binding-freebsd-x64": "npm:1.66.0" + "@oxlint/binding-linux-arm-gnueabihf": "npm:1.66.0" + "@oxlint/binding-linux-arm-musleabihf": "npm:1.66.0" + "@oxlint/binding-linux-arm64-gnu": "npm:1.66.0" + "@oxlint/binding-linux-arm64-musl": "npm:1.66.0" + "@oxlint/binding-linux-ppc64-gnu": "npm:1.66.0" + "@oxlint/binding-linux-riscv64-gnu": "npm:1.66.0" + "@oxlint/binding-linux-riscv64-musl": "npm:1.66.0" + "@oxlint/binding-linux-s390x-gnu": "npm:1.66.0" + "@oxlint/binding-linux-x64-gnu": "npm:1.66.0" + "@oxlint/binding-linux-x64-musl": "npm:1.66.0" + "@oxlint/binding-openharmony-arm64": "npm:1.66.0" + "@oxlint/binding-win32-arm64-msvc": "npm:1.66.0" + "@oxlint/binding-win32-ia32-msvc": "npm:1.66.0" + "@oxlint/binding-win32-x64-msvc": "npm:1.66.0" + peerDependencies: + oxlint-tsgolint: ">=0.22.1" + dependenciesMeta: + "@oxlint/binding-android-arm-eabi": + optional: true + "@oxlint/binding-android-arm64": + optional: true + "@oxlint/binding-darwin-arm64": + optional: true + "@oxlint/binding-darwin-x64": + optional: true + "@oxlint/binding-freebsd-x64": + optional: true + "@oxlint/binding-linux-arm-gnueabihf": + optional: true + "@oxlint/binding-linux-arm-musleabihf": + optional: true + "@oxlint/binding-linux-arm64-gnu": + optional: true + "@oxlint/binding-linux-arm64-musl": + optional: true + "@oxlint/binding-linux-ppc64-gnu": + optional: true + "@oxlint/binding-linux-riscv64-gnu": + optional: true + "@oxlint/binding-linux-riscv64-musl": + optional: true + "@oxlint/binding-linux-s390x-gnu": + optional: true + "@oxlint/binding-linux-x64-gnu": + optional: true + "@oxlint/binding-linux-x64-musl": + optional: true + "@oxlint/binding-openharmony-arm64": + optional: true + "@oxlint/binding-win32-arm64-msvc": + optional: true + "@oxlint/binding-win32-ia32-msvc": + optional: true + "@oxlint/binding-win32-x64-msvc": + optional: true + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + bin: + oxlint: bin/oxlint + checksum: 10c0/c43a1141159ef3a32d9d7d49852c9439b9e65dd7c0021ca76f5677f9c0b3a495270f00c2daa2115686997fbf5737a769882bbdeb84276558e0105e6ac6587432 languageName: node linkType: hard @@ -1303,20 +2547,20 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.3": - version: 4.0.3 - resolution: "picomatch@npm:4.0.3" - checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 - languageName: node - linkType: hard - -"picomatch@npm:^4.0.4": +"picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": version: 4.0.4 resolution: "picomatch@npm:4.0.4" checksum: 10c0/e2c6023372cc7b5764719a5ffb9da0f8e781212fa7ca4bd0562db929df8e117460f00dff3cb7509dacfc06b86de924b247f504d0ce1806a37fac4633081466b0 languageName: node linkType: hard +"pirates@npm:^4.0.7": + version: 4.0.7 + resolution: "pirates@npm:4.0.7" + checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a + languageName: node + linkType: hard + "postcss@npm:^8.5.15": version: 8.5.15 resolution: "postcss@npm:8.5.15" @@ -1328,6 +2572,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.6.2": + version: 3.8.3 + resolution: "prettier@npm:3.8.3" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/754816fd7593eb80f6376d7476d463e832c38a12f32775a82683adb6e35b772b1f484d65f19401507b983a8c8a7cd5a4a9f12006bd56491e8f35503473f77473 + languageName: node + linkType: hard + "pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" @@ -1346,16 +2599,6 @@ __metadata: languageName: node linkType: hard -"promise-retry@npm:^2.0.1": - version: 2.0.1 - resolution: "promise-retry@npm:2.0.1" - dependencies: - err-code: "npm:^2.0.2" - retry: "npm:^0.12.0" - checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 - languageName: node - linkType: hard - "react-is@npm:^18.0.0": version: 18.3.1 resolution: "react-is@npm:18.3.1" @@ -1363,13 +2606,6 @@ __metadata: languageName: node linkType: hard -"retry@npm:^0.12.0": - version: 0.12.0 - resolution: "retry@npm:0.12.0" - checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe - languageName: node - linkType: hard - "rolldown@npm:1.0.2": version: 1.0.2 resolution: "rolldown@npm:1.0.2" @@ -1441,12 +2677,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5": - version: 7.7.4 - resolution: "semver@npm:7.7.4" +"semver@npm:^7.3.5, semver@npm:^7.7.3": + version: 7.8.1 + resolution: "semver@npm:7.8.1" bin: semver: bin/semver.js - checksum: 10c0/5215ad0234e2845d4ea5bb9d836d42b03499546ddafb12075566899fc617f68794bb6f146076b6881d755de17d6c6cc73372555879ec7dce2c2feee947866ad2 + checksum: 10c0/92d6871d6347e1f99d0ba396a70f2545ccf2a032cda3d378fa0699edf7506b5c6d266aed55c8b88e72bd91a30d2351e4f39db479375374430fcdc4b58f4e3c1a languageName: node linkType: hard @@ -1457,31 +2693,10 @@ __metadata: languageName: node linkType: hard -"smart-buffer@npm:^4.2.0": - version: 4.2.0 - resolution: "smart-buffer@npm:4.2.0" - checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^8.0.3": - version: 8.0.5 - resolution: "socks-proxy-agent@npm:8.0.5" - dependencies: - agent-base: "npm:^7.1.2" - debug: "npm:^4.3.4" - socks: "npm:^2.8.3" - checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6 - languageName: node - linkType: hard - -"socks@npm:^2.8.3": - version: 2.8.7 - resolution: "socks@npm:2.8.7" - dependencies: - ip-address: "npm:^10.0.1" - smart-buffer: "npm:^4.2.0" - checksum: 10c0/2805a43a1c4bcf9ebf6e018268d87b32b32b06fbbc1f9282573583acc155860dc361500f89c73bfbb157caa1b4ac78059eac0ef15d1811eb0ca75e0bdadbc9d2 +"signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 languageName: node linkType: hard @@ -1492,15 +2707,6 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^13.0.0": - version: 13.0.0 - resolution: "ssri@npm:13.0.0" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/405f3a531cd98b013cecb355d63555dca42fd12c7bc6671738aaa9a82882ff41cdf0ef9a2b734ca4f9a760338f114c29d01d9238a65db3ccac27929bd6e6d4b2 - languageName: node - linkType: hard - "stackback@npm:0.0.2": version: 0.0.2 resolution: "stackback@npm:0.0.2" @@ -1525,15 +2731,15 @@ __metadata: linkType: hard "tar@npm:^7.5.4": - version: 7.5.7 - resolution: "tar@npm:7.5.7" + version: 7.5.15 + resolution: "tar@npm:7.5.15" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10c0/51f261afc437e1112c3e7919478d6176ea83f7f7727864d8c2cce10f0b03a631d1911644a567348c3063c45abdae39718ba97abb073d22aa3538b9a53ae1e31c + checksum: 10c0/8f039edb1d12fdd7df6c6f9877d125afe9f3da3f5f9317df326fdd090d48793d6998cede1506a1471f3e3a250db270a89dace28005eb5e99c5a9132d704ac956 languageName: node linkType: hard @@ -1544,24 +2750,21 @@ __metadata: languageName: node linkType: hard -"tinyexec@npm:^1.0.2": - version: 1.0.2 - resolution: "tinyexec@npm:1.0.2" - checksum: 10c0/1261a8e34c9b539a9aae3b7f0bb5372045ff28ee1eba035a2a059e532198fe1a182ec61ac60fa0b4a4129f0c4c4b1d2d57355b5cb9aa2d17ac9454ecace502ee +"tinybench@npm:^6.0.0": + version: 6.0.2 + resolution: "tinybench@npm:6.0.2" + checksum: 10c0/89e791f4dbafa788442f0cc3be49a487769bdb8990bb1b5a989b5b52215134840cf4e8ea1195080f28a6fb270c5b2e264435136935f43e1dd95b457f24c5dea1 languageName: node linkType: hard -"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15": - version: 0.2.15 - resolution: "tinyglobby@npm:0.2.15" - dependencies: - fdir: "npm:^6.5.0" - picomatch: "npm:^4.0.3" - checksum: 10c0/869c31490d0d88eedb8305d178d4c75e7463e820df5a9b9d388291daf93e8b1eb5de1dad1c1e139767e4269fe75f3b10d5009b2cc14db96ff98986920a186844 +"tinyexec@npm:^1.0.2": + version: 1.1.2 + resolution: "tinyexec@npm:1.1.2" + checksum: 10c0/9e0ef6c001ce54688cf16833a02f70a339276219ca947b88930b124267de2cffc764ff44e87e7369384b1d75ab63491465412cbbdf06f2437956b9ab66ab4491 languageName: node linkType: hard -"tinyglobby@npm:^0.2.16": +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15, tinyglobby@npm:^0.2.16": version: 0.2.16 resolution: "tinyglobby@npm:0.2.16" dependencies: @@ -1585,6 +2788,23 @@ __metadata: languageName: node linkType: hard +"typanion@npm:^3.14.0, typanion@npm:^3.8.0": + version: 3.14.0 + resolution: "typanion@npm:3.14.0" + checksum: 10c0/8b03b19844e6955bfd906c31dc781bae6d7f1fb3ce4fe24b7501557013d4889ae5cefe671dafe98d87ead0adceb8afcb8bc16df7dc0bd2b7331bac96f3a7cae2 + languageName: node + linkType: hard + +"typescript@npm:^5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + "typescript@npm:^6.0.3": version: 6.0.3 resolution: "typescript@npm:6.0.3" @@ -1595,6 +2815,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A^6.0.3#optional!builtin": version: 6.0.3 resolution: "typescript@patch:typescript@npm%3A6.0.3#optional!builtin::version=6.0.3&hash=5786d5" @@ -1619,21 +2849,24 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^5.0.0": - version: 5.0.0 - resolution: "unique-filename@npm:5.0.0" - dependencies: - unique-slug: "npm:^6.0.0" - checksum: 10c0/afb897e9cf4c2fb622ea716f7c2bb462001928fc5f437972213afdf1cc32101a230c0f1e9d96fc91ee5185eca0f2feb34127145874975f347be52eb91d6ccc2c +"undici-types@npm:~7.16.0": + version: 7.16.0 + resolution: "undici-types@npm:7.16.0" + checksum: 10c0/3033e2f2b5c9f1504bdc5934646cb54e37ecaca0f9249c983f7b1fc2e87c6d18399ebb05dc7fd5419e02b2e915f734d872a65da2e3eeed1813951c427d33cc9a languageName: node linkType: hard -"unique-slug@npm:^6.0.0": - version: 6.0.0 - resolution: "unique-slug@npm:6.0.0" - dependencies: - imurmurhash: "npm:^0.1.4" - checksum: 10c0/da7ade4cb04eb33ad0499861f82fe95ce9c7c878b7139dc54d140ecfb6a6541c18a5c8dac16188b8b379fe62c0c1f1b710814baac910cde5f4fec06212126c6a +"undici@npm:^6.25.0": + version: 6.25.0 + resolution: "undici@npm:6.25.0" + checksum: 10c0/2597cc6689bdb02c210c557b1f85febbfda65becae6e6fc1061508e2f33734d25207f81cd8af56ada9956329eb3a7bd7431e87dcfeceba20ee87059b57dcf985 + languageName: node + linkType: hard + +"universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": + version: 7.0.3 + resolution: "universal-user-agent@npm:7.0.3" + checksum: 10c0/6043be466a9bb96c0ce82392842d9fddf4c37e296f7bacc2cb25f47123990eb436c82df824644f9c5070a94dbdb117be17f66d54599ab143648ec57ef93dbcc8 languageName: node linkType: hard @@ -1694,7 +2927,7 @@ __metadata: languageName: node linkType: hard -"vitest@npm:^4.1.7": +"vitest@npm:^4.0.18, vitest@npm:^4.1.7": version: 4.1.7 resolution: "vitest@npm:4.1.7" dependencies: @@ -1763,13 +2996,13 @@ __metadata: linkType: hard "which@npm:^6.0.0": - version: 6.0.0 - resolution: "which@npm:6.0.0" + version: 6.0.1 + resolution: "which@npm:6.0.1" dependencies: - isexe: "npm:^3.1.1" + isexe: "npm:^4.0.0" bin: node-which: bin/which.js - checksum: 10c0/fe9d6463fe44a76232bb6e3b3181922c87510a5b250a98f1e43a69c99c079b3f42ddeca7e03d3e5f2241bf2d334f5a7657cfa868b97c109f3870625842f4cc15 + checksum: 10c0/7e710e54ea36d2d6183bee2f9caa27a3b47b9baf8dee55a199b736fcf85eab3b9df7556fca3d02b50af7f3dfba5ea3a45644189836df06267df457e354da66d5 languageName: node linkType: hard @@ -1785,13 +3018,6 @@ __metadata: languageName: node linkType: hard -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a - languageName: node - linkType: hard - "yallist@npm:^5.0.0": version: 5.0.0 resolution: "yallist@npm:5.0.0"