@designtools/next-plugin 0.1.6 → 0.1.8
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.
- package/README.md +58 -0
- package/dist/chunk-6DZX6EAA.mjs +37 -0
- package/dist/codesurface-mount-loader.js +6 -4
- package/dist/codesurface-mount-loader.mjs +6 -4
- package/dist/codesurface.d.mts +3 -1
- package/dist/codesurface.d.ts +3 -1
- package/dist/codesurface.js +192 -12
- package/dist/codesurface.mjs +193 -13
- package/dist/index.js +75 -162
- package/dist/index.mjs +75 -162
- package/package.json +2 -2
- package/src/codesurface-mount-loader.ts +10 -6
- package/src/codesurface.tsx +254 -25
- package/src/index.ts +72 -12
- package/src/preview-route.ts +33 -176
package/src/codesurface.tsx
CHANGED
|
@@ -9,12 +9,18 @@
|
|
|
9
9
|
* with proper lifecycle management.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { useEffect, useRef } from "react";
|
|
12
|
+
import { useEffect, useRef, useState, createElement } from "react";
|
|
13
|
+
import { createPortal } from "react-dom";
|
|
13
14
|
|
|
14
15
|
// Overlay elements are created imperatively (not React-rendered)
|
|
15
16
|
// because they need to be fixed-position overlays that don't interfere
|
|
16
17
|
// with the app's React tree.
|
|
17
18
|
|
|
19
|
+
interface PreviewCombination {
|
|
20
|
+
label: string;
|
|
21
|
+
props: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
export function CodeSurface() {
|
|
19
25
|
const stateRef = useRef({
|
|
20
26
|
selectionMode: false,
|
|
@@ -31,6 +37,29 @@ export function CodeSurface() {
|
|
|
31
37
|
selectedOverlay: null as HTMLDivElement | null,
|
|
32
38
|
});
|
|
33
39
|
|
|
40
|
+
// Preview overlay state
|
|
41
|
+
const [previewComponent, setPreviewComponent] = useState<React.ComponentType<any> | null>(null);
|
|
42
|
+
const [previewCombinations, setPreviewCombinations] = useState<PreviewCombination[]>([]);
|
|
43
|
+
const [previewDefaultChildren, setPreviewDefaultChildren] = useState("");
|
|
44
|
+
const [previewError, setPreviewError] = useState<string | null>(null);
|
|
45
|
+
const [showPreview, setShowPreview] = useState(false);
|
|
46
|
+
|
|
47
|
+
// Ref so the imperative useEffect can trigger state updates
|
|
48
|
+
const setPreviewRef = useRef({
|
|
49
|
+
setPreviewComponent,
|
|
50
|
+
setPreviewCombinations,
|
|
51
|
+
setPreviewDefaultChildren,
|
|
52
|
+
setPreviewError,
|
|
53
|
+
setShowPreview,
|
|
54
|
+
});
|
|
55
|
+
setPreviewRef.current = {
|
|
56
|
+
setPreviewComponent,
|
|
57
|
+
setPreviewCombinations,
|
|
58
|
+
setPreviewDefaultChildren,
|
|
59
|
+
setPreviewError,
|
|
60
|
+
setShowPreview,
|
|
61
|
+
};
|
|
62
|
+
|
|
34
63
|
useEffect(() => {
|
|
35
64
|
const s = stateRef.current;
|
|
36
65
|
|
|
@@ -593,6 +622,49 @@ export function CodeSurface() {
|
|
|
593
622
|
"letter-spacing", "text-align", "text-transform", "white-space",
|
|
594
623
|
];
|
|
595
624
|
|
|
625
|
+
/**
|
|
626
|
+
* Walk UP the fiber tree from a DOM element's fiber to find the
|
|
627
|
+
* component that renders this element as its root host element.
|
|
628
|
+
* Checks tags 0 (Function), 1 (Class), 11 (ForwardRef),
|
|
629
|
+
* 14 (Memo), 15 (SimpleMemo) as component boundaries.
|
|
630
|
+
*/
|
|
631
|
+
function findComponentFiberAbove(el: Element): any | null {
|
|
632
|
+
const fiber = getFiber(el);
|
|
633
|
+
if (!fiber) return null;
|
|
634
|
+
let candidate = fiber.return;
|
|
635
|
+
while (candidate) {
|
|
636
|
+
const tag = candidate.tag;
|
|
637
|
+
if (tag === 0 || tag === 1 || tag === 11 || tag === 14 || tag === 15) {
|
|
638
|
+
// Check if this component's root host element is our element
|
|
639
|
+
if (findOwnHostElement(candidate) === el) return candidate;
|
|
640
|
+
}
|
|
641
|
+
candidate = candidate.return;
|
|
642
|
+
}
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Read memoizedProps from a fiber, filtering to simple values only.
|
|
648
|
+
* Skips children, ref, key, className, style, and data-* props.
|
|
649
|
+
*/
|
|
650
|
+
function extractFiberProps(fiber: any): Record<string, string | number | boolean> | null {
|
|
651
|
+
const props = fiber?.memoizedProps;
|
|
652
|
+
if (!props || typeof props !== "object") return null;
|
|
653
|
+
const skipKeys = new Set(["children", "ref", "key", "className", "style"]);
|
|
654
|
+
const result: Record<string, string | number | boolean> = {};
|
|
655
|
+
let count = 0;
|
|
656
|
+
for (const k of Object.keys(props)) {
|
|
657
|
+
if (skipKeys.has(k)) continue;
|
|
658
|
+
if (k.startsWith("data-")) continue;
|
|
659
|
+
const v = props[k];
|
|
660
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
661
|
+
result[k] = v;
|
|
662
|
+
count++;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return count > 0 ? result : null;
|
|
666
|
+
}
|
|
667
|
+
|
|
596
668
|
function extractElementData(el: Element) {
|
|
597
669
|
const computed = getComputedStyle(el);
|
|
598
670
|
const rect = el.getBoundingClientRect();
|
|
@@ -635,16 +707,14 @@ export function CodeSurface() {
|
|
|
635
707
|
}
|
|
636
708
|
|
|
637
709
|
// For component instances: read data-instance-source directly from the DOM element.
|
|
638
|
-
// The Babel transform adds this attribute to component JSX (<Button>, <Card>)
|
|
639
|
-
// and it propagates via {...props} to the rendered DOM element, carrying exact
|
|
640
|
-
// page-level coordinates of each component usage site.
|
|
641
710
|
let instanceSourceFile: string | null = null;
|
|
642
711
|
let instanceSourceLine: number | null = null;
|
|
643
712
|
let instanceSourceCol: number | null = null;
|
|
644
713
|
let componentName: string | null = null;
|
|
645
714
|
|
|
715
|
+
const dataSlot = el.getAttribute("data-slot");
|
|
646
716
|
const instanceSource = el.getAttribute("data-instance-source");
|
|
647
|
-
if (instanceSource &&
|
|
717
|
+
if (instanceSource && dataSlot) {
|
|
648
718
|
const lc = instanceSource.lastIndexOf(":");
|
|
649
719
|
const slc = instanceSource.lastIndexOf(":", lc - 1);
|
|
650
720
|
if (slc > 0) {
|
|
@@ -654,13 +724,21 @@ export function CodeSurface() {
|
|
|
654
724
|
}
|
|
655
725
|
|
|
656
726
|
// Derive component name from data-slot (e.g. "card-title" -> "CardTitle")
|
|
657
|
-
|
|
658
|
-
componentName = slot
|
|
727
|
+
componentName = dataSlot
|
|
659
728
|
.split("-")
|
|
660
729
|
.map((s: string) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
661
730
|
.join("");
|
|
662
731
|
}
|
|
663
732
|
|
|
733
|
+
// Extract runtime props from React fiber for component instances
|
|
734
|
+
let fiberProps: Record<string, string | number | boolean> | null = null;
|
|
735
|
+
if (dataSlot) {
|
|
736
|
+
const compFiber = findComponentFiberAbove(el);
|
|
737
|
+
if (compFiber) {
|
|
738
|
+
fiberProps = extractFiberProps(compFiber);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
664
742
|
return {
|
|
665
743
|
tag: el.tagName.toLowerCase(),
|
|
666
744
|
className: (el.getAttribute("class") || "").trim(),
|
|
@@ -677,6 +755,7 @@ export function CodeSurface() {
|
|
|
677
755
|
instanceSourceLine,
|
|
678
756
|
instanceSourceCol,
|
|
679
757
|
componentName,
|
|
758
|
+
fiberProps,
|
|
680
759
|
};
|
|
681
760
|
}
|
|
682
761
|
|
|
@@ -732,6 +811,27 @@ export function CodeSurface() {
|
|
|
732
811
|
tick();
|
|
733
812
|
}
|
|
734
813
|
|
|
814
|
+
// --- Helper to hide/show selection overlays ---
|
|
815
|
+
function hideSelectionOverlays() {
|
|
816
|
+
if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
|
|
817
|
+
if (s.tooltip) s.tooltip.style.display = "none";
|
|
818
|
+
if (s.selectedOverlay) s.selectedOverlay.style.display = "none";
|
|
819
|
+
s.hoveredElement = null;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function enterSelectionMode() {
|
|
823
|
+
s.selectionMode = true;
|
|
824
|
+
document.body.style.cursor = "crosshair";
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function exitSelectionMode() {
|
|
828
|
+
s.selectionMode = false;
|
|
829
|
+
document.body.style.cursor = "";
|
|
830
|
+
if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
|
|
831
|
+
if (s.tooltip) s.tooltip.style.display = "none";
|
|
832
|
+
s.hoveredElement = null;
|
|
833
|
+
}
|
|
834
|
+
|
|
735
835
|
// --- Event handlers ---
|
|
736
836
|
function onMouseMove(e: MouseEvent) {
|
|
737
837
|
if (!s.selectionMode || !s.highlightOverlay || !s.tooltip) return;
|
|
@@ -772,15 +872,10 @@ export function CodeSurface() {
|
|
|
772
872
|
|
|
773
873
|
switch (msg.type) {
|
|
774
874
|
case "tool:enterSelectionMode":
|
|
775
|
-
|
|
776
|
-
document.body.style.cursor = "crosshair";
|
|
875
|
+
enterSelectionMode();
|
|
777
876
|
break;
|
|
778
877
|
case "tool:exitSelectionMode":
|
|
779
|
-
|
|
780
|
-
document.body.style.cursor = "";
|
|
781
|
-
if (s.highlightOverlay) s.highlightOverlay.style.display = "none";
|
|
782
|
-
if (s.tooltip) s.tooltip.style.display = "none";
|
|
783
|
-
s.hoveredElement = null;
|
|
878
|
+
exitSelectionMode();
|
|
784
879
|
break;
|
|
785
880
|
case "tool:previewInlineStyle": {
|
|
786
881
|
if (s.selectedElement && s.selectedElement instanceof HTMLElement) {
|
|
@@ -809,12 +904,7 @@ export function CodeSurface() {
|
|
|
809
904
|
case "tool:previewTokenValue": {
|
|
810
905
|
const prop = msg.property as string;
|
|
811
906
|
const value = msg.value as string;
|
|
812
|
-
// Track current preview value
|
|
813
907
|
s.tokenPreviewValues.set(prop, value);
|
|
814
|
-
// Inject a <style> tag override.
|
|
815
|
-
// Tailwind v4 resolves @theme variables at build time and inlines
|
|
816
|
-
// them into utility classes, so setting CSS custom properties on
|
|
817
|
-
// :root has no effect. Instead we target utility classes directly.
|
|
818
908
|
if (!s.tokenPreviewStyle) {
|
|
819
909
|
s.tokenPreviewStyle = document.createElement("style");
|
|
820
910
|
s.tokenPreviewStyle.id = "codesurface-token-preview";
|
|
@@ -822,13 +912,10 @@ export function CodeSurface() {
|
|
|
822
912
|
}
|
|
823
913
|
const cssRules: string[] = [];
|
|
824
914
|
for (const [k, v] of s.tokenPreviewValues) {
|
|
825
|
-
// Derive Tailwind utility class from CSS variable name:
|
|
826
|
-
// --shadow-sm → .shadow-sm, --shadow → .shadow
|
|
827
915
|
if (k.startsWith("--shadow")) {
|
|
828
|
-
const cls = k.slice(2);
|
|
916
|
+
const cls = k.slice(2);
|
|
829
917
|
cssRules.push(`.${cls}, [class*="${cls}"] { box-shadow: ${v} !important; }`);
|
|
830
918
|
} else {
|
|
831
|
-
// For other tokens (colors, spacing, etc.) override the custom property
|
|
832
919
|
cssRules.push(`*, *::before, *::after { ${k}: ${v} !important; }`);
|
|
833
920
|
}
|
|
834
921
|
}
|
|
@@ -886,6 +973,66 @@ export function CodeSurface() {
|
|
|
886
973
|
}
|
|
887
974
|
break;
|
|
888
975
|
}
|
|
976
|
+
case "tool:renderPreview": {
|
|
977
|
+
const { componentPath, exportName, combinations: combos, defaultChildren: children } = msg;
|
|
978
|
+
const currentRegistry = (window as any).__DESIGNTOOLS_REGISTRY__ as Record<string, () => Promise<any>> | undefined;
|
|
979
|
+
|
|
980
|
+
if (!currentRegistry) {
|
|
981
|
+
setPreviewRef.current.setPreviewError("No component registry available. Ensure designtools-registry.ts is imported.");
|
|
982
|
+
setPreviewRef.current.setShowPreview(true);
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const loader = currentRegistry[componentPath];
|
|
987
|
+
if (!loader) {
|
|
988
|
+
setPreviewRef.current.setPreviewError(
|
|
989
|
+
`Component "${componentPath}" not found in registry. Available: ${Object.keys(currentRegistry).join(", ")}`
|
|
990
|
+
);
|
|
991
|
+
setPreviewRef.current.setShowPreview(true);
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// Disable selection mode and hide overlays
|
|
996
|
+
exitSelectionMode();
|
|
997
|
+
hideSelectionOverlays();
|
|
998
|
+
|
|
999
|
+
// Load the component
|
|
1000
|
+
loader().then((mod: any) => {
|
|
1001
|
+
const Comp = mod[exportName] || mod.default;
|
|
1002
|
+
if (!Comp) {
|
|
1003
|
+
setPreviewRef.current.setPreviewError(`Export "${exportName}" not found in ${componentPath}`);
|
|
1004
|
+
setPreviewRef.current.setShowPreview(true);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
setPreviewRef.current.setPreviewError(null);
|
|
1009
|
+
setPreviewRef.current.setPreviewComponent(() => Comp);
|
|
1010
|
+
setPreviewRef.current.setPreviewCombinations(combos || []);
|
|
1011
|
+
setPreviewRef.current.setPreviewDefaultChildren(children || exportName);
|
|
1012
|
+
setPreviewRef.current.setShowPreview(true);
|
|
1013
|
+
|
|
1014
|
+
// Notify editor that preview is ready
|
|
1015
|
+
window.parent.postMessage(
|
|
1016
|
+
{ type: "tool:previewReady", cellCount: (combos || []).length },
|
|
1017
|
+
"*"
|
|
1018
|
+
);
|
|
1019
|
+
}).catch((err: any) => {
|
|
1020
|
+
setPreviewRef.current.setPreviewError(`Failed to load component: ${err.message}`);
|
|
1021
|
+
setPreviewRef.current.setShowPreview(true);
|
|
1022
|
+
});
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
case "tool:exitPreview": {
|
|
1026
|
+
setPreviewRef.current.setShowPreview(false);
|
|
1027
|
+
setPreviewRef.current.setPreviewComponent(null);
|
|
1028
|
+
setPreviewRef.current.setPreviewCombinations([]);
|
|
1029
|
+
setPreviewRef.current.setPreviewDefaultChildren("");
|
|
1030
|
+
setPreviewRef.current.setPreviewError(null);
|
|
1031
|
+
|
|
1032
|
+
// Restore selection mode
|
|
1033
|
+
enterSelectionMode();
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
889
1036
|
}
|
|
890
1037
|
}
|
|
891
1038
|
|
|
@@ -923,6 +1070,88 @@ export function CodeSurface() {
|
|
|
923
1070
|
};
|
|
924
1071
|
}, []);
|
|
925
1072
|
|
|
926
|
-
//
|
|
927
|
-
return null;
|
|
1073
|
+
// --- Preview overlay rendered via portal ---
|
|
1074
|
+
if (!showPreview) return null;
|
|
1075
|
+
|
|
1076
|
+
if (previewError) {
|
|
1077
|
+
return createPortal(
|
|
1078
|
+
<div style={{
|
|
1079
|
+
position: "fixed",
|
|
1080
|
+
inset: 0,
|
|
1081
|
+
zIndex: 999999,
|
|
1082
|
+
background: "var(--background, white)",
|
|
1083
|
+
overflow: "auto",
|
|
1084
|
+
padding: 32,
|
|
1085
|
+
}}>
|
|
1086
|
+
<div style={{ color: "var(--destructive, #ef4444)", fontFamily: "monospace", fontSize: 14 }}>
|
|
1087
|
+
{previewError}
|
|
1088
|
+
</div>
|
|
1089
|
+
</div>,
|
|
1090
|
+
document.body
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (!previewComponent) {
|
|
1095
|
+
return createPortal(
|
|
1096
|
+
<div style={{
|
|
1097
|
+
position: "fixed",
|
|
1098
|
+
inset: 0,
|
|
1099
|
+
zIndex: 999999,
|
|
1100
|
+
background: "var(--background, white)",
|
|
1101
|
+
overflow: "auto",
|
|
1102
|
+
padding: 32,
|
|
1103
|
+
}}>
|
|
1104
|
+
<div style={{ color: "var(--muted-foreground, #888)", fontFamily: "inherit", fontSize: 14 }}>
|
|
1105
|
+
Loading component...
|
|
1106
|
+
</div>
|
|
1107
|
+
</div>,
|
|
1108
|
+
document.body
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const Component = previewComponent;
|
|
1113
|
+
|
|
1114
|
+
return createPortal(
|
|
1115
|
+
<div style={{
|
|
1116
|
+
position: "fixed",
|
|
1117
|
+
inset: 0,
|
|
1118
|
+
zIndex: 999999,
|
|
1119
|
+
background: "var(--background, white)",
|
|
1120
|
+
overflow: "auto",
|
|
1121
|
+
padding: 32,
|
|
1122
|
+
}}>
|
|
1123
|
+
<div style={{
|
|
1124
|
+
display: "grid",
|
|
1125
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))",
|
|
1126
|
+
gap: 24,
|
|
1127
|
+
}}>
|
|
1128
|
+
{previewCombinations.map((combo, i) => (
|
|
1129
|
+
<div key={i} style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
1130
|
+
<div style={{
|
|
1131
|
+
fontSize: 11,
|
|
1132
|
+
fontWeight: 600,
|
|
1133
|
+
color: "var(--muted-foreground, #888)",
|
|
1134
|
+
textTransform: "uppercase" as const,
|
|
1135
|
+
letterSpacing: "0.05em",
|
|
1136
|
+
}}>
|
|
1137
|
+
{combo.label}
|
|
1138
|
+
</div>
|
|
1139
|
+
<div style={{
|
|
1140
|
+
padding: 16,
|
|
1141
|
+
border: "1px solid var(--border, #e5e7eb)",
|
|
1142
|
+
borderRadius: 8,
|
|
1143
|
+
display: "flex",
|
|
1144
|
+
alignItems: "center",
|
|
1145
|
+
justifyContent: "center",
|
|
1146
|
+
minHeight: 64,
|
|
1147
|
+
background: "var(--card, var(--background, #fff))",
|
|
1148
|
+
}}>
|
|
1149
|
+
{createElement(Component, combo.props, previewDefaultChildren)}
|
|
1150
|
+
</div>
|
|
1151
|
+
</div>
|
|
1152
|
+
))}
|
|
1153
|
+
</div>
|
|
1154
|
+
</div>,
|
|
1155
|
+
document.body
|
|
1156
|
+
);
|
|
928
1157
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,10 +8,20 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import path from "path";
|
|
11
|
-
import {
|
|
11
|
+
import { generateComponentRegistry } from "./preview-route.js";
|
|
12
12
|
|
|
13
13
|
export function withDesigntools<T extends Record<string, any>>(nextConfig: T = {} as T): T {
|
|
14
|
-
|
|
14
|
+
// Generate the component registry unconditionally (filesystem operation, not loader-specific).
|
|
15
|
+
// Use process.cwd() since withDesigntools() is called at config time from the project root.
|
|
16
|
+
const projectRoot = process.cwd();
|
|
17
|
+
const appDir = path.resolve(projectRoot, "app");
|
|
18
|
+
try {
|
|
19
|
+
generateComponentRegistry(appDir);
|
|
20
|
+
} catch {
|
|
21
|
+
// Non-fatal — isolation feature just won't work
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const result: any = {
|
|
15
25
|
...nextConfig,
|
|
16
26
|
webpack(config: any, context: any) {
|
|
17
27
|
// Only add the loader in development
|
|
@@ -42,16 +52,6 @@ export function withDesigntools<T extends Record<string, any>>(nextConfig: T = {
|
|
|
42
52
|
},
|
|
43
53
|
],
|
|
44
54
|
});
|
|
45
|
-
|
|
46
|
-
// Generate the component isolation preview route
|
|
47
|
-
// This creates app/__designtools/preview/page.tsx which Next.js
|
|
48
|
-
// picks up as a route automatically via file-system routing.
|
|
49
|
-
const appDir = path.resolve(context.dir, "app");
|
|
50
|
-
try {
|
|
51
|
-
generatePreviewRoute(appDir);
|
|
52
|
-
} catch {
|
|
53
|
-
// Non-fatal — isolation feature just won't work
|
|
54
|
-
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// Call the user's webpack config if provided
|
|
@@ -62,4 +62,64 @@ export function withDesigntools<T extends Record<string, any>>(nextConfig: T = {
|
|
|
62
62
|
return config;
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
|
+
|
|
66
|
+
// Turbopack support — register the same loaders via turbopack.rules
|
|
67
|
+
// following the exact pattern used by @next/mdx.
|
|
68
|
+
if (process.env.TURBOPACK) {
|
|
69
|
+
const sourceLoader = {
|
|
70
|
+
loader: path.resolve(__dirname, "loader.js"),
|
|
71
|
+
options: {
|
|
72
|
+
// In webpack, context.dir provides this. In turbopack config time,
|
|
73
|
+
// process.cwd() is equivalent since next.config runs from the project root.
|
|
74
|
+
cwd: projectRoot,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const mountLoader = {
|
|
79
|
+
loader: path.resolve(__dirname, "codesurface-mount-loader.js"),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Source annotation rule — uses a unique glob like @next/mdx does
|
|
83
|
+
const sourceRule = {
|
|
84
|
+
loaders: [sourceLoader],
|
|
85
|
+
as: "*.tsx",
|
|
86
|
+
condition: {
|
|
87
|
+
path: /\.(tsx|jsx)$/,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const wildcardGlob = "{*,designtools-source}";
|
|
92
|
+
let wildcardRule = (nextConfig as any).turbopack?.rules?.[wildcardGlob] ?? [];
|
|
93
|
+
wildcardRule = [
|
|
94
|
+
...(Array.isArray(wildcardRule) ? wildcardRule : [wildcardRule]),
|
|
95
|
+
sourceRule,
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// Mount loader rule for root layout files
|
|
99
|
+
const mountRule = {
|
|
100
|
+
loaders: [mountLoader],
|
|
101
|
+
as: "*.tsx",
|
|
102
|
+
condition: {
|
|
103
|
+
path: /layout\.(tsx|jsx)$/,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const mountGlob = "{*,designtools-mount}";
|
|
108
|
+
let mountGlobRule = (nextConfig as any).turbopack?.rules?.[mountGlob] ?? [];
|
|
109
|
+
mountGlobRule = [
|
|
110
|
+
...(Array.isArray(mountGlobRule) ? mountGlobRule : [mountGlobRule]),
|
|
111
|
+
mountRule,
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
result.turbopack = {
|
|
115
|
+
...(nextConfig as any).turbopack,
|
|
116
|
+
rules: {
|
|
117
|
+
...(nextConfig as any).turbopack?.rules,
|
|
118
|
+
[wildcardGlob]: wildcardRule,
|
|
119
|
+
[mountGlob]: mountGlobRule,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result as T;
|
|
65
125
|
}
|