@lego-build/plugins 0.0.1 → 0.0.3

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/dist/index.d.ts CHANGED
@@ -3,10 +3,36 @@ declare function setupIframeBridge(): void;
3
3
 
4
4
  declare function setupElementSelector(): void;
5
5
 
6
+ interface SourceLocatorOptions {
7
+ /** Files to include */
8
+ include?: string | string[];
9
+ /** Files to exclude */
10
+ exclude?: string | string[];
11
+ /** Prefix for data attributes (default: 'locator') */
12
+ prefix?: string;
13
+ /** Enable/disable the plugin (default: true) */
14
+ enable?: boolean;
15
+ /** Custom filter for tags */
16
+ filterTag?: string[] | ((tag: string) => boolean);
17
+ /** Auto inject iframe bridge import (default: true) */
18
+ autoInjectBridge?: boolean;
19
+ /** Entry files to inject bridge import (default: auto-detect main entry) */
20
+ entryFiles?: string[];
21
+ }
22
+ interface TransformResult {
23
+ code: string;
24
+ map: any;
25
+ }
6
26
  /**
7
- * Auto-initialize iframe bridge when running in iframe.
8
- * Call this in your entry point or use setupIframeBridge/setupElementSelector manually.
27
+ * Vite plugin for adding source locator attributes to JSX elements.
28
+ * This enables click-to-source functionality in the preview iframe.
9
29
  */
10
- declare function initIframeBridge(): void;
30
+ declare function sourceLocator(options?: SourceLocatorOptions): {
31
+ name: string;
32
+ enforce: "pre";
33
+ apply: "serve";
34
+ version: string;
35
+ transform(code: string, id: string): Promise<TransformResult | null | undefined>;
36
+ };
11
37
 
12
- export { initIframeBridge, sendToParent, setupElementSelector, setupIframeBridge };
38
+ export { type SourceLocatorOptions, sendToParent, setupElementSelector, setupIframeBridge, sourceLocator };
package/dist/index.js CHANGED
@@ -1,6 +1,19 @@
1
1
  // src/bridge.ts
2
+ function getParentOrigin() {
3
+ if (document.referrer) {
4
+ try {
5
+ return new URL(document.referrer).origin;
6
+ } catch {
7
+ }
8
+ }
9
+ if (typeof window !== "undefined" && window.location.hostname === "localhost") {
10
+ return "http://localhost:4000";
11
+ }
12
+ return "*";
13
+ }
14
+ var PARENT_ORIGIN = getParentOrigin();
2
15
  function sendToParent(type, payload) {
3
- window.parent.postMessage({ type, payload }, "*");
16
+ window.parent.postMessage({ type, payload }, PARENT_ORIGIN);
4
17
  }
5
18
  function setupIframeBridge() {
6
19
  const pushState = window.history.pushState.bind(window.history);
@@ -90,16 +103,37 @@ function createTooltip() {
90
103
  document.body.appendChild(tooltip);
91
104
  return tooltip;
92
105
  }
106
+ var EDITOR_MIN_WIDTH = 200;
107
+ var EDITOR_APPROX_HEIGHT = 180;
108
+ var EDITOR_OFFSET = 8;
109
+ var VIEWPORT_PADDING = 16;
93
110
  function createInlineEditor(state, element, onSave, onCancel) {
94
111
  const rect = element.getBoundingClientRect();
95
112
  const elementText = getElementTextContent(element);
113
+ const viewportWidth = window.innerWidth;
114
+ const viewportHeight = window.innerHeight;
115
+ const editorWidth = Math.max(rect.width, EDITOR_MIN_WIDTH);
116
+ let left = rect.left;
117
+ let top = rect.bottom + EDITOR_OFFSET;
118
+ if (left + editorWidth > viewportWidth - VIEWPORT_PADDING) {
119
+ left = viewportWidth - editorWidth - VIEWPORT_PADDING;
120
+ }
121
+ if (left < VIEWPORT_PADDING) {
122
+ left = VIEWPORT_PADDING;
123
+ }
124
+ if (top + EDITOR_APPROX_HEIGHT > viewportHeight - VIEWPORT_PADDING) {
125
+ top = rect.top - EDITOR_APPROX_HEIGHT - EDITOR_OFFSET;
126
+ if (top < VIEWPORT_PADDING) {
127
+ top = VIEWPORT_PADDING;
128
+ }
129
+ }
96
130
  const editor = document.createElement("div");
97
131
  editor.id = "lego-inline-editor";
98
132
  editor.style.cssText = `
99
133
  position: fixed;
100
- top: ${rect.top}px;
101
- left: ${rect.left}px;
102
- width: ${Math.max(rect.width, 200)}px;
134
+ top: ${top}px;
135
+ left: ${left}px;
136
+ width: ${editorWidth}px;
103
137
  z-index: 2147483647;
104
138
  background: white;
105
139
  border: 2px solid #3b82f6;
@@ -107,6 +141,7 @@ function createInlineEditor(state, element, onSave, onCancel) {
107
141
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
108
142
  padding: 8px;
109
143
  font-family: ui-sans-serif, system-ui, sans-serif;
144
+ box-sizing: border-box;
110
145
  `;
111
146
  const header = document.createElement("div");
112
147
  header.style.cssText = `
@@ -216,16 +251,43 @@ function createInlineEditor(state, element, onSave, onCancel) {
216
251
  return editor;
217
252
  }
218
253
  function getElementTextContent(element) {
219
- let text = "";
254
+ const MAX_TEXT_LENGTH = 200;
255
+ const truncateText = (text) => {
256
+ const trimmed = text.trim();
257
+ return trimmed.length > MAX_TEXT_LENGTH ? trimmed.slice(0, MAX_TEXT_LENGTH) + "..." : trimmed;
258
+ };
259
+ let directText = "";
220
260
  for (const node of element.childNodes) {
221
261
  if (node.nodeType === Node.TEXT_NODE) {
222
- text += node.textContent?.trim() || "";
262
+ directText += node.textContent || "";
223
263
  }
224
264
  }
225
- if (!text && element.children.length === 0) {
226
- text = element.textContent?.trim() || "";
265
+ directText = directText.trim();
266
+ if (directText) {
267
+ return truncateText(directText);
227
268
  }
228
- return text;
269
+ if (element.children.length === 1) {
270
+ const child = element.children[0];
271
+ const simpleWrapperTags = ["span", "strong", "em", "b", "i", "small", "mark", "del", "ins", "sub", "sup"];
272
+ if (simpleWrapperTags.includes(child.tagName.toLowerCase())) {
273
+ const childText = child.textContent?.trim() || "";
274
+ if (childText) {
275
+ return truncateText(childText);
276
+ }
277
+ }
278
+ }
279
+ const hasComplexChildren = Array.from(element.children).some((child) => {
280
+ const tagName = child.tagName.toLowerCase();
281
+ const complexTags = ["div", "section", "article", "ul", "ol", "table", "form", "nav", "header", "footer", "main"];
282
+ return complexTags.includes(tagName) || child.children.length > 0;
283
+ });
284
+ if (!hasComplexChildren && element.children.length <= 2) {
285
+ const fullText = element.textContent?.trim() || "";
286
+ if (fullText) {
287
+ return truncateText(fullText);
288
+ }
289
+ }
290
+ return "";
229
291
  }
230
292
  function getElementDepth(element) {
231
293
  let depth = 0;
@@ -465,6 +527,7 @@ function setupElementSelector() {
465
527
  };
466
528
  const handleClick = (e) => {
467
529
  if (!state.isActive) return;
530
+ e._legoSelectorHandled = true;
468
531
  if (state.isEditing) {
469
532
  const target = e.target;
470
533
  if (state.inlineEditor?.contains(target)) {
@@ -481,20 +544,23 @@ function setupElementSelector() {
481
544
  }
482
545
  e.preventDefault();
483
546
  e.stopPropagation();
484
- e.stopImmediatePropagation();
485
547
  cancelInlineEdit();
486
548
  return;
487
549
  }
488
550
  e.preventDefault();
489
551
  e.stopPropagation();
490
- e.stopImmediatePropagation();
491
552
  selectElement();
492
553
  };
493
554
  const handleDoubleClick = (e) => {
494
555
  if (!state.isActive || state.isEditing) return;
556
+ const target = e.target;
557
+ if (target.id === "lego-element-selector-overlay" || target.id === "lego-element-selector-tooltip" || target.id === "lego-inline-editor") {
558
+ return;
559
+ }
560
+ e._legoSelectorHandled = true;
495
561
  e.preventDefault();
496
562
  e.stopPropagation();
497
- e.stopImmediatePropagation();
563
+ state.hoveredElement = target;
498
564
  startEditing();
499
565
  };
500
566
  const activateSelector = () => {
@@ -548,19 +614,221 @@ function setupElementSelector() {
548
614
  });
549
615
  }
550
616
 
551
- // src/index.ts
552
- function initIframeBridge() {
553
- try {
554
- if (window.parent !== window) {
555
- setupIframeBridge();
556
- setupElementSelector();
617
+ // src/source-locator.ts
618
+ import path from "path";
619
+ import * as parser from "@babel/parser";
620
+ import traverse from "@babel/traverse";
621
+ import generate from "@babel/generator";
622
+ import * as t from "@babel/types";
623
+ import micromatch from "micromatch";
624
+ import { createHash } from "crypto";
625
+ var HTML_TAGS = [
626
+ "div",
627
+ "span",
628
+ "p",
629
+ "h1",
630
+ "h2",
631
+ "h3",
632
+ "h4",
633
+ "h5",
634
+ "h6",
635
+ "button",
636
+ "a",
637
+ "img",
638
+ "input",
639
+ "textarea",
640
+ "select",
641
+ "form",
642
+ "ul",
643
+ "ol",
644
+ "li",
645
+ "table",
646
+ "thead",
647
+ "tbody",
648
+ "tr",
649
+ "td",
650
+ "th",
651
+ "header",
652
+ "footer",
653
+ "nav",
654
+ "main",
655
+ "section",
656
+ "article",
657
+ "aside",
658
+ "label",
659
+ "strong",
660
+ "em",
661
+ "code",
662
+ "pre",
663
+ "blockquote"
664
+ ];
665
+ var TEXT_TAGS = ["label", "span", "button", "p", "div", "h1", "h2", "h3", "h4", "h5", "h6"];
666
+ function getWorkspaceRoot(filePath) {
667
+ let dir = path.resolve(filePath);
668
+ const { root } = path.parse(dir);
669
+ while (dir !== root) {
670
+ if (path.basename(dir) === "workspace") return dir;
671
+ dir = path.dirname(dir);
672
+ }
673
+ return path.resolve(process.cwd(), "..");
674
+ }
675
+ function shouldProcessTag(tagName, options) {
676
+ if (options.filterTag) {
677
+ if (Array.isArray(options.filterTag)) {
678
+ return options.filterTag.includes(tagName);
557
679
  }
558
- } catch {
680
+ return options.filterTag(tagName);
681
+ }
682
+ return HTML_TAGS.includes(tagName.toLowerCase());
683
+ }
684
+ function getElementTextContent2(nodePath) {
685
+ const parent = nodePath.parent;
686
+ if (!t.isJSXElement(parent)) return "";
687
+ let text = "";
688
+ let hasExpression = false;
689
+ for (const child of parent.children) {
690
+ if (t.isJSXText(child)) {
691
+ text += child.value;
692
+ } else if (t.isJSXExpressionContainer(child)) {
693
+ hasExpression = true;
694
+ }
695
+ }
696
+ return hasExpression ? "" : text.trim();
697
+ }
698
+ function transformJSX(code, filePath, prefix, options) {
699
+ const ast = parser.parse(code, {
700
+ sourceType: "module",
701
+ plugins: ["jsx", "typescript", "classProperties", "decorators-legacy"]
702
+ });
703
+ const workspaceRoot = getWorkspaceRoot(filePath);
704
+ const relativePath = path.relative(workspaceRoot, filePath);
705
+ let hasChanges = false;
706
+ traverse(ast, {
707
+ JSXOpeningElement(nodePath) {
708
+ const node = nodePath.node;
709
+ const hasLocatorAttr = node.attributes.some(
710
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name.startsWith(`data-${prefix}`)
711
+ );
712
+ if (hasLocatorAttr) return;
713
+ if (t.isJSXIdentifier(node.name) && node.name.name === "Fragment") return;
714
+ if (t.isJSXMemberExpression(node.name) && t.isJSXIdentifier(node.name.object) && node.name.object.name === "React" && t.isJSXIdentifier(node.name.property) && node.name.property.name === "Fragment")
715
+ return;
716
+ let tagName = "";
717
+ if (t.isJSXIdentifier(node.name)) {
718
+ tagName = node.name.name;
719
+ }
720
+ if (!shouldProcessTag(tagName, options)) return;
721
+ const line = node.loc?.start.line;
722
+ const column = node.loc?.start.column;
723
+ if (!line || column === void 0) return;
724
+ const textContent = TEXT_TAGS.includes(tagName.toLowerCase()) ? getElementTextContent2(nodePath) : "";
725
+ const attributes = [
726
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-path`), t.stringLiteral(relativePath)),
727
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-line`), t.stringLiteral(String(line))),
728
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-column`), t.stringLiteral(String(column))),
729
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-tag`), t.stringLiteral(tagName))
730
+ ];
731
+ if (textContent) {
732
+ attributes.push(
733
+ t.jsxAttribute(
734
+ t.jsxIdentifier(`data-${prefix}-text`),
735
+ t.stringLiteral(textContent)
736
+ )
737
+ );
738
+ }
739
+ const spreadIndex = node.attributes.findIndex((attr) => t.isJSXSpreadAttribute(attr));
740
+ if (spreadIndex === -1) {
741
+ node.attributes.push(...attributes);
742
+ } else {
743
+ node.attributes.splice(spreadIndex, 0, ...attributes);
744
+ }
745
+ hasChanges = true;
746
+ }
747
+ });
748
+ if (!hasChanges) return null;
749
+ const result = generate(ast, {}, code);
750
+ return {
751
+ code: result.code,
752
+ map: result.map
753
+ };
754
+ }
755
+ var transformCache = /* @__PURE__ */ new Map();
756
+ function isEntryFile(filePath, entryFiles) {
757
+ if (entryFiles && entryFiles.length > 0) {
758
+ return entryFiles.some((entry) => filePath.endsWith(entry));
759
+ }
760
+ const fileName = path.basename(filePath);
761
+ const entryPatterns = [
762
+ "main.tsx",
763
+ "main.ts",
764
+ "main.jsx",
765
+ "main.js",
766
+ "index.tsx",
767
+ "index.ts",
768
+ "index.jsx",
769
+ "index.js",
770
+ "App.tsx",
771
+ "App.jsx"
772
+ ];
773
+ return entryPatterns.includes(fileName);
774
+ }
775
+ function sourceLocator(options = {}) {
776
+ const {
777
+ include = ["src/**/*.{jsx,tsx,js,ts}"],
778
+ exclude = [],
779
+ prefix = "locator",
780
+ enable = true,
781
+ autoInjectBridge = true,
782
+ entryFiles
783
+ } = options;
784
+ const includePatterns = Array.isArray(include) ? include : [include];
785
+ const excludePatterns = Array.isArray(exclude) ? exclude : [exclude];
786
+ return {
787
+ name: "@lego/plugin-source-locator",
788
+ enforce: "pre",
789
+ apply: "serve",
790
+ version: "0.0.1",
791
+ async transform(code, id) {
792
+ if (!enable) return;
793
+ if (autoInjectBridge && isEntryFile(id, entryFiles)) {
794
+ const bridgeImport = `import "@lego-build/plugins";`;
795
+ if (!code.includes(bridgeImport) && !code.includes("@lego-build/plugins")) {
796
+ code = `${bridgeImport}
797
+ ${code}`;
798
+ }
799
+ }
800
+ const relativePath = path.relative(process.cwd(), id);
801
+ const isIncluded = micromatch.isMatch(relativePath, includePatterns);
802
+ const isExcluded = excludePatterns.length > 0 && micromatch.isMatch(relativePath, excludePatterns);
803
+ if (!isIncluded || isExcluded) return code !== code ? { code, map: null } : null;
804
+ const ext = path.extname(id);
805
+ if (![".jsx", ".tsx", ".js", ".ts"].includes(ext)) return null;
806
+ const hash = createHash("md5").update(code).digest("hex");
807
+ const cacheKey = `${id}:${hash}`;
808
+ if (transformCache.has(cacheKey)) {
809
+ return transformCache.get(cacheKey);
810
+ }
811
+ const result = transformJSX(code, id, prefix, options);
812
+ if (result) {
813
+ transformCache.set(cacheKey, result);
814
+ return result;
815
+ }
816
+ return null;
817
+ }
818
+ };
819
+ }
820
+
821
+ // src/index.ts
822
+ try {
823
+ if (typeof window !== "undefined" && window.parent !== window) {
824
+ setupIframeBridge();
825
+ setupElementSelector();
559
826
  }
827
+ } catch {
560
828
  }
561
829
  export {
562
- initIframeBridge,
563
830
  sendToParent,
564
831
  setupElementSelector,
565
- setupIframeBridge
832
+ setupIframeBridge,
833
+ sourceLocator
566
834
  };
@@ -0,0 +1,25 @@
1
+ interface SourceLocatorLoaderOptions {
2
+ /** Prefix for data attributes (default: 'locator') */
3
+ prefix?: string;
4
+ /** Custom filter for tags */
5
+ filterTag?: string[] | ((tag: string) => boolean);
6
+ }
7
+ /**
8
+ * Webpack loader for adding source locator attributes to JSX elements.
9
+ * Use in next.config.js with webpack config.
10
+ */
11
+ declare function sourceLocatorLoader(this: any, source: string): string;
12
+ /**
13
+ * Helper to add source locator loader to Next.js webpack config.
14
+ *
15
+ * @example
16
+ * // next.config.mjs
17
+ * import { withSourceLocator } from '@lego-build/plugins/next';
18
+ *
19
+ * export default withSourceLocator({
20
+ * // your next config
21
+ * });
22
+ */
23
+ declare function withSourceLocator(nextConfig?: any, loaderOptions?: SourceLocatorLoaderOptions): any;
24
+
25
+ export { type SourceLocatorLoaderOptions, sourceLocatorLoader as default, withSourceLocator };
@@ -0,0 +1,198 @@
1
+ // src/webpack-loader.ts
2
+ import path from "path";
3
+ import { createRequire } from "module";
4
+ import * as parser from "@babel/parser";
5
+ import traverse from "@babel/traverse";
6
+ import generate from "@babel/generator";
7
+ import * as t from "@babel/types";
8
+ var require2 = createRequire(import.meta.url);
9
+ var HTML_TAGS = [
10
+ "div",
11
+ "span",
12
+ "p",
13
+ "h1",
14
+ "h2",
15
+ "h3",
16
+ "h4",
17
+ "h5",
18
+ "h6",
19
+ "button",
20
+ "a",
21
+ "img",
22
+ "input",
23
+ "textarea",
24
+ "select",
25
+ "form",
26
+ "ul",
27
+ "ol",
28
+ "li",
29
+ "table",
30
+ "thead",
31
+ "tbody",
32
+ "tr",
33
+ "td",
34
+ "th",
35
+ "header",
36
+ "footer",
37
+ "nav",
38
+ "main",
39
+ "section",
40
+ "article",
41
+ "aside",
42
+ "label",
43
+ "strong",
44
+ "em",
45
+ "code",
46
+ "pre",
47
+ "blockquote"
48
+ ];
49
+ var TEXT_TAGS = ["label", "span", "button", "p", "div", "h1", "h2", "h3", "h4", "h5", "h6"];
50
+ function getWorkspaceRoot(filePath) {
51
+ let dir = path.resolve(filePath);
52
+ const { root } = path.parse(dir);
53
+ while (dir !== root) {
54
+ if (path.basename(dir) === "workspace") return dir;
55
+ dir = path.dirname(dir);
56
+ }
57
+ return path.resolve(process.cwd(), "..");
58
+ }
59
+ function shouldProcessTag(tagName, options) {
60
+ if (options.filterTag) {
61
+ if (Array.isArray(options.filterTag)) {
62
+ return options.filterTag.includes(tagName);
63
+ }
64
+ return options.filterTag(tagName);
65
+ }
66
+ return HTML_TAGS.includes(tagName.toLowerCase());
67
+ }
68
+ function getElementTextContent(nodePath) {
69
+ const parent = nodePath.parent;
70
+ if (!t.isJSXElement(parent)) return "";
71
+ let text = "";
72
+ let hasExpression = false;
73
+ for (const child of parent.children) {
74
+ if (t.isJSXText(child)) {
75
+ text += child.value;
76
+ } else if (t.isJSXExpressionContainer(child)) {
77
+ hasExpression = true;
78
+ }
79
+ }
80
+ return hasExpression ? "" : text.trim();
81
+ }
82
+ function transformJSX(code, filePath, prefix, options) {
83
+ const ast = parser.parse(code, {
84
+ sourceType: "module",
85
+ plugins: ["jsx", "typescript", "classProperties", "decorators-legacy"]
86
+ });
87
+ const workspaceRoot = getWorkspaceRoot(filePath);
88
+ const relativePath = path.relative(workspaceRoot, filePath);
89
+ let hasChanges = false;
90
+ traverse(ast, {
91
+ JSXOpeningElement(nodePath) {
92
+ const node = nodePath.node;
93
+ const hasLocatorAttr = node.attributes.some(
94
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name.startsWith(`data-${prefix}`)
95
+ );
96
+ if (hasLocatorAttr) return;
97
+ if (t.isJSXIdentifier(node.name) && node.name.name === "Fragment") return;
98
+ if (t.isJSXMemberExpression(node.name) && t.isJSXIdentifier(node.name.object) && node.name.object.name === "React" && t.isJSXIdentifier(node.name.property) && node.name.property.name === "Fragment")
99
+ return;
100
+ let tagName = "";
101
+ if (t.isJSXIdentifier(node.name)) {
102
+ tagName = node.name.name;
103
+ }
104
+ if (!shouldProcessTag(tagName, options)) return;
105
+ const line = node.loc?.start.line;
106
+ const column = node.loc?.start.column;
107
+ if (!line || column === void 0) return;
108
+ const textContent = TEXT_TAGS.includes(tagName.toLowerCase()) ? getElementTextContent(nodePath) : "";
109
+ const attributes = [
110
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-path`), t.stringLiteral(relativePath)),
111
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-line`), t.stringLiteral(String(line))),
112
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-column`), t.stringLiteral(String(column))),
113
+ t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-tag`), t.stringLiteral(tagName))
114
+ ];
115
+ if (textContent) {
116
+ attributes.push(
117
+ t.jsxAttribute(
118
+ t.jsxIdentifier(`data-${prefix}-text`),
119
+ t.stringLiteral(textContent)
120
+ )
121
+ );
122
+ }
123
+ const spreadIndex = node.attributes.findIndex((attr) => t.isJSXSpreadAttribute(attr));
124
+ if (spreadIndex === -1) {
125
+ node.attributes.push(...attributes);
126
+ } else {
127
+ node.attributes.splice(spreadIndex, 0, ...attributes);
128
+ }
129
+ hasChanges = true;
130
+ }
131
+ });
132
+ if (!hasChanges) return null;
133
+ const result = generate(ast, {}, code);
134
+ return result.code;
135
+ }
136
+ function sourceLocatorLoader(source) {
137
+ const options = this.getOptions() || {};
138
+ const { prefix = "locator" } = options;
139
+ if (process.env.NODE_ENV !== "development") {
140
+ return source;
141
+ }
142
+ const ext = path.extname(this.resourcePath);
143
+ if (![".jsx", ".tsx", ".js", ".ts"].includes(ext)) {
144
+ return source;
145
+ }
146
+ if (this.resourcePath.includes("node_modules")) {
147
+ return source;
148
+ }
149
+ try {
150
+ const result = transformJSX(source, this.resourcePath, prefix, options);
151
+ return result || source;
152
+ } catch (error) {
153
+ console.error(`[source-locator] Error transforming ${this.resourcePath}:`, error);
154
+ return source;
155
+ }
156
+ }
157
+ function withSourceLocator(nextConfig = {}, loaderOptions = {}) {
158
+ return {
159
+ ...nextConfig,
160
+ webpack(config, options) {
161
+ if (options.dev) {
162
+ config.module.rules.push({
163
+ test: /\.(jsx|tsx|js|ts)$/,
164
+ exclude: /node_modules/,
165
+ use: [
166
+ {
167
+ loader: require2.resolve("@lego-build/plugins/webpack-loader"),
168
+ options: loaderOptions
169
+ }
170
+ ]
171
+ });
172
+ if (!options.isServer) {
173
+ const originalEntry = config.entry;
174
+ config.entry = async () => {
175
+ const entries = await (typeof originalEntry === "function" ? originalEntry() : originalEntry);
176
+ for (const entryName of Object.keys(entries)) {
177
+ if (Array.isArray(entries[entryName])) {
178
+ const bridgePath = require2.resolve("@lego-build/plugins");
179
+ if (!entries[entryName].includes(bridgePath)) {
180
+ entries[entryName].unshift(bridgePath);
181
+ }
182
+ }
183
+ }
184
+ return entries;
185
+ };
186
+ }
187
+ }
188
+ if (typeof nextConfig.webpack === "function") {
189
+ return nextConfig.webpack(config, options);
190
+ }
191
+ return config;
192
+ }
193
+ };
194
+ }
195
+ export {
196
+ sourceLocatorLoader as default,
197
+ withSourceLocator
198
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lego-build/plugins",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -9,19 +9,47 @@
9
9
  "types": "./dist/index.d.ts",
10
10
  "import": "./dist/index.js"
11
11
  },
12
- "./auto": {
13
- "import": "./dist/auto.js"
12
+ "./webpack-loader": {
13
+ "import": "./dist/webpack-loader.js"
14
+ },
15
+ "./next": {
16
+ "import": "./dist/webpack-loader.js"
14
17
  }
15
18
  },
16
19
  "files": [
17
20
  "dist"
18
21
  ],
19
22
  "scripts": {
20
- "build": "tsup src/index.ts src/auto.ts --format esm --dts",
21
- "dev": "tsup src/index.ts src/auto.ts --format esm --dts --watch"
23
+ "build": "tsup src/index.ts src/webpack-loader.ts --format esm --dts",
24
+ "dev": "tsup src/index.ts src/webpack-loader.ts --format esm --dts --watch"
25
+ },
26
+ "dependencies": {
27
+ "@babel/generator": "^7.27.0",
28
+ "@babel/parser": "^7.27.0",
29
+ "@babel/traverse": "^7.27.0",
30
+ "@babel/types": "^7.27.0",
31
+ "micromatch": "^4.0.5"
22
32
  },
23
33
  "devDependencies": {
34
+ "@types/babel__generator": "^7.27.0",
35
+ "@types/babel__traverse": "^7.20.7",
36
+ "@types/micromatch": "^4.0.9",
37
+ "@types/node": "^22",
38
+ "@types/webpack": "^5.28.5",
24
39
  "tsup": "^8.0.0",
25
- "typescript": "^5.0.0"
40
+ "typescript": "^5.0.0",
41
+ "webpack": "^5.98.0"
42
+ },
43
+ "peerDependencies": {
44
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
45
+ "webpack": "^5.0.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "vite": {
49
+ "optional": true
50
+ },
51
+ "webpack": {
52
+ "optional": true
53
+ }
26
54
  }
27
55
  }