@dev-to/react-plugin 0.1.1 → 0.3.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.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/debugTools.d.ts +3 -1
- package/dist/debugTools.d.ts.map +1 -1
- package/dist/index.js +606 -396
- package/dist/libBuildUtils.d.ts.map +1 -1
- package/dist/loaderUmdWrapper.d.ts +13 -0
- package/dist/loaderUmdWrapper.d.ts.map +1 -0
- package/dist/virtualModules.d.ts +9 -0
- package/dist/virtualModules.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/constants.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const STABLE_INIT_PATH: "/__dev_to_react__/init.js";
|
|
|
6
6
|
export declare const STABLE_REACT_RUNTIME_PATH: "/__dev_to_react__/react-runtime.js";
|
|
7
7
|
export declare const STABLE_DEBUG_HTML_PATH: "/__dev_to_react__/debug.html";
|
|
8
8
|
export declare const STABLE_DEBUG_JSON_PATH: "/__dev_to_react__/debug.json";
|
|
9
|
+
export declare const STABLE_LOADER_BASE_PATH: "/__dev_to_react__/loader";
|
|
9
10
|
export declare const EVENT_FULL_RELOAD: "dev_to_react:full-reload";
|
|
10
11
|
export declare const EVENT_HMR_UPDATE: "dev_to_react:hmr-update";
|
|
11
12
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,WAAW,gBAAyB,CAAA;AACjD,eAAO,MAAM,iBAAiB,kBAA8B,CAAA;AAE5D,eAAO,MAAM,gBAAgB,qBAAyB,CAAA;AACtD,eAAO,MAAM,oBAAoB,iCAA6B,CAAA;AAC9D,eAAO,MAAM,gBAAgB,6BAAyB,CAAA;AACtD,eAAO,MAAM,yBAAyB,sCAAkC,CAAA;AACxE,eAAO,MAAM,sBAAsB,gCAA+B,CAAA;AAClE,eAAO,MAAM,sBAAsB,gCAA+B,CAAA;AAClE,eAAO,MAAM,uBAAuB,4BAAgC,CAAA;AAEpE,eAAO,MAAM,iBAAiB,4BAAiC,CAAA;AAC/D,eAAO,MAAM,gBAAgB,2BAAgC,CAAA"}
|
package/dist/debugTools.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ViteDevServer } from 'vite';
|
|
2
|
-
import type { BridgeContract, BridgeStats, DebugStartupState, DevComponentAudit } from './types.js';
|
|
2
|
+
import type { BridgeContract, BridgeStats, DebugStartupState, DevComponentAudit, ResolvedDevComponentConfig } from './types.js';
|
|
3
3
|
export interface DebugToolsContext {
|
|
4
4
|
contract: BridgeContract;
|
|
5
5
|
stats: BridgeStats;
|
|
6
6
|
audit: DevComponentAudit;
|
|
7
|
+
resolvedConfig: ResolvedDevComponentConfig;
|
|
8
|
+
configDir: string;
|
|
7
9
|
open?: boolean;
|
|
8
10
|
}
|
|
9
11
|
export declare function installDebugTools(server: ViteDevServer, ctx: DebugToolsContext, state: DebugStartupState): void;
|
package/dist/debugTools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debugTools.d.ts","sourceRoot":"","sources":["../src/debugTools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"debugTools.d.ts","sourceRoot":"","sources":["../src/debugTools.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AAEzC,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAA;AAG/H,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,WAAW,CAAA;IAClB,KAAK,EAAE,iBAAiB,CAAA;IACxB,cAAc,EAAE,0BAA0B,CAAA;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAqFD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,QAwUxG"}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { DEV_TO_REACT_BASE_PATH, DEV_TO_REACT_CONTRACT_KEY, DEV_TO_REACT_CONTRACT_PATH, DEV_TO_REACT_DEBUG_HTML_PATH, DEV_TO_REACT_DEBUG_JSON_PATH, DEV_TO_REACT_DEBUG_STATE_KEY, DEV_TO_REACT_DID_OPEN_BROWSER_KEY, DEV_TO_REACT_EVENT_FULL_RELOAD, DEV_TO_REACT_EVENT_HMR_UPDATE, DEV_TO_REACT_INIT_PATH, DEV_TO_REACT_NAMESPACE, DEV_TO_REACT_ORIGIN_KEY, DEV_TO_REACT_REACT_RUNTIME_PATH, DEV_TO_REACT_RESOLVE_ASSET_KEY } from "@dev-to/react-shared";
|
|
1
|
+
import { DEV_TO_REACT_BASE_PATH, DEV_TO_REACT_CONTRACT_KEY, DEV_TO_REACT_CONTRACT_PATH, DEV_TO_REACT_DEBUG_HTML_PATH, DEV_TO_REACT_DEBUG_JSON_PATH, DEV_TO_REACT_DEBUG_STATE_KEY, DEV_TO_REACT_DID_OPEN_BROWSER_KEY, DEV_TO_REACT_EVENT_FULL_RELOAD, DEV_TO_REACT_EVENT_HMR_UPDATE, DEV_TO_REACT_INIT_PATH, DEV_TO_REACT_LOADER_BASE_PATH, DEV_TO_REACT_NAMESPACE, DEV_TO_REACT_ORIGIN_KEY, DEV_TO_REACT_REACT_RUNTIME_PATH, DEV_TO_REACT_RESOLVE_ASSET_KEY } from "@dev-to/react-shared";
|
|
2
2
|
import node_fs from "node:fs";
|
|
3
3
|
import node_path from "node:path";
|
|
4
4
|
import picocolors from "picocolors";
|
|
5
5
|
import { mergeConfig } from "vite";
|
|
6
6
|
import { exec } from "node:child_process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
7
8
|
import node_os from "node:os";
|
|
8
|
-
import { pathToFileURL } from "node:url";
|
|
9
9
|
import typescript from "typescript";
|
|
10
10
|
const PLUGIN_NAME = DEV_TO_REACT_NAMESPACE;
|
|
11
|
-
const
|
|
11
|
+
const constants_PLUGIN_LOG_PREFIX = `[${PLUGIN_NAME}]`;
|
|
12
12
|
const STABLE_BASE_PATH = DEV_TO_REACT_BASE_PATH;
|
|
13
|
-
const
|
|
13
|
+
const constants_STABLE_CONTRACT_PATH = DEV_TO_REACT_CONTRACT_PATH;
|
|
14
14
|
const STABLE_INIT_PATH = DEV_TO_REACT_INIT_PATH;
|
|
15
15
|
const STABLE_REACT_RUNTIME_PATH = DEV_TO_REACT_REACT_RUNTIME_PATH;
|
|
16
16
|
const STABLE_DEBUG_HTML_PATH = DEV_TO_REACT_DEBUG_HTML_PATH;
|
|
17
17
|
const STABLE_DEBUG_JSON_PATH = DEV_TO_REACT_DEBUG_JSON_PATH;
|
|
18
|
+
const STABLE_LOADER_BASE_PATH = DEV_TO_REACT_LOADER_BASE_PATH;
|
|
18
19
|
const EVENT_FULL_RELOAD = DEV_TO_REACT_EVENT_FULL_RELOAD;
|
|
19
20
|
const EVENT_HMR_UPDATE = DEV_TO_REACT_EVENT_HMR_UPDATE;
|
|
20
21
|
function renderDebugHtml(params) {
|
|
@@ -51,7 +52,7 @@ function renderDebugHtml(params) {
|
|
|
51
52
|
lines.push(dim(' */'));
|
|
52
53
|
return `${lines.join('\n')}\n\n${escapeHtml(content)}`;
|
|
53
54
|
} catch (e) {
|
|
54
|
-
return escapeHtml(`// ${
|
|
55
|
+
return escapeHtml(`// ${constants_PLUGIN_LOG_PREFIX} 无法读取配置文件: ${e}`);
|
|
55
56
|
}
|
|
56
57
|
})();
|
|
57
58
|
return `<!doctype html>
|
|
@@ -374,7 +375,7 @@ CSS: <span class="str">dist/<name>/<name>.css</span></pre>
|
|
|
374
375
|
<div style="margin-top: 12px;">
|
|
375
376
|
<pre style="font-size: 12px; line-height: 1.7;">
|
|
376
377
|
<span class="kw">Endpoints:</span>
|
|
377
|
-
- Contract: <span class="str">${
|
|
378
|
+
- Contract: <span class="str">${constants_STABLE_CONTRACT_PATH}</span>
|
|
378
379
|
- Init: <span class="str">${STABLE_INIT_PATH}</span>
|
|
379
380
|
- Runtime: <span class="str">${STABLE_REACT_RUNTIME_PATH}</span>
|
|
380
381
|
|
|
@@ -510,7 +511,7 @@ function tryResolveWithExtensions(p) {
|
|
|
510
511
|
function resolveEntryAbsPath(rootDir, entry, defaultEntryAbs, fallbackRoot) {
|
|
511
512
|
const tryResolveWithBase = (baseDir)=>{
|
|
512
513
|
if ('/' === entry) {
|
|
513
|
-
if (!defaultEntryAbs) throw new Error(`${
|
|
514
|
+
if (!defaultEntryAbs) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 使用 / 作为入口时,必须提供 defaultEntryAbs 参数`);
|
|
514
515
|
return defaultEntryAbs;
|
|
515
516
|
}
|
|
516
517
|
const fsPath = toFsPathFromViteEntry(entry);
|
|
@@ -528,6 +529,452 @@ function resolveEntryAbsPath(rootDir, entry, defaultEntryAbs, fallbackRoot) {
|
|
|
528
529
|
if (fallbackRoot && fallbackRoot !== rootDir) return tryResolveWithBase(fallbackRoot);
|
|
529
530
|
return resolved;
|
|
530
531
|
}
|
|
532
|
+
function isLibBuild(env) {
|
|
533
|
+
return env?.command === 'build' && env?.mode === 'lib';
|
|
534
|
+
}
|
|
535
|
+
function toSafeOutDirName(componentName) {
|
|
536
|
+
return componentName.replace(/[\\/]/g, '_').replace(/\.\./g, '_');
|
|
537
|
+
}
|
|
538
|
+
function toSafeUmdName(componentName) {
|
|
539
|
+
let s = componentName.replace(/[^A-Za-z0-9_$]+/g, '_');
|
|
540
|
+
if (!s) s = 'ViteDevComponent';
|
|
541
|
+
if (/^\d/.test(s)) s = `_${s}`;
|
|
542
|
+
return s;
|
|
543
|
+
}
|
|
544
|
+
function isValidJsIdentifier(name) {
|
|
545
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
546
|
+
}
|
|
547
|
+
function analyzeExports(filePath) {
|
|
548
|
+
if (!node_fs.existsSync(filePath)) return {
|
|
549
|
+
hasDefault: false,
|
|
550
|
+
namedExports: []
|
|
551
|
+
};
|
|
552
|
+
const content = node_fs.readFileSync(filePath, 'utf-8');
|
|
553
|
+
const namedExports = [];
|
|
554
|
+
let hasDefault = false;
|
|
555
|
+
let scriptKind = typescript.ScriptKind.TS;
|
|
556
|
+
const ext = node_path.extname(filePath).toLowerCase();
|
|
557
|
+
if ('.tsx' === ext) scriptKind = typescript.ScriptKind.TSX;
|
|
558
|
+
else if ('.jsx' === ext) scriptKind = typescript.ScriptKind.JSX;
|
|
559
|
+
else if ('.js' === ext) scriptKind = typescript.ScriptKind.JS;
|
|
560
|
+
else if ('.ts' === ext) scriptKind = typescript.ScriptKind.TS;
|
|
561
|
+
let sourceFile;
|
|
562
|
+
try {
|
|
563
|
+
sourceFile = typescript.createSourceFile(filePath, content, typescript.ScriptTarget.Latest, true, scriptKind);
|
|
564
|
+
} catch (parseError) {
|
|
565
|
+
throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法解析入口文件 "${filePath}"。\n解析错误: ${parseError instanceof Error ? parseError.message : String(parseError)}\n请确保文件是有效的 TypeScript/JavaScript 文件。`);
|
|
566
|
+
}
|
|
567
|
+
typescript.getPreEmitDiagnostics(typescript.createProgram([
|
|
568
|
+
filePath
|
|
569
|
+
], {
|
|
570
|
+
target: typescript.ScriptTarget.Latest,
|
|
571
|
+
module: typescript.ModuleKind.ESNext,
|
|
572
|
+
jsx: scriptKind === typescript.ScriptKind.TSX || scriptKind === typescript.ScriptKind.JSX ? typescript.JsxEmit.React : void 0
|
|
573
|
+
}));
|
|
574
|
+
function visit(node) {
|
|
575
|
+
if (typescript.isExportAssignment(node)) {
|
|
576
|
+
if (true !== node.isExportEquals) hasDefault = true;
|
|
577
|
+
}
|
|
578
|
+
if (typescript.isFunctionDeclaration(node) || typescript.isClassDeclaration(node) || typescript.isVariableStatement(node) || typescript.isInterfaceDeclaration(node) || typescript.isTypeAliasDeclaration(node) || typescript.isEnumDeclaration(node)) {
|
|
579
|
+
const modifiers = typescript.getModifiers(node);
|
|
580
|
+
if (modifiers?.some((m)=>m.kind === typescript.SyntaxKind.ExportKeyword)) if (modifiers.some((m)=>m.kind === typescript.SyntaxKind.DefaultKeyword)) hasDefault = true;
|
|
581
|
+
else {
|
|
582
|
+
if (typescript.isFunctionDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
583
|
+
if (typescript.isClassDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
584
|
+
if (typescript.isVariableStatement(node)) node.declarationList.declarations.forEach((decl)=>{
|
|
585
|
+
if (typescript.isIdentifier(decl.name)) namedExports.push(decl.name.text);
|
|
586
|
+
});
|
|
587
|
+
if (typescript.isInterfaceDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
588
|
+
if (typescript.isTypeAliasDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
589
|
+
if (typescript.isEnumDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (typescript.isExportDeclaration(node) && node.exportClause) {
|
|
593
|
+
if (typescript.isNamedExports(node.exportClause)) node.exportClause.elements.forEach((element)=>{
|
|
594
|
+
if (element.name) {
|
|
595
|
+
const exportName = element.name.text;
|
|
596
|
+
const { propertyName } = element;
|
|
597
|
+
if (propertyName && 'default' === propertyName.text) namedExports.push(exportName);
|
|
598
|
+
else if ('default' === exportName) hasDefault = true;
|
|
599
|
+
else namedExports.push(exportName);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
else if (typescript.isNamespaceExport(node.exportClause)) namedExports.push(node.exportClause.name.text);
|
|
603
|
+
}
|
|
604
|
+
typescript.isExportDeclaration(node) && node.exportClause;
|
|
605
|
+
typescript.forEachChild(node, visit);
|
|
606
|
+
}
|
|
607
|
+
visit(sourceFile);
|
|
608
|
+
const uniqueExports = Array.from(new Set(namedExports));
|
|
609
|
+
if (!hasDefault && 0 === uniqueExports.length) {
|
|
610
|
+
const hasDefaultRegex = /export\s+default\s+/;
|
|
611
|
+
const hasNamedRegex = /export\s+(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
|
|
612
|
+
const regexHasDefault = hasDefaultRegex.test(content);
|
|
613
|
+
const regexNamedMatches = [];
|
|
614
|
+
let match;
|
|
615
|
+
while(null !== (match = hasNamedRegex.exec(content)))regexNamedMatches.push(match[1]);
|
|
616
|
+
if (regexHasDefault || regexNamedMatches.length > 0) {
|
|
617
|
+
console.warn(`${constants_PLUGIN_LOG_PREFIX} 警告:AST 分析未检测到导出,但正则检测到:\n 文件: ${filePath}\n 正则检测 default: ${regexHasDefault}\n 正则检测命名导出: ${regexNamedMatches.join(', ') || '无'}\n 这可能是 AST 解析问题,将尝试继续构建。`);
|
|
618
|
+
if (regexHasDefault) hasDefault = true;
|
|
619
|
+
if (regexNamedMatches.length > 0) namedExports.push(...regexNamedMatches);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
hasDefault,
|
|
624
|
+
namedExports: Array.from(new Set(namedExports))
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function generateLibVirtualEntryCode(params) {
|
|
628
|
+
const { defaultEntryAbs, componentName } = params;
|
|
629
|
+
if (!node_fs.existsSync(defaultEntryAbs)) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 入口文件不存在: "${defaultEntryAbs}"\n请检查文件路径是否正确。`);
|
|
630
|
+
const actualFile = tryResolveWithExtensions(defaultEntryAbs) || defaultEntryAbs;
|
|
631
|
+
if (!node_fs.existsSync(actualFile)) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 入口文件不存在: "${defaultEntryAbs}"\n尝试解析后的路径: "${actualFile}"\n请检查文件路径是否正确。`);
|
|
632
|
+
const importTarget = actualFile;
|
|
633
|
+
let exports;
|
|
634
|
+
try {
|
|
635
|
+
exports = analyzeExports(actualFile);
|
|
636
|
+
} catch (error) {
|
|
637
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
638
|
+
throw new Error(`${constants_PLUGIN_LOG_PREFIX} 分析入口文件 "${actualFile}" 的导出时出错:\n${errorMsg}\n\n请检查文件:\n 1. 文件是否存在且可读\n 2. 文件是否有语法错误\n 3. 文件是否有导出(export default 或命名导出)\n\n原始路径: "${defaultEntryAbs}"\n实际解析路径: "${actualFile}"`);
|
|
639
|
+
}
|
|
640
|
+
let code;
|
|
641
|
+
if (exports.hasDefault || 0 !== exports.namedExports.length) if (exports.hasDefault) {
|
|
642
|
+
let prefer = '';
|
|
643
|
+
if (exports.namedExports.includes(componentName)) prefer = isValidJsIdentifier(componentName) ? `mod.${componentName}` : `mod[${JSON.stringify(componentName)}]`;
|
|
644
|
+
const pickedExpr = prefer ? `${prefer} || mod.default || mod` : 'mod.default || mod';
|
|
645
|
+
code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
|
|
646
|
+
import * as mod from ${JSON.stringify(importTarget)};
|
|
647
|
+
const picked = ${pickedExpr};
|
|
648
|
+
const Card = picked && picked.default ? picked.default : picked;
|
|
649
|
+
export default Card;
|
|
650
|
+
export * from ${JSON.stringify(importTarget)};
|
|
651
|
+
`;
|
|
652
|
+
} else if (1 === exports.namedExports.length) {
|
|
653
|
+
const singleExport = exports.namedExports[0];
|
|
654
|
+
const exportAccess = isValidJsIdentifier(singleExport) ? `mod.${singleExport}` : `mod[${JSON.stringify(singleExport)}]`;
|
|
655
|
+
code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
|
|
656
|
+
import * as mod from ${JSON.stringify(importTarget)};
|
|
657
|
+
const Card = ${exportAccess};
|
|
658
|
+
export default Card;
|
|
659
|
+
export * from ${JSON.stringify(importTarget)};
|
|
660
|
+
`;
|
|
661
|
+
} else {
|
|
662
|
+
const hasComponentNameExport = exports.namedExports.some((exp)=>exp === componentName);
|
|
663
|
+
if (hasComponentNameExport) {
|
|
664
|
+
const exportAccess = isValidJsIdentifier(componentName) ? `mod.${componentName}` : `mod[${JSON.stringify(componentName)}]`;
|
|
665
|
+
code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
|
|
666
|
+
import * as mod from ${JSON.stringify(importTarget)};
|
|
667
|
+
const Card = ${exportAccess};
|
|
668
|
+
export default Card;
|
|
669
|
+
export * from ${JSON.stringify(importTarget)};
|
|
670
|
+
`;
|
|
671
|
+
} else throw new Error(`${constants_PLUGIN_LOG_PREFIX} Entry file "${defaultEntryAbs}" has multiple named exports (${exports.namedExports.join(', ')}), but none match componentName "${componentName}".\n\nPlease resolve this by:\n\n 1. Adding a default export (Recommended):\n export default function ${componentName}() { ... }\n\n 2. Adding a named export called "${componentName}":\n export function ${componentName}() { ... }\n\n 3. Keeping only one named export (it will be used automatically).\n\nCurrent componentName: "${componentName}"\nCurrent named exports: ${exports.namedExports.join(', ')}`);
|
|
672
|
+
}
|
|
673
|
+
else throw new Error(`${constants_PLUGIN_LOG_PREFIX} Entry file "${defaultEntryAbs}" does not have any exports.\n\nPlease ensure the file has one of the following:\n\n 1. export default (Recommended):\n export default function ${componentName}() { ... }\n or\n const ${componentName} = () => { ... };\n export default ${componentName};\n\n 2. Named export matching componentName:\n export function ${componentName}() { ... }\n or\n export const ${componentName} = () => { ... };\n\n 3. A single named export (any name):\n export function MyComponent() { ... }\n // If there is only one named export, it will be used automatically.\n\nCurrent componentName: "${componentName}"`);
|
|
674
|
+
return code;
|
|
675
|
+
}
|
|
676
|
+
function getLibVirtualEntryPath(componentName) {
|
|
677
|
+
return `virtual:${PLUGIN_NAME}-lib-entry:${componentName}`;
|
|
678
|
+
}
|
|
679
|
+
function normalizeLibCss(outDir, baseName) {
|
|
680
|
+
if (!node_fs.existsSync(outDir)) return;
|
|
681
|
+
const target = node_path.join(outDir, `${baseName}.css`);
|
|
682
|
+
if (node_fs.existsSync(target)) return;
|
|
683
|
+
const cssCandidates = [];
|
|
684
|
+
const scanDir = (dir, depth)=>{
|
|
685
|
+
if (depth < 0 || !node_fs.existsSync(dir)) return;
|
|
686
|
+
const entries = node_fs.readdirSync(dir, {
|
|
687
|
+
withFileTypes: true
|
|
688
|
+
});
|
|
689
|
+
for (const e of entries){
|
|
690
|
+
const full = node_path.join(dir, e.name);
|
|
691
|
+
if (e.isDirectory()) scanDir(full, depth - 1);
|
|
692
|
+
else if (e.isFile() && e.name.endsWith('.css')) cssCandidates.push(full);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
scanDir(outDir, 2);
|
|
696
|
+
if (1 !== cssCandidates.length) return;
|
|
697
|
+
const from = cssCandidates[0];
|
|
698
|
+
try {
|
|
699
|
+
node_fs.renameSync(from, target);
|
|
700
|
+
} catch {
|
|
701
|
+
try {
|
|
702
|
+
node_fs.copyFileSync(from, target);
|
|
703
|
+
node_fs.unlinkSync(from);
|
|
704
|
+
} catch {}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function resolveBuildTargets(params) {
|
|
708
|
+
const { componentMap, requestedRaw, defaultEntryAbs } = params;
|
|
709
|
+
const componentNames = Object.keys(componentMap);
|
|
710
|
+
const requestedList = requestedRaw ? requestedRaw.split(',').map((s)=>s.trim()).filter(Boolean) : [];
|
|
711
|
+
const actualConfiguredNames = componentNames.filter((n)=>'*' !== n);
|
|
712
|
+
if (actualConfiguredNames.length > 0) {
|
|
713
|
+
if (requestedList.length > 0) {
|
|
714
|
+
const picked = requestedList.filter((n)=>actualConfiguredNames.includes(n));
|
|
715
|
+
if (0 === picked.length) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 指定的 component 不在配置列表中:${requestedRaw}`);
|
|
716
|
+
return picked;
|
|
717
|
+
}
|
|
718
|
+
return actualConfiguredNames;
|
|
719
|
+
}
|
|
720
|
+
if (requestedList.length > 0) return requestedList;
|
|
721
|
+
const fallbackName = node_path.parse(defaultEntryAbs || 'index').name || 'index';
|
|
722
|
+
return [
|
|
723
|
+
fallbackName
|
|
724
|
+
];
|
|
725
|
+
}
|
|
726
|
+
function generateLibBuildNextConfig(params) {
|
|
727
|
+
const { rootDir, picked, componentMap, resolvedConfig, options, userConfig, configDir } = params;
|
|
728
|
+
const outBase = toSafeOutDirName(picked);
|
|
729
|
+
const outDir = node_path.resolve(rootDir, 'dist', outBase);
|
|
730
|
+
let resolvedEntryAbs = resolvedConfig.defaultEntryAbs;
|
|
731
|
+
const entryAbs = (()=>{
|
|
732
|
+
const entryFromMap = componentMap[picked];
|
|
733
|
+
if (entryFromMap) {
|
|
734
|
+
const abs = resolveEntryAbsPath(rootDir, entryFromMap, resolvedConfig.defaultEntryAbs, configDir);
|
|
735
|
+
if (!abs) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法解析入口:component="${picked}", entry="${entryFromMap}"`);
|
|
736
|
+
resolvedEntryAbs = abs;
|
|
737
|
+
}
|
|
738
|
+
return getLibVirtualEntryPath(picked);
|
|
739
|
+
})();
|
|
740
|
+
const virtualEntryCode = (()=>{
|
|
741
|
+
const entryFromMap = componentMap[picked];
|
|
742
|
+
if (entryFromMap) {
|
|
743
|
+
const abs = resolveEntryAbsPath(rootDir, entryFromMap, resolvedConfig.defaultEntryAbs, configDir);
|
|
744
|
+
if (!abs) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法解析入口:component="${picked}", entry="${entryFromMap}"`);
|
|
745
|
+
resolvedEntryAbs = abs;
|
|
746
|
+
return generateLibVirtualEntryCode({
|
|
747
|
+
rootDir,
|
|
748
|
+
defaultEntryAbs: abs,
|
|
749
|
+
componentName: picked
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
resolvedEntryAbs = resolvedConfig.defaultEntryAbs;
|
|
753
|
+
return generateLibVirtualEntryCode({
|
|
754
|
+
rootDir,
|
|
755
|
+
defaultEntryAbs: resolvedConfig.defaultEntryAbs,
|
|
756
|
+
componentName: picked
|
|
757
|
+
});
|
|
758
|
+
})();
|
|
759
|
+
const next = {
|
|
760
|
+
root: rootDir,
|
|
761
|
+
define: {
|
|
762
|
+
...userConfig.define || {},
|
|
763
|
+
'process.env.NODE_ENV': JSON.stringify('production')
|
|
764
|
+
},
|
|
765
|
+
build: {
|
|
766
|
+
outDir,
|
|
767
|
+
emptyOutDir: true,
|
|
768
|
+
cssCodeSplit: false,
|
|
769
|
+
lib: {
|
|
770
|
+
entry: entryAbs,
|
|
771
|
+
name: toSafeUmdName(picked),
|
|
772
|
+
formats: [
|
|
773
|
+
'umd'
|
|
774
|
+
],
|
|
775
|
+
fileName: ()=>`${outBase}.js`
|
|
776
|
+
},
|
|
777
|
+
rollupOptions: {
|
|
778
|
+
external: [
|
|
779
|
+
'react',
|
|
780
|
+
'react-dom',
|
|
781
|
+
'react-dom/client'
|
|
782
|
+
],
|
|
783
|
+
output: {
|
|
784
|
+
inlineDynamicImports: true,
|
|
785
|
+
exports: 'named',
|
|
786
|
+
globals: {
|
|
787
|
+
react: 'React',
|
|
788
|
+
'react-dom': 'ReactDOM',
|
|
789
|
+
'react-dom/client': 'ReactDOMClient'
|
|
790
|
+
},
|
|
791
|
+
assetFileNames: (assetInfo)=>{
|
|
792
|
+
const name = assetInfo?.name || '';
|
|
793
|
+
if (name.endsWith('.css')) return `${outBase}.css`;
|
|
794
|
+
return 'assets/[name]-[hash][extname]';
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
if (options.build) {
|
|
801
|
+
const merged = mergeConfig({
|
|
802
|
+
build: next.build
|
|
803
|
+
}, {
|
|
804
|
+
build: options.build
|
|
805
|
+
});
|
|
806
|
+
next.build = merged.build;
|
|
807
|
+
}
|
|
808
|
+
return {
|
|
809
|
+
next,
|
|
810
|
+
outDir: next.build?.outDir || outDir,
|
|
811
|
+
outBase,
|
|
812
|
+
buildTargets: [],
|
|
813
|
+
virtualEntryCode,
|
|
814
|
+
resolvedEntryAbs
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function createLoaderUmdWrapper(options) {
|
|
818
|
+
const { componentName, origin, contractEndpoint = constants_STABLE_CONTRACT_PATH, reactLoaderUrl = 'https://cdn.jsdelivr.net/npm/@dev-to/react-loader@latest/dist/index.umd.js' } = options;
|
|
819
|
+
const globalName = toSafeUmdName(componentName);
|
|
820
|
+
const code = `/**
|
|
821
|
+
* UMD Loader Wrapper for component: ${componentName}
|
|
822
|
+
* Global name: ${globalName}
|
|
823
|
+
* Generated by ${constants_PLUGIN_LOG_PREFIX}
|
|
824
|
+
*
|
|
825
|
+
* This wrapper uses @dev-to/react-loader to dynamically load and render the component.
|
|
826
|
+
*
|
|
827
|
+
* Usage:
|
|
828
|
+
* 1. Load React and ReactDOM:
|
|
829
|
+
* <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
830
|
+
* <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
831
|
+
*
|
|
832
|
+
* 2. Load ReactLoader:
|
|
833
|
+
* <script src="https://cdn.jsdelivr.net/npm/@dev-to/react-loader@latest/dist/index.umd.js"></script>
|
|
834
|
+
*
|
|
835
|
+
* 3. Load this wrapper:
|
|
836
|
+
* <script src="${origin}/__dev_to_react__/loader/${componentName}.js"></script>
|
|
837
|
+
*
|
|
838
|
+
* 4. Render the component (returns a Promise):
|
|
839
|
+
* <div id="app"></div>
|
|
840
|
+
* <script>
|
|
841
|
+
* window.${globalName}.render(
|
|
842
|
+
* document.getElementById('app'),
|
|
843
|
+
* { prop1: 'value1', prop2: 'value2' }
|
|
844
|
+
* ).then(function(root) {
|
|
845
|
+
* console.log('Component rendered successfully');
|
|
846
|
+
* }).catch(function(error) {
|
|
847
|
+
* console.error('Failed to render component:', error);
|
|
848
|
+
* });
|
|
849
|
+
* </script>
|
|
850
|
+
*
|
|
851
|
+
* Note: ReactLoader will be automatically loaded from CDN if not already available.
|
|
852
|
+
*/
|
|
853
|
+
(function (root, factory) {
|
|
854
|
+
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
|
855
|
+
// CommonJS
|
|
856
|
+
factory(exports, require('react'), require('react-dom'), require('@dev-to/react-loader'));
|
|
857
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
858
|
+
// AMD
|
|
859
|
+
define(['exports', 'react', 'react-dom', '@dev-to/react-loader'], factory);
|
|
860
|
+
} else {
|
|
861
|
+
// Browser globals
|
|
862
|
+
var globalObj = typeof globalThis !== 'undefined' ? globalThis : (typeof self !== 'undefined' ? self : root);
|
|
863
|
+
factory((globalObj.${globalName} = {}), globalObj.React, globalObj.ReactDOM, globalObj.DevToReactLoader);
|
|
864
|
+
}
|
|
865
|
+
})(this, function (exports, React, ReactDOM, ReactLoaderModule) {
|
|
866
|
+
'use strict';
|
|
867
|
+
|
|
868
|
+
var ReactLoader = null;
|
|
869
|
+
var loadingPromise = null;
|
|
870
|
+
|
|
871
|
+
// Helper function to load a script dynamically
|
|
872
|
+
function loadScript(src) {
|
|
873
|
+
return new Promise(function(resolve, reject) {
|
|
874
|
+
var script = document.createElement('script');
|
|
875
|
+
script.src = src;
|
|
876
|
+
script.onload = resolve;
|
|
877
|
+
script.onerror = reject;
|
|
878
|
+
document.head.appendChild(script);
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// Helper function to ensure ReactLoader is loaded
|
|
883
|
+
function ensureReactLoaderLoaded() {
|
|
884
|
+
if (ReactLoader) {
|
|
885
|
+
return Promise.resolve();
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
if (!loadingPromise) {
|
|
889
|
+
loadingPromise = (function() {
|
|
890
|
+
// First, try to get ReactLoader from the global scope
|
|
891
|
+
if (typeof window !== 'undefined' && window.DevToReactLoader && window.DevToReactLoader.ReactLoader) {
|
|
892
|
+
ReactLoader = window.DevToReactLoader.ReactLoader;
|
|
893
|
+
return Promise.resolve();
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// If not available, load it from URL
|
|
897
|
+
console.log('${constants_PLUGIN_LOG_PREFIX} Loading @dev-to/react-loader...');
|
|
898
|
+
return loadScript(${JSON.stringify(reactLoaderUrl)})
|
|
899
|
+
.then(function() {
|
|
900
|
+
if (typeof window !== 'undefined' && window.DevToReactLoader && window.DevToReactLoader.ReactLoader) {
|
|
901
|
+
ReactLoader = window.DevToReactLoader.ReactLoader;
|
|
902
|
+
console.log('${constants_PLUGIN_LOG_PREFIX} ReactLoader loaded successfully');
|
|
903
|
+
} else {
|
|
904
|
+
throw new Error('${constants_PLUGIN_LOG_PREFIX} ReactLoader not found after loading');
|
|
905
|
+
}
|
|
906
|
+
})
|
|
907
|
+
.catch(function(error) {
|
|
908
|
+
console.error('${constants_PLUGIN_LOG_PREFIX} Failed to load ReactLoader:', error);
|
|
909
|
+
throw error;
|
|
910
|
+
});
|
|
911
|
+
})();
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
return loadingPromise;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Try to get ReactLoader from the module if available
|
|
918
|
+
if (ReactLoaderModule && ReactLoaderModule.ReactLoader) {
|
|
919
|
+
ReactLoader = ReactLoaderModule.ReactLoader;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Component configuration
|
|
923
|
+
var config = {
|
|
924
|
+
origin: ${JSON.stringify(origin)},
|
|
925
|
+
name: ${JSON.stringify(componentName)},
|
|
926
|
+
contractEndpoint: ${JSON.stringify(contractEndpoint)}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Render the component using ReactLoader
|
|
931
|
+
*/
|
|
932
|
+
function render(targetElement, componentProps) {
|
|
933
|
+
if (!targetElement) {
|
|
934
|
+
throw new Error('${constants_PLUGIN_LOG_PREFIX} Target element is required');
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (!React || !React.createElement) {
|
|
938
|
+
throw new Error('${constants_PLUGIN_LOG_PREFIX} React is not loaded');
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (!ReactDOM || !ReactDOM.createRoot) {
|
|
942
|
+
throw new Error('${constants_PLUGIN_LOG_PREFIX} ReactDOM is not loaded');
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Ensure ReactLoader is available before rendering
|
|
946
|
+
return ensureReactLoaderLoaded().then(function() {
|
|
947
|
+
if (!ReactLoader) {
|
|
948
|
+
throw new Error('${constants_PLUGIN_LOG_PREFIX} ReactLoader initialization failed');
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// Create ReactLoader component props
|
|
952
|
+
var loaderProps = {
|
|
953
|
+
origin: config.origin,
|
|
954
|
+
name: config.name,
|
|
955
|
+
contractEndpoint: config.contractEndpoint,
|
|
956
|
+
componentProps: componentProps || {}
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
// Render using ReactLoader
|
|
960
|
+
var root = ReactDOM.createRoot(targetElement);
|
|
961
|
+
root.render(React.createElement(ReactLoader, loaderProps));
|
|
962
|
+
|
|
963
|
+
return root;
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Export the API
|
|
968
|
+
exports.render = render;
|
|
969
|
+
exports.config = config;
|
|
970
|
+
exports.default = { render: render, config: config };
|
|
971
|
+
|
|
972
|
+
// Mark as ES module
|
|
973
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
974
|
+
});
|
|
975
|
+
`;
|
|
976
|
+
return code;
|
|
977
|
+
}
|
|
531
978
|
function openBrowser(url) {
|
|
532
979
|
const bridgePath = STABLE_DEBUG_HTML_PATH;
|
|
533
980
|
if ('darwin' === process.platform) {
|
|
@@ -615,7 +1062,7 @@ function installDebugTools(server, ctx, state) {
|
|
|
615
1062
|
const logger = server.config.logger;
|
|
616
1063
|
const info = 'function' == typeof logger?.info ? logger.info.bind(logger) : console.log;
|
|
617
1064
|
info('');
|
|
618
|
-
info(`${
|
|
1065
|
+
info(`${constants_PLUGIN_LOG_PREFIX} Debug panel:`);
|
|
619
1066
|
urls.forEach((u)=>info(` ${picocolors.cyan(u)}`));
|
|
620
1067
|
info(` JSON: ${picocolors.cyan(`${proto}://localhost:${port}${STABLE_DEBUG_JSON_PATH}`)}`);
|
|
621
1068
|
info('');
|
|
@@ -635,7 +1082,7 @@ function installDebugTools(server, ctx, state) {
|
|
|
635
1082
|
const url = req.url || '';
|
|
636
1083
|
const pathname = String(url).split('?')[0];
|
|
637
1084
|
const now = Date.now();
|
|
638
|
-
if (pathname ===
|
|
1085
|
+
if (pathname === constants_STABLE_CONTRACT_PATH) {
|
|
639
1086
|
ctx.stats.contract.count += 1;
|
|
640
1087
|
ctx.stats.contract.lastAt = now;
|
|
641
1088
|
} else if (pathname === STABLE_INIT_PATH) {
|
|
@@ -723,6 +1170,52 @@ function installDebugTools(server, ctx, state) {
|
|
|
723
1170
|
}, null, 2));
|
|
724
1171
|
return;
|
|
725
1172
|
}
|
|
1173
|
+
if (pathname === `${STABLE_BASE_PATH}/react-loader.js`) try {
|
|
1174
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1175
|
+
const __dirname = node_path.dirname(__filename);
|
|
1176
|
+
const reactLoaderUmdPath = node_path.resolve(__dirname, '../../react-loader/dist/index.umd.js');
|
|
1177
|
+
if (node_fs.existsSync(reactLoaderUmdPath)) {
|
|
1178
|
+
const umdCode = node_fs.readFileSync(reactLoaderUmdPath, 'utf-8');
|
|
1179
|
+
res.statusCode = 200;
|
|
1180
|
+
res.setHeader('Content-Type', "application/javascript; charset=utf-8");
|
|
1181
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
1182
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
1183
|
+
res.end(umdCode);
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
res.statusCode = 404;
|
|
1187
|
+
res.end(`react-loader UMD not found at ${reactLoaderUmdPath}. Run 'pnpm build' in react-loader package.`);
|
|
1188
|
+
return;
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
res.statusCode = 500;
|
|
1191
|
+
res.end(`Error loading react-loader UMD: ${error}`);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (pathname.startsWith(STABLE_LOADER_BASE_PATH)) {
|
|
1195
|
+
const loaderPathPattern = new RegExp(`^${STABLE_LOADER_BASE_PATH}/([^/]+)\\.js$`);
|
|
1196
|
+
const match = pathname.match(loaderPathPattern);
|
|
1197
|
+
if (match) {
|
|
1198
|
+
const componentName = match[1];
|
|
1199
|
+
const isHttps = !!server.config.server.https;
|
|
1200
|
+
const proto = isHttps ? 'https' : 'http';
|
|
1201
|
+
const hostHeader = String(req.headers.host || '');
|
|
1202
|
+
const addr = server.httpServer?.address();
|
|
1203
|
+
const actualPort = addr && 'object' == typeof addr ? addr.port : void 0;
|
|
1204
|
+
const origin = hostHeader ? `${proto}://${hostHeader}` : `${proto}://localhost${actualPort ? `:${actualPort}` : ''}`;
|
|
1205
|
+
const code = createLoaderUmdWrapper({
|
|
1206
|
+
componentName,
|
|
1207
|
+
origin,
|
|
1208
|
+
contractEndpoint: constants_STABLE_CONTRACT_PATH,
|
|
1209
|
+
reactLoaderUrl: `${origin}${STABLE_BASE_PATH}/react-loader.js`
|
|
1210
|
+
});
|
|
1211
|
+
res.statusCode = 200;
|
|
1212
|
+
res.setHeader('Content-Type', "application/javascript; charset=utf-8");
|
|
1213
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
1214
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
1215
|
+
res.end(code);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
726
1219
|
if (url.startsWith(STABLE_DEBUG_HTML_PATH)) {
|
|
727
1220
|
const addr = server.httpServer?.address();
|
|
728
1221
|
const actualPort = addr && 'object' == typeof addr ? addr.port : void 0;
|
|
@@ -792,387 +1285,102 @@ function installDebugTools(server, ctx, state) {
|
|
|
792
1285
|
res.end(html);
|
|
793
1286
|
return;
|
|
794
1287
|
}
|
|
795
|
-
next();
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
function toViteFsPath(filePath) {
|
|
799
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
800
|
-
return normalized.startsWith('/') ? `/@fs${normalized}` : `/@fs/${normalized}`;
|
|
801
|
-
}
|
|
802
|
-
function resolveDefaultEntryAbs(rootDir) {
|
|
803
|
-
const appCandidates = [
|
|
804
|
-
node_path.resolve(rootDir, 'src/App.tsx'),
|
|
805
|
-
node_path.resolve(rootDir, 'src/App.jsx'),
|
|
806
|
-
node_path.resolve(rootDir, 'src/App.ts'),
|
|
807
|
-
node_path.resolve(rootDir, 'src/App.js')
|
|
808
|
-
];
|
|
809
|
-
const foundApp = appCandidates.find((p)=>node_fs.existsSync(p));
|
|
810
|
-
if (foundApp) return foundApp;
|
|
811
|
-
const indexCandidates = [
|
|
812
|
-
node_path.resolve(rootDir, 'src/index.ts'),
|
|
813
|
-
node_path.resolve(rootDir, 'src/index.tsx'),
|
|
814
|
-
node_path.resolve(rootDir, 'src/index.jsx'),
|
|
815
|
-
node_path.resolve(rootDir, 'src/index.js')
|
|
816
|
-
];
|
|
817
|
-
const foundIndex = indexCandidates.find((p)=>node_fs.existsSync(p));
|
|
818
|
-
if (foundIndex) return foundIndex;
|
|
819
|
-
return appCandidates[0];
|
|
820
|
-
}
|
|
821
|
-
function buildDevComponentMapFromRecord(rootDir, input, defaultEntryAbs, convertAt = false, fallbackRoot) {
|
|
822
|
-
const out = {};
|
|
823
|
-
for (const [componentName, entry] of Object.entries(input)){
|
|
824
|
-
if (!componentName || !entry) continue;
|
|
825
|
-
if ('/' === entry) {
|
|
826
|
-
out[componentName] = convertAt ? toViteFsPath(defaultEntryAbs) : '/';
|
|
827
|
-
continue;
|
|
828
|
-
}
|
|
829
|
-
if (entry.startsWith('http://') || entry.startsWith('https://') || entry.startsWith('/')) {
|
|
830
|
-
out[componentName] = entry;
|
|
831
|
-
continue;
|
|
832
|
-
}
|
|
833
|
-
const abs = node_path.isAbsolute(entry) ? entry : node_path.resolve(rootDir, entry);
|
|
834
|
-
let resolved = tryResolveWithExtensions(abs);
|
|
835
|
-
if (!resolved && fallbackRoot && fallbackRoot !== rootDir) {
|
|
836
|
-
const fallbackAbs = node_path.isAbsolute(entry) ? entry : node_path.resolve(fallbackRoot, entry);
|
|
837
|
-
resolved = tryResolveWithExtensions(fallbackAbs);
|
|
838
|
-
}
|
|
839
|
-
resolved = resolved || abs;
|
|
840
|
-
out[componentName] = toViteFsPath(resolved);
|
|
841
|
-
}
|
|
842
|
-
return out;
|
|
843
|
-
}
|
|
844
|
-
function getFsPathFromViteEntry(entry) {
|
|
845
|
-
if (!entry.startsWith('/@fs')) return null;
|
|
846
|
-
let p = entry.slice(4);
|
|
847
|
-
if (p.startsWith('/') && /\/[A-Za-z]:\//.test(p)) p = p.slice(1);
|
|
848
|
-
return p;
|
|
849
|
-
}
|
|
850
|
-
function resolveDevComponentConfig(rootDir, devComponentMap, fallbackRoot) {
|
|
851
|
-
const defaultEntryAbs = resolveDefaultEntryAbs(rootDir);
|
|
852
|
-
const defaultEntry = toViteFsPath(defaultEntryAbs);
|
|
853
|
-
let resolvedDevComponentMap = {};
|
|
854
|
-
if ('string' == typeof devComponentMap) {
|
|
855
|
-
const name = devComponentMap.trim();
|
|
856
|
-
resolvedDevComponentMap = name ? buildDevComponentMapFromRecord(rootDir, {
|
|
857
|
-
[name]: '/'
|
|
858
|
-
}, defaultEntryAbs, false, fallbackRoot) : {
|
|
859
|
-
'*': '/'
|
|
860
|
-
};
|
|
861
|
-
} else {
|
|
862
|
-
const input = devComponentMap ?? {};
|
|
863
|
-
resolvedDevComponentMap = 0 === Object.keys(input).length ? {
|
|
864
|
-
'*': '/'
|
|
865
|
-
} : buildDevComponentMapFromRecord(rootDir, input, defaultEntryAbs, false, fallbackRoot);
|
|
866
|
-
}
|
|
867
|
-
const audit = (()=>{
|
|
868
|
-
const missing = [];
|
|
869
|
-
for (const [componentName, entry] of Object.entries(resolvedDevComponentMap)){
|
|
870
|
-
const fsPath = getFsPathFromViteEntry(entry);
|
|
871
|
-
if (fsPath) {
|
|
872
|
-
const resolved = tryResolveWithExtensions(fsPath);
|
|
873
|
-
if (!resolved || !node_fs.existsSync(resolved)) missing.push({
|
|
874
|
-
componentName,
|
|
875
|
-
filePath: fsPath
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
return {
|
|
880
|
-
defaultEntryAbs,
|
|
881
|
-
defaultEntryExists: node_fs.existsSync(defaultEntryAbs),
|
|
882
|
-
componentMapCount: Object.keys(resolvedDevComponentMap).length,
|
|
883
|
-
missingEntries: missing
|
|
884
|
-
};
|
|
885
|
-
})();
|
|
886
|
-
return {
|
|
887
|
-
defaultEntryAbs,
|
|
888
|
-
defaultEntry,
|
|
889
|
-
componentMap: resolvedDevComponentMap,
|
|
890
|
-
audit
|
|
891
|
-
};
|
|
892
|
-
}
|
|
893
|
-
function isLibBuild(env) {
|
|
894
|
-
return env?.command === 'build' && env?.mode === 'lib';
|
|
895
|
-
}
|
|
896
|
-
function toSafeOutDirName(componentName) {
|
|
897
|
-
return componentName.replace(/[\\/]/g, '_').replace(/\.\./g, '_');
|
|
898
|
-
}
|
|
899
|
-
function toSafeUmdName(componentName) {
|
|
900
|
-
let s = componentName.replace(/[^A-Za-z0-9_$]+/g, '_');
|
|
901
|
-
if (!s) s = 'ViteDevComponent';
|
|
902
|
-
if (/^\d/.test(s)) s = `_${s}`;
|
|
903
|
-
return s;
|
|
904
|
-
}
|
|
905
|
-
function isValidJsIdentifier(name) {
|
|
906
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
907
|
-
}
|
|
908
|
-
function analyzeExports(filePath) {
|
|
909
|
-
if (!node_fs.existsSync(filePath)) return {
|
|
910
|
-
hasDefault: false,
|
|
911
|
-
namedExports: []
|
|
912
|
-
};
|
|
913
|
-
const content = node_fs.readFileSync(filePath, 'utf-8');
|
|
914
|
-
const namedExports = [];
|
|
915
|
-
let hasDefault = false;
|
|
916
|
-
let scriptKind = typescript.ScriptKind.TS;
|
|
917
|
-
const ext = node_path.extname(filePath).toLowerCase();
|
|
918
|
-
if ('.tsx' === ext) scriptKind = typescript.ScriptKind.TSX;
|
|
919
|
-
else if ('.jsx' === ext) scriptKind = typescript.ScriptKind.JSX;
|
|
920
|
-
else if ('.js' === ext) scriptKind = typescript.ScriptKind.JS;
|
|
921
|
-
else if ('.ts' === ext) scriptKind = typescript.ScriptKind.TS;
|
|
922
|
-
let sourceFile;
|
|
923
|
-
try {
|
|
924
|
-
sourceFile = typescript.createSourceFile(filePath, content, typescript.ScriptTarget.Latest, true, scriptKind);
|
|
925
|
-
} catch (parseError) {
|
|
926
|
-
throw new Error(`${PLUGIN_LOG_PREFIX} 无法解析入口文件 "${filePath}"。\n解析错误: ${parseError instanceof Error ? parseError.message : String(parseError)}\n请确保文件是有效的 TypeScript/JavaScript 文件。`);
|
|
927
|
-
}
|
|
928
|
-
typescript.getPreEmitDiagnostics(typescript.createProgram([
|
|
929
|
-
filePath
|
|
930
|
-
], {
|
|
931
|
-
target: typescript.ScriptTarget.Latest,
|
|
932
|
-
module: typescript.ModuleKind.ESNext,
|
|
933
|
-
jsx: scriptKind === typescript.ScriptKind.TSX || scriptKind === typescript.ScriptKind.JSX ? typescript.JsxEmit.React : void 0
|
|
934
|
-
}));
|
|
935
|
-
function visit(node) {
|
|
936
|
-
if (typescript.isExportAssignment(node)) {
|
|
937
|
-
if (true !== node.isExportEquals) hasDefault = true;
|
|
938
|
-
}
|
|
939
|
-
if (typescript.isFunctionDeclaration(node) || typescript.isClassDeclaration(node) || typescript.isVariableStatement(node) || typescript.isInterfaceDeclaration(node) || typescript.isTypeAliasDeclaration(node) || typescript.isEnumDeclaration(node)) {
|
|
940
|
-
const modifiers = typescript.getModifiers(node);
|
|
941
|
-
if (modifiers?.some((m)=>m.kind === typescript.SyntaxKind.ExportKeyword)) if (modifiers.some((m)=>m.kind === typescript.SyntaxKind.DefaultKeyword)) hasDefault = true;
|
|
942
|
-
else {
|
|
943
|
-
if (typescript.isFunctionDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
944
|
-
if (typescript.isClassDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
945
|
-
if (typescript.isVariableStatement(node)) node.declarationList.declarations.forEach((decl)=>{
|
|
946
|
-
if (typescript.isIdentifier(decl.name)) namedExports.push(decl.name.text);
|
|
947
|
-
});
|
|
948
|
-
if (typescript.isInterfaceDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
949
|
-
if (typescript.isTypeAliasDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
950
|
-
if (typescript.isEnumDeclaration(node) && node.name) namedExports.push(node.name.text);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
if (typescript.isExportDeclaration(node) && node.exportClause) {
|
|
954
|
-
if (typescript.isNamedExports(node.exportClause)) node.exportClause.elements.forEach((element)=>{
|
|
955
|
-
if (element.name) {
|
|
956
|
-
const exportName = element.name.text;
|
|
957
|
-
const { propertyName } = element;
|
|
958
|
-
if (propertyName && 'default' === propertyName.text) namedExports.push(exportName);
|
|
959
|
-
else if ('default' === exportName) hasDefault = true;
|
|
960
|
-
else namedExports.push(exportName);
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
else if (typescript.isNamespaceExport(node.exportClause)) namedExports.push(node.exportClause.name.text);
|
|
964
|
-
}
|
|
965
|
-
typescript.isExportDeclaration(node) && node.exportClause;
|
|
966
|
-
typescript.forEachChild(node, visit);
|
|
967
|
-
}
|
|
968
|
-
visit(sourceFile);
|
|
969
|
-
const uniqueExports = Array.from(new Set(namedExports));
|
|
970
|
-
if (!hasDefault && 0 === uniqueExports.length) {
|
|
971
|
-
const hasDefaultRegex = /export\s+default\s+/;
|
|
972
|
-
const hasNamedRegex = /export\s+(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
|
|
973
|
-
const regexHasDefault = hasDefaultRegex.test(content);
|
|
974
|
-
const regexNamedMatches = [];
|
|
975
|
-
let match;
|
|
976
|
-
while(null !== (match = hasNamedRegex.exec(content)))regexNamedMatches.push(match[1]);
|
|
977
|
-
if (regexHasDefault || regexNamedMatches.length > 0) {
|
|
978
|
-
console.warn(`${PLUGIN_LOG_PREFIX} 警告:AST 分析未检测到导出,但正则检测到:\n 文件: ${filePath}\n 正则检测 default: ${regexHasDefault}\n 正则检测命名导出: ${regexNamedMatches.join(', ') || '无'}\n 这可能是 AST 解析问题,将尝试继续构建。`);
|
|
979
|
-
if (regexHasDefault) hasDefault = true;
|
|
980
|
-
if (regexNamedMatches.length > 0) namedExports.push(...regexNamedMatches);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
return {
|
|
984
|
-
hasDefault,
|
|
985
|
-
namedExports: Array.from(new Set(namedExports))
|
|
986
|
-
};
|
|
1288
|
+
next();
|
|
1289
|
+
});
|
|
987
1290
|
}
|
|
988
|
-
function
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
const actualFile = tryResolveWithExtensions(defaultEntryAbs) || defaultEntryAbs;
|
|
992
|
-
if (!node_fs.existsSync(actualFile)) throw new Error(`${PLUGIN_LOG_PREFIX} 入口文件不存在: "${defaultEntryAbs}"\n尝试解析后的路径: "${actualFile}"\n请检查文件路径是否正确。`);
|
|
993
|
-
const importTarget = pathToFileURL(actualFile).href;
|
|
994
|
-
let exports;
|
|
995
|
-
try {
|
|
996
|
-
exports = analyzeExports(actualFile);
|
|
997
|
-
} catch (error) {
|
|
998
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
999
|
-
throw new Error(`${PLUGIN_LOG_PREFIX} 分析入口文件 "${actualFile}" 的导出时出错:\n${errorMsg}\n\n请检查文件:\n 1. 文件是否存在且可读\n 2. 文件是否有语法错误\n 3. 文件是否有导出(export default 或命名导出)\n\n原始路径: "${defaultEntryAbs}"\n实际解析路径: "${actualFile}"`);
|
|
1000
|
-
}
|
|
1001
|
-
let code;
|
|
1002
|
-
if (exports.hasDefault || 0 !== exports.namedExports.length) if (exports.hasDefault) {
|
|
1003
|
-
let prefer = '';
|
|
1004
|
-
if (exports.namedExports.includes(componentName)) prefer = isValidJsIdentifier(componentName) ? `mod.${componentName}` : `mod[${JSON.stringify(componentName)}]`;
|
|
1005
|
-
const pickedExpr = prefer ? `${prefer} || mod.default || mod` : 'mod.default || mod';
|
|
1006
|
-
code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
|
|
1007
|
-
import * as mod from ${JSON.stringify(importTarget)};
|
|
1008
|
-
const picked = ${pickedExpr};
|
|
1009
|
-
const Card = picked && picked.default ? picked.default : picked;
|
|
1010
|
-
export default Card;
|
|
1011
|
-
export * from ${JSON.stringify(importTarget)};
|
|
1012
|
-
`;
|
|
1013
|
-
} else if (1 === exports.namedExports.length) {
|
|
1014
|
-
const singleExport = exports.namedExports[0];
|
|
1015
|
-
const exportAccess = isValidJsIdentifier(singleExport) ? `mod.${singleExport}` : `mod[${JSON.stringify(singleExport)}]`;
|
|
1016
|
-
code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
|
|
1017
|
-
import * as mod from ${JSON.stringify(importTarget)};
|
|
1018
|
-
const Card = ${exportAccess};
|
|
1019
|
-
export default Card;
|
|
1020
|
-
export * from ${JSON.stringify(importTarget)};
|
|
1021
|
-
`;
|
|
1022
|
-
} else {
|
|
1023
|
-
const hasComponentNameExport = exports.namedExports.some((exp)=>exp === componentName);
|
|
1024
|
-
if (hasComponentNameExport) {
|
|
1025
|
-
const exportAccess = isValidJsIdentifier(componentName) ? `mod.${componentName}` : `mod[${JSON.stringify(componentName)}]`;
|
|
1026
|
-
code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
|
|
1027
|
-
import * as mod from ${JSON.stringify(importTarget)};
|
|
1028
|
-
const Card = ${exportAccess};
|
|
1029
|
-
export default Card;
|
|
1030
|
-
export * from ${JSON.stringify(importTarget)};
|
|
1031
|
-
`;
|
|
1032
|
-
} else throw new Error(`${PLUGIN_LOG_PREFIX} Entry file "${defaultEntryAbs}" has multiple named exports (${exports.namedExports.join(', ')}), but none match componentName "${componentName}".\n\nPlease resolve this by:\n\n 1. Adding a default export (Recommended):\n export default function ${componentName}() { ... }\n\n 2. Adding a named export called "${componentName}":\n export function ${componentName}() { ... }\n\n 3. Keeping only one named export (it will be used automatically).\n\nCurrent componentName: "${componentName}"\nCurrent named exports: ${exports.namedExports.join(', ')}`);
|
|
1033
|
-
}
|
|
1034
|
-
else throw new Error(`${PLUGIN_LOG_PREFIX} Entry file "${defaultEntryAbs}" does not have any exports.\n\nPlease ensure the file has one of the following:\n\n 1. export default (Recommended):\n export default function ${componentName}() { ... }\n or\n const ${componentName} = () => { ... };\n export default ${componentName};\n\n 2. Named export matching componentName:\n export function ${componentName}() { ... }\n or\n export const ${componentName} = () => { ... };\n\n 3. A single named export (any name):\n export function MyComponent() { ... }\n // If there is only one named export, it will be used automatically.\n\nCurrent componentName: "${componentName}"`);
|
|
1035
|
-
return code;
|
|
1291
|
+
function toViteFsPath(filePath) {
|
|
1292
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
1293
|
+
return normalized.startsWith('/') ? `/@fs${normalized}` : `/@fs/${normalized}`;
|
|
1036
1294
|
}
|
|
1037
|
-
function
|
|
1038
|
-
|
|
1295
|
+
function resolveDefaultEntryAbs(rootDir) {
|
|
1296
|
+
const appCandidates = [
|
|
1297
|
+
node_path.resolve(rootDir, 'src/App.tsx'),
|
|
1298
|
+
node_path.resolve(rootDir, 'src/App.jsx'),
|
|
1299
|
+
node_path.resolve(rootDir, 'src/App.ts'),
|
|
1300
|
+
node_path.resolve(rootDir, 'src/App.js')
|
|
1301
|
+
];
|
|
1302
|
+
const foundApp = appCandidates.find((p)=>node_fs.existsSync(p));
|
|
1303
|
+
if (foundApp) return foundApp;
|
|
1304
|
+
const indexCandidates = [
|
|
1305
|
+
node_path.resolve(rootDir, 'src/index.ts'),
|
|
1306
|
+
node_path.resolve(rootDir, 'src/index.tsx'),
|
|
1307
|
+
node_path.resolve(rootDir, 'src/index.jsx'),
|
|
1308
|
+
node_path.resolve(rootDir, 'src/index.js')
|
|
1309
|
+
];
|
|
1310
|
+
const foundIndex = indexCandidates.find((p)=>node_fs.existsSync(p));
|
|
1311
|
+
if (foundIndex) return foundIndex;
|
|
1312
|
+
return appCandidates[0];
|
|
1039
1313
|
}
|
|
1040
|
-
function
|
|
1041
|
-
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
const entries = node_fs.readdirSync(dir, {
|
|
1048
|
-
withFileTypes: true
|
|
1049
|
-
});
|
|
1050
|
-
for (const e of entries){
|
|
1051
|
-
const full = node_path.join(dir, e.name);
|
|
1052
|
-
if (e.isDirectory()) scanDir(full, depth - 1);
|
|
1053
|
-
else if (e.isFile() && e.name.endsWith('.css')) cssCandidates.push(full);
|
|
1314
|
+
function buildDevComponentMapFromRecord(rootDir, input, defaultEntryAbs, convertAt = false, fallbackRoot) {
|
|
1315
|
+
const out = {};
|
|
1316
|
+
for (const [componentName, entry] of Object.entries(input)){
|
|
1317
|
+
if (!componentName || !entry) continue;
|
|
1318
|
+
if ('/' === entry) {
|
|
1319
|
+
out[componentName] = convertAt ? toViteFsPath(defaultEntryAbs) : '/';
|
|
1320
|
+
continue;
|
|
1054
1321
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
const from = cssCandidates[0];
|
|
1059
|
-
try {
|
|
1060
|
-
node_fs.renameSync(from, target);
|
|
1061
|
-
} catch {
|
|
1062
|
-
try {
|
|
1063
|
-
node_fs.copyFileSync(from, target);
|
|
1064
|
-
node_fs.unlinkSync(from);
|
|
1065
|
-
} catch {}
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
function resolveBuildTargets(params) {
|
|
1069
|
-
const { componentMap, requestedRaw, defaultEntryAbs } = params;
|
|
1070
|
-
const componentNames = Object.keys(componentMap);
|
|
1071
|
-
const requestedList = requestedRaw ? requestedRaw.split(',').map((s)=>s.trim()).filter(Boolean) : [];
|
|
1072
|
-
const actualConfiguredNames = componentNames.filter((n)=>'*' !== n);
|
|
1073
|
-
if (actualConfiguredNames.length > 0) {
|
|
1074
|
-
if (requestedList.length > 0) {
|
|
1075
|
-
const picked = requestedList.filter((n)=>actualConfiguredNames.includes(n));
|
|
1076
|
-
if (0 === picked.length) throw new Error(`${PLUGIN_LOG_PREFIX} 指定的 component 不在配置列表中:${requestedRaw}`);
|
|
1077
|
-
return picked;
|
|
1322
|
+
if (entry.startsWith('http://') || entry.startsWith('https://') || entry.startsWith('/')) {
|
|
1323
|
+
out[componentName] = entry;
|
|
1324
|
+
continue;
|
|
1078
1325
|
}
|
|
1079
|
-
|
|
1326
|
+
const abs = node_path.isAbsolute(entry) ? entry : node_path.resolve(rootDir, entry);
|
|
1327
|
+
let resolved = tryResolveWithExtensions(abs);
|
|
1328
|
+
if (!resolved && fallbackRoot && fallbackRoot !== rootDir) {
|
|
1329
|
+
const fallbackAbs = node_path.isAbsolute(entry) ? entry : node_path.resolve(fallbackRoot, entry);
|
|
1330
|
+
resolved = tryResolveWithExtensions(fallbackAbs);
|
|
1331
|
+
}
|
|
1332
|
+
resolved = resolved || abs;
|
|
1333
|
+
out[componentName] = toViteFsPath(resolved);
|
|
1080
1334
|
}
|
|
1081
|
-
|
|
1082
|
-
const fallbackName = node_path.parse(defaultEntryAbs || 'index').name || 'index';
|
|
1083
|
-
return [
|
|
1084
|
-
fallbackName
|
|
1085
|
-
];
|
|
1335
|
+
return out;
|
|
1086
1336
|
}
|
|
1087
|
-
function
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
const next = {
|
|
1121
|
-
root: rootDir,
|
|
1122
|
-
define: {
|
|
1123
|
-
...userConfig.define || {},
|
|
1124
|
-
'process.env.NODE_ENV': JSON.stringify('production')
|
|
1125
|
-
},
|
|
1126
|
-
build: {
|
|
1127
|
-
outDir,
|
|
1128
|
-
emptyOutDir: true,
|
|
1129
|
-
cssCodeSplit: false,
|
|
1130
|
-
lib: {
|
|
1131
|
-
entry: entryAbs,
|
|
1132
|
-
name: toSafeUmdName(picked),
|
|
1133
|
-
formats: [
|
|
1134
|
-
'umd'
|
|
1135
|
-
],
|
|
1136
|
-
fileName: ()=>`${outBase}.js`
|
|
1137
|
-
},
|
|
1138
|
-
rollupOptions: {
|
|
1139
|
-
external: [
|
|
1140
|
-
'react',
|
|
1141
|
-
'react-dom',
|
|
1142
|
-
'react-dom/client'
|
|
1143
|
-
],
|
|
1144
|
-
output: {
|
|
1145
|
-
inlineDynamicImports: true,
|
|
1146
|
-
exports: 'named',
|
|
1147
|
-
globals: {
|
|
1148
|
-
react: 'React',
|
|
1149
|
-
'react-dom': 'ReactDOM',
|
|
1150
|
-
'react-dom/client': 'ReactDOMClient'
|
|
1151
|
-
},
|
|
1152
|
-
assetFileNames: (assetInfo)=>{
|
|
1153
|
-
const name = assetInfo?.name || '';
|
|
1154
|
-
if (name.endsWith('.css')) return `${outBase}.css`;
|
|
1155
|
-
return 'assets/[name]-[hash][extname]';
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1337
|
+
function getFsPathFromViteEntry(entry) {
|
|
1338
|
+
if (!entry.startsWith('/@fs')) return null;
|
|
1339
|
+
let p = entry.slice(4);
|
|
1340
|
+
if (p.startsWith('/') && /\/[A-Za-z]:\//.test(p)) p = p.slice(1);
|
|
1341
|
+
return p;
|
|
1342
|
+
}
|
|
1343
|
+
function resolveDevComponentConfig(rootDir, devComponentMap, fallbackRoot) {
|
|
1344
|
+
const defaultEntryAbs = resolveDefaultEntryAbs(rootDir);
|
|
1345
|
+
const defaultEntry = toViteFsPath(defaultEntryAbs);
|
|
1346
|
+
let resolvedDevComponentMap = {};
|
|
1347
|
+
if ('string' == typeof devComponentMap) {
|
|
1348
|
+
const name = devComponentMap.trim();
|
|
1349
|
+
resolvedDevComponentMap = name ? buildDevComponentMapFromRecord(rootDir, {
|
|
1350
|
+
[name]: '/'
|
|
1351
|
+
}, defaultEntryAbs, false, fallbackRoot) : {
|
|
1352
|
+
'*': '/'
|
|
1353
|
+
};
|
|
1354
|
+
} else {
|
|
1355
|
+
const input = devComponentMap ?? {};
|
|
1356
|
+
resolvedDevComponentMap = 0 === Object.keys(input).length ? {
|
|
1357
|
+
'*': '/'
|
|
1358
|
+
} : buildDevComponentMapFromRecord(rootDir, input, defaultEntryAbs, false, fallbackRoot);
|
|
1359
|
+
}
|
|
1360
|
+
const audit = (()=>{
|
|
1361
|
+
const missing = [];
|
|
1362
|
+
for (const [componentName, entry] of Object.entries(resolvedDevComponentMap)){
|
|
1363
|
+
const fsPath = getFsPathFromViteEntry(entry);
|
|
1364
|
+
if (fsPath) {
|
|
1365
|
+
const resolved = tryResolveWithExtensions(fsPath);
|
|
1366
|
+
if (!resolved || !node_fs.existsSync(resolved)) missing.push({
|
|
1367
|
+
componentName,
|
|
1368
|
+
filePath: fsPath
|
|
1369
|
+
});
|
|
1158
1370
|
}
|
|
1159
1371
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
next.build = merged.build;
|
|
1168
|
-
}
|
|
1372
|
+
return {
|
|
1373
|
+
defaultEntryAbs,
|
|
1374
|
+
defaultEntryExists: node_fs.existsSync(defaultEntryAbs),
|
|
1375
|
+
componentMapCount: Object.keys(resolvedDevComponentMap).length,
|
|
1376
|
+
missingEntries: missing
|
|
1377
|
+
};
|
|
1378
|
+
})();
|
|
1169
1379
|
return {
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
virtualEntryCode,
|
|
1175
|
-
resolvedEntryAbs
|
|
1380
|
+
defaultEntryAbs,
|
|
1381
|
+
defaultEntry,
|
|
1382
|
+
componentMap: resolvedDevComponentMap,
|
|
1383
|
+
audit
|
|
1176
1384
|
};
|
|
1177
1385
|
}
|
|
1178
1386
|
function injectReactImport(code, id) {
|
|
@@ -1201,7 +1409,7 @@ function transformAssetUrl(code, id) {
|
|
|
1201
1409
|
const ORIGIN = new URL(import.meta.url).origin;
|
|
1202
1410
|
return path.startsWith('http') ? path : ORIGIN + path;
|
|
1203
1411
|
} catch (e) {
|
|
1204
|
-
console.warn('${
|
|
1412
|
+
console.warn('${constants_PLUGIN_LOG_PREFIX} Failed to resolve static asset URL:', path, e);
|
|
1205
1413
|
return path;
|
|
1206
1414
|
}
|
|
1207
1415
|
})()`);
|
|
@@ -1239,7 +1447,7 @@ const __dev_to_react_resolveAsset = (path) => {
|
|
|
1239
1447
|
const origin = new URL(import.meta.url).origin;
|
|
1240
1448
|
return path.startsWith('/') ? origin + path : origin + '/' + path;
|
|
1241
1449
|
} catch (e) {
|
|
1242
|
-
console.warn('${
|
|
1450
|
+
console.warn('${constants_PLUGIN_LOG_PREFIX} Failed to resolve CSS asset URL:', path, e);
|
|
1243
1451
|
return path;
|
|
1244
1452
|
}
|
|
1245
1453
|
};
|
|
@@ -1263,7 +1471,7 @@ function createContractVirtualModuleCode(contract) {
|
|
|
1263
1471
|
const STATE = (G[DEBUG_KEY] ||= { logged: {} });
|
|
1264
1472
|
if (!STATE.logged.contract) {
|
|
1265
1473
|
STATE.logged.contract = true;
|
|
1266
|
-
console.groupCollapsed('${
|
|
1474
|
+
console.groupCollapsed('${constants_PLUGIN_LOG_PREFIX} contract loaded');
|
|
1267
1475
|
console.log('Origin:', ORIGIN);
|
|
1268
1476
|
console.log('Paths:', CONTRACT.paths);
|
|
1269
1477
|
console.log('Events:', CONTRACT.events);
|
|
@@ -1284,14 +1492,14 @@ function createInitVirtualModuleCode() {
|
|
|
1284
1492
|
import "/@vite/client";
|
|
1285
1493
|
import RefreshRuntime from "/@react-refresh";
|
|
1286
1494
|
|
|
1287
|
-
import CONTRACT, { ${contractExportName} as CONTRACT_NAMED } from "${
|
|
1495
|
+
import CONTRACT, { ${contractExportName} as CONTRACT_NAMED } from "${constants_STABLE_CONTRACT_PATH}";
|
|
1288
1496
|
|
|
1289
1497
|
if (typeof window !== 'undefined' && !window.__vite_plugin_react_preamble_installed__) {
|
|
1290
1498
|
RefreshRuntime.injectIntoGlobalHook(window);
|
|
1291
1499
|
window.$RefreshReg$ = (type, id) => RefreshRuntime.register(type, id);
|
|
1292
1500
|
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
1293
1501
|
window.__vite_plugin_react_preamble_installed__ = true;
|
|
1294
|
-
console.log('${
|
|
1502
|
+
console.log('${constants_PLUGIN_LOG_PREFIX} React Refresh preamble installed.');
|
|
1295
1503
|
}
|
|
1296
1504
|
|
|
1297
1505
|
{
|
|
@@ -1301,7 +1509,7 @@ function createInitVirtualModuleCode() {
|
|
|
1301
1509
|
const STATE = (G[DEBUG_KEY] ||= { logged: {} });
|
|
1302
1510
|
if (!STATE.logged.init) {
|
|
1303
1511
|
STATE.logged.init = true;
|
|
1304
|
-
console.groupCollapsed('${
|
|
1512
|
+
console.groupCollapsed('${constants_PLUGIN_LOG_PREFIX} init loaded (HMR enabled)');
|
|
1305
1513
|
console.log('Origin:', ORIGIN);
|
|
1306
1514
|
console.log('This module imports /@vite/client and installs react-refresh preamble.');
|
|
1307
1515
|
console.log('Important: init must run BEFORE importing react-dom/client in the host.');
|
|
@@ -1357,7 +1565,7 @@ function createReactRuntimeVirtualModuleCode() {
|
|
|
1357
1565
|
const STATE = (G[DEBUG_KEY] ||= { logged: {} });
|
|
1358
1566
|
if (!STATE.logged.runtime) {
|
|
1359
1567
|
STATE.logged.runtime = true;
|
|
1360
|
-
console.groupCollapsed('${
|
|
1568
|
+
console.groupCollapsed('${constants_PLUGIN_LOG_PREFIX} react-runtime loaded');
|
|
1361
1569
|
console.log('Origin:', ORIGIN);
|
|
1362
1570
|
console.log('React.version:', React?.version);
|
|
1363
1571
|
console.log('ReactDOMClient keys:', Object.keys(ReactDOMClient || {}));
|
|
@@ -1404,7 +1612,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1404
1612
|
else processedComponentMap[key] = value;
|
|
1405
1613
|
return {
|
|
1406
1614
|
paths: {
|
|
1407
|
-
contract:
|
|
1615
|
+
contract: constants_STABLE_CONTRACT_PATH,
|
|
1408
1616
|
initClient: STABLE_INIT_PATH,
|
|
1409
1617
|
reactRuntime: STABLE_REACT_RUNTIME_PATH
|
|
1410
1618
|
},
|
|
@@ -1492,7 +1700,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1492
1700
|
if (1 === Object.keys(resolvedConfig.componentMap).length && '/' === resolvedConfig.componentMap['*']) {
|
|
1493
1701
|
const warn = server.config.logger?.warn?.bind(server.config.logger) ?? console.warn;
|
|
1494
1702
|
warn('');
|
|
1495
|
-
warn(`⚠️ ${
|
|
1703
|
+
warn(`⚠️ ${constants_PLUGIN_LOG_PREFIX} No componentName configured. This works in dev mode but will fail in library build (--mode lib).`);
|
|
1496
1704
|
warn(' Please use devToReactPlugin({ ComponentName: "src/ComponentName.tsx" }) or devToReactPlugin({ ComponentName: "/" }) to specify components.');
|
|
1497
1705
|
warn(' Or use wildcard: devToReactPlugin({ "*": "/" }) or devToReactPlugin("*")');
|
|
1498
1706
|
warn('');
|
|
@@ -1501,6 +1709,8 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1501
1709
|
contract,
|
|
1502
1710
|
stats,
|
|
1503
1711
|
audit: resolvedConfig.audit,
|
|
1712
|
+
resolvedConfig,
|
|
1713
|
+
configDir,
|
|
1504
1714
|
open: options.open
|
|
1505
1715
|
}, debugState);
|
|
1506
1716
|
},
|
|
@@ -1531,7 +1741,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1531
1741
|
}).css;
|
|
1532
1742
|
if (isLibBuild(env)) {
|
|
1533
1743
|
const actualNames = Object.keys(contract.dev.componentMap).filter((n)=>'*' !== n);
|
|
1534
|
-
if (0 === actualNames.length && !process.env.DEV_TO_REACT_LIB_SECTION) throw new Error(` ${
|
|
1744
|
+
if (0 === actualNames.length && !process.env.DEV_TO_REACT_LIB_SECTION) throw new Error(` ${constants_PLUGIN_LOG_PREFIX} Library build (--mode lib) requires at least one explicit componentName for identification and distribution.\nCurrent configuration is in "global fallback mode", which cannot determine build targets.\n\nPlease use one of the following to specify components:\n - devToReactPlugin('ComponentName')\n - devToReactPlugin({ ComponentName: '/' })\n - devToReactPlugin({ ComponentName: 'src/ComponentName.tsx' })\n\n💡 Tip: Wildcards are convenient for development, but explicit naming is required for production builds.`);
|
|
1535
1745
|
const isChild = '1' === process.env.DEV_TO_REACT_LIB_CHILD;
|
|
1536
1746
|
const buildTargets = resolveBuildTargets({
|
|
1537
1747
|
componentMap: contract.dev.componentMap,
|
|
@@ -1595,7 +1805,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1595
1805
|
}
|
|
1596
1806
|
},
|
|
1597
1807
|
buildStart () {
|
|
1598
|
-
if (libBuildState.enabled && !libBuildState.virtualEntryCode) throw new Error(`${
|
|
1808
|
+
if (libBuildState.enabled && !libBuildState.virtualEntryCode) throw new Error(`${constants_PLUGIN_LOG_PREFIX} lib 构建模式已启用,但虚拟入口模块代码未生成。\n当前 component: "${libBuildState.currentComponent}"\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。`);
|
|
1599
1809
|
},
|
|
1600
1810
|
async closeBundle () {
|
|
1601
1811
|
if (!libBuildState.enabled) return;
|
|
@@ -1637,7 +1847,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1637
1847
|
}
|
|
1638
1848
|
},
|
|
1639
1849
|
resolveId (source) {
|
|
1640
|
-
if (source.includes(
|
|
1850
|
+
if (source.includes(constants_STABLE_CONTRACT_PATH)) return `\0virtual:${PLUGIN_NAME}-contract`;
|
|
1641
1851
|
if (source.includes(STABLE_INIT_PATH)) return `\0virtual:${PLUGIN_NAME}-init`;
|
|
1642
1852
|
if (source.includes(STABLE_REACT_RUNTIME_PATH)) return `\0virtual:${PLUGIN_NAME}-react-runtime`;
|
|
1643
1853
|
if (source.includes(`virtual:${PLUGIN_NAME}-lib-entry:`)) {
|
|
@@ -1655,7 +1865,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1655
1865
|
const componentName = id.replace(`\0virtual:${PLUGIN_NAME}-lib-entry:`, '');
|
|
1656
1866
|
if (libBuildState.enabled) {
|
|
1657
1867
|
if (libBuildState.virtualEntryCode) return libBuildState.virtualEntryCode;
|
|
1658
|
-
throw new Error(`${
|
|
1868
|
+
throw new Error(`${constants_PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 的代码未生成。\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。\n当前 libBuildState: ${JSON.stringify({
|
|
1659
1869
|
enabled: libBuildState.enabled,
|
|
1660
1870
|
currentComponent: libBuildState.currentComponent,
|
|
1661
1871
|
hasCode: !!libBuildState.virtualEntryCode
|
|
@@ -1676,11 +1886,11 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1676
1886
|
libBuildState.currentComponent = componentName;
|
|
1677
1887
|
return code;
|
|
1678
1888
|
} catch (error) {
|
|
1679
|
-
throw new Error(`${
|
|
1889
|
+
throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法生成虚拟入口模块代码:${error instanceof Error ? error.message : String(error)}`);
|
|
1680
1890
|
}
|
|
1681
1891
|
}
|
|
1682
1892
|
}
|
|
1683
|
-
throw new Error(`${
|
|
1893
|
+
throw new Error(`${constants_PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 在插件配置完成之前被请求,且无法延迟生成代码。\n这可能是 Vite/Rollup 的内部时序问题。\n请尝试重新运行构建命令,如果问题持续,请检查 vite.config.ts 中的配置。`);
|
|
1684
1894
|
}
|
|
1685
1895
|
return null;
|
|
1686
1896
|
},
|
|
@@ -1720,4 +1930,4 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
|
|
|
1720
1930
|
];
|
|
1721
1931
|
};
|
|
1722
1932
|
const viteHostReactBridgePlugin = devToReactPlugin;
|
|
1723
|
-
export { EVENT_FULL_RELOAD, EVENT_HMR_UPDATE,
|
|
1933
|
+
export { EVENT_FULL_RELOAD, EVENT_HMR_UPDATE, PLUGIN_NAME, STABLE_BASE_PATH, STABLE_DEBUG_HTML_PATH, STABLE_DEBUG_JSON_PATH, STABLE_INIT_PATH, STABLE_REACT_RUNTIME_PATH, constants_PLUGIN_LOG_PREFIX as PLUGIN_LOG_PREFIX, constants_STABLE_CONTRACT_PATH as STABLE_CONTRACT_PATH, devToReactPlugin, viteHostReactBridgePlugin };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"libBuildUtils.d.ts","sourceRoot":"","sources":["../src/libBuildUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"libBuildUtils.d.ts","sourceRoot":"","sources":["../src/libBuildUtils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAKnE,OAAO,KAAK,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAA;AAErF,qBAAqB;AACrB,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,SAAS,WAEzC;AAED,kBAAkB;AAClB,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,UAErD;AAED,uBAAuB;AACvB,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,UAKlD;AAED,qBAAqB;AACrB,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,WAE/C;AA8LD,6BAA6B;AAC7B,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB,GAAG,MAAM,CA+IT;AAED,kCAAkC;AAClC,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,oCAAoC;AACpC,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QA+B/D;AAED,kBAAkB;AAClB,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;CACxB,YA8BA;AAED,iBAAiB;AACjB,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,cAAc,EAAE,0BAA0B,CAAA;IAC1C,OAAO,EAAE,uBAAuB,CAAA;IAChC,UAAU,EAAE,UAAU,CAAA;CACvB,GAAG;IACF,IAAI,EAAE,UAAU,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAuGA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface CreateLoaderUmdWrapperOptions {
|
|
2
|
+
componentName: string;
|
|
3
|
+
origin: string;
|
|
4
|
+
contractEndpoint?: string;
|
|
5
|
+
reactLoaderUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Generate a lightweight UMD wrapper that uses @dev-to/react-loader to load the component.
|
|
9
|
+
* This wrapper depends on react-loader UMD build being available.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createLoaderUmdWrapper(options: CreateLoaderUmdWrapperOptions): string;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=loaderUmdWrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loaderUmdWrapper.d.ts","sourceRoot":"","sources":["../src/loaderUmdWrapper.ts"],"names":[],"mappings":"AAGA,UAAU,6BAA6B;IACrC,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,CAwKrF"}
|
package/dist/virtualModules.d.ts
CHANGED
|
@@ -2,4 +2,13 @@ import type { BridgeContract } from './types.js';
|
|
|
2
2
|
export declare function createContractVirtualModuleCode(contract: BridgeContract): string;
|
|
3
3
|
export declare function createInitVirtualModuleCode(): string;
|
|
4
4
|
export declare function createReactRuntimeVirtualModuleCode(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Generate loader wrapper code for a specific component.
|
|
7
|
+
* This creates a pre-configured loader script that can be imported directly.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createLoaderWrapperCode(params: {
|
|
10
|
+
componentName: string;
|
|
11
|
+
origin: string;
|
|
12
|
+
contractEndpoint?: string;
|
|
13
|
+
}): string;
|
|
5
14
|
//# sourceMappingURL=virtualModules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtualModules.d.ts","sourceRoot":"","sources":["../src/virtualModules.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,cAAc,UAuBvE;AAED,wBAAgB,2BAA2B,WAqE1C;AAED,wBAAgB,mCAAmC,WAuBlD"}
|
|
1
|
+
{"version":3,"file":"virtualModules.d.ts","sourceRoot":"","sources":["../src/virtualModules.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,cAAc,UAuBvE;AAED,wBAAgB,2BAA2B,WAqE1C;AAED,wBAAgB,mCAAmC,WAuBlD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,UAgDA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev-to/react-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"picocolors": "^1.1.0",
|
|
33
33
|
"typescript": "^5.4.5",
|
|
34
|
-
"@dev-to/react-shared": "0.1.
|
|
34
|
+
"@dev-to/react-shared": "0.1.2"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "rslib build",
|