@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.
@@ -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":"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 PLUGIN_LOG_PREFIX = `[${PLUGIN_NAME}]`;
11
+ const constants_PLUGIN_LOG_PREFIX = `[${PLUGIN_NAME}]`;
12
12
  const STABLE_BASE_PATH = DEV_TO_REACT_BASE_PATH;
13
- const STABLE_CONTRACT_PATH = DEV_TO_REACT_CONTRACT_PATH;
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(`// ${PLUGIN_LOG_PREFIX} 无法读取配置文件: ${e}`);
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/&lt;name&gt;/&lt;name&gt;.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">${STABLE_CONTRACT_PATH}</span>
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(`${PLUGIN_LOG_PREFIX} 使用 / 作为入口时,必须提供 defaultEntryAbs 参数`);
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(`${PLUGIN_LOG_PREFIX} Debug panel:`);
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 === STABLE_CONTRACT_PATH) {
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 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;
1291
+ function toViteFsPath(filePath) {
1292
+ const normalized = filePath.replace(/\\/g, '/');
1293
+ return normalized.startsWith('/') ? `/@fs${normalized}` : `/@fs/${normalized}`;
1036
1294
  }
1037
- function getLibVirtualEntryPath(componentName) {
1038
- return `virtual:${PLUGIN_NAME}-lib-entry:${componentName}`;
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 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);
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
- 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;
1322
+ if (entry.startsWith('http://') || entry.startsWith('https://') || entry.startsWith('/')) {
1323
+ out[componentName] = entry;
1324
+ continue;
1078
1325
  }
1079
- return actualConfiguredNames;
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
- if (requestedList.length > 0) return requestedList;
1082
- const fallbackName = node_path.parse(defaultEntryAbs || 'index').name || 'index';
1083
- return [
1084
- fallbackName
1085
- ];
1335
+ return out;
1086
1336
  }
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
- }
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
- if (options.build) {
1162
- const merged = mergeConfig({
1163
- build: next.build
1164
- }, {
1165
- build: options.build
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
- next,
1171
- outDir: next.build?.outDir || outDir,
1172
- outBase,
1173
- buildTargets: [],
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('${PLUGIN_LOG_PREFIX} Failed to resolve static asset URL:', path, e);
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('${PLUGIN_LOG_PREFIX} Failed to resolve CSS asset URL:', path, e);
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('${PLUGIN_LOG_PREFIX} contract loaded');
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 "${STABLE_CONTRACT_PATH}";
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('${PLUGIN_LOG_PREFIX} React Refresh preamble installed.');
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('${PLUGIN_LOG_PREFIX} init loaded (HMR enabled)');
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('${PLUGIN_LOG_PREFIX} react-runtime loaded');
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: STABLE_CONTRACT_PATH,
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(`⚠️ ${PLUGIN_LOG_PREFIX} No componentName configured. This works in dev mode but will fail in library build (--mode lib).`);
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(` ${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.`);
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(`${PLUGIN_LOG_PREFIX} lib 构建模式已启用,但虚拟入口模块代码未生成。\n当前 component: "${libBuildState.currentComponent}"\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。`);
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(STABLE_CONTRACT_PATH)) return `\0virtual:${PLUGIN_NAME}-contract`;
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(`${PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 的代码未生成。\n这可能是插件配置问题,请检查 vite.config.ts 中的配置。\n当前 libBuildState: ${JSON.stringify({
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(`${PLUGIN_LOG_PREFIX} 无法生成虚拟入口模块代码:${error instanceof Error ? error.message : String(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(`${PLUGIN_LOG_PREFIX} 虚拟入口模块 "${id}" (componentName: "${componentName}") 在插件配置完成之前被请求,且无法延迟生成代码。\n这可能是 Vite/Rollup 的内部时序问题。\n请尝试重新运行构建命令,如果问题持续,请检查 vite.config.ts 中的配置。`);
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, 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 };
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":"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,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"}
@@ -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.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.0"
34
+ "@dev-to/react-shared": "0.1.2"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "rslib build",