@standhigher/react-email-editor 0.4.3 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -1007,6 +1007,38 @@
1007
1007
 
1008
1008
  /* ---------- 画布 ---------- */
1009
1009
 
1010
+ .ree-stage-content,
1011
+ .ree-preview-buffer {
1012
+ position: relative;
1013
+ display: flex;
1014
+ flex: 1;
1015
+ min-width: 0;
1016
+ min-height: 0;
1017
+ isolation: isolate;
1018
+ }
1019
+
1020
+ .ree-stage-view.is-buffered,
1021
+ .ree-preview-stage.is-buffered {
1022
+ position: absolute;
1023
+ inset: 0;
1024
+ visibility: hidden;
1025
+ pointer-events: none;
1026
+ }
1027
+
1028
+ .ree-preview-pending {
1029
+ position: absolute;
1030
+ top: 12px;
1031
+ left: 50%;
1032
+ transform: translateX(-50%);
1033
+ padding: 6px 12px;
1034
+ border: 1px solid var(--ree-border);
1035
+ border-radius: 6px;
1036
+ background: var(--ree-panel);
1037
+ color: var(--ree-muted);
1038
+ font-size: 12px;
1039
+ pointer-events: none;
1040
+ }
1041
+
1010
1042
  .ree-stage-view {
1011
1043
  display: flex;
1012
1044
  flex: 1;
@@ -1090,6 +1122,18 @@
1090
1122
  box-shadow: var(--ree-shadow-md);
1091
1123
  }
1092
1124
 
1125
+ .ree-preview-error {
1126
+ position: absolute;
1127
+ top: 12px;
1128
+ left: 50%;
1129
+ transform: translateX(-50%);
1130
+ z-index: 1;
1131
+ max-width: calc(100% - 24px);
1132
+ border: 1px solid var(--ree-border);
1133
+ border-radius: 6px;
1134
+ background: var(--ree-panel);
1135
+ }
1136
+
1093
1137
  /* ---------- 区块 ---------- */
1094
1138
 
1095
1139
  .ree-block {
@@ -262,4 +262,8 @@ export const fragment = blockToHtml(block, undefined, "email", {
262
262
  });
263
263
  ```
264
264
 
265
- Complete documents shallow-merge variables → templateData → block.defaultData, with later data winning. Single-block rendering does not merge variables automatically; this example merges explicitly and uses the singular resolveHtmlTemplate option. Your application chooses the actual sending engine; preview only supports a subset of Go templates.
265
+ Complete documents shallow-merge variables → templateData → block.defaultData, with later data winning. Preview and sending apply listed-variable dot normalization while preserving the stored template source. Single-block rendering does not merge variables automatically; this example merges explicitly and uses the singular resolveHtmlTemplate option. Your application chooses the actual sending engine. The default preview supports basic syntax; inject templateEngine for complete Go semantics.
266
+
267
+ ## Custom template engine and standalone preview
268
+
269
+ See [Template preview and custom engines](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.md) for supported syntax, adapter behavior and integration examples.
@@ -262,4 +262,8 @@ export const fragment = blockToHtml(block, undefined, "email", {
262
262
  });
263
263
  ```
264
264
 
265
- 完整文档自动按顶层浅合并 `variables → templateData → block.defaultData`,后者优先。单区块不会自动合并变量;示例显式合并,并使用单数 `resolveHtmlTemplate`。真实发送端的模板引擎由应用决定,预览仅支持部分 Go template 语法。
265
+ 完整文档自动按顶层浅合并 `variables → templateData → block.defaultData`,后者优先。预览和发送会先按变量清单补点,存储中的模板原文保持不变;单区块不会自动合并变量,示例显式合并,并使用单数 `resolveHtmlTemplate`。真实发送端的模板引擎由应用决定;默认预览仅支持基础语法,完整 Go 语义通过 `templateEngine` 注入。
266
+
267
+ ## 自定义模板引擎与独立预览
268
+
269
+ 完整说明与接入示例见 [模板预览与自定义引擎](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.zh-CN.md)。
package/docs/api.md CHANGED
@@ -68,6 +68,7 @@ Requires an instance from `useEmailEditor()`. Returns cached `{ document }`, a d
68
68
  | `plugins` | `BlockPlugin[]` | `[]`; plugins created by `defineBlock()`. |
69
69
  | `builtins` | `false \| string[]` | All by default; false hides all builtins; arrays filter and order them. Hidden blocks still render existing nodes. Custom types are appended. |
70
70
  | `pickImage` | `(context: ImagePickerContext) => Promise<string \| null>` | Resolve a URL; null cancels; throwing shows retry. Context contains `value/label/signal`. |
71
+ | `templateEngine` | `TemplateEngine` | Shared synchronous template adapter; defaults to the basic engine. |
71
72
 
72
73
  Declare plugins at module scope, or use `useMemo` when they depend on application state. Stable option references reuse configuration; inline arrays do not recreate the editor or reset its document. The returned object contains:
73
74
 
@@ -76,6 +77,7 @@ Declare plugins at module scope, or use `useMemo` when they depend on applicatio
76
77
  | `definitions` | Readonly list of all registered plugins, including hidden builtins. |
77
78
  | `library` | Readonly list of insertable blocks in display order. |
78
79
  | `pickImage` | Registered resource picker. |
80
+ | `templateEngine` | Normalized engine shared by canvas, preview and resolved export. |
79
81
  | `createBlock(type)` | Registered type string → detached default `EmailBlockNode`; unknown types throw. |
80
82
  | `toHtml(document, options?)` | v4 document and `DocumentHtmlOptions` → complete HTML using this registry. |
81
83
  | `validate(document)` | Document → `ValidationIssue[]`, without rendering HTML. |
@@ -100,7 +102,7 @@ export const coupon = defineBlock({
100
102
  | `type` / `title` | Required nonempty unique type and library title. |
101
103
  | `renderEmail(ctx)` | Required; returns inner HTML or `EmailMarkup`; ctx contains `block/controls/style/body/mode/email`. |
102
104
  | `controls` | Map of names to EmailControls declarations, default `{}`; values and defaults are inferred. |
103
- | `icon` / `description` | Optional ReactNode/string icon and description. |
105
+ | `icon` / `description` | Optional ReactNode/HTML string icon and description. The icon defaults to the first character of the trimmed `title`, preserving case and displaying it as text. |
104
106
  | `override` | Must be true to replace a builtin with the same type. |
105
107
  | `containerStyles` | Spacing and appearance enabled by default; false disables all; `{ spacing?: boolean, appearance?: boolean }` controls each group. |
106
108
  | `styleControls` | Additional styles; matching IDs replace default container controls. |
@@ -166,7 +168,9 @@ Use these components only within a plugin Canvas or Inspector.
166
168
  | `getBlockDefinition(type, definitions?)` | Type and optional registry, default builtins | Matching runtime definition or undefined. Do not mutate frozen configuration definitions. |
167
169
  | `generateId(prefix = "block")` | String ID prefix | String with a random suffix; applications must still ensure IDs are unique within a document. |
168
170
 
169
- EmailDocument contains version/body/blocks/subject?; EmailBlockNode contains id/type/controls/style?/defaultData?. Only DOCUMENT_VERSION = 4 is supported; older documents are not migrated. JSON stores no components or render functions; register matching plugins again when loading it.
171
+ EmailDocument contains version/body/blocks/subject?; EmailBlockNode contains id/type/controls/style?/defaultData?. `version: number` is only a format identifier: new documents use `DOCUMENT_VERSION = 4`, while loading and exporting preserve the supplied value without version-based rejection or automatic migration of older structures. JSON stores no components or render functions; register matching plugins again when loading it.
172
+
173
+ Initialization, `setDocument()`, `config.validate()` and HTML export require a document object, a `blocks` array, non-empty string `id/type` values and an object `controls` for every block. Invalid structure throws a `TypeError` with the field path. A supplied `subject` must be a string; a supplied non-null `body` must be an object, with omitted settings still filled by `resolveEmailBody()`. Nodes with only legacy `fields` and no `controls` are rejected; control values continue to be normalized and validated by the plugin schema.
170
174
 
171
175
  Canvas, preview and email HTML share body settings:
172
176
 
@@ -175,7 +179,7 @@ Canvas, preview and email HTML share body settings:
175
179
  | textColor | #000000; default text color. |
176
180
  | backgroundColor | #ffffff; centered content background; outside remains white. |
177
181
  | contentWidth | 600; maximum content width in px. |
178
- | fontFamily | Arial, Helvetica, sans-serif. |
182
+ | fontFamily | `""`; the inspector shows `Default`. Omitted or empty values render with Arial, Helvetica, sans-serif. |
179
183
  | fontWeight | normal; also supports bold. |
180
184
  | linkColor / linkDecoration | #005bd3 / underline. |
181
185
  | contentPadding | 0px; CSS padding inside the border. |
@@ -195,6 +199,7 @@ The required document must use v4. Definitions default to DEFAULT_BLOCKS; for cu
195
199
  | resolveHtmlTemplates | false preserves templates; true resolves preview data. |
196
200
  | variables | [] of exact inserted tokens and preview values. |
197
201
  | templateData | Optional TemplateData for global objects and arrays. |
202
+ | templateEngine | Optional TemplateEngine; defaults to the basic engine. |
198
203
 
199
204
  Data is shallow-merged at the top level: node defaultData > templateData > variables. Default export only adds missing dots to tokens exactly matching variables[].name, preserving spaces: `{{ store_name }}` → `{{ .store_name }}`. Unlisted Go templates stay unchanged. Empty variable descriptions are hidden; value is only preview data.
200
205
 
@@ -208,12 +213,19 @@ Data is shallow-merged at the top level: node defaultData > templateData > varia
208
213
  | `buildRenderContext(node, mode, body = DEFAULT_EMAIL_BODY, definition = builtin lookup)` | Required mode; explicitly pass a custom definition | Detached normalized BlockRenderContext without rendering. |
209
214
  | `variablesToTemplateData(variables)` | Required Variable[] | TemplateData keyed by token paths with value as preview data; ignores absent values. |
210
215
  | `mergeTemplateData(variables, defaultData?)` | Variables and optional object | Shallow merge, defaultData wins; undefined if both contain no data. |
211
- | `renderTemplatePreview(html, defaultData?)` | HTML string and optional data | Supports a subset of Go paths, range, if and with; preserves input when data is omitted. |
216
+ | `renderTemplatePreview(template, data?, options?)` | Template, data and TemplateEngineOptions | Synchronous preview with the selected engine. The basic engine preserves the entire input when it contains unsupported syntax. |
217
+ | `validateTemplate(template, options?)` | Template and TemplateEngineOptions | TemplateValidationIssue[]; undefined when validation is unavailable. Never invoked automatically. |
218
+
219
+ Both DocumentHtmlOptions and BlockRenderOptions accept templateEngine?: TemplateEngine. BlockRenderOptions contains resolveHtmlTemplate?: boolean (singular), templateData?: TemplateData, variables?: Variable[], body?: EmailBodySettings. Body defaults to DEFAULT_EMAIL_BODY; templates are preserved by default. Single-block preview uses templateData ?? node.defaultData and does not merge variables automatically; call mergeTemplateData explicitly when needed. The fourth argument must be an object; boolean or null throws TypeError.
212
220
 
213
- BlockRenderOptions contains resolveHtmlTemplate?: boolean (singular), templateData?: TemplateData, variables?: Variable[], body?: EmailBodySettings. Body defaults to DEFAULT_EMAIL_BODY; templates are preserved by default. Single-block preview uses templateData ?? node.defaultData and does not merge variables automatically; call mergeTemplateData explicitly when needed. The fourth argument must be an object; boolean or null throws TypeError.
221
+ config.toHtml() uses the configured engine unless options.templateEngine explicitly overrides it. Passing config.definitions alone does not carry engine configuration.
214
222
 
215
223
  Mode selects the consumer: canvas for editing, email for control validation. It does not enable template resolution. Preview is a subset of Go templates, not a complete Go engine, HTML sanitizer or security sandbox.
216
224
 
225
+ ### Custom template engines
226
+
227
+ See [Template preview and custom engines](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.md) for supported syntax, adapter behavior and integration examples.
228
+
217
229
  ### email helpers in renderEmail
218
230
 
219
231
  | Method | Arguments and output |
@@ -250,7 +262,7 @@ Props and `RichText.inherited` fields are documented in the published `.d.ts`; u
250
262
  | --- | --- |
251
263
  | BLOCK_TYPES / BuiltinBlockType | heading/text/button/image/divider/spacer/html. |
252
264
  | DEFAULT_BLOCKS | All builtin BlockPlugin definitions; custom registries use config.definitions. |
253
- | DEFAULT_EMAIL_BODY / DOCUMENT_VERSION | Default body / document version 4. Do not mutate the default object. |
265
+ | DEFAULT_EMAIL_BODY / DOCUMENT_VERSION | Default body / format identifier 4 for new documents. Do not mutate the default object. |
254
266
  | FONT_FAMILIES | Readonly label/value font options. |
255
267
  | STYLE_GROUPS / StyleGroup | general/dimension/typography/decorations identifiers and display names. |
256
268
  | BORDER_STYLES / BorderStyle | none/solid/dashed/dotted. |
@@ -264,14 +276,14 @@ Fields in editor, document, control, plugin, style, rendering and template types
264
276
  ## Development and local linking
265
277
 
266
278
  ```bash
267
- pnpm typecheck
268
- pnpm lint
269
- pnpm build
279
+ npm run typecheck
280
+ npm run lint
281
+ npm run build
270
282
  node --test tests/*.test.mjs tests/*.test.ts
271
283
  ```
272
284
 
273
- Use `pnpm check` to run all four checks in order, or `pnpm test` after building to run only the Node tests. Node 24 LTS and pnpm 10.16.1 are the repository defaults. See the [release guide](./release.md) for package verification and publishing.
285
+ Use `npm run check` to run all four checks in order, or `npm test` after building to run only the Node tests. Node 24 LTS and npm 11.13.0 are the repository defaults. See the [release guide](./release.md) for package verification and publishing.
274
286
 
275
287
  Build before tests that depend on dist. Internal TypeScript tests require Node 22.18+. The package ships ESM, declarations and flattened CSS. React/React DOM are peer dependencies; TipTap, dnd-kit, CodeMirror and Lezer are dependencies, externalized during builds rather than bundled.
276
288
 
277
- Local consumers can use link:../react-email-editor. Run pnpm build first and pnpm build:watch to update dist. Both projects must share one React instance: Vite can use resolve.dedupe: ["react", "react-dom"]; Next.js can use transpilePackages: ["@standhigher/react-email-editor"], with Turbopack root covering the linked directory.
289
+ Local consumers can run `npm install ../react-email-editor`. Run `npm run build` first and `npm run build:watch` to update dist. Both projects must share one React instance: Vite can use resolve.dedupe: ["react", "react-dom"]; Next.js can use transpilePackages: ["@standhigher/react-email-editor"], with Turbopack root covering the linked directory.
package/docs/api.zh-CN.md CHANGED
@@ -68,6 +68,7 @@ export function EmailPage() {
68
68
  | `plugins` | `BlockPlugin[]` | `[]`,由 `defineBlock()` 创建。 |
69
69
  | `builtins` | `false \| string[]` | 省略显示全部;`false` 隐藏全部内置区块;数组筛选并排序。隐藏不影响已有节点渲染。自定义类型追加到后面。 |
70
70
  | `pickImage` | `(context: ImagePickerContext) => Promise<string \| null>` | 图片资源选择器;返回 URL,`null` 取消,抛错显示重试;`context` 提供 `value/label/signal`。 |
71
+ | `templateEngine` | `TemplateEngine` | 模板预览适配器;省略时使用默认基础引擎。 |
71
72
 
72
73
  插件建议模块级声明;依赖状态时用 `useMemo`。保持参数引用稳定可复用配置;内联数组不会重建编辑器或清空文档。配置返回值:
73
74
 
@@ -76,6 +77,7 @@ export function EmailPage() {
76
77
  | `definitions` | 全部注册插件的只读列表,包含隐藏的内置区块。 |
77
78
  | `library` | 按展示顺序排列的可插入区块只读列表。 |
78
79
  | `pickImage` | 注册的资源选择器。 |
80
+ | `templateEngine` | 当前画布、预览和解析导出共用的模板引擎。 |
79
81
  | `createBlock(type)` | 已注册的类型字符串 → 独立默认 `EmailBlockNode`;未知类型抛错。 |
80
82
  | `toHtml(document, options?)` | v4 文档与 `DocumentHtmlOptions` → 完整 HTML;自动使用当前注册表。 |
81
83
  | `validate(document)` | 文档 → `ValidationIssue[]`,不渲染 HTML。 |
@@ -100,7 +102,7 @@ export const coupon = defineBlock({
100
102
  | `type` / `title` | 必填非空字符串,分别为唯一类型与区块库标题。 |
101
103
  | `renderEmail(ctx)` | 必填,返回内部 HTML 字符串或 `EmailMarkup`;`ctx` 包含 `block/controls/style/body/mode/email`。 |
102
104
  | `controls` | 名称到 `EmailControls` 声明的映射,默认 `{}`;自动推导值类型与默认值。 |
103
- | `icon` / `description` | 可选图标(ReactNode 或字符串)及说明。 |
105
+ | `icon` / `description` | 可选图标(ReactNode 或 HTML 字符串)及说明。省略图标时,使用 `title` 去除首尾空白后的第一个字符,保留大小写并按纯文本显示。 |
104
106
  | `override` | 替换内置同名类型时必需设为 `true`。 |
105
107
  | `containerStyles` | 默认提供间距与外观;`false` 全禁用;`{ spacing?: boolean, appearance?: boolean }` 可分别禁用。 |
106
108
  | `styleControls` | 额外样式能力;同 `id` 覆盖默认容器能力。 |
@@ -166,7 +168,9 @@ export const coupon = defineBlock({
166
168
  | `getBlockDefinition(type, definitions?)` | 类型与可选注册表,默认内置定义 | 匹配的运行时定义或 `undefined`。不要修改配置中的冻结定义。 |
167
169
  | `generateId(prefix = "block")` | ID 前缀字符串 | 带随机后缀的字符串;应用仍需保持文档内 ID 唯一。 |
168
170
 
169
- `EmailDocument` 含 `version/body/blocks/subject?`;`EmailBlockNode` 含 `id/type/controls/style?/defaultData?`。只支持 `DOCUMENT_VERSION = 4`,不迁移旧文档。JSON 不包含插件组件与渲染逻辑,重新加载时仍需注册相同类型。
171
+ `EmailDocument` 含 `version/body/blocks/subject?`;`EmailBlockNode` 含 `id/type/controls/style?/defaultData?`。`version: number` 只做格式标识:新建文档写入 `DOCUMENT_VERSION = 4`,接入与导出保留传入值,不根据版本号拒绝文档,也不自动迁移旧结构。JSON 不包含插件组件与渲染逻辑,重新加载时仍需注册相同类型。
172
+
173
+ 初始化、`setDocument()`、`config.validate()` 和 HTML 导出统一检查文档为对象、`blocks` 为数组、每个区块的 `id/type` 为非空字符串、`controls` 为对象;结构不合法时直接抛出 `TypeError`,错误包含字段路径。`subject` 提供时必须为字符串;`body` 提供且非 `null` 时必须为对象,缺省值继续由 `resolveEmailBody()` 补齐。仅有旧 `fields` 而没有 `controls` 的节点不能接入;控件值仍按插件声明归一化与校验。
170
174
 
171
175
  主体设置由画布、预览和邮件 HTML 共用:
172
176
 
@@ -175,7 +179,7 @@ export const coupon = defineBlock({
175
179
  | `textColor` | `#000000`,文字颜色。 |
176
180
  | `backgroundColor` | `#ffffff`,居中内容容器背景;外侧白色。 |
177
181
  | `contentWidth` | `600`,内容最大宽度 px。 |
178
- | `fontFamily` | `Arial, Helvetica, sans-serif`。 |
182
+ | `fontFamily` | `""`,检查器显示 `Default`;未指定或为空时,渲染使用 `Arial, Helvetica, sans-serif`。 |
179
183
  | `fontWeight` | `normal`;也可 `bold`。 |
180
184
  | `linkColor` / `linkDecoration` | `#005bd3` / `underline`。 |
181
185
  | `contentPadding` | `0px`,边框内部 CSS padding。 |
@@ -195,6 +199,7 @@ export const coupon = defineBlock({
195
199
  | `resolveHtmlTemplates` | `false` 保留模板;`true` 解析预览值。 |
196
200
  | `variables` | `[]`,变量原文与预览文本。 |
197
201
  | `templateData` | 可选 `TemplateData`,全局对象或列表数据。 |
202
+ | `templateEngine` | 可选 `TemplateEngine`;省略时使用默认基础引擎。外部引擎负责完整语义和 HTML 转义,输出不会被重复转义。 |
198
203
 
199
204
  数据按顶层浅合并,优先级:节点 `defaultData` > `templateData` > `variables`。默认导出仅给与 `variables[].name` 精确匹配且缺点的 token 补点,并保留空格,例如 `{{ store_name }}` → `{{ .store_name }}`;列表外的 Go template 保持原样。变量 `description` 为空时不显示副文案,`value` 只用于预览。
200
205
 
@@ -208,12 +213,19 @@ export const coupon = defineBlock({
208
213
  | `buildRenderContext(node, mode, body = DEFAULT_EMAIL_BODY, definition = 内置查找结果)` | mode 必填;自定义区块需显式传入 definition | 独立归一化 `BlockRenderContext`,不渲染。 |
209
214
  | `variablesToTemplateData(variables)` | 必填 `Variable[]` | 以 name 路径为键、value 为值的 `TemplateData`;支持嵌套路径,忽略无 value 的项。 |
210
215
  | `mergeTemplateData(variables, defaultData?)` | 变量数组与可选对象 | 顶层合并,defaultData 覆盖变量;两者均无数据时返回 undefined。 |
211
- | `renderTemplatePreview(html, defaultData?)` | HTML 字符串与可选预览对象 | 支持部分字段路径、range、if、with 等 Go template 语法;省略数据时保留原文。 |
216
+ | `renderTemplatePreview(template, data?, options?)` | 模板、预览数据和可选 `templateEngine` | 使用注入引擎渲染;默认引擎支持基础字段路径、`range`、`if`、`with`,遇到不支持的表达式时保留完整输入。 |
217
+ | `validateTemplate(template, options?)` | 模板与可选 `templateEngine` | 显式调用外部引擎校验;未提供校验器时返回 `undefined`,不会自动阻止保存。 |
218
+
219
+ `DocumentHtmlOptions` 和 `BlockRenderOptions` 都含 `templateEngine?: TemplateEngine`。`BlockRenderOptions` 含 `resolveHtmlTemplate?: boolean`(注意单数)、`templateData?: TemplateData`、`variables?: Variable[]`、`body?: EmailBodySettings`。`body` 默认 `DEFAULT_EMAIL_BODY`;模板默认不解析。单区块预览取 `templateData ?? node.defaultData`,不会自动合并 variables;需要时显式调用 `mergeTemplateData()`。第四个参数必须为对象,传 boolean 或 null 会抛出 TypeError。
212
220
 
213
- `BlockRenderOptions` 含 `resolveHtmlTemplate?: boolean`(注意单数)、`templateData?: TemplateData`、`variables?: Variable[]`、`body?: EmailBodySettings`。`body` 默认 `DEFAULT_EMAIL_BODY`;模板默认不解析。单区块预览取 `templateData ?? node.defaultData`,不会自动合并 variables;需要时显式调用 `mergeTemplateData()`。第四个参数必须为对象,传 boolean 或 null 会抛出 TypeError。
221
+ `config.toHtml()` 默认使用配置中的引擎,`options.templateEngine` 可按次覆盖。只传 `config.definitions` 不会携带模板引擎配置。
214
222
 
215
223
  `mode` 表示消费场景:`canvas` 供画布,`email` 执行邮件控件校验;它不决定是否解析模板。模板预览不是完整 Go 引擎,也不是 HTML 消毒器或安全沙箱。
216
224
 
225
+ ### 自定义模板引擎
226
+
227
+ 完整说明与接入示例见 [模板预览与自定义引擎](https://github.com/standhigher/react-email-editor/blob/main/docs/template-engines.zh-CN.md)。
228
+
217
229
  ### renderEmail 中的 email 工具
218
230
 
219
231
  | 方法 | 参数与输出 |
@@ -250,7 +262,7 @@ Props 及 `RichText.inherited` 的字段说明包含在发布包的 `.d.ts` 注
250
262
  | --- | --- |
251
263
  | `BLOCK_TYPES` / `BuiltinBlockType` | heading、text、button、image、divider、spacer、html。 |
252
264
  | `DEFAULT_BLOCKS` | 所有内置 BlockPlugin 定义;自定义注册表使用 config.definitions。 |
253
- | `DEFAULT_EMAIL_BODY` / `DOCUMENT_VERSION` | 默认主体设置 / 文档版本 4。不要直接修改默认对象。 |
265
+ | `DEFAULT_EMAIL_BODY` / `DOCUMENT_VERSION` | 默认主体设置 / 新建文档的格式标识 4。不要直接修改默认对象。 |
254
266
  | `FONT_FAMILIES` | 只读字体选项 `{ label, value }[]`。 |
255
267
  | `STYLE_GROUPS` / `StyleGroup` | general/dimension/typography/decorations 的标识及展示名称。 |
256
268
  | `BORDER_STYLES` / `BorderStyle` | none/solid/dashed/dotted。 |
@@ -264,14 +276,14 @@ Props 及 `RichText.inherited` 的字段说明包含在发布包的 `.d.ts` 注
264
276
  ## 开发与本地联调
265
277
 
266
278
  ```bash
267
- pnpm typecheck
268
- pnpm lint
269
- pnpm build
279
+ npm run typecheck
280
+ npm run lint
281
+ npm run build
270
282
  node --test tests/*.test.mjs tests/*.test.ts
271
283
  ```
272
284
 
273
- `pnpm check` 按上述顺序执行全部检查;只运行 Node 测试时,构建后执行 `pnpm test`。本仓库默认使用 Node 24 LTS 和 pnpm 10.16.1。打包检查与发布流程见[发布指南](./release.zh-CN.md)。
285
+ `npm run check` 按上述顺序执行全部检查;只运行 Node 测试时,构建后执行 `npm test`。本仓库默认使用 Node 24 LTS 和 npm 11.13.0。打包检查与发布流程见[发布指南](./release.zh-CN.md)。
274
286
 
275
287
  先构建再运行依赖 dist 的测试;TypeScript 内部测试需要 Node 22.18+。包提供 ESM、类型与展开后的 CSS。React/React DOM 为 peer dependencies;TipTap、dnd-kit、CodeMirror、Lezer 等为 dependencies,构建时 external,不内联到发布包。
276
288
 
277
- 消费方可使用 `link:../react-email-editor`;先 `pnpm build`,再 `pnpm build:watch` 保持 dist 更新。两项目需共用一个 React 实例:Vite 可设置 `resolve.dedupe: ["react", "react-dom"]`;Next.js 可设置 `transpilePackages: ["@standhigher/react-email-editor"]`,Turbopack 的 root 需覆盖链接目录。
289
+ 消费方可运行 `npm install ../react-email-editor`;先 `npm run build`,再 `npm run build:watch` 保持 dist 更新。两项目需共用一个 React 实例:Vite 可设置 `resolve.dedupe: ["react", "react-dom"]`;Next.js 可设置 `transpilePackages: ["@standhigher/react-email-editor"]`,Turbopack 的 root 需覆盖链接目录。
package/docs/changelog.md CHANGED
@@ -2,6 +2,66 @@
2
2
 
3
3
  English | [简体中文](./changelog.zh-CN.md)
4
4
 
5
+ ## 0.5.2 — 2026-10-29
6
+
7
+ ### Fixes and improvements
8
+
9
+ - Fix canvas recreation and flicker when switching between editing and preview. Keep the editing canvas mounted and swap two iframe buffers after standby loading and height measurement, preserving separate editing and preview scroll positions. Exclude the hidden canvas from focus, keyboard and drag interactions while previewing.
10
+ - Debounce document, configuration, variable and device-width changes for 400ms with a maximum wait of 2 seconds and idle scheduling support. Process Preview and Retry clicks immediately while continuing to debounce subsequent changes in preview mode. Reuse ready or loading buffers with identical HTML and width to avoid remounts.
11
+ - Use `sandbox="allow-same-origin"` on preview iframes to block scripts and event handlers while retaining height measurement. Preserve exported HTML; this restriction is not complete HTML sanitization.
12
+ - Handle generation, load and measurement errors, including a 15-second load/measurement timeout, with error messages and Retry. Retain existing previews on failure and force a new load on retry. Prevent callbacks from stale, replaced or failed buffers from overwriting current content, and clean up timers, observers, listeners and animation frames on unmount.
13
+ - Update both template-engine guides. Bump the npm package from `0.5.1` to `0.5.2` as a patch, preserving the public API, email export and persisted document format. Preview no longer executes scripts in email HTML; custom engines and `renderEmail` should be pure and allow repeated background calls.
14
+
15
+ ### Validation
16
+
17
+ - `npm run check` passes type checking, Oxlint, the build and all 144 Node tests, covering debounce, maximum wait, buffer reuse, stale callbacks, timeouts, exceptions, retry and cleanup.
18
+ - Reuse the existing Chrome page to verify blocked scripts and event handlers, unchanged iframe nodes after subject-only edits, one load for five consecutive changes in preview mode, and a 15-second timeout that retains the old preview and recovers through Retry.
19
+ - Verify desktop 600px and mobile 360px previews and five consecutive editing/preview round trips. Canvas, rich-text and iframe nodes remain mounted, with no visible blank or unmeasured frame in sampled states. Restore the test document and remove temporary probes. `git diff --check` passes.
20
+
21
+ ## 0.5.1 — 2026-10-29
22
+
23
+ ### Fixes and improvements
24
+
25
+ - Accept documents according to their actual structure without requiring `version === 4`. Widen `EmailDocument.version` to `number`, preserving the supplied value through initialization, replacement, snapshots, reset and export; new documents still use `4`.
26
+ - Share structural checks across initialization, document replacement, validation and HTML export for the document object, `blocks` array and block `id/type/controls`. Invalid structure throws a `TypeError` with the field path. Preserve body normalization and plugin control defaults without automatically migrating nodes that only contain legacy `fields`.
27
+ - Update both API references and custom block guides. Require automatic updates to both changelogs before commits and choose major, minor, patch or no version bump according to the changes; keep the npm package version independent of the document format identifier.
28
+ - Bump the npm package from `0.5.0` to `0.5.1` as a patch correcting document loading restrictions, without changing the persisted document structure.
29
+
30
+ ### Validation
31
+
32
+ - Type checking, Oxlint, the build and all 127 Node tests pass, covering different document versions, invalid structures, plugin validation, template preview, snapshot isolation, subscriptions and reset.
33
+ - `git diff --check` passes. No UI or styles changed, so browser validation was not run.
34
+
35
+ ## 0.5.0 — 2026-10-29
36
+
37
+ ### Template preview and custom engines
38
+
39
+ - Add a synchronous `TemplateEngine` adapter through `useEmailEditorConfig({ templateEngine })`, sharing external rendering across canvas, editor preview and resolved export. `renderTemplatePreview()`, document rendering and single-block helpers accept the same `templateEngine` option.
40
+ - Export `defaultTemplateEngine` and explicitly invoked `validateTemplate()` without automatically blocking saving. Engine updates preserve documents and instances, instances remain isolated, and engines receive detached data copies.
41
+ - Support basic variables, root context and `if/range/with` in the default engine; preserve the entire render input when syntax is unsupported or control structures are incomplete. Delegate functions, pipelines and local variables to external engines without escaping their output again.
42
+ - Apply the same listed-variable dot normalization to canvas, preview and sending HTML, preserving whitespace and trim markers; JSON retains template source.
43
+ - Fix Unicode field paths and condition truthiness for Date, Map, Set and plain objects. Normalize non-`Error` render and validation exceptions while preserving the original `cause`, with error display, Retry and recovery after replacing an engine.
44
+ - Add dedicated English and Chinese template-engine documentation and integration examples; keep only documentation links in the READMEs.
45
+
46
+ ### Editor defaults
47
+
48
+ - Add and initially select `Default` in BODY Font family, storing `body.fontFamily` as `""` by default. Omitted, empty and whitespace-only values use the internal Arial, Helvetica, sans-serif font stack in the canvas, preview and exported email. Preserve explicit fonts and rich-text formatting; the document version remains `4`.
49
+ - When custom blocks omit `icon`, use the first Unicode code point of the trimmed `title` as the default icon and escape HTML special characters. Explicit icons retain precedence.
50
+
51
+ ### Toolchain and documentation
52
+
53
+ - Migrate project package management from pnpm to npm 11.13.0, replacing `pnpm-lock.yaml` with `package-lock.json` while preserving all direct dependency versions.
54
+ - Use `npm install` and `npm run dev` for local development and `npm ci` for locked installs. Switch validation, automatic fixes, packaging and pre-publish checks to npm, and remove pnpm-specific configuration.
55
+ - Update installation and command instructions in both READMEs, API development and local linking documentation, release guides and project collaboration rules.
56
+ - Limit READMEs to basic integration examples and documentation navigation; maintain feature details, defaults and behavior descriptions in `docs/`.
57
+
58
+ ### Validation
59
+
60
+ - `npm ci` succeeds; `npm run check` passes type checking, Oxlint, build and all 123 Node tests, covering engine selection, context, escaping, data isolation, variable normalization, error recovery and body default-font fallback and switching.
61
+ - `npm run dev` starts successfully; the homepage and `main.tsx` entry module both return HTTP 200.
62
+ - Verify canvas and preview variable output, non-`Error` exception display, Retry and recovery after replacing engines in Chrome; `git diff --check` passes.
63
+ - Verify initial BODY `Default` selection, explicit font switching and restoring the default in Chrome, with matching default fonts in the canvas, HTML-block iframe and desktop/mobile email previews.
64
+
5
65
  ## 0.4.3 — 2026-10-28
6
66
 
7
67
  ### Fixes and improvements
@@ -2,6 +2,66 @@
2
2
 
3
3
  [English](./changelog.md) | 简体中文
4
4
 
5
+ ## 0.5.2 — 2026-10-29
6
+
7
+ ### 修复与改进
8
+
9
+ - 修复编辑与预览切换时画布重建和闪动:编辑画布常驻,两个 iframe 在备用层完成加载与高度测量后交换显示,分别保留编辑区和预览区的滚动位置;预览时禁用隐藏画布的焦点、键盘和拖拽交互。
10
+ - 文档、配置、变量和设备宽度变化使用 400ms 防抖与最长 2 秒等待,支持空闲调度;点击 Preview 或 Retry 立即处理,预览模式中的后续修改仍防抖。HTML 和宽度一致时复用已就绪或正在加载的缓冲,避免重复挂载。
11
+ - 预览 iframe 使用 `sandbox="allow-same-origin"` 阻止脚本和事件属性执行,保持高度测量能力;导出 HTML 保持原有内容,此约束不等同于完整 HTML 消毒。
12
+ - 补齐生成、加载和测量错误,以及 15 秒加载测量超时提示与 Retry。已有预览在失败时继续显示,重试强制新加载;过期、被替换或已失败缓冲的回调不能覆盖当前内容,卸载时清理计时器、观察器、监听器和动画帧。
13
+ - 同步中英文模板引擎文档。本次按 patch 将 npm 包版本从 `0.5.1` 升至 `0.5.2`,公开 API、邮件导出与持久化文档格式不变;预览不再执行邮件 HTML 中的脚本,自定义引擎与 `renderEmail` 应允许后台重复调用并保持纯函数。
14
+
15
+ ### 验证
16
+
17
+ - `npm run check` 通过类型检查、Oxlint、构建与全部 144 项 Node 测试,覆盖防抖、最长等待、缓冲复用、过期回调、超时、异常、重试与清理。
18
+ - 复用现有 Chrome 页面验证脚本和事件属性被阻止、仅修改主题时 iframe 节点不变、预览内连续 5 次修改只加载 1 次,以及 15 秒超时保留旧预览并在 Retry 后恢复。
19
+ - 验证桌面 600px 与移动 360px 预览及连续五轮编辑/预览切换,画布、富文本和 iframe 节点保持不变,采样未出现可见空白或未测量帧;测试文档已还原,临时探针已清理。`git diff --check` 通过。
20
+
21
+ ## 0.5.1 — 2026-10-29
22
+
23
+ ### 修复与改进
24
+
25
+ - 文档接入改为检查实际结构,不再强制要求 `version === 4`;`EmailDocument.version` 类型放宽为 `number`,初始化、替换、快照、重置及导出保留传入值,新建文档仍写入 `4`。
26
+ - 初始化、文档替换、校验和 HTML 导出统一检查文档对象、`blocks` 数组及区块的 `id/type/controls`,非法结构抛出包含字段路径的 `TypeError`;保留主体默认值归一化和插件控件默认值,不自动迁移仅有旧 `fields` 的节点。
27
+ - 同步中英文 API 与自定义区块文档,并规定提交前自动更新中英文 changelog、按变更影响决定 major、minor、patch 或不升级版本;npm 包版本与文档格式标识独立。
28
+ - 本次以 patch 修正文档接入限制,npm 包版本从 `0.5.0` 升至 `0.5.1`,不改变文档持久化结构。
29
+
30
+ ### 验证
31
+
32
+ - 类型检查、Oxlint、构建及全部 127 项 Node 测试通过,覆盖不同版本接入、非法结构、插件校验、模板预览、快照隔离、订阅与重置。
33
+ - `git diff --check` 通过;本次未修改 UI 或样式,未启动浏览器验证。
34
+
35
+ ## 0.5.0 — 2026-10-29
36
+
37
+ ### 模板预览与自定义引擎
38
+
39
+ - 新增同步 `TemplateEngine` 适配器,通过 `useEmailEditorConfig({ templateEngine })` 让画布、编辑器预览和解析导出共用外部引擎;`renderTemplatePreview()`、文档及单区块渲染支持相同的 `templateEngine` 选项。
40
+ - 导出 `defaultTemplateEngine` 与按需调用的 `validateTemplate()`;语法校验不会自动阻止保存。引擎更新保留文档与实例,多实例之间独立,传入引擎的数据为隔离副本。
41
+ - 默认引擎支持基础变量、根上下文及 `if/range/with`;包含未支持语法或不完整控制结构时保留该次渲染的完整输入。函数、管道及局部变量交由外部引擎处理,外部输出不重复转义。
42
+ - 统一画布、预览和发送 HTML 的变量清单补点规则,保留空白与 trim 标记;JSON 保存模板原文。
43
+ - 修复 Unicode 字段路径解析,以及 Date、Map、Set 和普通对象的条件真值判断;渲染与校验将非 `Error` 异常归一化并保留原始 `cause`,支持错误提示、Retry 及更换引擎恢复。
44
+ - 新增中英文模板引擎独立文档与接入示例,README 仅保留文档引用。
45
+
46
+ ### 编辑器默认行为
47
+
48
+ - BODY 的 Font family 新增 `Default` 并默认选中,`body.fontFamily` 默认保存为 `""`;未指定、空字符串及纯空白值在画布、预览和邮件导出时使用内部默认字体 `Arial, Helvetica, sans-serif`。保留显式字体与富文本格式,文档版本仍为 `4`。
49
+ - 自定义区块省略 `icon` 时,使用 `title` 去除首尾空白后的首个 Unicode 码点作为默认图标,并转义 HTML 特殊字符;显式图标保持优先。
50
+
51
+ ### 工具链与文档
52
+
53
+ - 项目包管理器从 pnpm 迁移为 npm 11.13.0,使用 `package-lock.json` 替换 `pnpm-lock.yaml`,保持所有直接依赖版本不变。
54
+ - 开发启动统一使用 `npm install` 和 `npm run dev`;固定锁文件安装使用 `npm ci`。检查、自动修复、打包和发布前校验脚本均改为 npm,移除 pnpm 专属配置。
55
+ - 同步中英文 README、API 开发与本地联调说明、发布指南及项目协作规则中的安装和命令说明。
56
+ - 明确 README 只保留基本接入示例和文档导航,功能细节、默认值与行为说明统一维护在 `docs/`。
57
+
58
+ ### 验证
59
+
60
+ - `npm ci` 安装成功;`npm run check` 通过类型检查、Oxlint、构建及全部 123 项 Node 测试,覆盖模板引擎选择、上下文、转义、数据隔离、变量补点、异常恢复及主体默认字体回退与切换。
61
+ - `npm run dev` 启动成功,首页和 `main.tsx` 入口模块均返回 HTTP 200。
62
+ - Chrome 验证画布与预览的变量结果,以及非 `Error` 异常提示、Retry 和更换引擎恢复;`git diff --check` 通过。
63
+ - Chrome 验证主体默认选中 `Default`、显式字体切换及恢复默认,核对画布、HTML 区块 iframe 和桌面/移动邮件预览的默认字体一致。
64
+
5
65
  ## 0.4.3 — 2026-10-28
6
66
 
7
67
  ### 修复与改进
@@ -130,7 +130,7 @@ Preview precedence is node defaultData > templateData > variables, merged by top
130
130
  | `controlPath` | `string` | Control path, such as terms[0].text; empty when the plugin is missing. |
131
131
  | `message` | `string` | Reason validation failed. |
132
132
 
133
- Unsupported document versions throw directly. validate() does not check renderer execution; export can still fail due to a rendering error.
133
+ Invalid basic document structure throws a `TypeError`; documents are not rejected based on their `version` value. validate() does not check renderer execution; export can still fail due to a rendering error.
134
134
 
135
135
  ## 2. defineBlock(options)
136
136
 
@@ -164,7 +164,7 @@ const banner = defineBlock({
164
164
  | --- | --- | --- |
165
165
  | `type` | Yes | Stable unique type. Use an application namespace, such as my-app.banner. Do not casually change persisted types. |
166
166
  | `title` | Yes | Library and inspector name. type/title cannot be blank. |
167
- | `icon` | No | A string or ReactNode icon. |
167
+ | `icon` | No | HTML string or ReactNode icon; defaults to the first character of the trimmed `title`, preserving case and displaying it as text, e.g. `Coupon` → `C`, `优惠券` → `优`. |
168
168
  | `description` | No | Block description. |
169
169
  | `override` | No | Defaults to false; allows replacing a built-in with the same type during registration. |
170
170
  | `controls` | No | ControlSchema object defining content controls in declaration order, with inferred value types. Defaults to an empty object. |
@@ -467,7 +467,7 @@ Links allow http/https/mailto/tel, anchors, or templates; images allow http/http
467
467
 
468
468
  EmailBlockNode contains required id, type, controls and optional style/defaultData. EmailDocument contains version, body, blocks, and optional subject. Register matching plugins after JSON reload; JSON does not store components or renderers.
469
469
 
470
- The current document version is **4**. The entire API uses controls, with no old API aliases, old-data compatibility, or automatic migration. Unsupported versions throw. Create new data with createEmailDocument() and config.createBlock(); do not merely change an old JSON version number.
470
+ New documents use the format identifier **4**. `version` has type `number` and is only an identifier; loading checks the actual structure and preserves the supplied version. The entire API uses controls, with no old API aliases, old-data compatibility, or automatic migration. Changing an old JSON version number cannot make nodes without controls pass structural checks. Create new data with createEmailDocument() and config.createBlock().
471
471
 
472
472
  Missing plugins produce a canvas placeholder and preserve the node; email export throws. Canvas, Inspector, and canvas rendering errors are isolated by block with retry. Email rendering errors identify the block type, ID, and reason. getData()/getDocument() and onChange/onBodyChange arguments provide independent snapshots.
473
473
 
@@ -129,7 +129,7 @@ function useCouponConfig() {
129
129
  | `controlPath` | `string` | 控件路径,例如 `terms[0].text`;插件缺失时为空字符串。 |
130
130
  | `message` | `string` | 校验失败原因。 |
131
131
 
132
- 不支持的文档版本会直接抛错。`validate()` 不检查渲染器能否成功运行,导出仍可能因渲染错误失败。
132
+ 文档基本结构不合法时直接抛出 `TypeError`,不根据 `version` 值拒绝文档。`validate()` 不检查渲染器能否成功运行,导出仍可能因渲染错误失败。
133
133
 
134
134
  ## 2. defineBlock(options)
135
135
 
@@ -163,7 +163,7 @@ const banner = defineBlock({
163
163
  | --- | --- | --- |
164
164
  | `type` | 是 | 唯一且稳定的类型标识;建议业务命名空间,例如 `my-app.banner`。持久化后不能随意变更。 |
165
165
  | `title` | 是 | 区块库和检查器名称。`type` / `title` 不允许为空白。 |
166
- | `icon` | 否 | 字符串或 ReactNode 图标。 |
166
+ | `icon` | 否 | HTML 字符串或 ReactNode 图标;省略时使用 `title` 去除首尾空白后的第一个字符,保留大小写并按纯文本显示,例如 `Coupon` → `C`、`优惠券` → `优`。 |
167
167
  | `description` | 否 | 区块说明。 |
168
168
  | `override` | 否 | 默认 `false`;允许在注册时替换同类型的内置区块。 |
169
169
  | `controls` | 否 | `ControlSchema` 对象;按声明顺序定义内容控件,自动推导值类型,默认空对象。 |
@@ -466,7 +466,7 @@ renderEmail: ({ controls, email }) => email.html`
466
466
 
467
467
  节点使用 `EmailBlockNode`,包含必填 id、type、controls,以及可选 style、defaultData。文档使用 `EmailDocument`,包含 version、body、blocks 和可选 subject。保存 JSON 后重新加载时,仍需注册同 type 的插件,JSON 不保存组件或渲染函数。
468
468
 
469
- 当前文档版本为 **4**。全链路使用 controls,不提供旧 API 别名、旧数据兼容或自动迁移;不支持的版本明确报错。请用 createEmailDocument() 和 config.createBlock() 创建新数据,不要只修改旧 JSON 的版本号。
469
+ 新建文档的格式标识为 **4**。`version` 类型为 `number`,只做标识;接入依据实际数据结构,并保留传入的版本号。全链路使用 controls,不提供旧 API 别名、旧数据兼容或自动迁移;仅修改旧 JSON 的版本号不能让缺失 controls 的节点通过结构检查。请用 createEmailDocument() 和 config.createBlock() 创建新数据。
470
470
 
471
471
  缺少插件时画布显示占位并保留原始节点,邮件导出报错。Canvas、Inspector 及画布渲染异常按区块隔离并提供重试;邮件渲染错误包含区块 type、id 和原因。getData()/getDocument() 和 onChange/onBodyChange 参数均为独立快照。
472
472
 
package/docs/release.md CHANGED
@@ -4,7 +4,7 @@ English | [简体中文](./release.zh-CN.md)
4
4
 
5
5
  ## Toolchain and package configuration
6
6
 
7
- Development uses Node 24 LTS (`.nvmrc`) and pnpm 10.16.1 (`packageManager`). The complete test suite needs Node 22.18+. These development requirements are not consumer requirements, so the package does not declare `engines`. React / React DOM 18 remain the supported peers; React 19 requires a separate compatibility validation.
7
+ Development uses Node 24 LTS (`.nvmrc`) and npm 11.13.0 (`packageManager`). The complete test suite needs Node 22.18+. These development requirements are not consumer requirements, so the package does not declare `engines`. React / React DOM 18 remain the supported peers; React 19 requires a separate compatibility validation.
8
8
 
9
9
  The manifest declares MIT licensing, repository, homepage, issue tracker and the public npm registry. `sideEffects: ["**/*.css"]` preserves CSS imports while allowing unused JavaScript modules to be removed. Actual tree shaking depends on module contents and the consumer's bundler.
10
10
 
@@ -21,12 +21,12 @@ README language switches, documentation, changelog and section links use absolut
21
21
  ## Local checks and publishing
22
22
 
23
23
  ```bash
24
- pnpm install --frozen-lockfile
25
- pnpm check
24
+ npm ci
25
+ npm run check
26
26
  npm pack --dry-run
27
27
  ```
28
28
 
29
- `pnpm check` runs type checking, linting, build and Node tests in order. Tests cover package contents, README image URLs and consumer CSS preservation. `pnpm test` requires an existing build. `prepack` builds assets; `prepublishOnly` runs the complete check before local publishing.
29
+ `npm run check` runs type checking, linting, build and Node tests in order. Tests cover package contents, README image URLs and consumer CSS preservation. `npm test` requires an existing build. `prepack` builds assets; `prepublishOnly` runs the complete check before local publishing.
30
30
 
31
31
  To publish locally, first update both changelogs and select an unpublished version:
32
32
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 工具链与包配置
6
6
 
7
- 开发默认使用 Node 24 LTS(`.nvmrc`)和 pnpm 10.16.1(`packageManager`)。完整测试最低需要 Node 22.18;这不是消费方安装要求,因此包没有添加 `engines` 限制。当前仍只声明 React 18 / React DOM 18 支持,React 19 需完成独立兼容性验证后再扩大 peer 范围。
7
+ 开发默认使用 Node 24 LTS(`.nvmrc`)和 npm 11.13.0(`packageManager`)。完整测试最低需要 Node 22.18;这不是消费方安装要求,因此包没有添加 `engines` 限制。当前仍只声明 React 18 / React DOM 18 支持,React 19 需完成独立兼容性验证后再扩大 peer 范围。
8
8
 
9
9
  `package.json` 声明 MIT 许可证、仓库、主页、Issue 地址及公开 npm registry。`sideEffects: ["**/*.css"]` 保留消费方的 CSS 导入,允许 bundler 丢弃未使用的 JavaScript 模块;实际裁剪效果取决于模块内容和消费方 bundler。
10
10
 
@@ -21,12 +21,12 @@ README 的语言切换、文档、更新日志和章节导航使用 GitHub `blob
21
21
  ## 本地检查与发布
22
22
 
23
23
  ```bash
24
- pnpm install --frozen-lockfile
25
- pnpm check
24
+ npm ci
25
+ npm run check
26
26
  npm pack --dry-run
27
27
  ```
28
28
 
29
- `pnpm check` 顺序执行类型检查、lint、构建和 Node 测试;测试包含发布文件范围、README 图片地址及消费方 CSS 保留验证。`pnpm test` 只执行测试,需先构建。`prepack` 负责构建;本地 `npm publish` 的 `prepublishOnly` 会执行完整校验。
29
+ `npm run check` 顺序执行类型检查、lint、构建和 Node 测试;测试包含发布文件范围、README 图片地址及消费方 CSS 保留验证。`npm test` 只执行测试,需先构建。`prepack` 负责构建;本地 `npm publish` 的 `prepublishOnly` 会执行完整校验。
30
30
 
31
31
  需要本地手动发布时,在确认版本未发布、中英文更新日志已同步后执行:
32
32