mcpField
U
umbraco-example-generator

umbraco-example-generator

github.com/umbraco开发27375 次安装6 周前更新
代码仓库

关于 umbraco-example-generator

Umbraco Example Generator 用于生成可测试的 Umbraco backoffice 扩展示例,并借助 Umbraco 源码中的开发环境运行。适合扩展开发者,可从外部目录加载示例,并使用模拟 API 和热重载。

主要功能

  1. 01生成 backoffice 扩展示例
  2. 02从源码示例目录或外部路径加载
  3. 03使用模拟 API 运行扩展
  4. 04支持开发时热重载
  5. 05可为示例添加单元及 E2E 测试

适用场景

  1. 01制作扩展示范和可测试样例
  2. 02快速开发并查看扩展改动
  3. 03无 .NET 后端时测试扩展

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/umbraco/umbraco-cms-backoffice-skills --skill umbraco-example-generator

安装后在 Claude Code 中输入/umbraco-example-generator确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/workspace-feature-toggle/
feature-toggle-action.ts
feature-toggle-context.test.ts
feature-toggle-context.ts
feature-toggle-footer.element.ts
feature-toggle-view.element.ts
index.ts
package-lock.json
package.json
README.md
tsconfig.json
web-test-runner.config.mjs
examples/workspace-feature-toggle/tests/
feature-toggle.spec.ts
playwright.config.ts
warmup.setup.ts

详细介绍

Umbraco 示例生成器

生成完整、可测试的 Umbraco 后台扩展,并使用 Umbraco 源码的开发基础设施运行它们。

适用场景

  • 创建演示扩展
  • 构建可测试的扩展示例
  • 使用热重载快速开发
  • 在没有 .NET 后端的情况下测试扩展

相关技能

  • umbraco-unit-testing - 为示例添加单元测试
  • umbraco-mocked-backoffice - E2E 测试模式
  • umbraco-backoffice - 扩展类型蓝图

快速开始

1. 克隆 Umbraco 源码(一次性设置)
git clone https://github.com/umbraco/Umbraco-CMS
cd Umbraco-CMS/src/Umbraco.Web.UI.Client
npm install
2. 创建扩展文件夹
my-extension/
├── index.ts           # REQUIRED - exports manifests
└── my-element.ts      # Your element(s)
3. 创建 index.ts(导出清单)
import './my-element.js';

export const manifests = [
  {
    type: 'dashboard',
    alias: 'My.Dashboard',
    name: 'My Dashboard',
    element: 'my-element',
    meta: { label: 'My Dashboard', pathname: 'my-dashboard' },
    conditions: [{ alias: 'Umb.Condition.SectionAlias', match: 'Umb.Section.Content' }]
  }
];
4. 创建元素
// my-element.ts
import { LitElement, html, customElement } from '@umbraco-cms/backoffice/external/lit';

@customElement('my-element')
export class MyElement extends LitElement {
  render() {
    return html`<uui-box headline="Hello">It works!</uui-box>`;
  }
}
5. 运行
cd Umbraco-CMS/src/Umbraco.Web.UI.Client
VITE_EXAMPLE_PATH=/full/path/to/my-extension VITE_UMBRACO_USE_MSW=on npm run dev

打开 http://localhost:5173,你的扩展会显示在 Content 区域中。


工作原理

Umbraco 源码(Umbraco-CMS/src/Umbraco.Web.UI.Client)提供了两种加载扩展的方式:

1. 内部示例(npm run example)

将示例放在 Umbraco 源码中的 examples/ 文件夹内。

cd Umbraco-CMS/src/Umbraco.Web.UI.Client
npm run example
# Select from list of examples

工作原理:设置 VITE_EXAMPLE_PATH,并导入 ./examples/{name}/index.ts

2. 外部扩展(绝对路径 VITE_EXAMPLE_PATH)

来自文件系统任意位置的扩展,非常适合开发包。

cd Umbraco-CMS/src/Umbraco.Web.UI.Client
VITE_EXAMPLE_PATH=/path/to/your/extension VITE_UMBRACO_USE_MSW=on npm run dev

工作原理:

  1. VITE_UMBRACO_USE_MSW=on 会模拟 Umbraco 核心 API
  2. 通过 Vite 的 /@fs/ 前缀提供绝对路径 VITE_EXAMPLE_PATH
  3. index.ts 会导入 <VITE_EXAMPLE_PATH>/index.ts,并使用 umbExtensionsRegistry 注册其导出内容
  4. 共享包解析器会将 @umbraco-cms/backoffice、lit 和 @umbraco-ui/uui 映射到客户端自身的副本(避免重复加载 Lit/UUI)

绝对路径支持(步骤 2 和 4)需要使用构建时启用了外部示例支持的 v18 客户端。运行此仓库的模拟 E2E 套件时,Playwright 测试框架会自动将该支持注入 UMBRACO_CLIENT_PATH 中的客户端,并在之后还原——请参见 umbraco-mocked-backoffice 技能。

扩展加载(index.ts)
// From Umbraco-CMS/src/Umbraco.Web.UI.Client/index.ts
if (import.meta.env.VITE_EXAMPLE_PATH) {
  const examplePath = import.meta.env.VITE_EXAMPLE_PATH;
  // Absolute paths (external extensions) are served via Vite's /@fs/ prefix; relative paths load from ./
  const importPath = examplePath.startsWith('/') ? '/@fs' + examplePath : './' + examplePath;
  const js = await import(importPath + '/index.ts');
  if (js) {
    Object.keys(js).forEach((key) => {
      const value = js[key];
      if (Array.isArray(value)) {
        umbExtensionsRegistry.registerMany(value);
      } else if (typeof value === 'object') {
        umbExtensionsRegistry.register(value);
      }
    });
  }
}

要点:你的 index.ts 必须导出清单(数组或对象),这些清单会自动注册。


设置

先决条件

克隆并设置 Umbraco 源码:

git clone https://github.com/umbraco/Umbraco-CMS
cd Umbraco-CMS/src/Umbraco.Web.UI.Client
npm install
扩展结构

你的扩展需要以下最简结构:

my-extension/
├── index.ts              # Exports manifests array (REQUIRED)
├── my-element.ts         # Your element(s)
├── my-context.ts         # Context (if needed)
├── package.json          # Optional - for IDE support and tests
├── tsconfig.json         # Optional - for IDE support
└── README.md             # Documentation
必需:index.ts

你的 index.ts 必须导出将要注册的清单:

import './my-dashboard.element.js';

export const manifests = [
  {
    type: 'dashboard',
    alias: 'My.Dashboard',
    name: 'My Dashboard',
    element: 'my-dashboard',
    weight: 100,
    meta: {
      label: 'My Dashboard',
      pathname: 'my-dashboard'
    },
    conditions: [
      {
        alias: 'Umb.Condition.SectionAlias',
        match: 'Umb.Section.Content'
      }
    ]
  }
];
可选:package.json(用于 IDE 支持)
{
  "name": "my-extension",
  "type": "module",
  "devDependencies": {
    "@umbraco-cms/backoffice": "^18.0.0",
    "typescript": "~5.8.0"
  }
}

重要:@umbraco-cms/backoffice 依赖仅用于 IDE 的 TypeScript 支持。运行时,导入内容会从 Umbraco 主项目中解析。


运行扩展

启动模拟后台
cd /path/to/Umbraco-CMS/src/Umbraco.Web.UI.Client
VITE_EXAMPLE_PATH=/absolute/path/to/my-extension VITE_UMBRACO_USE_MSW=on npm run dev
在浏览器中打开

访问 http://localhost:5173,你的扩展会自动加载。

热重载

更改扩展文件会触发热重载,无需重启。


模式

基本元素
// my-dashboard.element.ts
import { LitElement, html, css, customElement } from '@umbraco-cms/backoffice/external/lit';

@customElement('my-dashboard')
export class MyDashboardElement extends LitElement {
  static override styles = css`
    :host {
      display: block;
      padding: var(--uui-size-layout-1);
    }
  `;

  override render() {
    return html`
      <uui-box headline="My Extension">
        <p>Running in the mocked backoffice!</p>
      </uui-box>
    `;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'my-dashboard': MyDashboardElement;
  }
}
带上下文的元素
import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { EXAMPLE_MY_CONTEXT } from './my-context.js';

@customElement('example-my-feature-view')
export class ExampleMyFeatureViewElement extends UmbLitElement {
  @state()
  private _value?: string;

  constructor() {
    super();
    this.consumeContext(EXAMPLE_MY_CONTEXT, (context) => {
      this.observe(context.value, (value) => {
        this._value = value;
      });
    });
  }

  override render() {
    return html`
      <uui-box headline="My Feature Example">
        <p>Current value: ${this._value ?? 'Loading...'}</p>
      </uui-box>
    `;
  }
}

export default ExampleMyFeatureViewElement;

declare global {
  interface HTMLElementTagNameMap {
    'example-my-feature-view': ExampleMyFeatureViewElement;
  }
}
上下文
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';

export class ExampleMyContext extends UmbContextBase {
  #value = new UmbStringState('initial');
  readonly value = this.#value.asObservable();

  constructor(host: UmbControllerHost) {
    super(host, EXAMPLE_MY_CONTEXT);
  }

  setValue(value: string) {
    this.#value.setValue(value);
  }

  getValue() {
    return this.#value.getValue();
  }

  public override destroy(): void {
    this.#value.destroy();
    super.destroy();
  }
}

export const EXAMPLE_MY_CONTEXT = new UmbContextToken<ExampleMyContext>(
  'ExampleMyContext'
);

export { ExampleMyContext as api };

添加测试

单元测试

使用 @open-wc/testing 添加单元测试。完整设置请参见 umbraco-unit-testing 技能。

npm install --save-dev @open-wc/testing @web/test-runner @web/test-runner-playwright
E2E 测试(Playwright)

添加针对模拟后台运行的 E2E 测试。测试模式请参见 umbraco-mocked-backoffice 技能。

npm install --save-dev @playwright/test
npx playwright install chromium

示例

参考示例

位置:./examples/workspace-feature-toggle/

一个完整的独立示例,展示了:

  • 使用 UmbArrayState 的工作区上下文
  • 使用上下文的工作区视图
  • 执行上下文方法的工作区操作
  • 显示摘要的工作区页脚应用
  • 38 个单元测试 + 13 个 E2E 测试
cd examples/workspace-feature-toggle
npm install
npm test              # Unit tests
npm run test:e2e      # E2E tests (requires mocked backoffice running)
Umbraco 官方示例

位置:Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/

27 个涵盖所有扩展类型的官方示例。运行任意示例:

cd Umbraco-CMS/src/Umbraco.Web.UI.Client
npm run example
# Select from list

命名约定

项目约定示例
目录使用 kebab-case 描述功能workspace-context-counter
别名前缀example.example.workspaceView.counter
元素前缀example-example-counter-view
上下文令牌EXAMPLE_ + SCREAMING_CASEEXAMPLE_COUNTER_CONTEXT

故障排除

扩展未显示
  1. 检查 index.ts 是否导出了 manifests 数组
  2. 确认 VITE_EXAMPLE_PATH 中的路径是绝对路径
  3. 检查浏览器控制台中是否有 📦 Loading external example from: 消息
  4. 确保条件与当前查看的部分匹配
导入错误

导入应使用 @umbraco-cms/backoffice/*。Vite 插件会从主项目中解析这些内容。

“CustomElementRegistry” 已定义

当前使用的是扩展的 node_modules,而不是主项目的。external-example-resolver 插件应能处理此问题,但请确保:

  • 使用 VITE_EXAMPLE_PATH=<abs> VITE_UMBRACO_USE_MSW=on npm run dev
  • 导入使用 @umbraco-cms/backoffice/*,而不是指向 node_modules 的相对路径
更改未热重载

确保文件位于 VITE_EXAMPLE_PATH 指定的路径下。系统只会监视该目录树中的文件。

评论

支持 Markdown