@zenweb/template 3.0.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/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # zenweb-template
2
+
3
+ zenweb template render module
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @zenweb/template
9
+ ```
10
+
11
+ ### nunjucks
12
+
13
+ ```bash
14
+ npm install nunjucks
15
+ npm install @types/nunjucks --save-dev
16
+ ```
17
+
18
+ ### handlebars
19
+
20
+ ```bash
21
+ npm install handlebars
22
+ ```
23
+
24
+ ## Config
25
+
26
+ src/index.ts
27
+ ```ts
28
+ import { create } from 'zenweb';
29
+ import modTemplate from '@zenweb/template';
30
+ import nunjucks from '@zenweb/template/engine/nunjucks';
31
+ import handlebars from '@zenweb/template/handlebars';
32
+
33
+ const app = create();
34
+
35
+ app.setup(modTemplate({
36
+ matchPath: [/\.html$/i],
37
+ engine: nunjucks({
38
+ filter: require('./template/filter'),
39
+ global: require('./template/global'),
40
+ }),
41
+ }));
42
+
43
+ // Multiple Engines
44
+ app.setup(modTemplate({
45
+ type: 'xml',
46
+ matchPath: [/\.xml$/i],
47
+ templateAffix: '.xml',
48
+ engineName: 'xml',
49
+ engine: handlebars(),
50
+ }));
51
+
52
+ app.start();
53
+ ```
54
+
55
+ ## Use
56
+
57
+ src/controller/demo.ts
58
+ ```ts
59
+ import { Context, mapping } from "zenweb";
60
+
61
+ export class DemoController {
62
+ @mapping({ path: ['/', '/index.html'] })
63
+ index(ctx: Context) {
64
+ ctx.template('index.html');
65
+ return { name: 'Hello' };
66
+ }
67
+
68
+ @mapping({ path: '/test.html' })
69
+ test() {
70
+ return { name: 'Test' };
71
+ }
72
+
73
+ @mapping()
74
+ xml(ctx: Context) {
75
+ ctx.template({ engine: 'xml' })
76
+ return { name: 'Test' };
77
+ }
78
+ }
79
+ ```
80
+
81
+ template/index.html
82
+ ```html
83
+ <h1>Hello {{name}}</h1>
84
+ ```
85
+
86
+ template/test.html
87
+ ```html
88
+ <h1>Hello {{name}}</h1>
89
+ ```
90
+
91
+ template/xml.xml
92
+ ```xml
93
+ <root>{{name}}</root>
94
+ ```
@@ -0,0 +1,14 @@
1
+ import { TemplateEngine } from '../types';
2
+ export interface HandlebarsOption extends CompileOptions {
3
+ /**
4
+ * 模版文件夹
5
+ * @default './template'
6
+ */
7
+ path?: string | string[];
8
+ /**
9
+ * 缓存已编译模版
10
+ * @default true
11
+ */
12
+ cache?: boolean;
13
+ }
14
+ export default function (option?: HandlebarsOption): TemplateEngine;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path = require("path");
4
+ const fs = require("fs");
5
+ const util_1 = require("util");
6
+ const handlebars_1 = require("handlebars");
7
+ const utils_1 = require("../utils");
8
+ const fs_exists = (0, util_1.promisify)(fs.exists);
9
+ const fs_readFile = (0, util_1.promisify)(fs.readFile);
10
+ function default_1(option) {
11
+ const opt = Object.assign({
12
+ path: './template',
13
+ cache: true,
14
+ }, option);
15
+ (0, utils_1.debug)('option: %o', opt);
16
+ const paths = (Array.isArray(opt.path) ? opt.path : [opt.path]).map(p => (0, utils_1.cwdPath)(p));
17
+ async function loadTemplate(template) {
18
+ for (const p of paths) {
19
+ const filename = path.join(p, template);
20
+ if (await fs_exists(filename)) {
21
+ return await fs_readFile(filename, 'utf-8');
22
+ }
23
+ }
24
+ throw new Error(`Template file not found: ${template}`);
25
+ }
26
+ const cache = {};
27
+ return async function handlebars(template, data) {
28
+ if (opt.cache && !cache[template]) {
29
+ const content = await loadTemplate(template);
30
+ cache[template] = (0, handlebars_1.compile)(content, opt);
31
+ }
32
+ return cache[template](data);
33
+ };
34
+ }
35
+ exports.default = default_1;
@@ -0,0 +1,36 @@
1
+ import { Environment, ConfigureOptions, Extension } from 'nunjucks';
2
+ import { TemplateEngine } from '../types';
3
+ export interface NunjucksOption {
4
+ /**
5
+ * 模版文件夹
6
+ * @default './template'
7
+ */
8
+ path?: string | string[];
9
+ /**
10
+ * 配置过滤器
11
+ */
12
+ filter?: {
13
+ [name: string]: (...args: any[]) => any;
14
+ };
15
+ /**
16
+ * 配置自定义扩展
17
+ */
18
+ extension?: {
19
+ [name: string]: Extension;
20
+ };
21
+ /**
22
+ * 配置全局变量
23
+ */
24
+ global?: {
25
+ [name: string]: any;
26
+ };
27
+ /**
28
+ * 设置 nunjucks 配置
29
+ */
30
+ nunjucksConfig?: ConfigureOptions;
31
+ /**
32
+ * 设置 nunjucks 环境变量
33
+ */
34
+ configureEnvironment?: (env: Environment) => any;
35
+ }
36
+ export default function (option?: NunjucksOption): TemplateEngine;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const nunjucks_1 = require("nunjucks");
4
+ const utils_1 = require("../utils");
5
+ function default_1(option) {
6
+ const opt = Object.assign({
7
+ path: './template',
8
+ }, option);
9
+ (0, utils_1.debug)('option: %o', opt);
10
+ const paths = (Array.isArray(opt.path) ? opt.path : [opt.path]).map(p => (0, utils_1.cwdPath)(p));
11
+ const env = (0, nunjucks_1.configure)(paths, opt.nunjucksConfig);
12
+ if (opt.filter) {
13
+ Object.entries(opt.filter).forEach(([name, func]) => env.addFilter(name, func));
14
+ }
15
+ if (opt.extension) {
16
+ Object.entries(opt.extension).forEach(([name, ext]) => env.addExtension(name, ext));
17
+ }
18
+ if (opt.global) {
19
+ Object.entries(opt.global).forEach(([name, value]) => env.addGlobal(name, value));
20
+ }
21
+ if (typeof opt.configureEnvironment === 'function') {
22
+ opt.configureEnvironment(env);
23
+ }
24
+ return function nunjucks(name, data) {
25
+ return new Promise((resolve, reject) => {
26
+ env.render(name, data, (err, res) => {
27
+ if (err)
28
+ return reject(err);
29
+ resolve(res);
30
+ });
31
+ });
32
+ };
33
+ }
34
+ exports.default = default_1;
@@ -0,0 +1,20 @@
1
+ import { SetupFunction } from '@zenweb/core';
2
+ import { RenderManager } from '@zenweb/result';
3
+ import { TemplateSetupOption, TemplateOption } from './types';
4
+ import { TEMPLATE_OPTION } from './render';
5
+ export * from './types';
6
+ export { RenderManager };
7
+ export default function setup(option: TemplateSetupOption): SetupFunction;
8
+ declare module '@zenweb/core' {
9
+ interface Context {
10
+ [TEMPLATE_OPTION]?: TemplateOption;
11
+ /**
12
+ * 启用并设置模版渲染
13
+ * - 不传参数: 启用
14
+ * - false: 关闭模版渲染
15
+ * - string: 设置模版文件名
16
+ * - TemplateOption: 详细设置
17
+ */
18
+ template(template_or_option?: false | string | TemplateOption): void;
19
+ }
20
+ }
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
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.RenderManager = void 0;
18
+ const result_1 = require("@zenweb/result");
19
+ Object.defineProperty(exports, "RenderManager", { enumerable: true, get: function () { return result_1.RenderManager; } });
20
+ const render_1 = require("./render");
21
+ __exportStar(require("./types"), exports);
22
+ function contextTemplate(template_or_option) {
23
+ if (template_or_option === false) {
24
+ delete this[render_1.TEMPLATE_OPTION];
25
+ return;
26
+ }
27
+ if (!this[render_1.TEMPLATE_OPTION]) {
28
+ this[render_1.TEMPLATE_OPTION] = {};
29
+ }
30
+ if (typeof template_or_option === 'string') {
31
+ Object.assign(this[render_1.TEMPLATE_OPTION], { template: template_or_option });
32
+ }
33
+ else if (typeof template_or_option === 'object') {
34
+ Object.assign(this[render_1.TEMPLATE_OPTION], template_or_option);
35
+ }
36
+ }
37
+ function setup(option) {
38
+ return async function template(setup) {
39
+ if (!option.engineName) {
40
+ option.engineName = option.engine.name;
41
+ }
42
+ setup.debug('option: %o', option);
43
+ setup.assertModuleExists('result', '@zenweb/result');
44
+ const renderManager = await setup.core.injector.getInstance(result_1.RenderManager);
45
+ renderManager.add(new render_1.TemplateRender(option));
46
+ if (!('template' in setup.app.context)) {
47
+ setup.defineContextProperty('template', { value: contextTemplate });
48
+ }
49
+ };
50
+ }
51
+ exports.default = setup;
@@ -0,0 +1,11 @@
1
+ import { Context } from '@zenweb/core';
2
+ import { ResultRender } from '@zenweb/result';
3
+ import { TemplateSetupOption } from './types';
4
+ export declare const TEMPLATE_OPTION: unique symbol;
5
+ export declare class TemplateRender implements ResultRender {
6
+ private _option;
7
+ type: string;
8
+ constructor(_option: TemplateSetupOption);
9
+ match(ctx: Context): boolean;
10
+ render(ctx: Context, data?: unknown): any;
11
+ }
package/dist/render.js ADDED
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TemplateRender = exports.TEMPLATE_OPTION = void 0;
4
+ const utils_1 = require("./utils");
5
+ exports.TEMPLATE_OPTION = Symbol('template@option');
6
+ class TemplateRender {
7
+ constructor(_option) {
8
+ this._option = _option;
9
+ this.type = this._option.type || 'html';
10
+ }
11
+ match(ctx) {
12
+ const opt = ctx[exports.TEMPLATE_OPTION];
13
+ if (opt && opt.engine === this._option.engineName) {
14
+ (0, utils_1.debug)('matchEngine: %o', this._option.engineName);
15
+ return true;
16
+ }
17
+ // 匹配路径规则
18
+ if (this._option.matchPath) {
19
+ for (const reg of this._option.matchPath) {
20
+ if (reg.test(ctx.path)) {
21
+ (0, utils_1.debug)('matchPath: %o', reg);
22
+ return true;
23
+ }
24
+ }
25
+ }
26
+ // 匹配 Accept 头类型
27
+ if (this._option.matchAccept) {
28
+ const _type = ctx.accepts(this._option.matchAccept);
29
+ if (_type) {
30
+ (0, utils_1.debug)('matchAccept: %o', _type);
31
+ return true;
32
+ }
33
+ }
34
+ return false;
35
+ }
36
+ render(ctx, data) {
37
+ console.log(this);
38
+ const opt = ctx[exports.TEMPLATE_OPTION];
39
+ let template = opt === null || opt === void 0 ? void 0 : opt.template;
40
+ if (!template) {
41
+ template = ctx.path.slice(1);
42
+ if (this._option.templateAffix) {
43
+ template += this._option.templateAffix;
44
+ }
45
+ }
46
+ return this._option.engine(template, data);
47
+ }
48
+ }
49
+ exports.TemplateRender = TemplateRender;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 渲染引擎的渲染方法
3
+ * - 方法名作为默认的引擎名称
4
+ */
5
+ export type TemplateEngine = (template: string, data?: any) => any;
6
+ export interface TemplateSetupOption {
7
+ /**
8
+ * 输出类型
9
+ * @default 'html'
10
+ */
11
+ type?: string;
12
+ /**
13
+ * 匹配路径结尾例如 `/.html$/i`
14
+ */
15
+ matchPath?: RegExp[];
16
+ /**
17
+ * 匹配 Accept Type 头信息
18
+ */
19
+ matchAccept?: string[];
20
+ /**
21
+ * 模版名后缀
22
+ * - 例如 '.html'
23
+ * - 在不指定 `TemplateOption.template` 时有效
24
+ */
25
+ templateAffix?: string;
26
+ /**
27
+ * 失败使用模版
28
+ */
29
+ failTemplate?: string;
30
+ /**
31
+ * 渲染引擎
32
+ */
33
+ engine: TemplateEngine;
34
+ /**
35
+ * 设置渲染引擎名称
36
+ * - 引擎名称默认取 `engine.name` 可以设置此项改名,可用于多模块切换
37
+ */
38
+ engineName?: string;
39
+ }
40
+ export interface TemplateOption {
41
+ /**
42
+ * 指定模版名,不指定取 `ctx.path`
43
+ */
44
+ template?: string;
45
+ /**
46
+ * 使用的渲染引擎
47
+ * - 对应的是 `TemplateSetupOption.engineName`
48
+ */
49
+ engine?: string;
50
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ /// <reference types="debug" />
2
+ export declare const debug: import("debug").Debugger;
3
+ export declare function cwdPath(p: string): string;
package/dist/utils.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cwdPath = exports.debug = void 0;
4
+ const path = require("path");
5
+ const debug_1 = require("debug");
6
+ exports.debug = (0, debug_1.default)('zenweb:template');
7
+ function cwdPath(p) {
8
+ if (p.startsWith('./')) {
9
+ return path.join(process.cwd(), p.slice(2));
10
+ }
11
+ return p;
12
+ }
13
+ exports.cwdPath = cwdPath;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@zenweb/template",
3
+ "version": "3.0.0",
4
+ "description": "zenweb template render module",
5
+ "exports": {
6
+ ".": "./dist/index.js",
7
+ "./engine": "./dist/engine"
8
+ },
9
+ "types": "./dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "rimraf dist && tsc",
12
+ "prepublishOnly": "npm run build",
13
+ "dev": "cd example && cross-env DEBUG=* ts-node app"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "author": {
19
+ "name": "YeFei",
20
+ "email": "316606233@qq.com"
21
+ },
22
+ "keywords": [
23
+ "zenweb",
24
+ "koa",
25
+ "template",
26
+ "nunjucks"
27
+ ],
28
+ "license": "MIT",
29
+ "homepage": "https://zenweb.node.ltd",
30
+ "devDependencies": {
31
+ "@types/nunjucks": "^3.2.1",
32
+ "cross-env": "^7.0.3",
33
+ "handlebars": "^4.7.7",
34
+ "nunjucks": "^3.2.3",
35
+ "rimraf": "^4.4.1",
36
+ "ts-node": "^10.9.1",
37
+ "typescript": "^5.0.2",
38
+ "zenweb": "^3.18.0"
39
+ },
40
+ "dependencies": {
41
+ "debug": "^4.3.4"
42
+ }
43
+ }