@vizejs/nuxt 0.285.0 → 0.286.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +137 -41
  2. package/package.json +4 -4
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import fs from "node:fs";
3
3
  import path, { dirname, resolve } from "node:path";
4
+ import { parseSync } from "vite";
4
5
  import { createHash } from "node:crypto";
5
6
  //#region src/components.ts
6
7
  const COMPONENT_CALL_RE = /_?resolveComponent\s*\(\s*["'`]([^"'`]+)["'`]\s*(?:,\s*[^)]+)?\)/g;
@@ -341,62 +342,157 @@ const I18N_FN_MAP = {
341
342
  $tm: "tm: $tm",
342
343
  $te: "te: $te"
343
344
  };
344
- const I18N_FN_RE = /(?<![.\w])\$([tdn]|rt|tm|te)\s*\(/g;
345
- const SETUP_FN_RE = /setup\s*\(__props[\s\S]*?\)\s*\{/;
346
- const USE_I18N_DESTRUCTURE_RE = /const\s*\{([^}]*)\}\s*=\s*useI18n\s*\(\s*\)\s*;?/;
345
+ const I18N_HELPER_LOCALS = new Set(Object.keys(I18N_FN_MAP));
346
+ const DEFAULT_PARSE_ID = "vize-nuxt-i18n-bridge.tsx";
347
347
  function getLocalAlias(specifier) {
348
348
  const colon = specifier.indexOf(":");
349
349
  return (colon === -1 ? specifier : specifier.slice(colon + 1)).trim();
350
350
  }
351
- function collectUsedI18nSpecifiers(code) {
352
- const used = /* @__PURE__ */ new Set();
353
- let firstUseIndex = Number.POSITIVE_INFINITY;
354
- for (const match of code.matchAll(I18N_FN_RE)) {
355
- const specifier = I18N_FN_MAP[`$${match[1]}`];
356
- if (specifier) {
357
- used.add(specifier);
358
- firstUseIndex = Math.min(firstUseIndex, match.index ?? firstUseIndex);
351
+ function isNode(value) {
352
+ return value != null && typeof value === "object" && typeof value.type === "string";
353
+ }
354
+ function getNodeStart(node) {
355
+ return typeof node.start === "number" ? node.start : null;
356
+ }
357
+ function getNodeEnd(node) {
358
+ return typeof node.end === "number" ? node.end : null;
359
+ }
360
+ function getNodeName(node) {
361
+ return isNode(node) && typeof node.name === "string" ? node.name : null;
362
+ }
363
+ function getChildNodes(node) {
364
+ const children = [];
365
+ for (const [key, value] of Object.entries(node)) {
366
+ if (key === "parent") continue;
367
+ if (Array.isArray(value)) {
368
+ for (const item of value) if (isNode(item)) children.push(item);
369
+ continue;
359
370
  }
371
+ if (isNode(value)) children.push(value);
360
372
  }
361
- return {
362
- firstUseIndex,
363
- specifiers: Array.from(used)
364
- };
373
+ return children;
374
+ }
375
+ function walkNode(node, visit) {
376
+ visit(node);
377
+ for (const child of getChildNodes(node)) walkNode(child, visit);
365
378
  }
366
- function collectDestructuredLocalNames(destructure) {
367
- const locals = /* @__PURE__ */ new Set();
368
- for (const rawPart of destructure.split(",")) {
369
- const part = rawPart.trim();
370
- if (!part) continue;
371
- const withoutDefault = (part.split("=")[0] ?? part).trim();
372
- const aliasMatch = withoutDefault.match(/^(?:\.\.\.)?[^:]+:\s*(.+)$/);
373
- const localName = (aliasMatch ? aliasMatch[1] : withoutDefault).trim();
374
- if (localName) locals.add(localName);
379
+ function parseProgram(code, id) {
380
+ try {
381
+ const result = parseSync(normalizeParseId(id), code);
382
+ if (isNode(result)) return result;
383
+ if (result != null && typeof result === "object") {
384
+ const program = result.program;
385
+ if (isNode(program)) return program;
386
+ }
387
+ return null;
388
+ } catch {
389
+ return null;
375
390
  }
376
- return locals;
377
391
  }
378
- function injectNuxtI18nHelpers(code) {
379
- const setupMatch = code.match(SETUP_FN_RE);
380
- if (!setupMatch || setupMatch.index === void 0) return code;
381
- const setupBodyStart = setupMatch.index + setupMatch[0].length;
382
- const setupBody = code.slice(setupBodyStart);
392
+ function normalizeParseId(id) {
393
+ return (id.startsWith("\0") ? id.slice(1) : id).split(/[?#]/, 1)[0] || DEFAULT_PARSE_ID;
394
+ }
395
+ function isSetupFunction(node) {
396
+ if (node.type !== "Property") return false;
397
+ const key = isNode(node.key) ? node.key : null;
398
+ const value = isNode(node.value) ? node.value : null;
399
+ if (getNodeName(key) !== "setup" || !value) return false;
400
+ if (value.type !== "FunctionExpression" && value.type !== "ArrowFunctionExpression") return false;
401
+ return getNodeName((Array.isArray(value.params) ? value.params : [])[0]) === "__props";
402
+ }
403
+ function findSetupFunctionBody(program) {
404
+ let setupBody = null;
405
+ walkNode(program, (node) => {
406
+ if (setupBody || !isSetupFunction(node)) return;
407
+ const value = isNode(node.value) ? node.value : null;
408
+ const body = value && isNode(value.body) ? value.body : null;
409
+ if (body?.type === "BlockStatement") setupBody = body;
410
+ });
411
+ return setupBody;
412
+ }
413
+ function isUseI18nCall(node) {
414
+ if (!isNode(node) || node.type !== "CallExpression") return false;
415
+ return getNodeName(isNode(node.callee) ? node.callee : null) === "useI18n";
416
+ }
417
+ function collectPatternBindingNames(pattern, locals) {
418
+ if (pattern.type === "Identifier" && typeof pattern.name === "string") {
419
+ locals.add(pattern.name);
420
+ return;
421
+ }
422
+ if (pattern.type === "AssignmentPattern" && isNode(pattern.left)) {
423
+ collectPatternBindingNames(pattern.left, locals);
424
+ return;
425
+ }
426
+ if (pattern.type === "RestElement" && isNode(pattern.argument)) {
427
+ collectPatternBindingNames(pattern.argument, locals);
428
+ return;
429
+ }
430
+ if (pattern.type === "Property" && isNode(pattern.value)) {
431
+ collectPatternBindingNames(pattern.value, locals);
432
+ return;
433
+ }
434
+ for (const child of getChildNodes(pattern)) collectPatternBindingNames(child, locals);
435
+ }
436
+ function findExistingUseI18nDestructure(setupBody) {
437
+ const statements = Array.isArray(setupBody.body) ? setupBody.body : [];
438
+ for (const statement of statements) {
439
+ if (!isNode(statement) || statement.type !== "VariableDeclaration") continue;
440
+ const declarations = Array.isArray(statement.declarations) ? statement.declarations : [];
441
+ for (const declaration of declarations) {
442
+ if (!isNode(declaration) || !isNode(declaration.id) || declaration.id.type !== "ObjectPattern") continue;
443
+ if (!isUseI18nCall(isNode(declaration.init) ? declaration.init : null)) continue;
444
+ const locals = /* @__PURE__ */ new Set();
445
+ collectPatternBindingNames(declaration.id, locals);
446
+ return {
447
+ declaration: statement,
448
+ pattern: declaration.id,
449
+ locals
450
+ };
451
+ }
452
+ }
453
+ return null;
454
+ }
455
+ function collectUsedI18nSpecifiers(setupBody) {
456
+ const used = /* @__PURE__ */ new Map();
457
+ walkNode(setupBody, (node) => {
458
+ if (node.type !== "CallExpression") return;
459
+ const fnName = getNodeName(isNode(node.callee) ? node.callee : null);
460
+ const callStart = getNodeStart(node);
461
+ if (!fnName || callStart == null || !I18N_HELPER_LOCALS.has(fnName)) return;
462
+ if (!used.has(fnName)) used.set(fnName, callStart);
463
+ });
464
+ const helpers = Array.from(used.entries()).sort((a, b) => a[1] - b[1]);
465
+ return {
466
+ firstUseIndex: helpers[0]?.[1] ?? Number.POSITIVE_INFINITY,
467
+ specifiers: helpers.map(([name]) => I18N_FN_MAP[name]).filter((value) => value != null)
468
+ };
469
+ }
470
+ function injectNuxtI18nHelpers(code, id = DEFAULT_PARSE_ID) {
471
+ const program = parseProgram(code, id);
472
+ if (!program) return code;
473
+ const setupBody = findSetupFunctionBody(program);
474
+ const setupBodyStart = setupBody ? getNodeStart(setupBody) : null;
475
+ if (!setupBody || setupBodyStart == null) return code;
476
+ const insertAt = setupBodyStart + 1;
383
477
  const { firstUseIndex, specifiers: usedSpecifiers } = collectUsedI18nSpecifiers(setupBody);
384
478
  if (usedSpecifiers.length === 0) return code;
385
- const existingMatch = setupBody.match(USE_I18N_DESTRUCTURE_RE);
386
- if (existingMatch && existingMatch.index !== void 0) {
387
- const existingLocals = collectDestructuredLocalNames(existingMatch[1]);
479
+ const existing = findExistingUseI18nDestructure(setupBody);
480
+ if (existing) {
388
481
  const missingSpecifiers = usedSpecifiers.filter((specifier) => {
389
- return !existingLocals.has(getLocalAlias(specifier));
482
+ return !existing.locals.has(getLocalAlias(specifier));
390
483
  });
391
484
  if (missingSpecifiers.length === 0) return code;
392
- if (existingMatch.index > firstUseIndex) return code.slice(0, setupBodyStart) + `\nconst { ${missingSpecifiers.join(", ")} } = useI18n();\n` + code.slice(setupBodyStart);
393
- const merged = existingMatch[1].trim();
485
+ const declarationStart = getNodeStart(existing.declaration);
486
+ const declarationEnd = getNodeEnd(existing.declaration);
487
+ const patternStart = getNodeStart(existing.pattern);
488
+ const patternEnd = getNodeEnd(existing.pattern);
489
+ if (declarationStart == null || declarationEnd == null || patternStart == null || patternEnd == null) return code;
490
+ if (declarationStart > firstUseIndex) return code.slice(0, insertAt) + `\nconst { ${missingSpecifiers.join(", ")} } = useI18n();\n` + code.slice(insertAt);
491
+ const merged = code.slice(patternStart + 1, patternEnd - 1).trim();
394
492
  const nextDestructure = merged ? `${merged}, ${missingSpecifiers.join(", ")}` : missingSpecifiers.join(", ");
395
- const matchStart = setupBodyStart + existingMatch.index;
396
- const matchEnd = matchStart + existingMatch[0].length;
397
- return code.slice(0, matchStart) + `const { ${nextDestructure} } = useI18n();` + code.slice(matchEnd);
493
+ return code.slice(0, declarationStart) + `const { ${nextDestructure} } = useI18n();` + code.slice(declarationEnd);
398
494
  }
399
- return code.slice(0, setupBodyStart) + `\nconst { ${usedSpecifiers.join(", ")} } = useI18n();\n` + code.slice(setupBodyStart);
495
+ return code.slice(0, insertAt) + `\nconst { ${usedSpecifiers.join(", ")} } = useI18n();\n` + code.slice(insertAt);
400
496
  }
401
497
  //#endregion
402
498
  //#region src/musea-components.ts
@@ -1003,7 +1099,7 @@ async function setupVizeNuxtModule(options, nuxt) {
1003
1099
  }
1004
1100
  }
1005
1101
  if (bridgeOptions.i18n) {
1006
- const nextResult = injectNuxtI18nHelpers(result);
1102
+ const nextResult = injectNuxtI18nHelpers(result, id);
1007
1103
  if (nextResult !== result) {
1008
1104
  result = nextResult;
1009
1105
  changed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizejs/nuxt",
3
- "version": "0.285.0",
3
+ "version": "0.286.0",
4
4
  "description": "Nuxt module for Vize - compiler, musea gallery, linter, and type checker",
5
5
  "keywords": [
6
6
  "compiler",
@@ -42,9 +42,9 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@nuxt/kit": "3.11.2",
45
- "@vizejs/vite-plugin": "0.285.0",
46
- "@vizejs/vite-plugin-musea": "0.285.0",
47
- "vize": "0.285.0"
45
+ "@vizejs/vite-plugin": "0.286.0",
46
+ "@vizejs/vite-plugin-musea": "0.286.0",
47
+ "vize": "0.286.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "typescript": "6.0.3",