@archie/devtools 0.1.4 → 0.1.6

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.
@@ -5134,7 +5134,7 @@ function shouldShowGrabFeedback(target, scannedByNodeId) {
5134
5134
  }
5135
5135
  const nodeId = host.getAttribute(DND_NODE_ID_ATTR);
5136
5136
  const scanned = nodeId ? scannedByNodeId.get(nodeId) : null;
5137
- return !scanned || scanned.draggable === false;
5137
+ return !scanned || !isEligibleForDrag(scanned);
5138
5138
  }
5139
5139
  function isEligibleForDrag(sc) {
5140
5140
  if (sc.draggable === false) return false;
@@ -5144,6 +5144,9 @@ function isEligibleForDrag(sc) {
5144
5144
  // Section, …) carrying the usage's projected meta.
5145
5145
  source === "projected-usage";
5146
5146
  }
5147
+ function filterEligibleForDrag(elements) {
5148
+ return elements.filter(isEligibleForDrag);
5149
+ }
5147
5150
  function isPointerInEdgeZone(px, py, guard) {
5148
5151
  return px <= guard || py <= guard || px >= window.innerWidth - guard || py >= window.innerHeight - guard;
5149
5152
  }
@@ -5414,7 +5417,12 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5414
5417
  eligibilitySource: smallEligibility,
5415
5418
  draggable: false,
5416
5419
  identityOrigin: templateFrameLabel(identityMeta3),
5417
- editorMeta: surfaceMeta2
5420
+ // `identityMeta` here is already `projectUsageMeta(el) ??
5421
+ // surfaceMeta` (no `ownerMeta` fallback in this branch) — see
5422
+ // the main scan's own `editorMeta: usageMeta ?? surfaceMeta`
5423
+ // below for the full rationale (ARCH-883-KOI
5424
+ // draggable-eligibility parity fix).
5425
+ editorMeta: identityMeta3
5418
5426
  });
5419
5427
  }
5420
5428
  }
@@ -5489,7 +5497,29 @@ function scanElements(root, depth = 0, usedNodeIds = /* @__PURE__ */ new Set(),
5489
5497
  containerStrategy,
5490
5498
  eligibilitySource: inferredEligibility,
5491
5499
  identityOrigin: templateFrameLabel(identityMeta2),
5492
- editorMeta: surfaceMeta
5500
+ // `usageMeta ?? surfaceMeta`, NOT `identityMeta` (which also falls
5501
+ // back to `ownerMeta`): `isEligibleForDrag` (`isNonDraggableNodeMeta`)
5502
+ // reads THIS field to gate the actual drag start, and `surfaceMeta`
5503
+ // names the kit TEMPLATE's own meta (`isKitOrigin: true`) for any
5504
+ // kit-rendered host — even one this scan just correctly computed
5505
+ // `draggable: true` for via the SAME `usageMeta` projection above (a
5506
+ // `<Kit.Card>` usage's single host root). Storing `surfaceMeta` here
5507
+ // silently re-refused every such drag at gesture start (ARCH-883-KOI
5508
+ // draggable-eligibility parity fix). Falling back to `identityMeta`
5509
+ // instead of `usageMeta` would overcorrect: `ownerMeta` (a STRUCTURAL
5510
+ // ancestor's meta, borrowed only for `sourceMetaNodeId` on an
5511
+ // inferred-sortable-descendant with no meta of its own) is not this
5512
+ // element's own identity and must not leak into its eligibility/lock
5513
+ // reporting — `scanner-ast-first.test.ts`'s "free of structural owner
5514
+ // meta contamination" pins `editorMeta` staying `undefined` there.
5515
+ //
5516
+ // A meta-less host projected by `computeUsageMeta` onto its component
5517
+ // usage (e.g. `<MetricCard label="x"/>`'s host, `fiber-bridge.ts`)
5518
+ // inherits that usage's own `containerKind` — usually `'none'` for a
5519
+ // self-closing usage — so a drop INTO it now correctly refuses
5520
+ // CONTAINER_NOT_PLAIN: the host is a component instance, not a plain
5521
+ // JSX container in the user's own file, matching acg.
5522
+ editorMeta: usageMeta ?? surfaceMeta
5493
5523
  });
5494
5524
  if (!isSvgRoot) results.push(...scanElements(el, depth + 1, usedNodeIds, seenResolvedHosts, resolvedUnreported));
5495
5525
  } else {
@@ -5513,7 +5543,8 @@ function buildMovedElementEntry(el, usedNodeIds) {
5513
5543
  const parentEl = el.parentElement;
5514
5544
  const surfaceMeta = getDirectEditorMeta(el) ?? void 0;
5515
5545
  const ownerMeta = getEditorMeta(el);
5516
- const identityMeta2 = projectUsageMeta(el) ?? ownerMeta ?? surfaceMeta;
5546
+ const usageMeta = projectUsageMeta(el);
5547
+ const identityMeta2 = usageMeta ?? ownerMeta ?? surfaceMeta;
5517
5548
  const selector = getUniqueSelector(el);
5518
5549
  const eligibility = isInspectorEligible(el);
5519
5550
  return {
@@ -5534,7 +5565,11 @@ function buildMovedElementEntry(el, usedNodeIds) {
5534
5565
  containerStrategy: parentEl && isFlexOrGridParent(parentEl) ? resolveContainerStrategy(parentEl) : null,
5535
5566
  eligibilitySource: eligibility ?? "force-allow",
5536
5567
  identityOrigin: templateFrameLabel(identityMeta2),
5537
- editorMeta: surfaceMeta
5568
+ // `usageMeta ?? surfaceMeta`, NOT `identityMeta` — same fix, same reason
5569
+ // as the main scan's own `editorMeta` (see there): `ownerMeta` is a
5570
+ // structural ancestor's meta borrowed only for `sourceMetaNodeId`/
5571
+ // `draggable`, and must not leak into eligibility/lock reporting.
5572
+ editorMeta: usageMeta ?? surfaceMeta
5538
5573
  };
5539
5574
  }
5540
5575
  function useElementScanner(enabled) {
@@ -8268,6 +8303,7 @@ function useDragVisualLoop({
8268
8303
 
8269
8304
  // src/client/dnd/design-mode/useDragLifecycle.ts
8270
8305
  import { useState as useState5, useCallback as useCallback6 } from "react";
8306
+ var GATE_REJECT = "GATE_REJECT";
8271
8307
  function emitRefused(code, cause, vid) {
8272
8308
  dispatchRuntimeEvent({
8273
8309
  type: "drag-refused",
@@ -8288,6 +8324,41 @@ function buildDragIntentEngineOptions(anchorElement, srcContainerId, blockedCont
8288
8324
  blockedContainerIds
8289
8325
  };
8290
8326
  }
8327
+ function resolveDragEndRefusal(params) {
8328
+ if (!params.hasDragSession || params.blockedByGuard) return { kind: "silent" };
8329
+ if (!params.hasActiveElement || !params.hasSourcePosition) {
8330
+ return {
8331
+ kind: "refuse",
8332
+ code: GATE_REJECT,
8333
+ cause: "Drag ended with no resolvable source position for the dragged node."
8334
+ };
8335
+ }
8336
+ if (!params.hasNestTarget && !params.intentValid) {
8337
+ return {
8338
+ kind: "refuse",
8339
+ code: GATE_REJECT,
8340
+ cause: "No droppable target was resolved under the pointer for this drag."
8341
+ };
8342
+ }
8343
+ return { kind: "continue" };
8344
+ }
8345
+ function resolveDragEndBranch(params) {
8346
+ if (!params.dropTargetExistsInMap) return { kind: "refuse", reason: "drop-target-missing" };
8347
+ if (!params.dropTargetIsDroppable) return { kind: "refuse", reason: "drop-target-not-droppable" };
8348
+ if (params.hasNestTarget) {
8349
+ if (params.nestIsSelfOrDescendant) return { kind: "refuse", reason: "nest-self-or-descendant" };
8350
+ if (params.nestIsAncestor) return { kind: "refuse", reason: "nest-ancestor" };
8351
+ if (!params.nestHasCommitNodeId) return { kind: "refuse", reason: "nest-no-commit-node" };
8352
+ return { kind: "nest" };
8353
+ }
8354
+ if (params.intentIsReorder) {
8355
+ if (!params.hasResolvedReorder || !params.resolvedReorderMatchesIntent) {
8356
+ return { kind: "refuse", reason: "stale-reorder" };
8357
+ }
8358
+ return { kind: "reorder" };
8359
+ }
8360
+ return { kind: "refuse", reason: "no-move" };
8361
+ }
8291
8362
  function buildRuntimeIdentityLocal(nodeId, resolveNodeElement, effectiveLaneSnapshot) {
8292
8363
  if (!nodeId) return null;
8293
8364
  const element = resolveNodeElement(nodeId);
@@ -8541,7 +8612,7 @@ function useDragLifecycle({
8541
8612
  draggable: found.draggable
8542
8613
  });
8543
8614
  emitRefused(
8544
- resolveRefusalCode(found.editorMeta, "GATE_REJECT"),
8615
+ resolveRefusalCode(found.editorMeta, GATE_REJECT),
8545
8616
  "Node is not draggable (dynamic render, chrome, or kit-origin).",
8546
8617
  resolveVidForScannedNode(activeId, refs.nodeMapRef.current, found.element)
8547
8618
  );
@@ -8570,7 +8641,7 @@ function useDragLifecycle({
8570
8641
  const anchorScanned = refs.nodeMapRef.current.get(anchor.anchorNodeId) ?? found;
8571
8642
  if (!anchorScanned || !isEligibleForDrag(anchorScanned)) {
8572
8643
  emitRefused(
8573
- resolveRefusalCode(anchorScanned?.editorMeta, "GATE_REJECT"),
8644
+ resolveRefusalCode(anchorScanned?.editorMeta, GATE_REJECT),
8574
8645
  "Resolved drag anchor is not draggable.",
8575
8646
  resolveVidForScannedNode(
8576
8647
  anchor.anchorNodeId,
@@ -8660,90 +8731,131 @@ function useDragLifecycle({
8660
8731
  const intentHasContainer = intent ? refs.projectionRef.current.containerIndex.has(intent.containerId) : false;
8661
8732
  const intentHasSlot = intent ? intent.mode === "nest" || refs.projectionRef.current.slotIndex.has(intent.slotId) : false;
8662
8733
  const intentValid = !!intent && intentHasContainer && intentHasSlot;
8663
- if (!blockedByGuard && activeEl && sourcePos && (nestTarget || intentValid)) {
8664
- const dropTargetId = nestTarget?.nodeId ?? intent?.containerId;
8665
- if (dropTargetId && !refs.nodeMapRef.current.has(dropTargetId)) {
8666
- clearDragState();
8667
- syncHistoryAvailability();
8668
- return;
8669
- }
8670
- if (dropTargetId && !refs.droppableContainerIdsRef.current?.has(dropTargetId)) {
8671
- const targetContainerEl = resolveNodeElement(dropTargetId);
8672
- const targetMeta = refs.nodeMapRef.current.get(dropTargetId)?.editorMeta;
8673
- emitRefused(
8674
- resolveRefusalCode(targetMeta, "CONTAINER_NOT_PLAIN"),
8675
- "Drop target is outside the droppable set for this node.",
8676
- resolveVidForScannedNode(dropTargetId, refs.nodeMapRef.current, targetContainerEl)
8677
- );
8678
- clearDragState();
8679
- syncHistoryAvailability();
8680
- return;
8681
- }
8682
- let command = null;
8683
- if (nestTarget) {
8684
- if (!activeEl.contains(nestTarget.element) && !nestTarget.element.contains(activeEl) && sourcePos?.commitNodeId) {
8685
- const nestPlacement = resolveAppendCommitPlacement(
8686
- nestTarget.element,
8687
- sourcePos.commitNodeId,
8688
- nestTarget.nodeId
8734
+ const anchorVid = () => resolveVidForScannedNode(anchorNodeId, refs.nodeMapRef.current, activeEl);
8735
+ const gate = resolveDragEndRefusal({
8736
+ hasDragSession: !!dragSession,
8737
+ blockedByGuard,
8738
+ hasActiveElement: !!activeEl,
8739
+ hasSourcePosition: !!sourcePos,
8740
+ hasNestTarget: !!nestTarget,
8741
+ intentValid
8742
+ });
8743
+ if (gate.kind === "silent") {
8744
+ clearDragState();
8745
+ syncHistoryAvailability();
8746
+ return;
8747
+ }
8748
+ if (gate.kind === "refuse") {
8749
+ emitRefused(gate.code, gate.cause, anchorVid());
8750
+ clearDragState();
8751
+ syncHistoryAvailability();
8752
+ return;
8753
+ }
8754
+ const el = activeEl;
8755
+ const pos = sourcePos;
8756
+ const dropTargetId = nestTarget?.nodeId ?? intent?.containerId;
8757
+ const resolvedReorder = refs.resolvedReorderRef.current;
8758
+ const branch = resolveDragEndBranch({
8759
+ dropTargetExistsInMap: refs.nodeMapRef.current.has(dropTargetId),
8760
+ dropTargetIsDroppable: !!refs.droppableContainerIdsRef.current?.has(dropTargetId),
8761
+ hasNestTarget: !!nestTarget,
8762
+ nestIsSelfOrDescendant: !!nestTarget && el.contains(nestTarget.element),
8763
+ nestIsAncestor: !!nestTarget && nestTarget.element.contains(el),
8764
+ nestHasCommitNodeId: !!pos.commitNodeId,
8765
+ intentIsReorder: intentValid && intent?.mode === "reorder",
8766
+ hasResolvedReorder: !!resolvedReorder,
8767
+ resolvedReorderMatchesIntent: !!resolvedReorder && !!intent && resolvedReorder.visual.containerId === intent.containerId
8768
+ });
8769
+ if (branch.kind === "refuse") {
8770
+ switch (branch.reason) {
8771
+ case "drop-target-missing":
8772
+ emitRefused(
8773
+ "VID_NOT_FOUND",
8774
+ "Drop target no longer exists.",
8775
+ resolveVidForScannedNodeStrict(dropTargetId, refs.nodeMapRef.current)
8689
8776
  );
8690
- command = {
8691
- type: classifyCommandType(sourcePos, nestPlacement, true),
8692
- nodeId: anchorNodeId,
8693
- commitNodeId: sourcePos.commitNodeId,
8694
- from: sourcePos,
8695
- to: nestPlacement,
8696
- at: Date.now()
8697
- };
8698
- }
8699
- } else if (intentValid && intent.mode === "reorder") {
8700
- const resolvedReorder = refs.resolvedReorderRef.current;
8701
- if (!resolvedReorder || resolvedReorder.visual.containerId !== intent.containerId) {
8702
- clearDragState();
8703
- syncHistoryAvailability();
8704
- return;
8705
- }
8706
- const targetPlacement = resolvedReorder.commit;
8707
- command = {
8708
- type: classifyCommandType(sourcePos, targetPlacement, false),
8709
- nodeId: anchorNodeId,
8710
- commitNodeId: targetPlacement.commitNodeId,
8711
- from: sourcePos,
8712
- to: targetPlacement,
8713
- at: Date.now()
8714
- };
8715
- }
8716
- if (command) {
8717
- const anchorEl = resolveNodeElement(anchorNodeId) ?? activeEl;
8718
- const selectedEl = resolveNodeElement(selectedNodeId) ?? anchorEl;
8719
- const nodeKey = getNodeKeyFromElement(anchorEl);
8720
- const inspRef = getInspectorRefFromElement(anchorEl);
8721
- const selectedNodeKey = getNodeKeyFromElement(selectedEl);
8722
- const selectedInspectorRef = getInspectorRefFromElement(selectedEl);
8723
- const sourceLaneSnapshot = refs.sourceCommitLaneRef.current ?? getCommitLaneSnapshot(command.from.laneContainerId);
8724
- const targetLaneSnapshot = command.to.laneContainerId === command.from.laneContainerId ? sourceLaneSnapshot : getCommitLaneSnapshot(command.to.laneContainerId);
8725
- const committed = refs.historyRef.current.execute(command, applyCommand);
8726
- if (committed) {
8727
- activeEl.setAttribute("data-dnd-moved", "");
8728
- selectElement(null);
8729
- recordCommittedMove({
8730
- command,
8731
- anchorNodeId,
8732
- selectedNodeId,
8733
- anchorEl,
8734
- selectedEl,
8735
- nodeKey,
8736
- inspRef,
8737
- selectedNodeKey,
8738
- selectedInspectorRef,
8739
- sourceLaneSnapshot,
8740
- targetLaneSnapshot,
8741
- resolveNodeElement,
8742
- recordMoveChange,
8743
- nodeMap: refs.nodeMapRef.current
8744
- });
8777
+ break;
8778
+ case "drop-target-not-droppable": {
8779
+ const targetMeta = refs.nodeMapRef.current.get(dropTargetId)?.editorMeta;
8780
+ emitRefused(
8781
+ resolveRefusalCode(targetMeta, "CONTAINER_NOT_PLAIN"),
8782
+ "Drop target is outside the droppable set for this node.",
8783
+ resolveVidForScannedNode(dropTargetId, refs.nodeMapRef.current, resolveNodeElement(dropTargetId))
8784
+ );
8785
+ break;
8745
8786
  }
8787
+ case "nest-self-or-descendant":
8788
+ emitRefused(GATE_REJECT, "Cannot nest an element into itself or its own descendant.", anchorVid());
8789
+ break;
8790
+ case "nest-ancestor":
8791
+ emitRefused(GATE_REJECT, "Cannot nest an element into its own ancestor.", anchorVid());
8792
+ break;
8793
+ case "nest-no-commit-node":
8794
+ emitRefused(GATE_REJECT, "Cannot resolve the dragged node's source position for nesting.", anchorVid());
8795
+ break;
8796
+ case "stale-reorder":
8797
+ emitRefused(GATE_REJECT, "Resolved reorder placement no longer matches the drag intent.", anchorVid());
8798
+ break;
8799
+ case "no-move":
8800
+ emitRefused(GATE_REJECT, "Could not build a move for the resolved drag intent.", anchorVid());
8801
+ break;
8746
8802
  }
8803
+ clearDragState();
8804
+ syncHistoryAvailability();
8805
+ return;
8806
+ }
8807
+ const command = branch.kind === "nest" ? (() => {
8808
+ const target = nestTarget;
8809
+ const nestPlacement = resolveAppendCommitPlacement(target.element, pos.commitNodeId, target.nodeId);
8810
+ return {
8811
+ type: classifyCommandType(pos, nestPlacement, true),
8812
+ nodeId: anchorNodeId,
8813
+ commitNodeId: pos.commitNodeId,
8814
+ from: pos,
8815
+ to: nestPlacement,
8816
+ at: Date.now()
8817
+ };
8818
+ })() : (() => {
8819
+ const targetPlacement = resolvedReorder.commit;
8820
+ return {
8821
+ type: classifyCommandType(pos, targetPlacement, false),
8822
+ nodeId: anchorNodeId,
8823
+ commitNodeId: targetPlacement.commitNodeId,
8824
+ from: pos,
8825
+ to: targetPlacement,
8826
+ at: Date.now()
8827
+ };
8828
+ })();
8829
+ const anchorEl = resolveNodeElement(anchorNodeId) ?? el;
8830
+ const selectedEl = resolveNodeElement(selectedNodeId) ?? anchorEl;
8831
+ const nodeKey = getNodeKeyFromElement(anchorEl);
8832
+ const inspRef = getInspectorRefFromElement(anchorEl);
8833
+ const selectedNodeKey = getNodeKeyFromElement(selectedEl);
8834
+ const selectedInspectorRef = getInspectorRefFromElement(selectedEl);
8835
+ const sourceLaneSnapshot = refs.sourceCommitLaneRef.current ?? getCommitLaneSnapshot(command.from.laneContainerId);
8836
+ const targetLaneSnapshot = command.to.laneContainerId === command.from.laneContainerId ? sourceLaneSnapshot : getCommitLaneSnapshot(command.to.laneContainerId);
8837
+ const committed = refs.historyRef.current.execute(command, applyCommand);
8838
+ if (committed) {
8839
+ el.setAttribute("data-dnd-moved", "");
8840
+ selectElement(null);
8841
+ recordCommittedMove({
8842
+ command,
8843
+ anchorNodeId,
8844
+ selectedNodeId,
8845
+ anchorEl,
8846
+ selectedEl,
8847
+ nodeKey,
8848
+ inspRef,
8849
+ selectedNodeKey,
8850
+ selectedInspectorRef,
8851
+ sourceLaneSnapshot,
8852
+ targetLaneSnapshot,
8853
+ resolveNodeElement,
8854
+ recordMoveChange,
8855
+ nodeMap: refs.nodeMapRef.current
8856
+ });
8857
+ } else {
8858
+ emitRefused(GATE_REJECT, "The resolved move could not be applied.", anchorVid());
8747
8859
  }
8748
8860
  clearDragState();
8749
8861
  syncHistoryAvailability();
@@ -9282,7 +9394,7 @@ function HitArea({
9282
9394
  "div",
9283
9395
  {
9284
9396
  ref: divRef,
9285
- "data-design-mode-ui": true,
9397
+ "data-design-mode-ui": "hit-area",
9286
9398
  style: { position: "fixed", left, top, width, height, zIndex: 99998, cursor },
9287
9399
  onPointerMove: (e) => {
9288
9400
  if (!isSelected || isDragging) {
@@ -9373,9 +9485,7 @@ function DesignModeOverlay() {
9373
9485
  const draggableElements = useMemo6(() => {
9374
9486
  if (elements.length === 0) return [];
9375
9487
  const projection = buildProjection(elements);
9376
- return collectDraggableAnchors(elements, projection, nodeMap).filter(
9377
- (el) => el.draggable !== false
9378
- );
9488
+ return filterEligibleForDrag(collectDraggableAnchors(elements, projection, nodeMap));
9379
9489
  }, [elements, nodeMap]);
9380
9490
  const arrangeHitAreaElement = useMemo6(() => {
9381
9491
  if (!arrangeSelectedNodeId) return null;
@@ -9985,7 +10095,7 @@ function isPreviewOrigin(origin) {
9985
10095
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
9986
10096
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
9987
10097
  function readBuildVersion() {
9988
- return "0.1.4" ? "0.1.4" : null;
10098
+ return "0.1.6" ? "0.1.6" : null;
9989
10099
  }
9990
10100
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
9991
10101
  var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  meaningfulChildren
3
- } from "./chunk-U4KG6UDY.mjs";
3
+ } from "./chunk-ZG6HZFTJ.mjs";
4
4
 
5
5
  // src/babel-plugin-collection-binding.ts
6
6
  import { VISITOR_KEYS } from "@babel/types";
package/dist/index.d.mts CHANGED
@@ -682,6 +682,15 @@ interface ArchieDevToolsOptions {
682
682
  * @default /src\/pages\//
683
683
  */
684
684
  dndPages?: RegExp;
685
+ /**
686
+ * Strip `data-vid` from JSX output on a production build (`vite build`,
687
+ * not `vite dev`). ON by default — `data-vid` is a build/editor-time
688
+ * identity nothing at runtime needs, and nothing else in the deploy
689
+ * pipeline strips it. Set `false` only if a consumer has a real reason to
690
+ * ship it (e.g. a tool downstream of the build still reads it).
691
+ * @default true
692
+ */
693
+ stripVidsInBuild?: boolean;
685
694
  }
686
695
  interface ArchieDevToolsPlugin {
687
696
  name: string;
package/dist/index.d.ts CHANGED
@@ -682,6 +682,15 @@ interface ArchieDevToolsOptions {
682
682
  * @default /src\/pages\//
683
683
  */
684
684
  dndPages?: RegExp;
685
+ /**
686
+ * Strip `data-vid` from JSX output on a production build (`vite build`,
687
+ * not `vite dev`). ON by default — `data-vid` is a build/editor-time
688
+ * identity nothing at runtime needs, and nothing else in the deploy
689
+ * pipeline strips it. Set `false` only if a consumer has a real reason to
690
+ * ship it (e.g. a tool downstream of the build still reads it).
691
+ * @default true
692
+ */
693
+ stripVidsInBuild?: boolean;
685
694
  }
686
695
  interface ArchieDevToolsPlugin {
687
696
  name: string;
package/dist/index.js CHANGED
@@ -381,6 +381,7 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
381
381
  const allPlain = children.every((c) => {
382
382
  if (t.isJSXElement(c)) return true;
383
383
  if (t.isJSXText(c)) return c.value.trim() === "";
384
+ if (t.isJSXExpressionContainer(c) && t.isJSXEmptyExpression(c.expression)) return true;
384
385
  return false;
385
386
  });
386
387
  return allPlain ? "plain" : "expression";
@@ -1210,12 +1211,42 @@ function babelPluginEditorMeta({ types: t }, opts = {}) {
1210
1211
  };
1211
1212
  }
1212
1213
 
1214
+ // src/babel-plugin-strip-data-vid.ts
1215
+ var import_editor_protocol2 = require("@8base-archie/editor-protocol");
1216
+ function isStaticDataVidAttribute(t, attr) {
1217
+ if (!t.isJSXAttribute(attr)) return false;
1218
+ if (!t.isJSXIdentifier(attr.name, { name: import_editor_protocol2.VID_ATTR })) return false;
1219
+ const { value } = attr;
1220
+ if (t.isStringLiteral(value)) return true;
1221
+ if (t.isJSXExpressionContainer(value) && t.isStringLiteral(value.expression)) return true;
1222
+ return false;
1223
+ }
1224
+ function babelPluginStripDataVid({
1225
+ types: t
1226
+ }) {
1227
+ return {
1228
+ name: "archie-strip-data-vid",
1229
+ visitor: {
1230
+ JSXElement: {
1231
+ exit(path) {
1232
+ const opening = path.node.openingElement;
1233
+ if (!opening.attributes.some((attr) => isStaticDataVidAttribute(t, attr))) return;
1234
+ opening.attributes = opening.attributes.filter(
1235
+ (attr) => !isStaticDataVidAttribute(t, attr)
1236
+ );
1237
+ }
1238
+ }
1239
+ }
1240
+ };
1241
+ }
1242
+
1213
1243
  // src/index.ts
1214
1244
  var LOCAL_INSPECTOR_SERVE_PATH = "/_archie/inspector.js";
1215
1245
  function archieDevTools(options = {}) {
1216
1246
  const {
1217
1247
  debug = false,
1218
- dndPages = /src\/pages\//
1248
+ dndPages = /src\/pages\//,
1249
+ stripVidsInBuild = true
1219
1250
  } = options;
1220
1251
  const log = (...args) => {
1221
1252
  if (debug) console.log("[Archie DevTools]", ...args);
@@ -1223,6 +1254,7 @@ function archieDevTools(options = {}) {
1223
1254
  let babelCore = null;
1224
1255
  let includeHostSourceRef = false;
1225
1256
  let isDevServer = false;
1257
+ let isProductionBuild = false;
1226
1258
  const transformedByHook = /* @__PURE__ */ new Set();
1227
1259
  const warnedMissingVidFiles = /* @__PURE__ */ new Set();
1228
1260
  function warnMissingVid(info) {
@@ -1249,6 +1281,7 @@ function archieDevTools(options = {}) {
1249
1281
  configResolved(config) {
1250
1282
  includeHostSourceRef = !config.isProduction;
1251
1283
  isDevServer = config.command === "serve";
1284
+ isProductionBuild = config.command === "build" && stripVidsInBuild;
1252
1285
  },
1253
1286
  configureServer(server) {
1254
1287
  server.middlewares.use(LOCAL_INSPECTOR_SERVE_PATH, (_req, res) => {
@@ -1288,6 +1321,9 @@ function archieDevTools(options = {}) {
1288
1321
  includeHostSourceRef,
1289
1322
  onMissingVid: isDevServer ? warnMissingVid : void 0
1290
1323
  }),
1324
+ // AFTER babelPluginEditorMeta — see babel-plugin-strip-data-vid.ts
1325
+ // for why plugin order (not call order) makes that safe.
1326
+ ...isProductionBuild ? [babelPluginStripDataVid] : [],
1291
1327
  ...dndPages.test(id) ? [(b) => babelPluginDndAuto(b, { importSource: INTERNAL_DND_SCOPE_IMPORT_SOURCE })] : []
1292
1328
  ],
1293
1329
  parserOpts: {
@@ -1331,6 +1367,7 @@ function archieDevTools(options = {}) {
1331
1367
  onMissingVid: isDevServer ? warnMissingVid : void 0
1332
1368
  })
1333
1369
  );
1370
+ if (isProductionBuild) babelOptions.plugins.push(babelPluginStripDataVid);
1334
1371
  }
1335
1372
  if (dndPages.test(context.id) && !transformedByHook.has(context.id)) {
1336
1373
  log("Applying DnD transform to:", context.id);
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __dirname,
3
3
  babelPluginEditorMeta
4
- } from "./chunk-U4KG6UDY.mjs";
4
+ } from "./chunk-ZG6HZFTJ.mjs";
5
5
 
6
6
  // src/index.ts
7
7
  import { readFileSync } from "fs";
@@ -174,12 +174,42 @@ function babelPluginDndAuto({ types: t }, opts = {}) {
174
174
  };
175
175
  }
176
176
 
177
+ // src/babel-plugin-strip-data-vid.ts
178
+ import { VID_ATTR } from "@8base-archie/editor-protocol";
179
+ function isStaticDataVidAttribute(t, attr) {
180
+ if (!t.isJSXAttribute(attr)) return false;
181
+ if (!t.isJSXIdentifier(attr.name, { name: VID_ATTR })) return false;
182
+ const { value } = attr;
183
+ if (t.isStringLiteral(value)) return true;
184
+ if (t.isJSXExpressionContainer(value) && t.isStringLiteral(value.expression)) return true;
185
+ return false;
186
+ }
187
+ function babelPluginStripDataVid({
188
+ types: t
189
+ }) {
190
+ return {
191
+ name: "archie-strip-data-vid",
192
+ visitor: {
193
+ JSXElement: {
194
+ exit(path) {
195
+ const opening = path.node.openingElement;
196
+ if (!opening.attributes.some((attr) => isStaticDataVidAttribute(t, attr))) return;
197
+ opening.attributes = opening.attributes.filter(
198
+ (attr) => !isStaticDataVidAttribute(t, attr)
199
+ );
200
+ }
201
+ }
202
+ }
203
+ };
204
+ }
205
+
177
206
  // src/index.ts
178
207
  var LOCAL_INSPECTOR_SERVE_PATH = "/_archie/inspector.js";
179
208
  function archieDevTools(options = {}) {
180
209
  const {
181
210
  debug = false,
182
- dndPages = /src\/pages\//
211
+ dndPages = /src\/pages\//,
212
+ stripVidsInBuild = true
183
213
  } = options;
184
214
  const log = (...args) => {
185
215
  if (debug) console.log("[Archie DevTools]", ...args);
@@ -187,6 +217,7 @@ function archieDevTools(options = {}) {
187
217
  let babelCore = null;
188
218
  let includeHostSourceRef = false;
189
219
  let isDevServer = false;
220
+ let isProductionBuild = false;
190
221
  const transformedByHook = /* @__PURE__ */ new Set();
191
222
  const warnedMissingVidFiles = /* @__PURE__ */ new Set();
192
223
  function warnMissingVid(info) {
@@ -213,6 +244,7 @@ function archieDevTools(options = {}) {
213
244
  configResolved(config) {
214
245
  includeHostSourceRef = !config.isProduction;
215
246
  isDevServer = config.command === "serve";
247
+ isProductionBuild = config.command === "build" && stripVidsInBuild;
216
248
  },
217
249
  configureServer(server) {
218
250
  server.middlewares.use(LOCAL_INSPECTOR_SERVE_PATH, (_req, res) => {
@@ -252,6 +284,9 @@ function archieDevTools(options = {}) {
252
284
  includeHostSourceRef,
253
285
  onMissingVid: isDevServer ? warnMissingVid : void 0
254
286
  }),
287
+ // AFTER babelPluginEditorMeta — see babel-plugin-strip-data-vid.ts
288
+ // for why plugin order (not call order) makes that safe.
289
+ ...isProductionBuild ? [babelPluginStripDataVid] : [],
255
290
  ...dndPages.test(id) ? [(b) => babelPluginDndAuto(b, { importSource: INTERNAL_DND_SCOPE_IMPORT_SOURCE })] : []
256
291
  ],
257
292
  parserOpts: {
@@ -295,6 +330,7 @@ function archieDevTools(options = {}) {
295
330
  onMissingVid: isDevServer ? warnMissingVid : void 0
296
331
  })
297
332
  );
333
+ if (isProductionBuild) babelOptions.plugins.push(babelPluginStripDataVid);
298
334
  }
299
335
  if (dndPages.test(context.id) && !transformedByHook.has(context.id)) {
300
336
  log("Applying DnD transform to:", context.id);