@nuvio/vite-plugin 0.5.4 → 1.0.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.
@@ -14089,13 +14089,156 @@ var require_lib3 = __commonJS({
14089
14089
  }
14090
14090
  });
14091
14091
 
14092
- // src/read-dep-version.ts
14092
+ // src/resolve-classname-mode.ts
14093
+ var PATCHABLE_MODES = /* @__PURE__ */ new Set([
14094
+ "literal-only",
14095
+ "cn-basic",
14096
+ "cn-conditional",
14097
+ "classnames-static"
14098
+ ]);
14099
+ function resolvePatchClassNameMode(entry, pluginDefault = "literal-only") {
14100
+ const detected = entry.classNameMode;
14101
+ if (detected && PATCHABLE_MODES.has(detected)) {
14102
+ return detected;
14103
+ }
14104
+ return pluginDefault;
14105
+ }
14106
+
14107
+ // src/detect-libraries.ts
14093
14108
  import * as fs from "fs";
14094
14109
  import * as path from "path";
14110
+ function hasDep(packageJson, name) {
14111
+ const sections = ["dependencies", "devDependencies", "peerDependencies"];
14112
+ for (const key of sections) {
14113
+ const deps = packageJson[key];
14114
+ if (deps && typeof deps === "object" && name in deps) {
14115
+ return true;
14116
+ }
14117
+ }
14118
+ return false;
14119
+ }
14120
+ function dirHasUiComponents(dirAbs) {
14121
+ try {
14122
+ const names = fs.readdirSync(dirAbs);
14123
+ return names.some((n) => /\.(tsx|jsx)$/.test(n));
14124
+ } catch {
14125
+ return false;
14126
+ }
14127
+ }
14128
+ function pathExists(root, rel) {
14129
+ return fs.existsSync(path.join(root, rel));
14130
+ }
14131
+ function detectProjectLibraries(projectRootAbs, packageJson) {
14132
+ const root = path.resolve(projectRootAbs);
14133
+ const found = /* @__PURE__ */ new Set();
14134
+ let pkg = packageJson;
14135
+ if (!pkg) {
14136
+ try {
14137
+ pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
14138
+ } catch {
14139
+ pkg = {};
14140
+ }
14141
+ }
14142
+ for (const rel of ["components/ui", "src/components/ui"]) {
14143
+ const uiDir = path.join(root, rel);
14144
+ if (dirHasUiComponents(uiDir)) {
14145
+ found.add("shadcn");
14146
+ break;
14147
+ }
14148
+ }
14149
+ if (hasDep(pkg, "daisyui")) {
14150
+ found.add("daisyui");
14151
+ }
14152
+ const pkgName = String(pkg.name ?? "");
14153
+ if (/tailadmin/i.test(pkgName)) {
14154
+ found.add("tailadmin");
14155
+ }
14156
+ if (pathExists(root, "src/layout/AppSidebar.tsx") || pathExists(root, "src/components/ecommerce")) {
14157
+ found.add("tailadmin");
14158
+ }
14159
+ return [...found];
14160
+ }
14161
+
14162
+ // src/handle-tag-element.ts
14163
+ import fs2 from "fs";
14164
+ import path2 from "path";
14165
+ import { insertDataNuvioIdAtLocation, isValidNuvioId } from "@nuvio/ast-engine";
14166
+ import {
14167
+ PROTOCOL_VERSION,
14168
+ serializeServerMessage
14169
+ } from "@nuvio/shared";
14170
+ import { assertPathWithinRoot } from "@nuvio/shared/secure-path";
14171
+ async function handleTagElementMessage(ws, msg, ctx) {
14172
+ const ackFail = (errorCode, errorMessage) => {
14173
+ ws.send(
14174
+ serializeServerMessage({
14175
+ type: "tagElementAck",
14176
+ protocolVersion: PROTOCOL_VERSION,
14177
+ requestId: msg.requestId,
14178
+ ok: false,
14179
+ errorCode,
14180
+ errorMessage
14181
+ })
14182
+ );
14183
+ };
14184
+ if (!isValidNuvioId(msg.nuvioId)) {
14185
+ ackFail("invalid_id", "Id must be segmented lowercase (e.g. page.title)");
14186
+ return;
14187
+ }
14188
+ if (ctx.idToEntry.has(msg.nuvioId) || ctx.duplicateIds.some((d) => d.id === msg.nuvioId)) {
14189
+ ackFail("duplicate_id", "That id is already used \u2014 pick another name");
14190
+ return;
14191
+ }
14192
+ const fileAbs = path2.isAbsolute(msg.file) ? path2.resolve(msg.file) : path2.resolve(ctx.projectRoot, msg.file);
14193
+ try {
14194
+ assertPathWithinRoot(ctx.writeGuardRoot, fileAbs);
14195
+ } catch (e) {
14196
+ ackFail("path_escape", String(e));
14197
+ return;
14198
+ }
14199
+ let source;
14200
+ try {
14201
+ source = fs2.readFileSync(fileAbs, "utf8");
14202
+ } catch (e) {
14203
+ ackFail("read_error", String(e));
14204
+ return;
14205
+ }
14206
+ const result = await insertDataNuvioIdAtLocation(
14207
+ source,
14208
+ fileAbs,
14209
+ msg.line,
14210
+ msg.column,
14211
+ msg.nuvioId
14212
+ );
14213
+ if (!result.ok) {
14214
+ ackFail(result.code, result.message);
14215
+ return;
14216
+ }
14217
+ try {
14218
+ fs2.writeFileSync(fileAbs, result.source, "utf8");
14219
+ } catch (e) {
14220
+ ackFail("write_error", String(e));
14221
+ return;
14222
+ }
14223
+ ctx.onIndexRebuilt();
14224
+ ws.send(
14225
+ serializeServerMessage({
14226
+ type: "tagElementAck",
14227
+ protocolVersion: PROTOCOL_VERSION,
14228
+ requestId: msg.requestId,
14229
+ ok: true,
14230
+ id: result.id
14231
+ })
14232
+ );
14233
+ }
14234
+
14235
+ // src/read-dep-version.ts
14236
+ import * as fs3 from "fs";
14237
+ import * as path3 from "path";
14095
14238
  function readPackageVersion(rootAbs, pkgName) {
14096
- const pkgPath = path.join(rootAbs, "node_modules", pkgName, "package.json");
14239
+ const pkgPath = path3.join(rootAbs, "node_modules", pkgName, "package.json");
14097
14240
  try {
14098
- const raw = fs.readFileSync(pkgPath, "utf8");
14241
+ const raw = fs3.readFileSync(pkgPath, "utf8");
14099
14242
  const json = JSON.parse(raw);
14100
14243
  return typeof json.version === "string" ? json.version : void 0;
14101
14244
  } catch {
@@ -14103,7 +14246,7 @@ function readPackageVersion(rootAbs, pkgName) {
14103
14246
  }
14104
14247
  }
14105
14248
  function readRuntimeVersions(projectRootAbs) {
14106
- const root = path.resolve(projectRootAbs);
14249
+ const root = path3.resolve(projectRootAbs);
14107
14250
  return {
14108
14251
  viteVersion: readPackageVersion(root, "vite"),
14109
14252
  reactVersion: readPackageVersion(root, "react"),
@@ -14124,18 +14267,26 @@ function pathnameFromUpgradeUrl(url) {
14124
14267
  }
14125
14268
 
14126
14269
  // src/source-index.ts
14127
- import * as fs2 from "fs";
14128
- import * as path3 from "path";
14270
+ import * as fs4 from "fs";
14271
+ import * as path5 from "path";
14129
14272
  import { parse as parse2 } from "@babel/parser";
14130
14273
  import traverseImport2 from "@babel/traverse";
14131
14274
  import fg from "fast-glob";
14132
14275
 
14133
14276
  // src/source-index-metadata.ts
14134
14277
  var t2 = __toESM(require_lib3(), 1);
14278
+ import {
14279
+ classifyHostClassNameMode,
14280
+ readFlattenedClassName
14281
+ } from "@nuvio/ast-engine";
14282
+ import {
14283
+ libraryGuidanceForEntry,
14284
+ resolveEntryLibraryHint
14285
+ } from "@nuvio/shared";
14135
14286
 
14136
14287
  // src/source-index-text-targets.ts
14137
14288
  var t = __toESM(require_lib3(), 1);
14138
- import path2 from "path";
14289
+ import path4 from "path";
14139
14290
  var TEXT_TAG_PRIORITY = [
14140
14291
  "h1",
14141
14292
  "h2",
@@ -14274,7 +14425,7 @@ function tryAddTarget(openingPath, hostId, hostCtx, fileAbs, seen, targets) {
14274
14425
  targets.push({
14275
14426
  key,
14276
14427
  label: formatLabel(tagName, textPreview, nuvioId),
14277
- file: path2.resolve(fileAbs),
14428
+ file: path4.resolve(fileAbs),
14278
14429
  line: loc.line,
14279
14430
  column: loc.column,
14280
14431
  tagName,
@@ -14357,7 +14508,7 @@ function collectStyleTargets(hostOpeningPath, hostId, hostCtx, fileAbs) {
14357
14508
  targets.push({
14358
14509
  key: nuvioId === hostId ? "host" : nuvioId,
14359
14510
  label: nuvioId === hostId ? `${tagName} \xB7 container` : `${tagName} \xB7 ${nuvioId}`,
14360
- file: path2.resolve(fileAbs),
14511
+ file: path4.resolve(fileAbs),
14361
14512
  line: loc.line,
14362
14513
  column: loc.column,
14363
14514
  tagName,
@@ -14429,27 +14580,32 @@ function readClassName(opening) {
14429
14580
  }
14430
14581
  return { hasLiteralClassName: false, classNameComputed: false };
14431
14582
  }
14432
- function readClassNameWithMode(opening, classNameMode) {
14433
- const direct = readClassName(opening);
14434
- if (direct.hasLiteralClassName || classNameMode !== "cn-basic") {
14435
- return direct;
14583
+ function readClassNameDetected(opening) {
14584
+ const mode = classifyHostClassNameMode(opening);
14585
+ if (mode === "unsupported") {
14586
+ return {
14587
+ hasLiteralClassName: false,
14588
+ classNameComputed: true,
14589
+ classNameMode: mode
14590
+ };
14436
14591
  }
14437
- for (const attr of opening.attributes) {
14438
- if (attr.type !== "JSXAttribute" || attr.name.type !== "JSXIdentifier" || attr.name.name !== "className") {
14439
- continue;
14440
- }
14441
- if (attr.value?.type === "JSXExpressionContainer" && attr.value.expression.type === "CallExpression" && attr.value.expression.callee.type === "Identifier" && (attr.value.expression.callee.name === "cn" || attr.value.expression.callee.name === "clsx") && attr.value.expression.arguments.every((arg) => arg.type === "StringLiteral")) {
14442
- return {
14443
- hasLiteralClassName: true,
14444
- classNameValue: attr.value.expression.arguments.map((arg) => arg.value).join(" "),
14445
- classNameComputed: false
14446
- };
14447
- }
14592
+ const flattened = readFlattenedClassName(opening, mode);
14593
+ if (flattened !== void 0) {
14594
+ return {
14595
+ hasLiteralClassName: true,
14596
+ classNameValue: flattened,
14597
+ classNameComputed: false,
14598
+ classNameMode: mode
14599
+ };
14448
14600
  }
14449
- return direct;
14601
+ const direct = readClassName(opening);
14602
+ return {
14603
+ ...direct,
14604
+ classNameMode: mode
14605
+ };
14450
14606
  }
14451
- function isInsideMap2(path4) {
14452
- let p = path4.parentPath;
14607
+ function isInsideMap2(path6) {
14608
+ let p = path6.parentPath;
14453
14609
  while (p) {
14454
14610
  if (p.isCallExpression()) {
14455
14611
  const callee = p.node.callee;
@@ -14461,8 +14617,8 @@ function isInsideMap2(path4) {
14461
14617
  }
14462
14618
  return false;
14463
14619
  }
14464
- function findEnclosingComponentName(path4) {
14465
- let p = path4.parentPath;
14620
+ function findEnclosingComponentName(path6) {
14621
+ let p = path6.parentPath;
14466
14622
  while (p) {
14467
14623
  if (p.isFunctionDeclaration()) {
14468
14624
  const id = p.node.id;
@@ -14571,14 +14727,14 @@ function inferHierarchyRole(ctx, hostId) {
14571
14727
  }
14572
14728
  return "unknown";
14573
14729
  }
14574
- function analyzeHost(openingPath, classNameMode = "literal-only") {
14730
+ function analyzeHost(openingPath, _legacyClassNameMode) {
14575
14731
  const opening = openingPath.node;
14576
14732
  const parent = openingPath.parentPath;
14577
14733
  if (!parent?.isJSXElement()) {
14578
14734
  return null;
14579
14735
  }
14580
14736
  const tagName = getTagName2(opening);
14581
- const classInfo = readClassNameWithMode(opening, classNameMode);
14737
+ const classInfo = readClassNameDetected(opening);
14582
14738
  const insideMap = isInsideMap2(openingPath);
14583
14739
  const textEditable = !jsxElementHasElementChildren2(parent.node);
14584
14740
  return {
@@ -14587,11 +14743,12 @@ function analyzeHost(openingPath, classNameMode = "literal-only") {
14587
14743
  hasLiteralClassName: classInfo.hasLiteralClassName,
14588
14744
  classNameValue: classInfo.classNameValue,
14589
14745
  classNameComputed: classInfo.classNameComputed,
14746
+ classNameMode: classInfo.classNameMode,
14590
14747
  insideMap,
14591
14748
  textEditable
14592
14749
  };
14593
14750
  }
14594
- function computeRiskMetadata(ctx) {
14751
+ function computeRiskMetadata(ctx, libraryHint) {
14595
14752
  const unsupportedReasons = [];
14596
14753
  let riskLevel = "safe";
14597
14754
  const isNative = NATIVE_TAG.test(ctx.tagName);
@@ -14605,19 +14762,29 @@ function computeRiskMetadata(ctx) {
14605
14762
  if (ctx.classNameComputed) {
14606
14763
  setRisk("unsupported");
14607
14764
  unsupportedReasons.push(
14608
- "className is not a string literal \uFFFD only literal className strings are patchable in this version."
14765
+ ctx.classNameMode === "unsupported" ? "className uses a dynamic pattern nuvio cannot patch safely yet (e.g. variables, template literals, or complex cn() args)." : "className is not patchable for this element."
14609
14766
  );
14610
14767
  }
14611
14768
  if (ctx.insideMap) {
14612
14769
  setRisk("caution");
14613
14770
  unsupportedReasons.push(
14614
- "Element is inside a .map() \uFFFD text/class changes may affect every rendered item."
14771
+ "Element is inside a .map() ? text/class changes may affect every rendered item."
14615
14772
  );
14616
14773
  }
14617
14774
  if (!isNative) {
14618
14775
  setRisk("caution");
14776
+ const libraryNote = libraryGuidanceForEntry({
14777
+ id: "",
14778
+ file: "",
14779
+ line: 0,
14780
+ column: 0,
14781
+ tagName: ctx.tagName,
14782
+ libraryHint,
14783
+ riskLevel,
14784
+ hasLiteralClassName: ctx.hasLiteralClassName
14785
+ });
14619
14786
  unsupportedReasons.push(
14620
- "Custom React component \uFFFD className must forward to a DOM node for visual changes."
14787
+ libraryNote ?? "Custom React component ? className must forward to a DOM node for visual changes."
14621
14788
  );
14622
14789
  }
14623
14790
  let textEditable = ctx.textEditable;
@@ -14629,7 +14796,7 @@ function computeRiskMetadata(ctx) {
14629
14796
  }
14630
14797
  if (ctx.hasLiteralClassName && ctx.classNameValue?.includes("dark:")) {
14631
14798
  setRisk("caution");
14632
- unsupportedReasons.push("Responsive/dark: utilities present \uFFFD edit with care.");
14799
+ unsupportedReasons.push("Responsive/dark: utilities present ? edit with care.");
14633
14800
  }
14634
14801
  const structuralEditable = (riskLevel === "safe" || riskLevel === "caution") && !ctx.insideMap && !ctx.classNameComputed;
14635
14802
  return {
@@ -14639,8 +14806,9 @@ function computeRiskMetadata(ctx) {
14639
14806
  structuralEditable
14640
14807
  };
14641
14808
  }
14642
- function buildIndexEntry(base, ctx, openingPath) {
14643
- const risk = computeRiskMetadata(ctx);
14809
+ function buildIndexEntry(base, ctx, openingPath, detectedLibraries = []) {
14810
+ const libraryHint = ctx.libraryHint ?? resolveEntryLibraryHint(base.file, ctx.tagName, detectedLibraries);
14811
+ const risk = computeRiskMetadata(ctx, libraryHint);
14644
14812
  const textTargets = openingPath !== void 0 ? collectTextTargets(openingPath, base.id, ctx, base.file) : void 0;
14645
14813
  const styleTargets = openingPath !== void 0 ? collectStyleTargets(openingPath, base.id, ctx, base.file) : void 0;
14646
14814
  const patchHostId = textTargets && textTargets.length > 0 ? resolveEntryPatchHostId(base.id, ctx, textTargets) : base.id;
@@ -14664,7 +14832,9 @@ function buildIndexEntry(base, ctx, openingPath) {
14664
14832
  patchHostId,
14665
14833
  primaryTextTargetKey,
14666
14834
  textTargets: textTargets && textTargets.length > 0 ? textTargets : void 0,
14667
- styleTargets: styleTargets && styleTargets.length > 0 ? styleTargets : void 0
14835
+ styleTargets: styleTargets && styleTargets.length > 0 ? styleTargets : void 0,
14836
+ classNameMode: ctx.classNameMode,
14837
+ libraryHint
14668
14838
  };
14669
14839
  }
14670
14840
 
@@ -14704,19 +14874,19 @@ function parseTableDataArray(code, file) {
14704
14874
  }
14705
14875
  const traverseFn = getTraverseFn();
14706
14876
  traverseFn(ast, {
14707
- VariableDeclarator(path4) {
14708
- if (!t3.isIdentifier(path4.node.id)) {
14877
+ VariableDeclarator(path6) {
14878
+ if (!t3.isIdentifier(path6.node.id)) {
14709
14879
  return;
14710
14880
  }
14711
- const name = path4.node.id.name;
14881
+ const name = path6.node.id.name;
14712
14882
  if (!name.endsWith("Data") && name !== "tableData") {
14713
14883
  return;
14714
14884
  }
14715
- if (!t3.isArrayExpression(path4.node.init)) {
14885
+ if (!t3.isArrayExpression(path6.node.init)) {
14716
14886
  return;
14717
14887
  }
14718
14888
  const rows = [];
14719
- for (const el of path4.node.init.elements) {
14889
+ for (const el of path6.node.init.elements) {
14720
14890
  if (!el || !t3.isObjectExpression(el)) {
14721
14891
  continue;
14722
14892
  }
@@ -14736,7 +14906,7 @@ function parseTableDataArray(code, file) {
14736
14906
  rows.push({ rowKey, fields });
14737
14907
  }
14738
14908
  if (rows.length > 0) {
14739
- const line = path4.node.loc?.start.line ?? 1;
14909
+ const line = path6.node.loc?.start.line ?? 1;
14740
14910
  out.set(name, { rows, line });
14741
14911
  }
14742
14912
  }
@@ -14924,11 +15094,16 @@ function extractIdsFromSource(fileAbs, code, options) {
14924
15094
  const pushEntry = (id) => {
14925
15095
  const ctx = analyzeHost(p, options?.classNameMode ?? "literal-only");
14926
15096
  if (!ctx) {
14927
- acc.push({ id, file: path3.resolve(fileAbs), line, column });
15097
+ acc.push({ id, file: path5.resolve(fileAbs), line, column });
14928
15098
  return;
14929
15099
  }
14930
15100
  acc.push(
14931
- buildIndexEntry({ id, file: path3.resolve(fileAbs), line, column }, ctx, p)
15101
+ buildIndexEntry(
15102
+ { id, file: path5.resolve(fileAbs), line, column },
15103
+ ctx,
15104
+ p,
15105
+ options?.detectedLibraries ?? []
15106
+ )
14932
15107
  );
14933
15108
  };
14934
15109
  if (prop === "data-nuvio-id") {
@@ -14961,10 +15136,10 @@ function extractIdsFromSource(fileAbs, code, options) {
14961
15136
  return acc;
14962
15137
  }
14963
15138
  function buildSourceIndex(rootAbs, globPatterns, options) {
14964
- const root = path3.resolve(rootAbs);
15139
+ const root = path5.resolve(rootAbs);
14965
15140
  const parseErrors = [];
14966
15141
  const normalizedPatterns = globPatterns.map((g) => {
14967
- const resolved = path3.isAbsolute(g) ? path3.resolve(g) : path3.resolve(root, g);
15142
+ const resolved = path5.isAbsolute(g) ? path5.resolve(g) : path5.resolve(root, g);
14968
15143
  return resolved.replace(/\\/g, "/");
14969
15144
  });
14970
15145
  const matched = fg.sync(normalizedPatterns, {
@@ -14978,12 +15153,12 @@ function buildSourceIndex(rootAbs, globPatterns, options) {
14978
15153
  for (const file of files) {
14979
15154
  let code;
14980
15155
  try {
14981
- code = fs2.readFileSync(file, "utf8");
15156
+ code = fs4.readFileSync(file, "utf8");
14982
15157
  } catch (e) {
14983
15158
  parseErrors.push({ file, message: String(e) });
14984
15159
  continue;
14985
15160
  }
14986
- fileToSource.set(path3.resolve(file), code);
15161
+ fileToSource.set(path5.resolve(file), code);
14987
15162
  let occurrences;
14988
15163
  try {
14989
15164
  occurrences = extractIdsFromSource(file, code, options);
@@ -15046,7 +15221,7 @@ function isBetterIndex(candidate, current) {
15046
15221
  return false;
15047
15222
  }
15048
15223
  function pickBestSourceIndex(rootCandidates, globPatterns, options) {
15049
- const roots = [...new Set(rootCandidates.map((r) => path3.resolve(r)).filter((r) => r.length > 0))];
15224
+ const roots = [...new Set(rootCandidates.map((r) => path5.resolve(r)).filter((r) => r.length > 0))];
15050
15225
  if (roots.length === 0) {
15051
15226
  return {
15052
15227
  entries: [],
@@ -15066,6 +15241,11 @@ function pickBestSourceIndex(rootCandidates, globPatterns, options) {
15066
15241
  }
15067
15242
 
15068
15243
  export {
15244
+ __toESM,
15245
+ resolvePatchClassNameMode,
15246
+ detectProjectLibraries,
15247
+ handleTagElementMessage,
15248
+ require_lib3 as require_lib,
15069
15249
  readRuntimeVersions,
15070
15250
  pathnameFromUpgradeUrl,
15071
15251
  extractIdsFromSource,