@zenweb/template 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,187 @@
1
+ # @zenweb/template - AI API Reference
2
+
3
+ AI 专用 API 参考文档,随 npm 包发布,供 AI 编程助手使用。
4
+
5
+ ## 模块概述
6
+
7
+ `@zenweb/template` 是 ZenWeb 框架的服务端模版渲染模块。集成模版引擎(如 Nunjucks、Handlebars)并根据请求条件(路径匹配、Accept 头、控制器显式指定)按需渲染输出。支持多个模版引擎实例共存。
8
+
9
+ ## 导出
10
+
11
+ ```ts
12
+ export default function setup(option: TemplateSetupOption): SetupFunction;
13
+ export { $template, TemplateSetupOption, TemplateOption, TemplateEngine };
14
+ ```
15
+
16
+ ## 类型
17
+
18
+ ```ts
19
+ /**
20
+ * 渲染引擎方法签名
21
+ * - 函数名 (name) 作为默认引擎名称
22
+ */
23
+ type TemplateEngine = (template: string, data?: any) => any;
24
+
25
+ interface TemplateSetupOption {
26
+ /** 输出类型,默认 'html' */
27
+ type?: string;
28
+ /** 匹配路径,例如 [/\.html$/i] */
29
+ matchPath?: RegExp[];
30
+ /** 忽略路径,例如 [/^\/api\//i] */
31
+ ignorePath?: RegExp[];
32
+ /** 匹配 Accept 头类型 */
33
+ matchAccept?: string[];
34
+ /**
35
+ * 模版名后缀,例如 '.html'
36
+ * 在不指定 TemplateOption.template 时有效
37
+ */
38
+ templateAffix?: string;
39
+ /**
40
+ * ctx.fail 操作使用的模版(全局)
41
+ * 不指定则使用 TemplateOption.template 值
42
+ * 控制器中可设置 TemplateOption.failTemplate = false 关闭
43
+ */
44
+ failTemplate?: string;
45
+ /** 渲染引擎 */
46
+ engine: TemplateEngine;
47
+ /**
48
+ * 自定义引擎名称
49
+ * 默认取 engine.name,多引擎共存时用于区分
50
+ */
51
+ engineName?: string;
52
+ }
53
+
54
+ interface TemplateOption {
55
+ /** 指定模版名,不指定取 ctx.path */
56
+ template?: string;
57
+ /**
58
+ * 失败模版
59
+ * 默认使用全局 failTemplate
60
+ * 设为 false 强制使用 template
61
+ */
62
+ failTemplate?: string | false;
63
+ /** 使用的引擎名称,对应 TemplateSetupOption.engineName */
64
+ engine?: string;
65
+ }
66
+ ```
67
+
68
+ ## setup(option) (default export)
69
+
70
+ 模块安装函数,用于 `app.setup(modTemplate({ ... }))`。
71
+
72
+ **处理流程:**
73
+ 1. 设置引擎名称(默认取 `option.engine.name`)
74
+ 2. 断言 `@zenweb/result` 模块已安装
75
+ 3. 创建 `TemplateRender` 实例并注册到 `RenderManager`
76
+ 4. 扩展 Context,挂载 `ctx.template()` 方法
77
+
78
+ **基本用法:**
79
+ ```ts
80
+ import { create } from 'zenweb';
81
+ import modTemplate from '@zenweb/template';
82
+ import nunjucks from '@zenweb/template-nunjucks';
83
+
84
+ create()
85
+ .setup(modTemplate({
86
+ matchPath: [/\.html$/i],
87
+ engine: nunjucks(),
88
+ }))
89
+ .start();
90
+ ```
91
+
92
+ **多引擎配置:**
93
+ ```ts
94
+ create()
95
+ .setup(modTemplate({
96
+ matchPath: [/\.html$/i],
97
+ engine: nunjucks(),
98
+ }))
99
+ .setup(modTemplate({
100
+ type: 'xml',
101
+ matchAccept: ['xml'],
102
+ templateAffix: '.xml',
103
+ engine: handlebars(),
104
+ engineName: 'xml',
105
+ }))
106
+ .start();
107
+ ```
108
+
109
+ ## $template(template_or_option?)
110
+
111
+ 全局方法,在控制器或服务中使用,基于 `@zenweb/core` 的 `$getContext()` 获取当前请求上下文。
112
+
113
+ ```ts
114
+ $template() // 启用模版渲染(使用默认匹配)
115
+ $template('detail.html') // 指定模版文件
116
+ $template({ failTemplate: 'error.html' }) // 指定失败模版
117
+ $template({ engine: 'xml' }) // 切换引擎
118
+ $template(false) // 关闭当前请求的模版渲染
119
+ ```
120
+
121
+ **控制器示例:**
122
+ ```ts
123
+ import { Context, mapping } from 'zenweb';
124
+ import { $template } from '@zenweb/template';
125
+
126
+ export class Controller {
127
+ @mapping({ path: '/user.html' })
128
+ user(ctx: Context) {
129
+ $template(); // 启用渲染
130
+ return { name: 'test' }; // 渲染 user.html 模版
131
+ }
132
+
133
+ @mapping({ path: '/api/data' })
134
+ data() {
135
+ $template(false); // 禁用渲染,返回 JSON
136
+ return { name: 'test' };
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## Context 挂载
142
+
143
+ ```ts
144
+ interface Context {
145
+ /** 当前请求的模版渲染选项 */
146
+ templateOption?: TemplateOption;
147
+
148
+ /**
149
+ * 启用并设置模版渲染
150
+ * 不传参数: 启用
151
+ * false: 关闭模版渲染
152
+ * string: 设置模版文件名
153
+ * TemplateOption: 详细设置
154
+ */
155
+ template(template_or_option?: false | string | TemplateOption): void;
156
+ }
157
+ ```
158
+
159
+ ## 匹配规则
160
+
161
+ `TemplateRender.match()` 按以下优先级判断请求是否需要模版渲染:
162
+
163
+ 1. **引擎名匹配** — `ctx.templateOption.engine === option.engineName` 时直接匹配
164
+ 2. **模版后缀匹配** — `ctx.templateOption.template` 以 `templateAffix` 结尾时匹配
165
+ 3. **忽略路径** — `ignorePath` 中任一正则匹配 `ctx.path` 时返回 `false`
166
+ 4. **路径匹配** — `matchPath` 中任一正则匹配 `ctx.path` 时匹配
167
+ 5. **Accept 头匹配** — `matchAccept` 包含请求 Accept 类型时匹配
168
+ 6. 以上均不满足则不匹配,回退到 JSON 渲染
169
+
170
+ ## 渲染流程
171
+
172
+ `TemplateRender.render()` 按以下逻辑确定模版名:
173
+
174
+ 1. 若数据为 `ResultFail` 实例,包装为 `{ fail: data }`,并使用 `failTemplate`
175
+ 2. 模版名优先级:`opt.template` → `ctx.path.slice(1) + templateAffix`
176
+ 3. 调用 `engine(template, data)` 渲染输出
177
+
178
+ ## 依赖模块
179
+
180
+ - `@zenweb/result` — 必须在此之前安装,提供 `RenderManager` 和 `ResultRender` 接口
181
+
182
+ ## 已知模版引擎适配器
183
+
184
+ | 包名 | 引擎 |
185
+ |------|------|
186
+ | `@zenweb/template-nunjucks` | Nunjucks |
187
+ | `@zenweb/template-handlebars` | Handlebars |
package/dist/global.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TemplateOption } from "./types.js";
1
+ import { TemplateOption } from "./types";
2
2
  /**
3
3
  * 启用并设置模版渲染
4
4
  * - 不传参数: 启用
package/dist/global.js CHANGED
@@ -1,4 +1,7 @@
1
- import { $getContext } from "@zenweb/core";
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.$template = $template;
4
+ const core_1 = require("@zenweb/core");
2
5
  /**
3
6
  * 启用并设置模版渲染
4
7
  * - 不传参数: 启用
@@ -6,6 +9,6 @@ import { $getContext } from "@zenweb/core";
6
9
  * - string: 设置模版文件名
7
10
  * - TemplateOption: 详细设置
8
11
  */
9
- export function $template(template_or_option) {
10
- return $getContext().template(template_or_option);
12
+ function $template(template_or_option) {
13
+ return (0, core_1.$getContext)().template(template_or_option);
11
14
  }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SetupFunction } from '@zenweb/core';
2
- import { TemplateSetupOption, TemplateOption } from './types.js';
3
- export * from './types.js';
4
- export * from './global.js';
2
+ import { TemplateSetupOption, TemplateOption } from './types';
3
+ export * from './types';
4
+ export * from './global';
5
5
  export default function setup(option: TemplateSetupOption): SetupFunction;
6
6
  declare module '@zenweb/core' {
7
7
  interface Context {
package/dist/index.js CHANGED
@@ -1,7 +1,24 @@
1
- import { RenderManager } from '@zenweb/result';
2
- import { TemplateRender } from './render.js';
3
- export * from './types.js';
4
- export * from './global.js';
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.default = setup;
18
+ const result_1 = require("@zenweb/result");
19
+ const render_1 = require("./render");
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./global"), exports);
5
22
  function contextTemplate(template_or_option) {
6
23
  if (template_or_option === false) {
7
24
  this.templateOption = undefined;
@@ -17,15 +34,15 @@ function contextTemplate(template_or_option) {
17
34
  Object.assign(this.templateOption, template_or_option);
18
35
  }
19
36
  }
20
- export default function setup(option) {
37
+ function setup(option) {
21
38
  return async function template(setup) {
22
39
  if (!option.engineName) {
23
40
  option.engineName = option.engine.name;
24
41
  }
25
42
  setup.debug('option: %o', option);
26
43
  setup.assertModuleExists('result', '@zenweb/result');
27
- const renderManager = await setup.core.injector.getInstance(RenderManager);
28
- renderManager.add(new TemplateRender(option));
44
+ const renderManager = await setup.core.injector.getInstance(result_1.RenderManager);
45
+ renderManager.add(new render_1.TemplateRender(option));
29
46
  if (!('template' in setup.app.context)) {
30
47
  setup.defineContextProperty('template', { value: contextTemplate });
31
48
  }
package/dist/render.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context } from '@zenweb/core';
2
2
  import { ResultRender } from '@zenweb/result';
3
- import { TemplateSetupOption } from './types.js';
3
+ import { TemplateSetupOption } from './types';
4
4
  export declare class TemplateRender implements ResultRender {
5
5
  private _option;
6
6
  enwrap: boolean;
package/dist/render.js CHANGED
@@ -1,11 +1,12 @@
1
- import { ResultFail } from '@zenweb/result';
2
- import { debug } from './utils.js';
3
- export class TemplateRender {
4
- _option;
5
- enwrap = false;
6
- type;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TemplateRender = void 0;
4
+ const result_1 = require("@zenweb/result");
5
+ const utils_1 = require("./utils");
6
+ class TemplateRender {
7
7
  constructor(_option) {
8
8
  this._option = _option;
9
+ this.enwrap = false;
9
10
  this.type = _option.type || 'html';
10
11
  }
11
12
  match(ctx) {
@@ -13,12 +14,12 @@ export class TemplateRender {
13
14
  if (opt) {
14
15
  // 匹配引擎
15
16
  if (opt.engine === this._option.engineName) {
16
- debug('matchEngine: %o', this._option.engineName);
17
+ (0, utils_1.debug)('matchEngine: %o', this._option.engineName);
17
18
  return true;
18
19
  }
19
20
  // 指定模版
20
21
  if (opt.template && this._option.templateAffix && opt.template.endsWith(this._option.templateAffix)) {
21
- debug('matchTemplateAffix: %o', this._option.templateAffix);
22
+ (0, utils_1.debug)('matchTemplateAffix: %o', this._option.templateAffix);
22
23
  return true;
23
24
  }
24
25
  }
@@ -26,7 +27,7 @@ export class TemplateRender {
26
27
  if (this._option.ignorePath) {
27
28
  for (const reg of this._option.ignorePath) {
28
29
  if (reg.test(ctx.path)) {
29
- debug('ignorePath: %o', reg);
30
+ (0, utils_1.debug)('ignorePath: %o', reg);
30
31
  return false;
31
32
  }
32
33
  }
@@ -35,7 +36,7 @@ export class TemplateRender {
35
36
  if (this._option.matchPath) {
36
37
  for (const reg of this._option.matchPath) {
37
38
  if (reg.test(ctx.path)) {
38
- debug('matchPath: %o', reg);
39
+ (0, utils_1.debug)('matchPath: %o', reg);
39
40
  return true;
40
41
  }
41
42
  }
@@ -44,7 +45,7 @@ export class TemplateRender {
44
45
  if (this._option.matchAccept) {
45
46
  const _type = ctx.accepts().some(accept => this._option.matchAccept?.includes(accept));
46
47
  if (_type) {
47
- debug('matchAccept: %o', _type);
48
+ (0, utils_1.debug)('matchAccept: %o', _type);
48
49
  return true;
49
50
  }
50
51
  }
@@ -53,7 +54,7 @@ export class TemplateRender {
53
54
  render(ctx, data) {
54
55
  const opt = ctx.templateOption || {};
55
56
  let template = opt.template;
56
- if (data instanceof ResultFail) {
57
+ if (data instanceof result_1.ResultFail) {
57
58
  data = { fail: data };
58
59
  if (opt.failTemplate !== false) {
59
60
  template = opt.failTemplate || this._option.failTemplate || template;
@@ -68,3 +69,4 @@ export class TemplateRender {
68
69
  return this._option.engine(template, data);
69
70
  }
70
71
  }
72
+ exports.TemplateRender = TemplateRender;
package/dist/types.js CHANGED
@@ -1 +1,2 @@
1
- export {};
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/utils.js CHANGED
@@ -1,7 +1,44 @@
1
- import * as path from 'path';
2
- import { debug as _debug } from '@zenweb/core';
3
- export const debug = _debug.extend('template');
4
- export function cwdPath(p) {
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.debug = void 0;
37
+ exports.cwdPath = cwdPath;
38
+ const path = __importStar(require("path"));
39
+ const core_1 = require("@zenweb/core");
40
+ exports.debug = core_1.debug.extend('template');
41
+ function cwdPath(p) {
5
42
  if (p.startsWith('./')) {
6
43
  return path.join(process.cwd(), p.slice(2));
7
44
  }
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@zenweb/template",
3
- "type": "module",
4
- "version": "5.1.0",
3
+ "version": "5.2.0",
5
4
  "description": "zenweb template render module",
6
5
  "exports": "./dist/index.js",
7
6
  "types": "./dist/index.d.ts",
8
7
  "scripts": {
9
- "build": "rimraf dist && tsc",
8
+ "build": "rimraf dist && tsc -p tsconfig.build.json",
10
9
  "prepack": "npm run build",
11
- "dev": "cd example && node --env-file=.env --import tsx/esm app.ts"
10
+ "dev": "cd example && node --env-file=.env -r ts-node/register app.ts"
12
11
  },
13
12
  "files": [
13
+ "AGENTS.md",
14
14
  "dist"
15
15
  ],
16
16
  "author": {
@@ -25,17 +25,17 @@
25
25
  "license": "MIT",
26
26
  "homepage": "https://zenweb.node.ltd",
27
27
  "devDependencies": {
28
- "@types/node": "^20.10.6",
29
- "@zenweb/inject": "^5.1.0",
30
- "@zenweb/template-handlebars": "^3.0.0",
31
- "@zenweb/template-nunjucks": "^3.3.0",
28
+ "@types/node": "^16.18.126",
29
+ "@zenweb/inject": "^5.4.0",
30
+ "@zenweb/template-handlebars": "^4.1.0",
31
+ "@zenweb/template-nunjucks": "^4.1.0",
32
32
  "rimraf": "^4.4.1",
33
- "tsx": "^4.19.1",
34
- "typescript": "^5.3.3",
35
- "zenweb": "^5.1.0"
33
+ "ts-node": "^10.9.2",
34
+ "typescript": "^6.0.3",
35
+ "zenweb": "^6.4.0"
36
36
  },
37
37
  "dependencies": {
38
- "@zenweb/core": "^5.1.0",
39
- "@zenweb/result": "^5.0.0"
38
+ "@zenweb/core": "^5.3.0",
39
+ "@zenweb/result": "^5.3.0"
40
40
  }
41
41
  }