@dev-to/react-plugin 0.1.1 → 0.2.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.
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAYA,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;AAElE,eAAO,MAAM,iBAAiB,4BAAiC,CAAA;AAC/D,eAAO,MAAM,gBAAgB,2BAAgC,CAAA"}
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"}
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"debugTools.d.ts","sourceRoot":"","sources":["../src/debugTools.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AAEzC,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,WAAW,CAAA;IAClB,KAAK,EAAE,iBAAiB,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAqFD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,QAkQxG"}
1
+ {"version":3,"file":"debugTools.d.ts","sourceRoot":"","sources":["../src/debugTools.ts"],"names":[],"mappings":"AAqBA,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,QAuSxG"}
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
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
7
  import node_os from "node:os";
8
- import { pathToFileURL } from "node:url";
9
8
  import typescript from "typescript";
10
9
  const PLUGIN_NAME = DEV_TO_REACT_NAMESPACE;
11
- const PLUGIN_LOG_PREFIX = `[${PLUGIN_NAME}]`;
10
+ const constants_PLUGIN_LOG_PREFIX = `[${PLUGIN_NAME}]`;
12
11
  const STABLE_BASE_PATH = DEV_TO_REACT_BASE_PATH;
13
- const STABLE_CONTRACT_PATH = DEV_TO_REACT_CONTRACT_PATH;
12
+ const constants_STABLE_CONTRACT_PATH = DEV_TO_REACT_CONTRACT_PATH;
14
13
  const STABLE_INIT_PATH = DEV_TO_REACT_INIT_PATH;
15
14
  const STABLE_REACT_RUNTIME_PATH = DEV_TO_REACT_REACT_RUNTIME_PATH;
16
15
  const STABLE_DEBUG_HTML_PATH = DEV_TO_REACT_DEBUG_HTML_PATH;
17
16
  const STABLE_DEBUG_JSON_PATH = DEV_TO_REACT_DEBUG_JSON_PATH;
17
+ const STABLE_LOADER_BASE_PATH = DEV_TO_REACT_LOADER_BASE_PATH;
18
18
  const EVENT_FULL_RELOAD = DEV_TO_REACT_EVENT_FULL_RELOAD;
19
19
  const EVENT_HMR_UPDATE = DEV_TO_REACT_EVENT_HMR_UPDATE;
20
20
  function renderDebugHtml(params) {
@@ -51,7 +51,7 @@ function renderDebugHtml(params) {
51
51
  lines.push(dim(' */'));
52
52
  return `${lines.join('\n')}\n\n${escapeHtml(content)}`;
53
53
  } catch (e) {
54
- return escapeHtml(`// ${PLUGIN_LOG_PREFIX} 无法读取配置文件: ${e}`);
54
+ return escapeHtml(`// ${constants_PLUGIN_LOG_PREFIX} 无法读取配置文件: ${e}`);
55
55
  }
56
56
  })();
57
57
  return `<!doctype html>
@@ -374,7 +374,7 @@ CSS: <span class="str">dist/&lt;name&gt;/&lt;name&gt;.css</span></pre>
374
374
  <div style="margin-top: 12px;">
375
375
  <pre style="font-size: 12px; line-height: 1.7;">
376
376
  <span class="kw">Endpoints:</span>
377
- - Contract: <span class="str">${STABLE_CONTRACT_PATH}</span>
377
+ - Contract: <span class="str">${constants_STABLE_CONTRACT_PATH}</span>
378
378
  - Init: <span class="str">${STABLE_INIT_PATH}</span>
379
379
  - Runtime: <span class="str">${STABLE_REACT_RUNTIME_PATH}</span>
380
380
 
@@ -510,7 +510,7 @@ function tryResolveWithExtensions(p) {
510
510
  function resolveEntryAbsPath(rootDir, entry, defaultEntryAbs, fallbackRoot) {
511
511
  const tryResolveWithBase = (baseDir)=>{
512
512
  if ('/' === entry) {
513
- if (!defaultEntryAbs) throw new Error(`${PLUGIN_LOG_PREFIX} 使用 / 作为入口时,必须提供 defaultEntryAbs 参数`);
513
+ if (!defaultEntryAbs) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 使用 / 作为入口时,必须提供 defaultEntryAbs 参数`);
514
514
  return defaultEntryAbs;
515
515
  }
516
516
  const fsPath = toFsPathFromViteEntry(entry);
@@ -528,6 +528,396 @@ function resolveEntryAbsPath(rootDir, entry, defaultEntryAbs, fallbackRoot) {
528
528
  if (fallbackRoot && fallbackRoot !== rootDir) return tryResolveWithBase(fallbackRoot);
529
529
  return resolved;
530
530
  }
531
+ function isLibBuild(env) {
532
+ return env?.command === 'build' && env?.mode === 'lib';
533
+ }
534
+ function toSafeOutDirName(componentName) {
535
+ return componentName.replace(/[\\/]/g, '_').replace(/\.\./g, '_');
536
+ }
537
+ function toSafeUmdName(componentName) {
538
+ let s = componentName.replace(/[^A-Za-z0-9_$]+/g, '_');
539
+ if (!s) s = 'ViteDevComponent';
540
+ if (/^\d/.test(s)) s = `_${s}`;
541
+ return s;
542
+ }
543
+ function isValidJsIdentifier(name) {
544
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
545
+ }
546
+ function analyzeExports(filePath) {
547
+ if (!node_fs.existsSync(filePath)) return {
548
+ hasDefault: false,
549
+ namedExports: []
550
+ };
551
+ const content = node_fs.readFileSync(filePath, 'utf-8');
552
+ const namedExports = [];
553
+ let hasDefault = false;
554
+ let scriptKind = typescript.ScriptKind.TS;
555
+ const ext = node_path.extname(filePath).toLowerCase();
556
+ if ('.tsx' === ext) scriptKind = typescript.ScriptKind.TSX;
557
+ else if ('.jsx' === ext) scriptKind = typescript.ScriptKind.JSX;
558
+ else if ('.js' === ext) scriptKind = typescript.ScriptKind.JS;
559
+ else if ('.ts' === ext) scriptKind = typescript.ScriptKind.TS;
560
+ let sourceFile;
561
+ try {
562
+ sourceFile = typescript.createSourceFile(filePath, content, typescript.ScriptTarget.Latest, true, scriptKind);
563
+ } catch (parseError) {
564
+ throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法解析入口文件 "${filePath}"。\n解析错误: ${parseError instanceof Error ? parseError.message : String(parseError)}\n请确保文件是有效的 TypeScript/JavaScript 文件。`);
565
+ }
566
+ typescript.getPreEmitDiagnostics(typescript.createProgram([
567
+ filePath
568
+ ], {
569
+ target: typescript.ScriptTarget.Latest,
570
+ module: typescript.ModuleKind.ESNext,
571
+ jsx: scriptKind === typescript.ScriptKind.TSX || scriptKind === typescript.ScriptKind.JSX ? typescript.JsxEmit.React : void 0
572
+ }));
573
+ function visit(node) {
574
+ if (typescript.isExportAssignment(node)) {
575
+ if (true !== node.isExportEquals) hasDefault = true;
576
+ }
577
+ if (typescript.isFunctionDeclaration(node) || typescript.isClassDeclaration(node) || typescript.isVariableStatement(node) || typescript.isInterfaceDeclaration(node) || typescript.isTypeAliasDeclaration(node) || typescript.isEnumDeclaration(node)) {
578
+ const modifiers = typescript.getModifiers(node);
579
+ if (modifiers?.some((m)=>m.kind === typescript.SyntaxKind.ExportKeyword)) if (modifiers.some((m)=>m.kind === typescript.SyntaxKind.DefaultKeyword)) hasDefault = true;
580
+ else {
581
+ if (typescript.isFunctionDeclaration(node) && node.name) namedExports.push(node.name.text);
582
+ if (typescript.isClassDeclaration(node) && node.name) namedExports.push(node.name.text);
583
+ if (typescript.isVariableStatement(node)) node.declarationList.declarations.forEach((decl)=>{
584
+ if (typescript.isIdentifier(decl.name)) namedExports.push(decl.name.text);
585
+ });
586
+ if (typescript.isInterfaceDeclaration(node) && node.name) namedExports.push(node.name.text);
587
+ if (typescript.isTypeAliasDeclaration(node) && node.name) namedExports.push(node.name.text);
588
+ if (typescript.isEnumDeclaration(node) && node.name) namedExports.push(node.name.text);
589
+ }
590
+ }
591
+ if (typescript.isExportDeclaration(node) && node.exportClause) {
592
+ if (typescript.isNamedExports(node.exportClause)) node.exportClause.elements.forEach((element)=>{
593
+ if (element.name) {
594
+ const exportName = element.name.text;
595
+ const { propertyName } = element;
596
+ if (propertyName && 'default' === propertyName.text) namedExports.push(exportName);
597
+ else if ('default' === exportName) hasDefault = true;
598
+ else namedExports.push(exportName);
599
+ }
600
+ });
601
+ else if (typescript.isNamespaceExport(node.exportClause)) namedExports.push(node.exportClause.name.text);
602
+ }
603
+ typescript.isExportDeclaration(node) && node.exportClause;
604
+ typescript.forEachChild(node, visit);
605
+ }
606
+ visit(sourceFile);
607
+ const uniqueExports = Array.from(new Set(namedExports));
608
+ if (!hasDefault && 0 === uniqueExports.length) {
609
+ const hasDefaultRegex = /export\s+default\s+/;
610
+ const hasNamedRegex = /export\s+(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
611
+ const regexHasDefault = hasDefaultRegex.test(content);
612
+ const regexNamedMatches = [];
613
+ let match;
614
+ while(null !== (match = hasNamedRegex.exec(content)))regexNamedMatches.push(match[1]);
615
+ if (regexHasDefault || regexNamedMatches.length > 0) {
616
+ console.warn(`${constants_PLUGIN_LOG_PREFIX} 警告:AST 分析未检测到导出,但正则检测到:\n 文件: ${filePath}\n 正则检测 default: ${regexHasDefault}\n 正则检测命名导出: ${regexNamedMatches.join(', ') || '无'}\n 这可能是 AST 解析问题,将尝试继续构建。`);
617
+ if (regexHasDefault) hasDefault = true;
618
+ if (regexNamedMatches.length > 0) namedExports.push(...regexNamedMatches);
619
+ }
620
+ }
621
+ return {
622
+ hasDefault,
623
+ namedExports: Array.from(new Set(namedExports))
624
+ };
625
+ }
626
+ function generateLibVirtualEntryCode(params) {
627
+ const { defaultEntryAbs, componentName } = params;
628
+ if (!node_fs.existsSync(defaultEntryAbs)) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 入口文件不存在: "${defaultEntryAbs}"\n请检查文件路径是否正确。`);
629
+ const actualFile = tryResolveWithExtensions(defaultEntryAbs) || defaultEntryAbs;
630
+ if (!node_fs.existsSync(actualFile)) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 入口文件不存在: "${defaultEntryAbs}"\n尝试解析后的路径: "${actualFile}"\n请检查文件路径是否正确。`);
631
+ const importTarget = actualFile;
632
+ let exports;
633
+ try {
634
+ exports = analyzeExports(actualFile);
635
+ } catch (error) {
636
+ const errorMsg = error instanceof Error ? error.message : String(error);
637
+ 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}"`);
638
+ }
639
+ let code;
640
+ if (exports.hasDefault || 0 !== exports.namedExports.length) if (exports.hasDefault) {
641
+ let prefer = '';
642
+ if (exports.namedExports.includes(componentName)) prefer = isValidJsIdentifier(componentName) ? `mod.${componentName}` : `mod[${JSON.stringify(componentName)}]`;
643
+ const pickedExpr = prefer ? `${prefer} || mod.default || mod` : 'mod.default || mod';
644
+ code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
645
+ import * as mod from ${JSON.stringify(importTarget)};
646
+ const picked = ${pickedExpr};
647
+ const Card = picked && picked.default ? picked.default : picked;
648
+ export default Card;
649
+ export * from ${JSON.stringify(importTarget)};
650
+ `;
651
+ } else if (1 === exports.namedExports.length) {
652
+ const singleExport = exports.namedExports[0];
653
+ const exportAccess = isValidJsIdentifier(singleExport) ? `mod.${singleExport}` : `mod[${JSON.stringify(singleExport)}]`;
654
+ code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
655
+ import * as mod from ${JSON.stringify(importTarget)};
656
+ const Card = ${exportAccess};
657
+ export default Card;
658
+ export * from ${JSON.stringify(importTarget)};
659
+ `;
660
+ } else {
661
+ const hasComponentNameExport = exports.namedExports.some((exp)=>exp === componentName);
662
+ if (hasComponentNameExport) {
663
+ const exportAccess = isValidJsIdentifier(componentName) ? `mod.${componentName}` : `mod[${JSON.stringify(componentName)}]`;
664
+ code = `/** AUTO-GENERATED by ${PLUGIN_NAME} */
665
+ import * as mod from ${JSON.stringify(importTarget)};
666
+ const Card = ${exportAccess};
667
+ export default Card;
668
+ export * from ${JSON.stringify(importTarget)};
669
+ `;
670
+ } 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(', ')}`);
671
+ }
672
+ 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}"`);
673
+ return code;
674
+ }
675
+ function getLibVirtualEntryPath(componentName) {
676
+ return `virtual:${PLUGIN_NAME}-lib-entry:${componentName}`;
677
+ }
678
+ function normalizeLibCss(outDir, baseName) {
679
+ if (!node_fs.existsSync(outDir)) return;
680
+ const target = node_path.join(outDir, `${baseName}.css`);
681
+ if (node_fs.existsSync(target)) return;
682
+ const cssCandidates = [];
683
+ const scanDir = (dir, depth)=>{
684
+ if (depth < 0 || !node_fs.existsSync(dir)) return;
685
+ const entries = node_fs.readdirSync(dir, {
686
+ withFileTypes: true
687
+ });
688
+ for (const e of entries){
689
+ const full = node_path.join(dir, e.name);
690
+ if (e.isDirectory()) scanDir(full, depth - 1);
691
+ else if (e.isFile() && e.name.endsWith('.css')) cssCandidates.push(full);
692
+ }
693
+ };
694
+ scanDir(outDir, 2);
695
+ if (1 !== cssCandidates.length) return;
696
+ const from = cssCandidates[0];
697
+ try {
698
+ node_fs.renameSync(from, target);
699
+ } catch {
700
+ try {
701
+ node_fs.copyFileSync(from, target);
702
+ node_fs.unlinkSync(from);
703
+ } catch {}
704
+ }
705
+ }
706
+ function resolveBuildTargets(params) {
707
+ const { componentMap, requestedRaw, defaultEntryAbs } = params;
708
+ const componentNames = Object.keys(componentMap);
709
+ const requestedList = requestedRaw ? requestedRaw.split(',').map((s)=>s.trim()).filter(Boolean) : [];
710
+ const actualConfiguredNames = componentNames.filter((n)=>'*' !== n);
711
+ if (actualConfiguredNames.length > 0) {
712
+ if (requestedList.length > 0) {
713
+ const picked = requestedList.filter((n)=>actualConfiguredNames.includes(n));
714
+ if (0 === picked.length) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 指定的 component 不在配置列表中:${requestedRaw}`);
715
+ return picked;
716
+ }
717
+ return actualConfiguredNames;
718
+ }
719
+ if (requestedList.length > 0) return requestedList;
720
+ const fallbackName = node_path.parse(defaultEntryAbs || 'index').name || 'index';
721
+ return [
722
+ fallbackName
723
+ ];
724
+ }
725
+ function generateLibBuildNextConfig(params) {
726
+ const { rootDir, picked, componentMap, resolvedConfig, options, userConfig, configDir } = params;
727
+ const outBase = toSafeOutDirName(picked);
728
+ const outDir = node_path.resolve(rootDir, 'dist', outBase);
729
+ let resolvedEntryAbs = resolvedConfig.defaultEntryAbs;
730
+ const entryAbs = (()=>{
731
+ const entryFromMap = componentMap[picked];
732
+ if (entryFromMap) {
733
+ const abs = resolveEntryAbsPath(rootDir, entryFromMap, resolvedConfig.defaultEntryAbs, configDir);
734
+ if (!abs) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法解析入口:component="${picked}", entry="${entryFromMap}"`);
735
+ resolvedEntryAbs = abs;
736
+ }
737
+ return getLibVirtualEntryPath(picked);
738
+ })();
739
+ const virtualEntryCode = (()=>{
740
+ const entryFromMap = componentMap[picked];
741
+ if (entryFromMap) {
742
+ const abs = resolveEntryAbsPath(rootDir, entryFromMap, resolvedConfig.defaultEntryAbs, configDir);
743
+ if (!abs) throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法解析入口:component="${picked}", entry="${entryFromMap}"`);
744
+ resolvedEntryAbs = abs;
745
+ return generateLibVirtualEntryCode({
746
+ rootDir,
747
+ defaultEntryAbs: abs,
748
+ componentName: picked
749
+ });
750
+ }
751
+ resolvedEntryAbs = resolvedConfig.defaultEntryAbs;
752
+ return generateLibVirtualEntryCode({
753
+ rootDir,
754
+ defaultEntryAbs: resolvedConfig.defaultEntryAbs,
755
+ componentName: picked
756
+ });
757
+ })();
758
+ const next = {
759
+ root: rootDir,
760
+ define: {
761
+ ...userConfig.define || {},
762
+ 'process.env.NODE_ENV': JSON.stringify('production')
763
+ },
764
+ build: {
765
+ outDir,
766
+ emptyOutDir: true,
767
+ cssCodeSplit: false,
768
+ lib: {
769
+ entry: entryAbs,
770
+ name: toSafeUmdName(picked),
771
+ formats: [
772
+ 'umd'
773
+ ],
774
+ fileName: ()=>`${outBase}.js`
775
+ },
776
+ rollupOptions: {
777
+ external: [
778
+ 'react',
779
+ 'react-dom',
780
+ 'react-dom/client'
781
+ ],
782
+ output: {
783
+ inlineDynamicImports: true,
784
+ exports: 'named',
785
+ globals: {
786
+ react: 'React',
787
+ 'react-dom': 'ReactDOM',
788
+ 'react-dom/client': 'ReactDOMClient'
789
+ },
790
+ assetFileNames: (assetInfo)=>{
791
+ const name = assetInfo?.name || '';
792
+ if (name.endsWith('.css')) return `${outBase}.css`;
793
+ return 'assets/[name]-[hash][extname]';
794
+ }
795
+ }
796
+ }
797
+ }
798
+ };
799
+ if (options.build) {
800
+ const merged = mergeConfig({
801
+ build: next.build
802
+ }, {
803
+ build: options.build
804
+ });
805
+ next.build = merged.build;
806
+ }
807
+ return {
808
+ next,
809
+ outDir: next.build?.outDir || outDir,
810
+ outBase,
811
+ buildTargets: [],
812
+ virtualEntryCode,
813
+ resolvedEntryAbs
814
+ };
815
+ }
816
+ function createLoaderUmdWrapper(options) {
817
+ const { componentName, origin, contractEndpoint = constants_STABLE_CONTRACT_PATH } = options;
818
+ const globalName = toSafeUmdName(componentName);
819
+ const code = `/**
820
+ * UMD Loader Wrapper for component: ${componentName}
821
+ * Global name: ${globalName}
822
+ * Generated by ${constants_PLUGIN_LOG_PREFIX}
823
+ *
824
+ * This wrapper uses @dev-to/react-loader to dynamically load and render the component.
825
+ *
826
+ * Usage:
827
+ * 1. Load React and ReactDOM:
828
+ * <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
829
+ * <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
830
+ *
831
+ * 2. Load ReactLoader:
832
+ * <script src="https://cdn.jsdelivr.net/npm/@dev-to/react-loader@latest/dist/index.umd.js"></script>
833
+ *
834
+ * 3. Load this wrapper:
835
+ * <script src="${origin}/__dev_to_react__/loader/${componentName}.js"></script>
836
+ *
837
+ * 4. Render the component:
838
+ * <div id="app"></div>
839
+ * <script>
840
+ * window.${globalName}.render(
841
+ * document.getElementById('app'),
842
+ * { /* your props */ }
843
+ * );
844
+ * </script>
845
+ */
846
+ (function (root, factory) {
847
+ if (typeof exports === 'object' && typeof module !== 'undefined') {
848
+ // CommonJS
849
+ factory(exports, require('react'), require('react-dom'), require('@dev-to/react-loader'));
850
+ } else if (typeof define === 'function' && define.amd) {
851
+ // AMD
852
+ define(['exports', 'react', 'react-dom', '@dev-to/react-loader'], factory);
853
+ } else {
854
+ // Browser globals
855
+ var globalObj = typeof globalThis !== 'undefined' ? globalThis : (typeof self !== 'undefined' ? self : root);
856
+ factory((globalObj.${globalName} = {}), globalObj.React, globalObj.ReactDOM, globalObj.DevToReactLoader);
857
+ }
858
+ })(this, function (exports, React, ReactDOM, ReactLoaderModule) {
859
+ 'use strict';
860
+
861
+ // Get ReactLoader component from the module
862
+ var ReactLoader = ReactLoaderModule && ReactLoaderModule.ReactLoader;
863
+
864
+ if (!ReactLoader) {
865
+ throw new Error(
866
+ '${constants_PLUGIN_LOG_PREFIX} ReactLoader not found. ' +
867
+ 'Please load @dev-to/react-loader before this script: ' +
868
+ '<script src="https://cdn.jsdelivr.net/npm/@dev-to/react-loader@latest/dist/index.umd.js"></script>'
869
+ );
870
+ }
871
+
872
+ // Component configuration
873
+ var config = {
874
+ origin: ${JSON.stringify(origin)},
875
+ name: ${JSON.stringify(componentName)},
876
+ contractEndpoint: ${JSON.stringify(contractEndpoint)}
877
+ };
878
+
879
+ /**
880
+ * Render the component using ReactLoader
881
+ */
882
+ function render(targetElement, componentProps) {
883
+ if (!targetElement) {
884
+ throw new Error('${constants_PLUGIN_LOG_PREFIX} Target element is required');
885
+ }
886
+
887
+ if (!React || !React.createElement) {
888
+ throw new Error('${constants_PLUGIN_LOG_PREFIX} React is not loaded');
889
+ }
890
+
891
+ if (!ReactDOM || !ReactDOM.createRoot) {
892
+ throw new Error('${constants_PLUGIN_LOG_PREFIX} ReactDOM is not loaded');
893
+ }
894
+
895
+ // Create ReactLoader component props
896
+ var loaderProps = {
897
+ origin: config.origin,
898
+ name: config.name,
899
+ contractEndpoint: config.contractEndpoint,
900
+ componentProps: componentProps || {}
901
+ };
902
+
903
+ // Render using ReactLoader
904
+ var root = ReactDOM.createRoot(targetElement);
905
+ root.render(React.createElement(ReactLoader, loaderProps));
906
+
907
+ return root;
908
+ }
909
+
910
+ // Export the API
911
+ exports.render = render;
912
+ exports.config = config;
913
+ exports.default = { render: render, config: config };
914
+
915
+ // Mark as ES module
916
+ Object.defineProperty(exports, '__esModule', { value: true });
917
+ });
918
+ `;
919
+ return code;
920
+ }
531
921
  function openBrowser(url) {
532
922
  const bridgePath = STABLE_DEBUG_HTML_PATH;
533
923
  if ('darwin' === process.platform) {
@@ -615,7 +1005,7 @@ function installDebugTools(server, ctx, state) {
615
1005
  const logger = server.config.logger;
616
1006
  const info = 'function' == typeof logger?.info ? logger.info.bind(logger) : console.log;
617
1007
  info('');
618
- info(`${PLUGIN_LOG_PREFIX} Debug panel:`);
1008
+ info(`${constants_PLUGIN_LOG_PREFIX} Debug panel:`);
619
1009
  urls.forEach((u)=>info(` ${picocolors.cyan(u)}`));
620
1010
  info(` JSON: ${picocolors.cyan(`${proto}://localhost:${port}${STABLE_DEBUG_JSON_PATH}`)}`);
621
1011
  info('');
@@ -635,7 +1025,7 @@ function installDebugTools(server, ctx, state) {
635
1025
  const url = req.url || '';
636
1026
  const pathname = String(url).split('?')[0];
637
1027
  const now = Date.now();
638
- if (pathname === STABLE_CONTRACT_PATH) {
1028
+ if (pathname === constants_STABLE_CONTRACT_PATH) {
639
1029
  ctx.stats.contract.count += 1;
640
1030
  ctx.stats.contract.lastAt = now;
641
1031
  } else if (pathname === STABLE_INIT_PATH) {
@@ -723,6 +1113,30 @@ function installDebugTools(server, ctx, state) {
723
1113
  }, null, 2));
724
1114
  return;
725
1115
  }
1116
+ if (pathname.startsWith(STABLE_LOADER_BASE_PATH)) {
1117
+ const loaderPathPattern = new RegExp(`^${STABLE_LOADER_BASE_PATH}/([^/]+)\\.js$`);
1118
+ const match = pathname.match(loaderPathPattern);
1119
+ if (match) {
1120
+ const componentName = match[1];
1121
+ const isHttps = !!server.config.server.https;
1122
+ const proto = isHttps ? 'https' : 'http';
1123
+ const hostHeader = String(req.headers.host || '');
1124
+ const addr = server.httpServer?.address();
1125
+ const actualPort = addr && 'object' == typeof addr ? addr.port : void 0;
1126
+ const origin = hostHeader ? `${proto}://${hostHeader}` : `${proto}://localhost${actualPort ? `:${actualPort}` : ''}`;
1127
+ const code = createLoaderUmdWrapper({
1128
+ componentName,
1129
+ origin,
1130
+ contractEndpoint: constants_STABLE_CONTRACT_PATH
1131
+ });
1132
+ res.statusCode = 200;
1133
+ res.setHeader('Content-Type', "application/javascript; charset=utf-8");
1134
+ res.setHeader('Access-Control-Allow-Origin', '*');
1135
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
1136
+ res.end(code);
1137
+ return;
1138
+ }
1139
+ }
726
1140
  if (url.startsWith(STABLE_DEBUG_HTML_PATH)) {
727
1141
  const addr = server.httpServer?.address();
728
1142
  const actualPort = addr && 'object' == typeof addr ? addr.port : void 0;
@@ -792,387 +1206,102 @@ function installDebugTools(server, ctx, state) {
792
1206
  res.end(html);
793
1207
  return;
794
1208
  }
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
- };
1209
+ next();
1210
+ });
987
1211
  }
988
- function generateLibVirtualEntryCode(params) {
989
- const { defaultEntryAbs, componentName } = params;
990
- if (!node_fs.existsSync(defaultEntryAbs)) throw new Error(`${PLUGIN_LOG_PREFIX} 入口文件不存在: "${defaultEntryAbs}"\n请检查文件路径是否正确。`);
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;
1212
+ function toViteFsPath(filePath) {
1213
+ const normalized = filePath.replace(/\\/g, '/');
1214
+ return normalized.startsWith('/') ? `/@fs${normalized}` : `/@fs/${normalized}`;
1036
1215
  }
1037
- function getLibVirtualEntryPath(componentName) {
1038
- return `virtual:${PLUGIN_NAME}-lib-entry:${componentName}`;
1216
+ function resolveDefaultEntryAbs(rootDir) {
1217
+ const appCandidates = [
1218
+ node_path.resolve(rootDir, 'src/App.tsx'),
1219
+ node_path.resolve(rootDir, 'src/App.jsx'),
1220
+ node_path.resolve(rootDir, 'src/App.ts'),
1221
+ node_path.resolve(rootDir, 'src/App.js')
1222
+ ];
1223
+ const foundApp = appCandidates.find((p)=>node_fs.existsSync(p));
1224
+ if (foundApp) return foundApp;
1225
+ const indexCandidates = [
1226
+ node_path.resolve(rootDir, 'src/index.ts'),
1227
+ node_path.resolve(rootDir, 'src/index.tsx'),
1228
+ node_path.resolve(rootDir, 'src/index.jsx'),
1229
+ node_path.resolve(rootDir, 'src/index.js')
1230
+ ];
1231
+ const foundIndex = indexCandidates.find((p)=>node_fs.existsSync(p));
1232
+ if (foundIndex) return foundIndex;
1233
+ return appCandidates[0];
1039
1234
  }
1040
- function normalizeLibCss(outDir, baseName) {
1041
- if (!node_fs.existsSync(outDir)) return;
1042
- const target = node_path.join(outDir, `${baseName}.css`);
1043
- if (node_fs.existsSync(target)) return;
1044
- const cssCandidates = [];
1045
- const scanDir = (dir, depth)=>{
1046
- if (depth < 0 || !node_fs.existsSync(dir)) return;
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);
1235
+ function buildDevComponentMapFromRecord(rootDir, input, defaultEntryAbs, convertAt = false, fallbackRoot) {
1236
+ const out = {};
1237
+ for (const [componentName, entry] of Object.entries(input)){
1238
+ if (!componentName || !entry) continue;
1239
+ if ('/' === entry) {
1240
+ out[componentName] = convertAt ? toViteFsPath(defaultEntryAbs) : '/';
1241
+ continue;
1054
1242
  }
1055
- };
1056
- scanDir(outDir, 2);
1057
- if (1 !== cssCandidates.length) return;
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;
1243
+ if (entry.startsWith('http://') || entry.startsWith('https://') || entry.startsWith('/')) {
1244
+ out[componentName] = entry;
1245
+ continue;
1078
1246
  }
1079
- return actualConfiguredNames;
1247
+ const abs = node_path.isAbsolute(entry) ? entry : node_path.resolve(rootDir, entry);
1248
+ let resolved = tryResolveWithExtensions(abs);
1249
+ if (!resolved && fallbackRoot && fallbackRoot !== rootDir) {
1250
+ const fallbackAbs = node_path.isAbsolute(entry) ? entry : node_path.resolve(fallbackRoot, entry);
1251
+ resolved = tryResolveWithExtensions(fallbackAbs);
1252
+ }
1253
+ resolved = resolved || abs;
1254
+ out[componentName] = toViteFsPath(resolved);
1080
1255
  }
1081
- if (requestedList.length > 0) return requestedList;
1082
- const fallbackName = node_path.parse(defaultEntryAbs || 'index').name || 'index';
1083
- return [
1084
- fallbackName
1085
- ];
1256
+ return out;
1086
1257
  }
1087
- function generateLibBuildNextConfig(params) {
1088
- const { rootDir, picked, componentMap, resolvedConfig, options, userConfig, configDir } = params;
1089
- const outBase = toSafeOutDirName(picked);
1090
- const outDir = node_path.resolve(rootDir, 'dist', outBase);
1091
- let resolvedEntryAbs = resolvedConfig.defaultEntryAbs;
1092
- const entryAbs = (()=>{
1093
- const entryFromMap = componentMap[picked];
1094
- if (entryFromMap) {
1095
- const abs = resolveEntryAbsPath(rootDir, entryFromMap, resolvedConfig.defaultEntryAbs, configDir);
1096
- if (!abs) throw new Error(`${PLUGIN_LOG_PREFIX} 无法解析入口:component="${picked}", entry="${entryFromMap}"`);
1097
- resolvedEntryAbs = abs;
1098
- }
1099
- return getLibVirtualEntryPath(picked);
1100
- })();
1101
- const virtualEntryCode = (()=>{
1102
- const entryFromMap = componentMap[picked];
1103
- if (entryFromMap) {
1104
- const abs = resolveEntryAbsPath(rootDir, entryFromMap, resolvedConfig.defaultEntryAbs, configDir);
1105
- if (!abs) throw new Error(`${PLUGIN_LOG_PREFIX} 无法解析入口:component="${picked}", entry="${entryFromMap}"`);
1106
- resolvedEntryAbs = abs;
1107
- return generateLibVirtualEntryCode({
1108
- rootDir,
1109
- defaultEntryAbs: abs,
1110
- componentName: picked
1111
- });
1112
- }
1113
- resolvedEntryAbs = resolvedConfig.defaultEntryAbs;
1114
- return generateLibVirtualEntryCode({
1115
- rootDir,
1116
- defaultEntryAbs: resolvedConfig.defaultEntryAbs,
1117
- componentName: picked
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
- }
1258
+ function getFsPathFromViteEntry(entry) {
1259
+ if (!entry.startsWith('/@fs')) return null;
1260
+ let p = entry.slice(4);
1261
+ if (p.startsWith('/') && /\/[A-Za-z]:\//.test(p)) p = p.slice(1);
1262
+ return p;
1263
+ }
1264
+ function resolveDevComponentConfig(rootDir, devComponentMap, fallbackRoot) {
1265
+ const defaultEntryAbs = resolveDefaultEntryAbs(rootDir);
1266
+ const defaultEntry = toViteFsPath(defaultEntryAbs);
1267
+ let resolvedDevComponentMap = {};
1268
+ if ('string' == typeof devComponentMap) {
1269
+ const name = devComponentMap.trim();
1270
+ resolvedDevComponentMap = name ? buildDevComponentMapFromRecord(rootDir, {
1271
+ [name]: '/'
1272
+ }, defaultEntryAbs, false, fallbackRoot) : {
1273
+ '*': '/'
1274
+ };
1275
+ } else {
1276
+ const input = devComponentMap ?? {};
1277
+ resolvedDevComponentMap = 0 === Object.keys(input).length ? {
1278
+ '*': '/'
1279
+ } : buildDevComponentMapFromRecord(rootDir, input, defaultEntryAbs, false, fallbackRoot);
1280
+ }
1281
+ const audit = (()=>{
1282
+ const missing = [];
1283
+ for (const [componentName, entry] of Object.entries(resolvedDevComponentMap)){
1284
+ const fsPath = getFsPathFromViteEntry(entry);
1285
+ if (fsPath) {
1286
+ const resolved = tryResolveWithExtensions(fsPath);
1287
+ if (!resolved || !node_fs.existsSync(resolved)) missing.push({
1288
+ componentName,
1289
+ filePath: fsPath
1290
+ });
1158
1291
  }
1159
1292
  }
1160
- };
1161
- if (options.build) {
1162
- const merged = mergeConfig({
1163
- build: next.build
1164
- }, {
1165
- build: options.build
1166
- });
1167
- next.build = merged.build;
1168
- }
1293
+ return {
1294
+ defaultEntryAbs,
1295
+ defaultEntryExists: node_fs.existsSync(defaultEntryAbs),
1296
+ componentMapCount: Object.keys(resolvedDevComponentMap).length,
1297
+ missingEntries: missing
1298
+ };
1299
+ })();
1169
1300
  return {
1170
- next,
1171
- outDir: next.build?.outDir || outDir,
1172
- outBase,
1173
- buildTargets: [],
1174
- virtualEntryCode,
1175
- resolvedEntryAbs
1301
+ defaultEntryAbs,
1302
+ defaultEntry,
1303
+ componentMap: resolvedDevComponentMap,
1304
+ audit
1176
1305
  };
1177
1306
  }
1178
1307
  function injectReactImport(code, id) {
@@ -1201,7 +1330,7 @@ function transformAssetUrl(code, id) {
1201
1330
  const ORIGIN = new URL(import.meta.url).origin;
1202
1331
  return path.startsWith('http') ? path : ORIGIN + path;
1203
1332
  } catch (e) {
1204
- console.warn('${PLUGIN_LOG_PREFIX} Failed to resolve static asset URL:', path, e);
1333
+ console.warn('${constants_PLUGIN_LOG_PREFIX} Failed to resolve static asset URL:', path, e);
1205
1334
  return path;
1206
1335
  }
1207
1336
  })()`);
@@ -1239,7 +1368,7 @@ const __dev_to_react_resolveAsset = (path) => {
1239
1368
  const origin = new URL(import.meta.url).origin;
1240
1369
  return path.startsWith('/') ? origin + path : origin + '/' + path;
1241
1370
  } catch (e) {
1242
- console.warn('${PLUGIN_LOG_PREFIX} Failed to resolve CSS asset URL:', path, e);
1371
+ console.warn('${constants_PLUGIN_LOG_PREFIX} Failed to resolve CSS asset URL:', path, e);
1243
1372
  return path;
1244
1373
  }
1245
1374
  };
@@ -1263,7 +1392,7 @@ function createContractVirtualModuleCode(contract) {
1263
1392
  const STATE = (G[DEBUG_KEY] ||= { logged: {} });
1264
1393
  if (!STATE.logged.contract) {
1265
1394
  STATE.logged.contract = true;
1266
- console.groupCollapsed('${PLUGIN_LOG_PREFIX} contract loaded');
1395
+ console.groupCollapsed('${constants_PLUGIN_LOG_PREFIX} contract loaded');
1267
1396
  console.log('Origin:', ORIGIN);
1268
1397
  console.log('Paths:', CONTRACT.paths);
1269
1398
  console.log('Events:', CONTRACT.events);
@@ -1284,14 +1413,14 @@ function createInitVirtualModuleCode() {
1284
1413
  import "/@vite/client";
1285
1414
  import RefreshRuntime from "/@react-refresh";
1286
1415
 
1287
- import CONTRACT, { ${contractExportName} as CONTRACT_NAMED } from "${STABLE_CONTRACT_PATH}";
1416
+ import CONTRACT, { ${contractExportName} as CONTRACT_NAMED } from "${constants_STABLE_CONTRACT_PATH}";
1288
1417
 
1289
1418
  if (typeof window !== 'undefined' && !window.__vite_plugin_react_preamble_installed__) {
1290
1419
  RefreshRuntime.injectIntoGlobalHook(window);
1291
1420
  window.$RefreshReg$ = (type, id) => RefreshRuntime.register(type, id);
1292
1421
  window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
1293
1422
  window.__vite_plugin_react_preamble_installed__ = true;
1294
- console.log('${PLUGIN_LOG_PREFIX} React Refresh preamble installed.');
1423
+ console.log('${constants_PLUGIN_LOG_PREFIX} React Refresh preamble installed.');
1295
1424
  }
1296
1425
 
1297
1426
  {
@@ -1301,7 +1430,7 @@ function createInitVirtualModuleCode() {
1301
1430
  const STATE = (G[DEBUG_KEY] ||= { logged: {} });
1302
1431
  if (!STATE.logged.init) {
1303
1432
  STATE.logged.init = true;
1304
- console.groupCollapsed('${PLUGIN_LOG_PREFIX} init loaded (HMR enabled)');
1433
+ console.groupCollapsed('${constants_PLUGIN_LOG_PREFIX} init loaded (HMR enabled)');
1305
1434
  console.log('Origin:', ORIGIN);
1306
1435
  console.log('This module imports /@vite/client and installs react-refresh preamble.');
1307
1436
  console.log('Important: init must run BEFORE importing react-dom/client in the host.');
@@ -1357,7 +1486,7 @@ function createReactRuntimeVirtualModuleCode() {
1357
1486
  const STATE = (G[DEBUG_KEY] ||= { logged: {} });
1358
1487
  if (!STATE.logged.runtime) {
1359
1488
  STATE.logged.runtime = true;
1360
- console.groupCollapsed('${PLUGIN_LOG_PREFIX} react-runtime loaded');
1489
+ console.groupCollapsed('${constants_PLUGIN_LOG_PREFIX} react-runtime loaded');
1361
1490
  console.log('Origin:', ORIGIN);
1362
1491
  console.log('React.version:', React?.version);
1363
1492
  console.log('ReactDOMClient keys:', Object.keys(ReactDOMClient || {}));
@@ -1404,7 +1533,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1404
1533
  else processedComponentMap[key] = value;
1405
1534
  return {
1406
1535
  paths: {
1407
- contract: STABLE_CONTRACT_PATH,
1536
+ contract: constants_STABLE_CONTRACT_PATH,
1408
1537
  initClient: STABLE_INIT_PATH,
1409
1538
  reactRuntime: STABLE_REACT_RUNTIME_PATH
1410
1539
  },
@@ -1492,7 +1621,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1492
1621
  if (1 === Object.keys(resolvedConfig.componentMap).length && '/' === resolvedConfig.componentMap['*']) {
1493
1622
  const warn = server.config.logger?.warn?.bind(server.config.logger) ?? console.warn;
1494
1623
  warn('');
1495
- warn(`⚠️ ${PLUGIN_LOG_PREFIX} No componentName configured. This works in dev mode but will fail in library build (--mode lib).`);
1624
+ warn(`⚠️ ${constants_PLUGIN_LOG_PREFIX} No componentName configured. This works in dev mode but will fail in library build (--mode lib).`);
1496
1625
  warn(' Please use devToReactPlugin({ ComponentName: "src/ComponentName.tsx" }) or devToReactPlugin({ ComponentName: "/" }) to specify components.');
1497
1626
  warn(' Or use wildcard: devToReactPlugin({ "*": "/" }) or devToReactPlugin("*")');
1498
1627
  warn('');
@@ -1501,6 +1630,8 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1501
1630
  contract,
1502
1631
  stats,
1503
1632
  audit: resolvedConfig.audit,
1633
+ resolvedConfig,
1634
+ configDir,
1504
1635
  open: options.open
1505
1636
  }, debugState);
1506
1637
  },
@@ -1531,7 +1662,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1531
1662
  }).css;
1532
1663
  if (isLibBuild(env)) {
1533
1664
  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(` ${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.`);
1665
+ 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
1666
  const isChild = '1' === process.env.DEV_TO_REACT_LIB_CHILD;
1536
1667
  const buildTargets = resolveBuildTargets({
1537
1668
  componentMap: contract.dev.componentMap,
@@ -1595,7 +1726,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1595
1726
  }
1596
1727
  },
1597
1728
  buildStart () {
1598
- if (libBuildState.enabled && !libBuildState.virtualEntryCode) throw new Error(`${PLUGIN_LOG_PREFIX} lib 构建模式已启用,但虚拟入口模块代码未生成。\n当前 component: "${libBuildState.currentComponent}"\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。`);
1729
+ if (libBuildState.enabled && !libBuildState.virtualEntryCode) throw new Error(`${constants_PLUGIN_LOG_PREFIX} lib 构建模式已启用,但虚拟入口模块代码未生成。\n当前 component: "${libBuildState.currentComponent}"\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。`);
1599
1730
  },
1600
1731
  async closeBundle () {
1601
1732
  if (!libBuildState.enabled) return;
@@ -1637,7 +1768,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1637
1768
  }
1638
1769
  },
1639
1770
  resolveId (source) {
1640
- if (source.includes(STABLE_CONTRACT_PATH)) return `\0virtual:${PLUGIN_NAME}-contract`;
1771
+ if (source.includes(constants_STABLE_CONTRACT_PATH)) return `\0virtual:${PLUGIN_NAME}-contract`;
1641
1772
  if (source.includes(STABLE_INIT_PATH)) return `\0virtual:${PLUGIN_NAME}-init`;
1642
1773
  if (source.includes(STABLE_REACT_RUNTIME_PATH)) return `\0virtual:${PLUGIN_NAME}-react-runtime`;
1643
1774
  if (source.includes(`virtual:${PLUGIN_NAME}-lib-entry:`)) {
@@ -1655,7 +1786,7 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1655
1786
  const componentName = id.replace(`\0virtual:${PLUGIN_NAME}-lib-entry:`, '');
1656
1787
  if (libBuildState.enabled) {
1657
1788
  if (libBuildState.virtualEntryCode) return libBuildState.virtualEntryCode;
1658
- throw new Error(`${PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 的代码未生成。\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。\n当前 libBuildState: ${JSON.stringify({
1789
+ throw new Error(`${constants_PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 的代码未生成。\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。\n当前 libBuildState: ${JSON.stringify({
1659
1790
  enabled: libBuildState.enabled,
1660
1791
  currentComponent: libBuildState.currentComponent,
1661
1792
  hasCode: !!libBuildState.virtualEntryCode
@@ -1676,11 +1807,11 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1676
1807
  libBuildState.currentComponent = componentName;
1677
1808
  return code;
1678
1809
  } catch (error) {
1679
- throw new Error(`${PLUGIN_LOG_PREFIX} 无法生成虚拟入口模块代码:${error instanceof Error ? error.message : String(error)}`);
1810
+ throw new Error(`${constants_PLUGIN_LOG_PREFIX} 无法生成虚拟入口模块代码:${error instanceof Error ? error.message : String(error)}`);
1680
1811
  }
1681
1812
  }
1682
1813
  }
1683
- throw new Error(`${PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 在插件配置完成之前被请求,且无法延迟生成代码。\n这可能是 Vite/Rollup 的内部时序问题。\n请尝试重新运行构建命令,如果问题持续,请检查 vite.config.ts 中的配置。`);
1814
+ throw new Error(`${constants_PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 在插件配置完成之前被请求,且无法延迟生成代码。\n这可能是 Vite/Rollup 的内部时序问题。\n请尝试重新运行构建命令,如果问题持续,请检查 vite.config.ts 中的配置。`);
1684
1815
  }
1685
1816
  return null;
1686
1817
  },
@@ -1720,4 +1851,4 @@ const devToReactPlugin = (devComponentMap = {}, options = {})=>{
1720
1851
  ];
1721
1852
  };
1722
1853
  const viteHostReactBridgePlugin = devToReactPlugin;
1723
- export { EVENT_FULL_RELOAD, EVENT_HMR_UPDATE, PLUGIN_LOG_PREFIX, PLUGIN_NAME, STABLE_BASE_PATH, STABLE_CONTRACT_PATH, STABLE_DEBUG_HTML_PATH, STABLE_DEBUG_JSON_PATH, STABLE_INIT_PATH, STABLE_REACT_RUNTIME_PATH, devToReactPlugin, viteHostReactBridgePlugin };
1854
+ 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":"AAKA,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"}
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,12 @@
1
+ interface CreateLoaderUmdWrapperOptions {
2
+ componentName: string;
3
+ origin: string;
4
+ contractEndpoint?: string;
5
+ }
6
+ /**
7
+ * Generate a lightweight UMD wrapper that uses @dev-to/react-loader to load the component.
8
+ * This wrapper depends on react-loader UMD build being available.
9
+ */
10
+ export declare function createLoaderUmdWrapper(options: CreateLoaderUmdWrapperOptions): string;
11
+ export {};
12
+ //# 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;CAC1B;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,CA2GrF"}
@@ -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.1.1",
3
+ "version": "0.2.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.0"
34
+ "@dev-to/react-shared": "0.1.1"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "rslib build",