@lego-build/plugins 0.0.1 → 0.0.2
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 +26 -4
- package/dist/index.js +183 -9
- package/package.json +22 -6
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,32 @@ 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
|
+
}
|
|
18
|
+
interface TransformResult {
|
|
19
|
+
code: string;
|
|
20
|
+
map: any;
|
|
21
|
+
}
|
|
6
22
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
23
|
+
* Vite plugin for adding source locator attributes to JSX elements.
|
|
24
|
+
* This enables click-to-source functionality in the preview iframe.
|
|
9
25
|
*/
|
|
10
|
-
declare function
|
|
26
|
+
declare function sourceLocator(options?: SourceLocatorOptions): {
|
|
27
|
+
name: string;
|
|
28
|
+
enforce: "pre";
|
|
29
|
+
apply: "serve";
|
|
30
|
+
version: string;
|
|
31
|
+
transform(code: string, id: string): Promise<TransformResult | null | undefined>;
|
|
32
|
+
};
|
|
11
33
|
|
|
12
|
-
export {
|
|
34
|
+
export { type SourceLocatorOptions, sendToParent, setupElementSelector, setupIframeBridge, sourceLocator };
|
package/dist/index.js
CHANGED
|
@@ -548,19 +548,193 @@ function setupElementSelector() {
|
|
|
548
548
|
});
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
-
// src/
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
551
|
+
// src/source-locator.ts
|
|
552
|
+
import path from "path";
|
|
553
|
+
import * as parser from "@babel/parser";
|
|
554
|
+
import traverse from "@babel/traverse";
|
|
555
|
+
import generate from "@babel/generator";
|
|
556
|
+
import * as t from "@babel/types";
|
|
557
|
+
import micromatch from "micromatch";
|
|
558
|
+
import { createHash } from "crypto";
|
|
559
|
+
var HTML_TAGS = [
|
|
560
|
+
"div",
|
|
561
|
+
"span",
|
|
562
|
+
"p",
|
|
563
|
+
"h1",
|
|
564
|
+
"h2",
|
|
565
|
+
"h3",
|
|
566
|
+
"h4",
|
|
567
|
+
"h5",
|
|
568
|
+
"h6",
|
|
569
|
+
"button",
|
|
570
|
+
"a",
|
|
571
|
+
"img",
|
|
572
|
+
"input",
|
|
573
|
+
"textarea",
|
|
574
|
+
"select",
|
|
575
|
+
"form",
|
|
576
|
+
"ul",
|
|
577
|
+
"ol",
|
|
578
|
+
"li",
|
|
579
|
+
"table",
|
|
580
|
+
"thead",
|
|
581
|
+
"tbody",
|
|
582
|
+
"tr",
|
|
583
|
+
"td",
|
|
584
|
+
"th",
|
|
585
|
+
"header",
|
|
586
|
+
"footer",
|
|
587
|
+
"nav",
|
|
588
|
+
"main",
|
|
589
|
+
"section",
|
|
590
|
+
"article",
|
|
591
|
+
"aside",
|
|
592
|
+
"label",
|
|
593
|
+
"strong",
|
|
594
|
+
"em",
|
|
595
|
+
"code",
|
|
596
|
+
"pre",
|
|
597
|
+
"blockquote"
|
|
598
|
+
];
|
|
599
|
+
var TEXT_TAGS = ["label", "span", "button", "p", "div", "h1", "h2", "h3", "h4", "h5", "h6"];
|
|
600
|
+
function getWorkspaceRoot(filePath) {
|
|
601
|
+
let dir = path.resolve(filePath);
|
|
602
|
+
const { root } = path.parse(dir);
|
|
603
|
+
while (dir !== root) {
|
|
604
|
+
if (path.basename(dir) === "workspace") return dir;
|
|
605
|
+
dir = path.dirname(dir);
|
|
606
|
+
}
|
|
607
|
+
return path.resolve(process.cwd(), "..");
|
|
608
|
+
}
|
|
609
|
+
function shouldProcessTag(tagName, options) {
|
|
610
|
+
if (options.filterTag) {
|
|
611
|
+
if (Array.isArray(options.filterTag)) {
|
|
612
|
+
return options.filterTag.includes(tagName);
|
|
613
|
+
}
|
|
614
|
+
return options.filterTag(tagName);
|
|
615
|
+
}
|
|
616
|
+
return HTML_TAGS.includes(tagName.toLowerCase());
|
|
617
|
+
}
|
|
618
|
+
function getElementTextContent2(nodePath) {
|
|
619
|
+
const parent = nodePath.parent;
|
|
620
|
+
if (!t.isJSXElement(parent)) return "";
|
|
621
|
+
let text = "";
|
|
622
|
+
let hasExpression = false;
|
|
623
|
+
for (const child of parent.children) {
|
|
624
|
+
if (t.isJSXText(child)) {
|
|
625
|
+
text += child.value;
|
|
626
|
+
} else if (t.isJSXExpressionContainer(child)) {
|
|
627
|
+
hasExpression = true;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return hasExpression ? "" : text.trim();
|
|
631
|
+
}
|
|
632
|
+
function transformJSX(code, filePath, prefix, options) {
|
|
633
|
+
const ast = parser.parse(code, {
|
|
634
|
+
sourceType: "module",
|
|
635
|
+
plugins: ["jsx", "typescript", "classProperties", "decorators-legacy"]
|
|
636
|
+
});
|
|
637
|
+
const workspaceRoot = getWorkspaceRoot(filePath);
|
|
638
|
+
const relativePath = path.relative(workspaceRoot, filePath);
|
|
639
|
+
let hasChanges = false;
|
|
640
|
+
traverse(ast, {
|
|
641
|
+
JSXOpeningElement(nodePath) {
|
|
642
|
+
const node = nodePath.node;
|
|
643
|
+
const hasLocatorAttr = node.attributes.some(
|
|
644
|
+
(attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name.startsWith(`data-${prefix}`)
|
|
645
|
+
);
|
|
646
|
+
if (hasLocatorAttr) return;
|
|
647
|
+
if (t.isJSXIdentifier(node.name) && node.name.name === "Fragment") return;
|
|
648
|
+
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")
|
|
649
|
+
return;
|
|
650
|
+
let tagName = "";
|
|
651
|
+
if (t.isJSXIdentifier(node.name)) {
|
|
652
|
+
tagName = node.name.name;
|
|
653
|
+
}
|
|
654
|
+
if (!shouldProcessTag(tagName, options)) return;
|
|
655
|
+
const line = node.loc?.start.line;
|
|
656
|
+
const column = node.loc?.start.column;
|
|
657
|
+
if (!line || column === void 0) return;
|
|
658
|
+
const textContent = TEXT_TAGS.includes(tagName.toLowerCase()) ? getElementTextContent2(nodePath) : "";
|
|
659
|
+
const attributes = [
|
|
660
|
+
t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-path`), t.stringLiteral(relativePath)),
|
|
661
|
+
t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-line`), t.stringLiteral(String(line))),
|
|
662
|
+
t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-column`), t.stringLiteral(String(column))),
|
|
663
|
+
t.jsxAttribute(t.jsxIdentifier(`data-${prefix}-tag`), t.stringLiteral(tagName))
|
|
664
|
+
];
|
|
665
|
+
if (textContent) {
|
|
666
|
+
attributes.push(
|
|
667
|
+
t.jsxAttribute(
|
|
668
|
+
t.jsxIdentifier(`data-${prefix}-text`),
|
|
669
|
+
t.stringLiteral(textContent)
|
|
670
|
+
)
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
const spreadIndex = node.attributes.findIndex((attr) => t.isJSXSpreadAttribute(attr));
|
|
674
|
+
if (spreadIndex === -1) {
|
|
675
|
+
node.attributes.push(...attributes);
|
|
676
|
+
} else {
|
|
677
|
+
node.attributes.splice(spreadIndex, 0, ...attributes);
|
|
678
|
+
}
|
|
679
|
+
hasChanges = true;
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
if (!hasChanges) return null;
|
|
683
|
+
const result = generate(ast, {}, code);
|
|
684
|
+
return {
|
|
685
|
+
code: result.code,
|
|
686
|
+
map: result.map
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
var transformCache = /* @__PURE__ */ new Map();
|
|
690
|
+
function sourceLocator(options = {}) {
|
|
691
|
+
const {
|
|
692
|
+
include = ["src/**/*.{jsx,tsx,js,ts}"],
|
|
693
|
+
exclude = [],
|
|
694
|
+
prefix = "locator",
|
|
695
|
+
enable = true
|
|
696
|
+
} = options;
|
|
697
|
+
const includePatterns = Array.isArray(include) ? include : [include];
|
|
698
|
+
const excludePatterns = Array.isArray(exclude) ? exclude : [exclude];
|
|
699
|
+
return {
|
|
700
|
+
name: "@lego/plugin-source-locator",
|
|
701
|
+
enforce: "pre",
|
|
702
|
+
apply: "serve",
|
|
703
|
+
version: "0.0.1",
|
|
704
|
+
async transform(code, id) {
|
|
705
|
+
if (!enable) return;
|
|
706
|
+
const relativePath = path.relative(process.cwd(), id);
|
|
707
|
+
const isIncluded = micromatch.isMatch(relativePath, includePatterns);
|
|
708
|
+
const isExcluded = excludePatterns.length > 0 && micromatch.isMatch(relativePath, excludePatterns);
|
|
709
|
+
if (!isIncluded || isExcluded) return;
|
|
710
|
+
const ext = path.extname(id);
|
|
711
|
+
if (![".jsx", ".tsx", ".js", ".ts"].includes(ext)) return;
|
|
712
|
+
const hash = createHash("md5").update(code).digest("hex");
|
|
713
|
+
const cacheKey = `${id}:${hash}`;
|
|
714
|
+
if (transformCache.has(cacheKey)) {
|
|
715
|
+
return transformCache.get(cacheKey);
|
|
716
|
+
}
|
|
717
|
+
const result = transformJSX(code, id, prefix, options);
|
|
718
|
+
if (result) {
|
|
719
|
+
transformCache.set(cacheKey, result);
|
|
720
|
+
return result;
|
|
721
|
+
}
|
|
722
|
+
return null;
|
|
557
723
|
}
|
|
558
|
-
}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/index.ts
|
|
728
|
+
try {
|
|
729
|
+
if (typeof window !== "undefined" && window.parent !== window) {
|
|
730
|
+
setupIframeBridge();
|
|
731
|
+
setupElementSelector();
|
|
559
732
|
}
|
|
733
|
+
} catch {
|
|
560
734
|
}
|
|
561
735
|
export {
|
|
562
|
-
initIframeBridge,
|
|
563
736
|
sendToParent,
|
|
564
737
|
setupElementSelector,
|
|
565
|
-
setupIframeBridge
|
|
738
|
+
setupIframeBridge,
|
|
739
|
+
sourceLocator
|
|
566
740
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lego-build/plugins",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -8,20 +8,36 @@
|
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"import": "./dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"./auto": {
|
|
13
|
-
"import": "./dist/auto.js"
|
|
14
11
|
}
|
|
15
12
|
},
|
|
16
13
|
"files": [
|
|
17
14
|
"dist"
|
|
18
15
|
],
|
|
19
16
|
"scripts": {
|
|
20
|
-
"build": "tsup src/index.ts
|
|
21
|
-
"dev": "tsup src/index.ts
|
|
17
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
18
|
+
"dev": "tsup src/index.ts --format esm --dts --watch"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@babel/generator": "^7.27.0",
|
|
22
|
+
"@babel/parser": "^7.27.0",
|
|
23
|
+
"@babel/traverse": "^7.27.0",
|
|
24
|
+
"@babel/types": "^7.27.0",
|
|
25
|
+
"micromatch": "^4.0.5"
|
|
22
26
|
},
|
|
23
27
|
"devDependencies": {
|
|
28
|
+
"@types/babel__generator": "^7.27.0",
|
|
29
|
+
"@types/babel__traverse": "^7.20.7",
|
|
30
|
+
"@types/micromatch": "^4.0.9",
|
|
31
|
+
"@types/node": "^22",
|
|
24
32
|
"tsup": "^8.0.0",
|
|
25
33
|
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"vite": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
26
42
|
}
|
|
27
43
|
}
|