@antdv-next/docs-plugins 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,7 +68,8 @@ provideDemoContext({
68
68
  '@antdv-next/icons': () => import('@antdv-next/icons'),
69
69
  },
70
70
  openPlayground: code => window.open(loadPlaygroundUrl(code), '_blank'),
71
- // openStackBlitz / demoPageUrl / iframeRenderer / preferredCodeType 可选
71
+ // openStackBlitz / demoPageUrl / editUrl / iframeRenderer / preferredCodeType 可选
72
+ // copyCode: false 可隐藏操作栏复制按钮(默认显示)
72
73
  })
73
74
  ```
74
75
 
@@ -79,7 +80,7 @@ provideDemoContext({
79
80
  ```
80
81
 
81
82
  - 展开代码面板即得可编辑编辑器(Sandpack),编辑内容经 `@vue/compiler-sfc` + `sucrase` 在浏览器端实时编译并替换预览
82
- - 未提供的能力(如 StackBlitz)对应按钮自动隐藏
83
+ - 操作栏按钮全部由站点能力驱动:未提供的能力(如 StackBlitz)对应按钮自动隐藏;复制按钮默认显示,`copyCode: false` 可关闭;标题区编辑图标由 `editUrl` 提供时显示
83
84
 
84
85
  建议在 vite 中对这些依赖做 `resolve.dedupe`,保证与站点共享同一实例:
85
86
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@antdv-next/docs-plugins",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.3",
5
5
  "private": false,
6
6
  "description": "Vite / markdown-it plugins and demo components for docs-base documentation sites",
7
7
  "license": "MIT",
@@ -73,6 +73,7 @@
73
73
  "@vue/tsconfig": "^0.8.1",
74
74
  "@vueuse/core": "^14.4.0",
75
75
  "antdv-style": "^1.0.0-rc.1",
76
+ "bumpp": "^11.1.0",
76
77
  "eslint": "^9.39.5",
77
78
  "postcss": "^8.5.25",
78
79
  "sandpack-vue3": "^3.1.12",
@@ -89,6 +90,7 @@
89
90
  "dev": "tsdown --watch",
90
91
  "lint": "eslint .",
91
92
  "lint:fix": "eslint --fix .",
92
- "typecheck": "tsc --noEmit"
93
+ "typecheck": "tsc --noEmit",
94
+ "bump": "bumpp --tag \"@antdv-next/docs-plugins@\""
93
95
  }
94
96
  }
@@ -20,7 +20,10 @@ async function ensureDependencies(extraModules: Record<string, () => Promise<any
20
20
  Object.entries(extraModules).map(async ([name, loader]) => {
21
21
  try {
22
22
  const mod = await loader()
23
- resolved[name] = (mod as any).default || mod
23
+ // 保存模块命名空间:命名导入从空间上取(default 导入经 transformCode
24
+ // `mod.default || mod` 兜底)。取 mod.default 会丢掉命名导出(如 icons 的
25
+ // 836 个图标,其 default 只是单个图标组件)。
26
+ resolved[name] = mod
24
27
  }
25
28
  catch {
26
29
  // 依赖不可用时跳过,保持 modulesMap 中无该条目
@@ -38,6 +41,42 @@ function buildModulesMap(extraModules: Record<string, () => Promise<any>>) {
38
41
  }
39
42
  }
40
43
 
44
+ /**
45
+ * 从模块命名空间收集组件,建立 名称/kebab/camel 三种索引。
46
+ * 模板里 `<a-avatar>` / `<ax-bubble>` 经编译为 _resolveComponent("a-avatar"),
47
+ * 运行时解析需要站点全局注册;这里在编译期直接映射到模块里的 AAvatar/AxBubble,
48
+ * 使在线编辑不依赖站点全量注册(保留按需引入与摇树)。
49
+ */
50
+ function buildComponentsMap(modulesMap: Record<string, any>): Record<string, unknown> {
51
+ const comps: Record<string, unknown> = {}
52
+ for (const mod of Object.values(modulesMap)) {
53
+ if (!mod || typeof mod !== 'object')
54
+ continue
55
+ for (const value of Object.values(mod)) {
56
+ if (!value || typeof value !== 'object' || !('name' in value))
57
+ continue
58
+ const name = value.name
59
+ if (typeof name !== 'string' || !name)
60
+ continue
61
+ comps[name] = value
62
+ // 与 vue 的 hyphenate 一致:AAvatar -> a-avatar,AxBubble -> ax-bubble
63
+ const kebab = name.replace(/\B([A-Z])/g, '-$1').toLowerCase()
64
+ comps[kebab] = value
65
+ comps[kebab.replace(/-(\w)/g, (_, c: string) => c.toUpperCase())] = value
66
+ }
67
+ }
68
+ return comps
69
+ }
70
+
71
+ /** 把 _resolveComponent("name") 替换为组件映射查找,未命中时回退运行时解析 */
72
+ function injectComponentResolution(code: string): string {
73
+ return code.replace(
74
+ /_resolveComponent\(\s*(['"])([^'"]+)\1(?:,([^)]*))?\)/g,
75
+ (_, quote: string, name: string, rest?: string) =>
76
+ `(__componentsMap[${quote}${name}${quote}] || _resolveComponent(${quote}${name}${quote}${rest ? `,${rest}` : ''}))`,
77
+ )
78
+ }
79
+
41
80
  function transformCode(code: string): string {
42
81
  let result = code
43
82
 
@@ -179,11 +218,15 @@ export async function compileSfcSource(
179
218
  // Transform imports / exports to work with new Function
180
219
  jsCode = transformCode(jsCode)
181
220
 
221
+ // 编译期组件解析:模板中的 <a-*>/<ax-*> 映射到模块内的组件,不依赖站点全局注册
222
+ const componentsMap = buildComponentsMap(modulesMap)
223
+ jsCode = injectComponentResolution(jsCode)
224
+
182
225
  // Evaluate the compiled code
183
226
  const __exports__: Record<string, any> = {}
184
227
  // eslint-disable-next-line no-new-func
185
- const fn = new Function('__modules__', '__exports__', jsCode)
186
- fn(modulesMap, __exports__)
228
+ const fn = new Function('__modules__', '__componentsMap', '__exports__', jsCode)
229
+ fn(modulesMap, componentsMap, __exports__)
187
230
 
188
231
  // For options API + separate template, attach the render function
189
232
  if (!descriptor.scriptSetup && descriptor.script && descriptor.template) {
@@ -32,6 +32,10 @@ export interface DemoContext {
32
32
  openStackBlitz?: (options: { title: string, code: string }) => void
33
33
  /** demo 独立页地址(不提供则隐藏按钮) */
34
34
  demoPageUrl?: (id: string) => string
35
+ /** 源码编辑地址(不提供则隐藏标题区编辑图标) */
36
+ editUrl?: (id: string) => string
37
+ /** 操作栏是否显示复制按钮(默认 true) */
38
+ copyCode?: boolean
35
39
  /** iframe 模式渲染器(不提供则忽略 iframe 属性) */
36
40
  iframeRenderer?: (id: string, height?: string) => any
37
41
  /** 记住 TS/JS 页签偏好(不提供则仅组件内记忆) */
@@ -41,7 +45,9 @@ export interface DemoContext {
41
45
  }
42
46
  }
43
47
 
44
- export const demoContextKey: InjectionKey<DemoContext> = Symbol('demo-context')
48
+ // Symbol.for 全局注册:站点(或依赖预打包)可能产生多份 context 模块实例,
49
+ // 普通 Symbol 会让 provide/inject 键不匹配导致上下文丢失
50
+ export const demoContextKey: InjectionKey<DemoContext> = Symbol.for('antdv-next.docs-plugins.demo-context')
45
51
 
46
52
  export function provideDemoContext(context: DemoContext) {
47
53
  provide(demoContextKey, context)