Skip to content

test(vue): add Vite integration example - #2014

Open
eoinest wants to merge 7 commits into
e/cli/add-vue-extractionfrom
e/vue/add-vue-vite-example
Open

test(vue): add Vite integration example#2014
eoinest wants to merge 7 commits into
e/cli/add-vue-extractionfrom
e/vue/add-vue-vite-example

Conversation

@eoinest

@eoinest eoinest commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add a production Vite + Vue example covering the complete lightweight gt-vue surface, including child-only Var, typed value bindings, and Vue TSX through local re-exports, namespace components, and forwarded translators.
  • Demonstrate async English/French catalogs, reactive locale switching, plural/branch state, literal STRING lookups, and the bring-your-own-static-hosting model.
  • Use only Vite's standard Vue plugin: no GT compiler plugin, framework server, or development hot-reload integration.

Testing

  • gt generate and gt validate — passed with exactly 21 entries; regenerated English/French catalogs remained byte-for-byte unchanged
  • vue-tsc -b and Vite 7.3.1 production build — passed
  • Dev and production-preview Chromium loops — passed EN → FR → EN → FR, number/plural and branch updates, localized currency/date values, literal braces, document language, and ARIA states with zero browser warnings/errors
  • Lazy loading — French chunk fetched exactly once; no English catalog chunk fetched for the source locale
  • Fresh tarball consumers on Vue 3.3.13 and 3.5.40 — all five GT packages resolved from local archives with zero workspace: references; packed CLI generate/validate, typecheck, build, and browser loops passed

Notes

Greptile Summary

This PR adds a new examples/vite-vue workspace that demonstrates the full lightweight gt-vue API on top of Vite 7 and Vue 3, using only the standard @vitejs/plugin-vue and @vitejs/plugin-vue-jsx plugins — no GT compiler, server, or hot-reload integration. The PR description documents comprehensive manual testing (EN ↔ FR loops, lazy loading, typecheck, production build) and confirms gt generate/gt validate passed with exactly 21 entries.

  • App.vue exercises <T>, child-only <Var>, <Plural>, <Branch>, <Num>, <Currency>, <DateTime>, useGT(), msg()/useMessages(), and reactive useLocale()/useSetLocale() with correct ARIA state management and document.lang synchronisation via a watcher.
  • TsxCompatibilityCard.tsx demonstrates three TSX patterns against the same catalog: a locally re-exported <T> with <Vue.Fragment>, a namespace <GT.T>, and a forwarded GTFunction helper.
  • translations.ts uses import.meta.glob for per-locale lazy-loading; vite.config.ts adds resolve.dedupe: ['vue'] to prevent duplicate Vue instances from linked workspace packages.

Confidence Score: 5/5

  • This PR adds only a self-contained example workspace with no changes to library source code, no published packages, and no modifications to shared build infrastructure beyond a one-line changeset ignore entry.
  • All changes are confined to a new examples/vite-vue directory and a single pnpm-lock.yaml update. No library code, shared utilities, or CI pipelines are modified. The example is correctly structured, TypeScript-strict, and the PR author reports full test coverage including typecheck, production build, and browser verification loops.
  • No files require special attention.

Important Files Changed

Filename Overview
examples/vite-vue/src/App.vue Main SFC demonstrating the complete gt-vue API: T, Var, Plural, Branch, Currency, DateTime, Num, useGT, useMessages, useLocale, useSetLocale, and msg. Locale switching correctly uses try/finally to reset loading state. ARIA states and document.lang are properly managed.
examples/vite-vue/src/translations.ts Uses import.meta.glob for lazy-loaded per-locale JSON chunks. Returns an empty catalog for unknown locales (graceful degradation). Vite will code-split each locale into its own chunk; the French chunk is only fetched on first locale switch.
examples/vite-vue/src/components/TsxCompatibilityCard.tsx Demonstrates three TSX usage patterns: local re-exported T with Vue.Fragment, namespace GT.T component, and a forwarded GTFunction. All patterns exercise the same catalog keys validated by gt generate/validate.
examples/vite-vue/src/i18n.ts Barrel re-exporting gt-vue as both named exports and as a GT namespace, and vue as a Vue namespace, to enable namespace-style component usage in TSX (GT.T, Vue.Fragment). Intentional pattern for the compatibility demo.
examples/vite-vue/vite.config.ts Registers vue and vueJsx plugins. The resolve.dedupe: ['vue'] entry is important and correct — it prevents duplicate Vue instances when workspace packages also depend on Vue.
examples/vite-vue/package.json Pre-scripts build the relevant workspace packages before each command (dev, generate, typecheck, build). The build script chains generate → tsc → vite build, ensuring catalogs are always up to date before a production build.
.changeset/config.json Adds vite-vue to the changeset ignored packages list, consistent with other example workspaces. Correct — example apps should not generate changesets.

Sequence Diagram

sequenceDiagram
    participant User
    participant App.vue
    participant gt-vue
    participant translations.ts
    participant ViteChunk as Vite JSON chunk

    User->>App.vue: Click language button
    App.vue->>App.vue: selectLocale(nextLocale)
    Note over App.vue: changingLocale = true
    App.vue->>gt-vue: setLocale(nextLocale)
    gt-vue->>translations.ts: loadTranslations(locale)
    translations.ts->>ViteChunk: dynamic import('./_gt/fr.json')
    ViteChunk-->>translations.ts: TranslationCatalog
    translations.ts-->>gt-vue: "TranslationCatalog (or {} if not found)"
    gt-vue-->>App.vue: locale Ref updated
    Note over App.vue: changingLocale = false (finally)
    App.vue->>App.vue: watch(locale) fires
    App.vue->>App.vue: "document.documentElement.lang = locale"
    App.vue-->>User: UI re-renders with new locale
Loading

Reviews (17): Last reviewed commit: "test(vue): exercise TSX extraction in Vi..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
generaltranslation 18.09 KB (0%)
generaltranslation/runtime 14.87 KB (0%)
generaltranslation/id 2.55 KB (0%)
generaltranslation/internal 7.36 KB (0%)
generaltranslation/types 115 B (0%)
generaltranslation/errors 81 B (0%)
@generaltranslation/format 9.5 KB (0%)
@generaltranslation/format/types 89 B (0%)
@generaltranslation/format/internal 880 B (0%)
gt-i18n 11.96 KB (0%)
gt-i18n/types 13 B (0%)
gt-i18n/internal 22.4 KB (0%)
gt-i18n/internal/cookies 228 B (0%)
gt-i18n/internal/string 2.9 KB (0%)
gt-i18n/internal/types 13 B (0%)
@generaltranslation/react-core/pure 25.6 KB (0%)
@generaltranslation/react-core/hooks 20.6 KB (0%)
@generaltranslation/react-core/components 22.74 KB (0%)
@generaltranslation/react-core/components-rsc 26.19 KB (0%)
gt-react (client) 32.14 KB (0%)
gt-react (rsc) 28.71 KB (0%)
gt-react (server) 31.77 KB (0%)
gt-react/macros 8.81 KB (0%)
gt-vue 8.18 KB (0%)
gt-next (client) 44.2 KB (0%)
gt-next (rsc) 48.16 KB (0%)
gt-next (server) 44.31 KB (0%)
gt-next/config 270.72 KB (0%)
gt-next/server 46.71 KB (0%)
gt-next/middleware 36.82 KB (0%)
gt-next/link 43.02 KB (0%)
gt-next/internal/_dictionary 144 B (0%)
gt-next/internal/_load-translations 144 B (0%)
gt-next/internal/_load-dictionary 144 B (0%)
gt-next/internal/_getLocale 125 B (0%)
gt-next/internal/_getRegion 122 B (0%)
gt-node 23.61 KB (0%)
gt-node/types 219 B (0%)
gt-node/internal 13.43 KB (0%)
gt-tanstack-start (client) 31.68 KB (0%)
gt-tanstack-start (server) 32.13 KB (0%)
gt-tanstack-start/server 10.27 KB (0%)
gt-react-native 30.11 KB (0%)
gt-react-native/plugin 4.6 KB (0%)
gt-react-native/internal 746 B (0%)

@eoinest

eoinest commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

4 similar comments
@eoinest

eoinest commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest

eoinest commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest

eoinest commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest

eoinest commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest

eoinest commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from 06e8490 to bd5ef4d Compare August 1, 2026 02:50
@eoinest

eoinest commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from bd5ef4d to 6714637 Compare August 1, 2026 03:09
@eoinest

eoinest commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from 6714637 to b983e51 Compare August 5, 2026 20:15
@eoinest

eoinest commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest

eoinest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

1 similar comment
@eoinest

eoinest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

archie-mckenzie
archie-mckenzie previously approved these changes Aug 6, 2026
@eoinest

eoinest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

1 similar comment
@eoinest

eoinest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest
eoinest changed the base branch from e/cli/add-vue-extraction to e/release/enter-iris-prerelease August 6, 2026 19:05
@eoinest
eoinest changed the base branch from e/release/enter-iris-prerelease to e/cli/add-vue-extraction August 6, 2026 19:17
@eoinest

eoinest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest
eoinest dismissed archie-mckenzie’s stale review August 6, 2026 20:16

The merge-base changed after approval.

@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from bf57f83 to 6f54b09 Compare August 6, 2026 22:29
@eoinest

eoinest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from 6f54b09 to 9137192 Compare August 7, 2026 00:43
@eoinest

eoinest commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai

eoinest added a commit that referenced this pull request Aug 7, 2026
## Summary

- Add an unstable, lightweight `gt-vue` runtime with `T`, child-only
`Var`, typed-value `Num` / `DateTime` / `Currency`, `Plural`, `Branch`,
`useGT`, `useMessages`, and `msg`.
- Keep string translation deliberately narrow: `$context` is the only
public metadata option; all content is literal STRING data with no ICU
interpolation, formatting fields, or development hot reload.
- Provide isolated `createGT()` instances, async per-locale catalog
caching, reactive locale changes, latest-wins concurrency, retryable
loader diagnostics, and source-locale fast paths for client and SSR use.
- Preserve Vue component props/listeners and scoped slots, keep
rich-content IDs and plural selection compatible with react-core,
exclude presentation/runtime attributes from persisted Branch hashes,
and suppress unsupported fallthrough attrs on wrapperless value
components.
- Share the literal STRING hash/registration contract through
`gt-i18n/internal/string`; `gt-i18n` is a real external runtime
dependency in both ESM and CJS.

## Testing

- `gt-vue`: full runtime suite passed, including SSR/hydration, keyed
VNode reconciliation, opaque slots, Suspense, and concurrency
- `gt-i18n`: 33 files / 314 tests
- `@generaltranslation/react-core`: 12 files / 50 tests, including
shared hand-pinned wire-format fixtures
- Forced Turbo build/typecheck for the runtime dependency graph — passed
- Root library-default, lint, and format gates — passed
- Size limits — passed (`gt-vue` 7.56 kB Brotli;
`gt-i18n/internal/string` 2.97 kB)
- Packed consumers on Vue 3.3.13 and 3.5.40 — passed ESM, CJS, SSR,
`vue-tsc`, Vite production builds, and real Chromium locale/formatting
loops with zero console warnings/errors
- Changesets simulation — `gt-vue@0.1.0` correctly depends on
`gt-i18n@1.1.0`

## Notes

- `gt-vue` is explicitly documented as unstable and independently
versioned during 0.x.
- Changesets: minor for both the first `gt-vue` release and the literal
STRING contract update in `gt-i18n`.
- Stack 1/4. Base: `main`. Next: #2013, #2014, then #2033.
- This PR is not being merged or published yet.

<!-- greptile_comment -->

<h3>Greptile Summary</h3>

This PR introduces `gt-vue`, a new lightweight Vue 3 i18n runtime
package, plus a shared `gt-i18n/internal/string` contract for
STRING-format translation hashing and cookie utilities consumed by both
Vue and React runtimes. It also refactors `gt-i18n`'s `msg` to delegate
to a new `msgString` path when `$format: 'STRING'` is requested, and
consolidates cookie-name constants from `react-core` into `gt-i18n`.

- **`gt-vue` runtime**: adds `T`, `Var`, `Num`, `DateTime`, `Currency`,
`Plural`, `Branch`, `createGT`, `useGT`, `useMessages`, `useLocale`,
`useSetLocale`, and `msg` — with reactive locale state, per-locale
catalog caching, latest-wins concurrency, SSR/hydration support, and
source-locale fast paths.
- **`gt-i18n` additions**: `msgString`, `encodeMsg`,
`hashStringMessage`, browser cookie utilities, and new internal entry
points (`internal-string`, `internal-cookies`) shared across frameworks.
- **`react-core` cleanup**: `cookieNames.ts` now re-exports from
`gt-i18n/internal/cookies` rather than duplicating constants.

<details open><summary><h3>Confidence Score: 5/5</h3></summary>

- This PR is safe to review and merge — no functional regressions or
data-loss paths were found across the new Vue runtime, the shared
gt-i18n STRING contract, or the react-core consolidation.
- The new gt-vue package adds a large surface area (rich-content
rendering, locale concurrency, SSR/hydration, cookie management) but
each subsystem is self-contained and backed by targeted tests. The
latest-wins concurrency logic in setLocale, the revision-counter
reactivity contract, the source-locale fast-path that deliberately skips
catalog reads, the VNode identity cache rollback on render errors, and
the `isEncodedTranslationOptions` type guard before msg decode are all
implemented correctly. The react-core cookie constant consolidation is a
straight re-export with no behavioral change. No blocking issues were
found.
- No files require special attention.
</details>

<details><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| packages/vue/src/runtime/state.ts | Core plugin factory. Locale
concurrency (latest-wins), per-locale catalog caching, cookie-backed
locale persistence, and reactive revision counter are all implemented
correctly. `console.error` on load failure is intentional diagnostic
output, not debug logging. |
| packages/vue/src/rendering/translateVueChildren.ts | Rich-content
translation engine. Source serialization, hash-based catalog lookup,
variable/branch/plural rendering, VNode identity caching with rollback,
and metadata copying all look correct. `copyRenderMetadata` relies on
undocumented `ctx` / `slotScopeIds` fields (flagged in a previous review
thread). `cloneWithProps` for components intentionally passes a VNode to
`h()` to take the clone-and-renormalize path. |
| packages/vue/src/runtime/localeCookie.ts | Cookie-backed locale
accessor. SSR/hydration contract is correct: explicit locale overrides a
stale browser cookie on first mount, `getLocale` reads live cookie on
browser, `serverLocale` closure isolates per-request SSR state. |
| packages/vue/src/composables/strings.ts | useGT and useMessages
composables. Encoded msg metadata is decoded and short-circuited through
the precomputed hash path; raw strings fall through to the regular
STRING hash lookup. Nullish passthrough is correct. |
| packages/vue/src/components/variables.ts | Num, DateTime, Currency,
and Var components. Null/empty-string guards, NaN fallback to
String(value), and _locale override for the rich-translation pipeline
are all correctly implemented. |
| packages/vue/src/components/branches.ts | Plural and Branch
components. getBranchNames deduplicates attr/slot names via Set
(regression test included). getBranchContent slot-wins logic and
getFormatLocales fallback are correct. |
| packages/i18n/src/translation-functions/msg/msgString.ts | New
STRING-format msg registration path. Array recursion with $id indexing,
STRING format tagging, and encodeMsg delegation are all correct. The
early return when options is absent preserves the original input's type.
|
| packages/i18n/src/utils/hashStringMessage.ts | Canonical STRING
catalog key computation. Precomputed $_hash short-circuit is used
correctly by both msgString and translateString to avoid re-hashing. |
| packages/react-core/src/setup/cookieNames.ts | Replaced duplicated
cookie constants with re-exports from gt-i18n/internal/cookies. Single
source of truth for cookie names across runtimes. |
| packages/vue/src/components/utils.ts | Shared component utilities.
isBranchAttribute correctly excludes event handlers, ARIA/data
attributes, and the NON_BRANCH_ATTRIBUTE_NAMES set. getBranchNames
deduplication and getBranchContent slot-priority logic look correct. |

</details>

<details><summary><h3>Sequence Diagram</h3></summary>

```mermaid
sequenceDiagram
    participant App as Vue App
    participant Plugin as GTPlugin (createGT)
    participant State as GTState
    participant Cookie as CookieBackedLocale
    participant Loader as loadTranslations
    participant Catalog as catalogs Map
    participant T as T Component
    participant Renderer as translateVueChildren

    App->>Plugin: app.use(plugin)
    Plugin->>State: provide(gtContextKey, state)
    Plugin->>Loader: load(getLocale()) [async, fire-and-forget]
    Loader-->>Catalog: catalogs.set(locale, catalog)
    Loader-->>State: "revision.value += 1"

    App->>Plugin: setLocale('fr')
    Plugin->>Loader: load('fr')
    note over Plugin,Loader: concurrent requests share one Promise<br/>latest-wins: older requests return early
    Loader-->>Catalog: catalogs.set('fr', catalog)
    Plugin->>Cookie: setLocale('fr') → writes cookie
    Plugin-->>State: "revision.value += 1"

    T->>State: getLocale() → reads revision (reactive)
    T->>State: getCatalog() → reads revision (reactive)
    T->>Renderer: translateVueChildren(vnodes, state, options)
    Renderer->>Renderer: createSourceNodes → serializeNodes → hashSource
    Renderer->>Catalog: getCatalog()[hash]
    Renderer-->>T: translated VNodeChild
```
</details>

<sub>Reviews (13): Last reviewed commit: ["fix(vue): persist locale in
browser
cook..."](163973a)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=48899958)</sub>

<!-- /greptile_comment -->
@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from 9137192 to c03f9c6 Compare August 7, 2026 00:53
@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from c03f9c6 to 9ef233a Compare August 7, 2026 02:19
@eoinest
eoinest force-pushed the e/vue/add-vue-vite-example branch from 9ef233a to d984cbd Compare August 7, 2026 02:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants