feat(parity): export + i18n key parity gates (§8b)#347
Merged
jedrazb merged 2 commits into1.0.0-releasefrom May 4, 2026
Merged
feat(parity): export + i18n key parity gates (§8b)#347jedrazb merged 2 commits into1.0.0-releasefrom
jedrazb merged 2 commits into1.0.0-releasefrom
Conversation
Implements §8b.3-8b.8 of the vue-editor-robust-implementation OpenSpec change. Two new scripts gate React/Vue adapter drift in the dev loop and CI, fast (<1s combined). scripts/check-export-parity.mjs - Tier 1, STRICT: package.json `exports` subpaths must match between React and Vue, with documented opt-outs in notes/intentional-export-divergence.md (`./react`, `./ui`, `./styles.css` are permanent react-only; `./core`, `./headless`, `./core-plugins`, `./mcp`, `./i18n/*.json` are temporary while Vue catches up). - Tier 2, INFORMATIONAL: AST-walked named exports from src/index.ts on both adapters. Logs the drift count (518 react-only, 29 vue-only today) so the gap stays visible. Task §13b flips this to strict on un-stub. Uses the TS compiler API for accurate parsing of barrel re-exports, named exports, type exports, and aliases. - Pulls the divergence opt-out from a markdown notes file so reasons live next to the path; regex matches only the FIRST backtick on a list-item line so prose secondary backticks don't widen the opt-out. scripts/check-i18n-parity.mjs - Walks every locale present in BOTH packages/react/i18n/ and packages/vue/i18n/, diffs key paths via the shared getLeafPaths helper. - Vue-only locales fail the gate (Vue can't ship a translation React doesn't have without breaking the source-of-truth contract). - React-only locales are informational; Vue catches up via community translation issues #341-344, #302, #303. scripts/lib/i18n-keys.mjs (new) - Hoisted `getLeafPaths` helper used by both scripts. The existing `validate-i18n.mjs` had its own copy; refactored to import from the shared lib. METADATA_KEYS expanded from `['_lang']` to "any key starting with `_`" to cover future metadata fields without script edits. scripts/lib/named-exports.mjs (new) - TS-compiler-API-based AST walker. Captures top-level `export const`, `export function`, `export class`, `export type`, `export interface`, `export enum`, `export default`, `export { X, Y as Z }`, and `export * from './foo'` (recursively, with cycle protection). Pre-commit hook gains a `bun run check:parity` step before lint-staged. Combined gate runs in <100ms — no perceptible commit slowdown. packages/vue/i18n/en.json: synced with React's source-of-truth so the English key set matches (Vue PR #245 had drift; bulk-copied from packages/react/i18n/en.json — 633 keys total, including agentPanel.* which Vue doesn't yet use but ships forward-looking). intentional-export-divergence.md: 8 React-only subpaths documented with reason and lifecycle (permanent vs hardening-temporary). Tasks 8b.3, 8b.4, 8b.5, 8b.6, 8b.7, 8b.8 — done. New task 13.6a — flip STRICT_NAMED_EXPORTS at un-stub. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Round-2 review feedback: HIGH (simplifier): both parity scripts repeated the same set-diff + formatted-report pattern. Extracted into scripts/lib/parity-report.mjs exporting diffSets() and formatDiff(). Both scripts now compose: build two Sets, call diffSets, hand the result to formatDiff with the appropriate strict/informational flag and labels. Truncation policy (at 25) becomes a parameter — divergence between gates is one number per call site, not three implementations. NIT (reviewer): spec said "diff dist/index.d.ts named exports" but implementation walks src/index.ts via the TS compiler API. The src walk is more accurate (resolves through TS path aliases, no build required, follows barrel re-exports recursively). Updated the spec requirement wording to match implementation reality + add a sentence explaining the choice. Also documented the two-tier (strict subpath / informational named-export) gate split there. Verified end-to-end: - bun run check:parity — clean (subpath strict pass, named informational with the same 518/29 count, i18n en pass) - bun run typecheck — clean across all 5 packages - Smoke: planted `export const TestExport123 = 1` in packages/react/src/index.ts → count became 519 react-only; reverted cleanly. The drift detection works exactly as before the refactor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements §8b.3-8b.8 of the vue-editor-robust-implementation OpenSpec change. Two new gates fail the pre-commit hook (and CI) when React and Vue adapter public surfaces drift.
What ships
scripts/check-export-parity.mjsTwo-tier gate:
package.jsonexportssubpathssrc/index.ts(AST-walked)Tier 2 stays informational during hardening; task §13b flips
STRICT_NAMED_EXPORTS = trueonce the parity matrix turns green. Documented in the notes file.scripts/check-i18n-parity.mjsWalks every locale present in both
packages/react/i18n/andpackages/vue/i18n/. Diffs key paths via the sharedgetLeafPathshelper.Shared libs (DRY)
scripts/lib/i18n-keys.mjs—getLeafPathshoisted fromscripts/validate-i18n.mjs. METADATA_KEYS broadened from['_lang']to "any key starting with_" to cover future metadata.scripts/lib/named-exports.mjs— TS-compiler-API-based AST walker for top-level exports + barrel re-exports + cycle-safe star re-exports.Plumbing
bun run check:export-parity,bun run check:i18n-parity,bun run check:paritybun run check:paritybefore lint-staged. Combined gate <100ms — no commit slowdown.packages/vue/i18n/en.jsonsynced with React's source-of-truth (633 keys; PR feat: add vue version of docx editor #245 had drift).notes/intentional-export-divergence.md— 8 documented React-only subpaths with lifecycle (permanent vs hardening-temporary).Reviewer findings addressed
Two BUGs from round-1 review:
exports) — implemented via TS AST walker, but tiered as INFORMATIONAL during hardening because 547 names of drift today would block every commit. Strict-mode flag flipped at un-stub.NITs addressed:
_*metadata skip (was hardcoded_lang), sharedgetLeafPaths(was duplicated acrossvalidate-i18n.mjs), opt-out parser anchored to first-backtick-on-list-item (so prose with secondary backticks doesn't widen the opt-out).Verification
bun run check:parity— subpath strict pass, named informational, i18n en passbun run typecheck— clean across all 5 packagesbun run i18n:validate— pre-existing he.json drift NOT regressed by this PR (verified via stash test)./vue-onlysubpath added topackages/vue/package.jsonexports — gate fails as expected with clear messageTasks
check-export-parity.mjsbun run check:export-paritybun run check:parityvia the same lint job)check-i18n-parity.mjsi18n/dir (mirrors React)bun run check:i18n-parity+ pre-commit🤖 Generated with Claude Code