@cloudpss/template 0.5.23 → 0.5.24
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/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +20 -3
- package/dist/parser.js +79 -43
- package/dist/parser.js.map +1 -1
- package/dist/template/compiler.js +25 -15
- package/dist/template/compiler.js.map +1 -1
- package/dist/template/index.d.ts +9 -1
- package/dist/template/index.js +0 -1
- package/dist/template/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/parser.ts +97 -53
- package/src/template/compiler.ts +27 -18
- package/src/template/index.ts +13 -2
- package/tests/parser.js +88 -3
- package/tests/template.js +47 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { parseTemplate } from './parser.js';
|
|
2
|
-
export type { Template } from './parser.js';
|
|
1
|
+
export { parseTemplate, parseInterpolation } from './parser.js';
|
|
2
|
+
export type { Template, FormulaTemplate, InterpolationTemplate } from './parser.js';
|
|
3
3
|
export { template } from './template/index.js';
|
|
4
4
|
export type { TemplateFunction, TemplateOptions } from './template/index.js';
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/parser.d.ts
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export type
|
|
1
|
+
/** 公式模板 */
|
|
2
|
+
export type FormulaTemplate = {
|
|
3
3
|
type: 'formula';
|
|
4
4
|
value: string;
|
|
5
|
-
}
|
|
5
|
+
};
|
|
6
|
+
/** 字符串插值模板 */
|
|
7
|
+
export type InterpolationTemplate = {
|
|
6
8
|
type: 'interpolation';
|
|
7
9
|
templates: string[];
|
|
8
10
|
values: string[];
|
|
9
11
|
};
|
|
12
|
+
/** 字符串模板 */
|
|
13
|
+
export type Template = string | FormulaTemplate | InterpolationTemplate;
|
|
10
14
|
/** 字符串模板类型 */
|
|
11
15
|
export type TemplateType = (Template & object)['type'];
|
|
16
|
+
/**
|
|
17
|
+
* 解析字符串插值模板
|
|
18
|
+
* @example parseInterpolation("hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
|
|
19
|
+
* // {
|
|
20
|
+
* // type: 'interpolation',
|
|
21
|
+
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. Give me $$100.'],
|
|
22
|
+
* // values: ['name', 'age', 'age+1']
|
|
23
|
+
* // }
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseInterpolation(template: string, start?: number, length?: number): InterpolationTemplate;
|
|
12
26
|
/**
|
|
13
27
|
* 解析字符串模板
|
|
28
|
+
* - 如果模板以 `=` 开头,则表示是一个公式
|
|
29
|
+
* - 如果模板以 `$` 开头,则表示是一个插值模板
|
|
30
|
+
* - 否则表示是一个普通字符串
|
|
14
31
|
*/
|
|
15
32
|
export declare function parseTemplate(template: string): Template;
|
package/dist/parser.js
CHANGED
|
@@ -1,88 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const PARSER_STATUS_TEXT = 0;
|
|
2
|
+
const PARSER_STATUS_EXPRESSION_SIMPLE = 1;
|
|
3
|
+
const PARSER_STATUS_EXPRESSION_COMPLEX = 2;
|
|
4
|
+
/** 检查字符满足 [a-zA-Z0-9_] */
|
|
5
|
+
function isIdentifierChar(char) {
|
|
6
|
+
const charCode = char.charCodeAt(0);
|
|
7
|
+
return ((charCode >= 97 && charCode <= 122) ||
|
|
8
|
+
(charCode >= 65 && charCode <= 90) ||
|
|
9
|
+
(charCode >= 48 && charCode <= 57) ||
|
|
10
|
+
charCode === 95);
|
|
11
|
+
}
|
|
12
|
+
/** 字符串插值模板标识符 */
|
|
13
|
+
const INTERPOLATION_CHAR = '$';
|
|
14
|
+
/** 字符串插值模板表达式开始标识符 */
|
|
15
|
+
const INTERPOLATION_EXPRESSION_START = '{';
|
|
16
|
+
/** 字符串插值模板表达式结束标识符 */
|
|
17
|
+
const INTERPOLATION_EXPRESSION_END = '}';
|
|
8
18
|
/**
|
|
9
19
|
* 解析字符串插值模板
|
|
10
|
-
* @example
|
|
20
|
+
* @example parseInterpolationImpl("hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
|
|
11
21
|
* // {
|
|
12
22
|
* // type: 'interpolation',
|
|
13
23
|
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. Give me $$100.'],
|
|
14
24
|
* // values: ['name', 'age', 'age+1']
|
|
15
25
|
* // }
|
|
16
26
|
*/
|
|
17
|
-
function
|
|
27
|
+
function parseInterpolationImpl(template, start, length) {
|
|
18
28
|
const templates = [];
|
|
19
29
|
const values = [];
|
|
20
30
|
let currentTemplate = '';
|
|
21
31
|
let currentValue = '';
|
|
22
32
|
let expressionComplexDepth = 0;
|
|
23
|
-
let status =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (status ===
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// End of input
|
|
33
|
-
currentTemplate += char;
|
|
34
|
-
continue;
|
|
33
|
+
let status = PARSER_STATUS_TEXT;
|
|
34
|
+
const end = start + length - 1;
|
|
35
|
+
for (let i = start; i <= end; i++) {
|
|
36
|
+
if (status === PARSER_STATUS_TEXT) {
|
|
37
|
+
const nextInterpolationChar = template.indexOf(INTERPOLATION_CHAR, i);
|
|
38
|
+
if (nextInterpolationChar < 0 || nextInterpolationChar >= end) {
|
|
39
|
+
// No more interpolation
|
|
40
|
+
currentTemplate += template.slice(i, end + 1);
|
|
41
|
+
break;
|
|
35
42
|
}
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
currentTemplate += template.slice(i, nextInterpolationChar);
|
|
44
|
+
const nextChar = template.charAt(nextInterpolationChar + 1);
|
|
45
|
+
i = nextInterpolationChar;
|
|
46
|
+
if (nextChar === INTERPOLATION_CHAR) {
|
|
38
47
|
// Escaped dollar sign
|
|
39
|
-
currentTemplate +=
|
|
48
|
+
currentTemplate += INTERPOLATION_CHAR;
|
|
40
49
|
i++;
|
|
41
50
|
continue;
|
|
42
51
|
}
|
|
43
|
-
if (nextChar ===
|
|
44
|
-
// Start of expression
|
|
52
|
+
if (nextChar === INTERPOLATION_EXPRESSION_START) {
|
|
53
|
+
// Start of complex expression
|
|
45
54
|
templates.push(currentTemplate);
|
|
46
55
|
currentTemplate = '';
|
|
47
|
-
status =
|
|
56
|
+
status = PARSER_STATUS_EXPRESSION_COMPLEX;
|
|
48
57
|
expressionComplexDepth = 1;
|
|
49
58
|
i++;
|
|
50
59
|
continue;
|
|
51
60
|
}
|
|
52
|
-
if (
|
|
53
|
-
// Start of expression
|
|
61
|
+
if (isIdentifierChar(nextChar)) {
|
|
62
|
+
// Start of simple expression
|
|
54
63
|
templates.push(currentTemplate);
|
|
55
64
|
currentTemplate = '';
|
|
56
|
-
|
|
65
|
+
currentValue = nextChar;
|
|
66
|
+
status = PARSER_STATUS_EXPRESSION_SIMPLE;
|
|
67
|
+
i++;
|
|
57
68
|
continue;
|
|
58
69
|
}
|
|
59
70
|
// Not an expression
|
|
60
|
-
currentTemplate +=
|
|
71
|
+
currentTemplate += INTERPOLATION_CHAR;
|
|
61
72
|
continue;
|
|
62
73
|
}
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
const char = template.charAt(i);
|
|
75
|
+
if (status === PARSER_STATUS_EXPRESSION_SIMPLE) {
|
|
76
|
+
if (isIdentifierChar(char)) {
|
|
65
77
|
currentValue += char;
|
|
66
78
|
continue;
|
|
67
79
|
}
|
|
68
80
|
// End of expression
|
|
69
81
|
values.push(currentValue);
|
|
70
82
|
currentValue = '';
|
|
71
|
-
status =
|
|
83
|
+
status = PARSER_STATUS_TEXT;
|
|
72
84
|
i--;
|
|
73
85
|
continue;
|
|
74
86
|
}
|
|
75
|
-
if (status ===
|
|
76
|
-
if (char ===
|
|
87
|
+
if (status === PARSER_STATUS_EXPRESSION_COMPLEX) {
|
|
88
|
+
if (char === INTERPOLATION_EXPRESSION_START) {
|
|
77
89
|
expressionComplexDepth++;
|
|
78
90
|
}
|
|
79
|
-
else if (char ===
|
|
91
|
+
else if (char === INTERPOLATION_EXPRESSION_END) {
|
|
80
92
|
expressionComplexDepth--;
|
|
81
93
|
if (expressionComplexDepth === 0) {
|
|
82
94
|
// End of expression
|
|
83
95
|
values.push(currentValue.trim());
|
|
84
96
|
currentValue = '';
|
|
85
|
-
status =
|
|
97
|
+
status = PARSER_STATUS_TEXT;
|
|
86
98
|
continue;
|
|
87
99
|
}
|
|
88
100
|
}
|
|
@@ -90,26 +102,47 @@ function parseInterpolation(template) {
|
|
|
90
102
|
continue;
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
|
-
if (status ===
|
|
105
|
+
if (status === PARSER_STATUS_TEXT) {
|
|
94
106
|
templates.push(currentTemplate);
|
|
95
107
|
}
|
|
96
|
-
else if (status ===
|
|
108
|
+
else if (status === PARSER_STATUS_EXPRESSION_SIMPLE) {
|
|
97
109
|
values.push(currentValue);
|
|
98
110
|
templates.push('');
|
|
99
111
|
}
|
|
100
112
|
else {
|
|
101
113
|
throw new Error('Unexpected end of input');
|
|
102
114
|
}
|
|
103
|
-
if (values.length === 0)
|
|
104
|
-
return templates[0];
|
|
105
115
|
return {
|
|
106
116
|
type: 'interpolation',
|
|
107
117
|
templates,
|
|
108
118
|
values,
|
|
109
119
|
};
|
|
110
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* 解析字符串插值模板
|
|
123
|
+
* @example parseInterpolation("hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
|
|
124
|
+
* // {
|
|
125
|
+
* // type: 'interpolation',
|
|
126
|
+
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. Give me $$100.'],
|
|
127
|
+
* // values: ['name', 'age', 'age+1']
|
|
128
|
+
* // }
|
|
129
|
+
*/
|
|
130
|
+
export function parseInterpolation(template, start, length) {
|
|
131
|
+
if (start == null)
|
|
132
|
+
start = 0;
|
|
133
|
+
if (length == null)
|
|
134
|
+
length = template.length - start;
|
|
135
|
+
if (start < 0 || start > template.length)
|
|
136
|
+
throw new Error('start out of range');
|
|
137
|
+
if (length < 0 || start + length > template.length)
|
|
138
|
+
throw new Error('length out of range');
|
|
139
|
+
return parseInterpolationImpl(template, start, length);
|
|
140
|
+
}
|
|
111
141
|
/**
|
|
112
142
|
* 解析字符串模板
|
|
143
|
+
* - 如果模板以 `=` 开头,则表示是一个公式
|
|
144
|
+
* - 如果模板以 `$` 开头,则表示是一个插值模板
|
|
145
|
+
* - 否则表示是一个普通字符串
|
|
113
146
|
*/
|
|
114
147
|
export function parseTemplate(template) {
|
|
115
148
|
if (!template)
|
|
@@ -121,7 +154,10 @@ export function parseTemplate(template) {
|
|
|
121
154
|
};
|
|
122
155
|
}
|
|
123
156
|
if (template.startsWith('$')) {
|
|
124
|
-
|
|
157
|
+
const result = parseInterpolationImpl(template, 1, template.length - 1);
|
|
158
|
+
if (result.values.length === 0)
|
|
159
|
+
return result.templates[0];
|
|
160
|
+
return result;
|
|
125
161
|
}
|
|
126
162
|
return template;
|
|
127
163
|
}
|
package/dist/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,8 @@ function isError(value) {
|
|
|
12
12
|
return value instanceof Error || (typeof DOMException == 'function' && value instanceof DOMException);
|
|
13
13
|
}
|
|
14
14
|
const KNOWN_ERRORS = [EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError];
|
|
15
|
+
/** 模板序列号 */
|
|
16
|
+
let seq = 0;
|
|
15
17
|
/** 创建模板 */
|
|
16
18
|
export class TemplateCompiler {
|
|
17
19
|
template;
|
|
@@ -34,9 +36,9 @@ export class TemplateCompiler {
|
|
|
34
36
|
buildString(str) {
|
|
35
37
|
const parsed = parseTemplate(str);
|
|
36
38
|
if (typeof parsed === 'string')
|
|
37
|
-
return JSON.stringify(parsed);
|
|
39
|
+
return [JSON.stringify(parsed), false];
|
|
38
40
|
if (parsed.type === 'formula')
|
|
39
|
-
return this.buildEval(parsed.value, parsed.type);
|
|
41
|
+
return [this.buildEval(parsed.value, parsed.type), true];
|
|
40
42
|
let result = '';
|
|
41
43
|
for (let i = 0; i < parsed.templates.length; i++) {
|
|
42
44
|
if (parsed.templates[i]) {
|
|
@@ -48,22 +50,22 @@ export class TemplateCompiler {
|
|
|
48
50
|
result += '+' + this.buildEval(parsed.values[i], parsed.type);
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
|
-
return
|
|
53
|
+
return [result, true];
|
|
52
54
|
}
|
|
53
55
|
/** 构建 Error */
|
|
54
56
|
buildError(err) {
|
|
55
57
|
if (typeof DOMException == 'function' && err instanceof DOMException) {
|
|
56
|
-
return `new DOMException(${this.buildString(err.message)}, ${this.buildString(err.name)})`;
|
|
58
|
+
return `new DOMException(${this.buildString(err.message)[0]}, ${this.buildString(err.name)[0]})`;
|
|
57
59
|
}
|
|
58
60
|
const constructor = KNOWN_ERRORS.find((type) => err instanceof type)?.name ?? 'Error';
|
|
59
61
|
if (err.name === constructor) {
|
|
60
|
-
return `new ${constructor}(${this.buildString(err.message)})`;
|
|
62
|
+
return `new ${constructor}(${this.buildString(err.message)[0]})`;
|
|
61
63
|
}
|
|
62
|
-
return `Object.assign(new ${constructor}(${this.buildString(err.message)}), {name: ${this.buildString(err.name)}})`;
|
|
64
|
+
return `Object.assign(new ${constructor}(${this.buildString(err.message)[0]}), {name: ${this.buildString(err.name)[0]}})`;
|
|
63
65
|
}
|
|
64
66
|
/** 构建数组 */
|
|
65
67
|
buildArray(arr) {
|
|
66
|
-
return `[${arr.map(this.buildValue.bind(this)).join('
|
|
68
|
+
return `[${arr.map(this.buildValue.bind(this)).join(',\n')}]`;
|
|
67
69
|
}
|
|
68
70
|
/** 构建 ArrayBuffer */
|
|
69
71
|
buildArrayBuffer(buffer) {
|
|
@@ -83,12 +85,18 @@ export class TemplateCompiler {
|
|
|
83
85
|
continue;
|
|
84
86
|
const value = obj[key];
|
|
85
87
|
if (result)
|
|
86
|
-
result += '
|
|
88
|
+
result += ',\n';
|
|
87
89
|
if (this.options.objectKeyMode === 'ignore') {
|
|
88
90
|
result += JSON.stringify(key);
|
|
89
91
|
}
|
|
90
92
|
else {
|
|
91
|
-
|
|
93
|
+
const [e, isExpression] = this.buildString(key);
|
|
94
|
+
if (isExpression) {
|
|
95
|
+
result += '[' + e + ']';
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
result += e;
|
|
99
|
+
}
|
|
92
100
|
}
|
|
93
101
|
result += ':';
|
|
94
102
|
result += this.buildValue(value);
|
|
@@ -114,7 +122,7 @@ export class TemplateCompiler {
|
|
|
114
122
|
if (typeof value == 'number')
|
|
115
123
|
return String(value);
|
|
116
124
|
if (typeof value == 'string')
|
|
117
|
-
return this.buildString(value);
|
|
125
|
+
return this.buildString(value)[0];
|
|
118
126
|
/* c8 ignore next */
|
|
119
127
|
if (typeof value != 'object')
|
|
120
128
|
throw new Error(`Unsupported value: ${Object.prototype.toString.call(value)}`);
|
|
@@ -141,11 +149,13 @@ export class TemplateCompiler {
|
|
|
141
149
|
const params = [...this.params];
|
|
142
150
|
try {
|
|
143
151
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
144
|
-
const result = new Function(...params.map(([key]) => key),
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
const result = new Function(...params.map(([key]) => key), [
|
|
153
|
+
`//# sourceURL=cloudpss-template[${seq++}]`, // sourceURL 用于调试
|
|
154
|
+
this.options.evaluator.async
|
|
155
|
+
? `return async (context = {}) => (${source});`
|
|
156
|
+
: `return (context = {}) => (${source});`,
|
|
157
|
+
].join('\n'))(...params.map(([, value]) => value));
|
|
158
|
+
Object.defineProperty(result, 'source', { value: source, configurable: true });
|
|
149
159
|
return result;
|
|
150
160
|
/* c8 ignore next 3 */
|
|
151
161
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAqB,MAAM,cAAc,CAAC;AAGhE,sBAAsB;AACtB,SAAS,aAAa,CAAC,KAAa;IAChC,IAAI,KAAK,YAAY,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,OAAO,iBAAiB,IAAI,UAAU,IAAI,KAAK,YAAY,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAC9F,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,gBAAgB;AAChB,SAAS,OAAO,CAAC,KAAc;IAC3B,OAAO,KAAK,YAAY,KAAK,IAAI,CAAC,OAAO,YAAY,IAAI,UAAU,IAAI,KAAK,YAAY,YAAY,CAAC,CAAC;AAC1G,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAExG,WAAW;AACX,MAAM,OAAO,gBAAgB;IAEZ;IACA;IAFb,YACa,QAAiB,EACjB,OAAkC;QADlC,aAAQ,GAAR,QAAQ,CAAS;QACjB,YAAO,GAAP,OAAO,CAA2B;IAC5C,CAAC;IACa,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IACpC,QAAQ,GAAc,EAAE,CAAC;IAC1C,WAAW;IACH,SAAS,CAAC,UAAkB,EAAE,IAAkB;QACpD,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,YAAY;IACJ,WAAW,CAAC,GAAW;QAC3B,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"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"}
|
package/dist/template/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ import type { TemplateType } from '../parser.js';
|
|
|
2
2
|
/** 模板编译求值 */
|
|
3
3
|
export type TemplateEvaluator = {
|
|
4
4
|
/** 注入为 `evaluator` 变量,可在生成的求值代码中使用 */
|
|
5
|
-
inject
|
|
5
|
+
inject?: unknown;
|
|
6
|
+
/** 求值代码是否为异步 @default false */
|
|
7
|
+
async?: boolean;
|
|
6
8
|
/** 生成求值代码,可用变量:`evaluator` `context` */
|
|
7
9
|
compile: (expression: string, type: TemplateType) => string;
|
|
8
10
|
};
|
|
@@ -27,4 +29,10 @@ export type TemplateFunction<T = unknown, C = Record<string, unknown>> = ((conte
|
|
|
27
29
|
source: string;
|
|
28
30
|
};
|
|
29
31
|
/** 创建模板 */
|
|
32
|
+
export declare function template<T = unknown, C = Record<string, unknown>>(template: T, options: TemplateOptions & {
|
|
33
|
+
evaluator: {
|
|
34
|
+
async: true;
|
|
35
|
+
};
|
|
36
|
+
}): TemplateFunction<Promise<T>, C>;
|
|
37
|
+
/** 创建模板 */
|
|
30
38
|
export declare function template<T = unknown, C = Record<string, unknown>>(template: T, options?: TemplateOptions): TemplateFunction<T, C>;
|
package/dist/template/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/template/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/template/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AA4BjD,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IAC/C,OAAO,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE;QAC1B,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,SAAS;gBACV,OAAO,WAAW,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;YACpD,KAAK,eAAe;gBAChB,OAAO,YAAY,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC;YAC5D,sBAAsB;YACtB;gBACI,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAA8B,EAAE,CAAC,CAAC;QAC/E,CAAC;IACL,CAAC;CACJ,CAAC;AAiBF,WAAW;AACX,MAAM,UAAU,QAAQ,CACpB,QAAW,EACX,UAA2B,EAAE;IAE7B,MAAM,GAAG,GAAG;QACR,aAAa,EAAE,UAAU;QACzB,SAAS,EAAE,gBAAgB;QAC3B,GAAG,OAAO;KACb,CAAC;IACF,OAAO,IAAI,gBAAgB,CAAC,QAAQ,EAAE,GAAgC,CAAC,CAAC,KAAK,EAA4B,CAAC;AAC9G,CAAC"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { parseTemplate } from './parser.js';
|
|
2
|
-
export type { Template } from './parser.js';
|
|
1
|
+
export { parseTemplate, parseInterpolation } from './parser.js';
|
|
2
|
+
export type { Template, FormulaTemplate, InterpolationTemplate } from './parser.js';
|
|
3
3
|
export { template } from './template/index.js';
|
|
4
4
|
export type { TemplateFunction, TemplateOptions } from './template/index.js';
|
package/src/parser.ts
CHANGED
|
@@ -1,105 +1,129 @@
|
|
|
1
|
+
/** 公式模板 */
|
|
2
|
+
export type FormulaTemplate = {
|
|
3
|
+
type: 'formula';
|
|
4
|
+
value: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
/** 字符串插值模板 */
|
|
8
|
+
export type InterpolationTemplate = {
|
|
9
|
+
type: 'interpolation';
|
|
10
|
+
templates: string[];
|
|
11
|
+
values: string[];
|
|
12
|
+
};
|
|
13
|
+
|
|
1
14
|
/** 字符串模板 */
|
|
2
|
-
export type Template =
|
|
3
|
-
| string
|
|
4
|
-
| {
|
|
5
|
-
type: 'formula';
|
|
6
|
-
value: string;
|
|
7
|
-
}
|
|
8
|
-
| {
|
|
9
|
-
type: 'interpolation';
|
|
10
|
-
templates: string[];
|
|
11
|
-
values: string[];
|
|
12
|
-
};
|
|
15
|
+
export type Template = string | FormulaTemplate | InterpolationTemplate;
|
|
13
16
|
|
|
14
17
|
/** 字符串模板类型 */
|
|
15
18
|
export type TemplateType = (Template & object)['type'];
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const PARSER_STATUS_TEXT = 0;
|
|
21
|
+
const PARSER_STATUS_EXPRESSION_SIMPLE = 1;
|
|
22
|
+
const PARSER_STATUS_EXPRESSION_COMPLEX = 2;
|
|
23
|
+
/** 解析状态机 */
|
|
24
|
+
type ParserStatus =
|
|
25
|
+
| typeof PARSER_STATUS_TEXT
|
|
26
|
+
| typeof PARSER_STATUS_EXPRESSION_SIMPLE
|
|
27
|
+
| typeof PARSER_STATUS_EXPRESSION_COMPLEX;
|
|
28
|
+
|
|
29
|
+
/** 检查字符满足 [a-zA-Z0-9_] */
|
|
30
|
+
function isIdentifierChar(char: string): boolean {
|
|
31
|
+
const charCode = char.charCodeAt(0);
|
|
32
|
+
return (
|
|
33
|
+
(charCode >= 97 && charCode <= 122) ||
|
|
34
|
+
(charCode >= 65 && charCode <= 90) ||
|
|
35
|
+
(charCode >= 48 && charCode <= 57) ||
|
|
36
|
+
charCode === 95
|
|
37
|
+
);
|
|
21
38
|
}
|
|
22
|
-
|
|
39
|
+
|
|
40
|
+
/** 字符串插值模板标识符 */
|
|
41
|
+
const INTERPOLATION_CHAR = '$';
|
|
42
|
+
/** 字符串插值模板表达式开始标识符 */
|
|
43
|
+
const INTERPOLATION_EXPRESSION_START = '{';
|
|
44
|
+
/** 字符串插值模板表达式结束标识符 */
|
|
45
|
+
const INTERPOLATION_EXPRESSION_END = '}';
|
|
23
46
|
|
|
24
47
|
/**
|
|
25
48
|
* 解析字符串插值模板
|
|
26
|
-
* @example
|
|
49
|
+
* @example parseInterpolationImpl("hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
|
|
27
50
|
* // {
|
|
28
51
|
* // type: 'interpolation',
|
|
29
52
|
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. Give me $$100.'],
|
|
30
53
|
* // values: ['name', 'age', 'age+1']
|
|
31
54
|
* // }
|
|
32
55
|
*/
|
|
33
|
-
function
|
|
56
|
+
function parseInterpolationImpl(template: string, start: number, length: number): InterpolationTemplate {
|
|
34
57
|
const templates: string[] = [];
|
|
35
58
|
const values: string[] = [];
|
|
36
59
|
let currentTemplate = '';
|
|
37
60
|
let currentValue = '';
|
|
38
61
|
let expressionComplexDepth = 0;
|
|
39
|
-
let status =
|
|
40
|
-
|
|
41
|
-
for (let i = 1; i < template.length; i++) {
|
|
42
|
-
const char = template[i];
|
|
62
|
+
let status: ParserStatus = PARSER_STATUS_TEXT;
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
continue;
|
|
64
|
+
const end = start + length - 1;
|
|
65
|
+
for (let i = start; i <= end; i++) {
|
|
66
|
+
if (status === PARSER_STATUS_TEXT) {
|
|
67
|
+
const nextInterpolationChar = template.indexOf(INTERPOLATION_CHAR, i);
|
|
68
|
+
if (nextInterpolationChar < 0 || nextInterpolationChar >= end) {
|
|
69
|
+
// No more interpolation
|
|
70
|
+
currentTemplate += template.slice(i, end + 1);
|
|
71
|
+
break;
|
|
53
72
|
}
|
|
54
|
-
|
|
55
|
-
|
|
73
|
+
currentTemplate += template.slice(i, nextInterpolationChar);
|
|
74
|
+
const nextChar = template.charAt(nextInterpolationChar + 1);
|
|
75
|
+
i = nextInterpolationChar;
|
|
76
|
+
if (nextChar === INTERPOLATION_CHAR) {
|
|
56
77
|
// Escaped dollar sign
|
|
57
|
-
currentTemplate +=
|
|
78
|
+
currentTemplate += INTERPOLATION_CHAR;
|
|
58
79
|
i++;
|
|
59
80
|
continue;
|
|
60
81
|
}
|
|
61
|
-
if (nextChar ===
|
|
62
|
-
// Start of expression
|
|
82
|
+
if (nextChar === INTERPOLATION_EXPRESSION_START) {
|
|
83
|
+
// Start of complex expression
|
|
63
84
|
templates.push(currentTemplate);
|
|
64
85
|
currentTemplate = '';
|
|
65
|
-
status =
|
|
86
|
+
status = PARSER_STATUS_EXPRESSION_COMPLEX;
|
|
66
87
|
expressionComplexDepth = 1;
|
|
67
88
|
i++;
|
|
68
89
|
continue;
|
|
69
90
|
}
|
|
70
|
-
if (
|
|
71
|
-
// Start of expression
|
|
91
|
+
if (isIdentifierChar(nextChar)) {
|
|
92
|
+
// Start of simple expression
|
|
72
93
|
templates.push(currentTemplate);
|
|
73
94
|
currentTemplate = '';
|
|
74
|
-
|
|
95
|
+
currentValue = nextChar;
|
|
96
|
+
status = PARSER_STATUS_EXPRESSION_SIMPLE;
|
|
97
|
+
i++;
|
|
75
98
|
continue;
|
|
76
99
|
}
|
|
77
100
|
// Not an expression
|
|
78
|
-
currentTemplate +=
|
|
101
|
+
currentTemplate += INTERPOLATION_CHAR;
|
|
79
102
|
continue;
|
|
80
103
|
}
|
|
81
|
-
|
|
82
|
-
|
|
104
|
+
const char = template.charAt(i);
|
|
105
|
+
if (status === PARSER_STATUS_EXPRESSION_SIMPLE) {
|
|
106
|
+
if (isIdentifierChar(char)) {
|
|
83
107
|
currentValue += char;
|
|
84
108
|
continue;
|
|
85
109
|
}
|
|
86
110
|
// End of expression
|
|
87
111
|
values.push(currentValue);
|
|
88
112
|
currentValue = '';
|
|
89
|
-
status =
|
|
113
|
+
status = PARSER_STATUS_TEXT;
|
|
90
114
|
i--;
|
|
91
115
|
continue;
|
|
92
116
|
}
|
|
93
|
-
if (status ===
|
|
94
|
-
if (char ===
|
|
117
|
+
if (status === PARSER_STATUS_EXPRESSION_COMPLEX) {
|
|
118
|
+
if (char === INTERPOLATION_EXPRESSION_START) {
|
|
95
119
|
expressionComplexDepth++;
|
|
96
|
-
} else if (char ===
|
|
120
|
+
} else if (char === INTERPOLATION_EXPRESSION_END) {
|
|
97
121
|
expressionComplexDepth--;
|
|
98
122
|
if (expressionComplexDepth === 0) {
|
|
99
123
|
// End of expression
|
|
100
124
|
values.push(currentValue.trim());
|
|
101
125
|
currentValue = '';
|
|
102
|
-
status =
|
|
126
|
+
status = PARSER_STATUS_TEXT;
|
|
103
127
|
continue;
|
|
104
128
|
}
|
|
105
129
|
}
|
|
@@ -107,17 +131,15 @@ function parseInterpolation(template: string): Template {
|
|
|
107
131
|
continue;
|
|
108
132
|
}
|
|
109
133
|
}
|
|
110
|
-
if (status ===
|
|
134
|
+
if (status === PARSER_STATUS_TEXT) {
|
|
111
135
|
templates.push(currentTemplate);
|
|
112
|
-
} else if (status ===
|
|
136
|
+
} else if (status === PARSER_STATUS_EXPRESSION_SIMPLE) {
|
|
113
137
|
values.push(currentValue);
|
|
114
138
|
templates.push('');
|
|
115
139
|
} else {
|
|
116
140
|
throw new Error('Unexpected end of input');
|
|
117
141
|
}
|
|
118
142
|
|
|
119
|
-
if (values.length === 0) return templates[0];
|
|
120
|
-
|
|
121
143
|
return {
|
|
122
144
|
type: 'interpolation',
|
|
123
145
|
templates,
|
|
@@ -125,8 +147,28 @@ function parseInterpolation(template: string): Template {
|
|
|
125
147
|
};
|
|
126
148
|
}
|
|
127
149
|
|
|
150
|
+
/**
|
|
151
|
+
* 解析字符串插值模板
|
|
152
|
+
* @example parseInterpolation("hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
|
|
153
|
+
* // {
|
|
154
|
+
* // type: 'interpolation',
|
|
155
|
+
* // templates: ['hello ', '! I am ', ' years old. I\'ll be ', ' next year. Give me $$100.'],
|
|
156
|
+
* // values: ['name', 'age', 'age+1']
|
|
157
|
+
* // }
|
|
158
|
+
*/
|
|
159
|
+
export function parseInterpolation(template: string, start?: number, length?: number): InterpolationTemplate {
|
|
160
|
+
if (start == null) start = 0;
|
|
161
|
+
if (length == null) length = template.length - start;
|
|
162
|
+
if (start < 0 || start > template.length) throw new Error('start out of range');
|
|
163
|
+
if (length < 0 || start + length > template.length) throw new Error('length out of range');
|
|
164
|
+
return parseInterpolationImpl(template, start, length);
|
|
165
|
+
}
|
|
166
|
+
|
|
128
167
|
/**
|
|
129
168
|
* 解析字符串模板
|
|
169
|
+
* - 如果模板以 `=` 开头,则表示是一个公式
|
|
170
|
+
* - 如果模板以 `$` 开头,则表示是一个插值模板
|
|
171
|
+
* - 否则表示是一个普通字符串
|
|
130
172
|
*/
|
|
131
173
|
export function parseTemplate(template: string): Template {
|
|
132
174
|
if (!template) return '';
|
|
@@ -137,7 +179,9 @@ export function parseTemplate(template: string): Template {
|
|
|
137
179
|
};
|
|
138
180
|
}
|
|
139
181
|
if (template.startsWith('$')) {
|
|
140
|
-
|
|
182
|
+
const result = parseInterpolationImpl(template, 1, template.length - 1);
|
|
183
|
+
if (result.values.length === 0) return result.templates[0];
|
|
184
|
+
return result;
|
|
141
185
|
}
|
|
142
186
|
return template;
|
|
143
187
|
}
|
package/src/template/compiler.ts
CHANGED
|
@@ -15,6 +15,9 @@ function isError(value: unknown): value is Error {
|
|
|
15
15
|
|
|
16
16
|
const KNOWN_ERRORS = [EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError] as const;
|
|
17
17
|
|
|
18
|
+
/** 模板序列号 */
|
|
19
|
+
let seq = 0;
|
|
20
|
+
|
|
18
21
|
/** 创建模板 */
|
|
19
22
|
export class TemplateCompiler {
|
|
20
23
|
constructor(
|
|
@@ -32,10 +35,10 @@ export class TemplateCompiler {
|
|
|
32
35
|
return evaluator.compile(expression, type);
|
|
33
36
|
}
|
|
34
37
|
/** 构建字符串 */
|
|
35
|
-
private buildString(str: string): string {
|
|
38
|
+
private buildString(str: string): [result: string, isExpression: boolean] {
|
|
36
39
|
const parsed = parseTemplate(str);
|
|
37
|
-
if (typeof parsed === 'string') return JSON.stringify(parsed);
|
|
38
|
-
if (parsed.type === 'formula') return this.buildEval(parsed.value, parsed.type);
|
|
40
|
+
if (typeof parsed === 'string') return [JSON.stringify(parsed), false];
|
|
41
|
+
if (parsed.type === 'formula') return [this.buildEval(parsed.value, parsed.type), true];
|
|
39
42
|
let result = '';
|
|
40
43
|
for (let i = 0; i < parsed.templates.length; i++) {
|
|
41
44
|
if (parsed.templates[i]) {
|
|
@@ -46,22 +49,22 @@ export class TemplateCompiler {
|
|
|
46
49
|
result += '+' + this.buildEval(parsed.values[i], parsed.type);
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
|
-
return
|
|
52
|
+
return [result, true];
|
|
50
53
|
}
|
|
51
54
|
/** 构建 Error */
|
|
52
55
|
private buildError(err: Error): string {
|
|
53
56
|
if (typeof DOMException == 'function' && err instanceof DOMException) {
|
|
54
|
-
return `new DOMException(${this.buildString(err.message)}, ${this.buildString(err.name)})`;
|
|
57
|
+
return `new DOMException(${this.buildString(err.message)[0]}, ${this.buildString(err.name)[0]})`;
|
|
55
58
|
}
|
|
56
59
|
const constructor = KNOWN_ERRORS.find((type) => err instanceof type)?.name ?? 'Error';
|
|
57
60
|
if (err.name === constructor) {
|
|
58
|
-
return `new ${constructor}(${this.buildString(err.message)})`;
|
|
61
|
+
return `new ${constructor}(${this.buildString(err.message)[0]})`;
|
|
59
62
|
}
|
|
60
|
-
return `Object.assign(new ${constructor}(${this.buildString(err.message)}), {name: ${this.buildString(err.name)}})`;
|
|
63
|
+
return `Object.assign(new ${constructor}(${this.buildString(err.message)[0]}), {name: ${this.buildString(err.name)[0]}})`;
|
|
61
64
|
}
|
|
62
65
|
/** 构建数组 */
|
|
63
66
|
private buildArray(arr: unknown[]): string {
|
|
64
|
-
return `[${arr.map(this.buildValue.bind(this)).join('
|
|
67
|
+
return `[${arr.map(this.buildValue.bind(this)).join(',\n')}]`;
|
|
65
68
|
}
|
|
66
69
|
/** 构建 ArrayBuffer */
|
|
67
70
|
private buildArrayBuffer(buffer: ArrayBuffer | SharedArrayBuffer): string {
|
|
@@ -79,11 +82,16 @@ export class TemplateCompiler {
|
|
|
79
82
|
for (const key in obj) {
|
|
80
83
|
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
|
|
81
84
|
const value = obj[key];
|
|
82
|
-
if (result) result += '
|
|
85
|
+
if (result) result += ',\n';
|
|
83
86
|
if (this.options.objectKeyMode === 'ignore') {
|
|
84
87
|
result += JSON.stringify(key);
|
|
85
88
|
} else {
|
|
86
|
-
|
|
89
|
+
const [e, isExpression] = this.buildString(key);
|
|
90
|
+
if (isExpression) {
|
|
91
|
+
result += '[' + e + ']';
|
|
92
|
+
} else {
|
|
93
|
+
result += e;
|
|
94
|
+
}
|
|
87
95
|
}
|
|
88
96
|
result += ':';
|
|
89
97
|
result += this.buildValue(value);
|
|
@@ -100,7 +108,7 @@ export class TemplateCompiler {
|
|
|
100
108
|
if (typeof value == 'symbol') return 'undefined';
|
|
101
109
|
if (typeof value == 'bigint') return `${value}n`;
|
|
102
110
|
if (typeof value == 'number') return String(value);
|
|
103
|
-
if (typeof value == 'string') return this.buildString(value);
|
|
111
|
+
if (typeof value == 'string') return this.buildString(value)[0];
|
|
104
112
|
/* c8 ignore next */
|
|
105
113
|
if (typeof value != 'object') throw new Error(`Unsupported value: ${Object.prototype.toString.call(value)}`);
|
|
106
114
|
if (value instanceof Date) return `new Date(${value.getTime()})`;
|
|
@@ -122,13 +130,14 @@ export class TemplateCompiler {
|
|
|
122
130
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
123
131
|
const result = new Function(
|
|
124
132
|
...params.map(([key]) => key),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
[
|
|
134
|
+
`//# sourceURL=cloudpss-template[${seq++}]`, // sourceURL 用于调试
|
|
135
|
+
this.options.evaluator.async
|
|
136
|
+
? `return async (context = {}) => (${source});`
|
|
137
|
+
: `return (context = {}) => (${source});`,
|
|
138
|
+
].join('\n'),
|
|
139
|
+
)(...params.map(([, value]) => value)) as TemplateFunction;
|
|
140
|
+
Object.defineProperty(result, 'source', { value: source, configurable: true });
|
|
132
141
|
return result;
|
|
133
142
|
/* c8 ignore next 3 */
|
|
134
143
|
} catch (e) {
|
package/src/template/index.ts
CHANGED
|
@@ -4,7 +4,9 @@ import { TemplateCompiler } from './compiler.js';
|
|
|
4
4
|
/** 模板编译求值 */
|
|
5
5
|
export type TemplateEvaluator = {
|
|
6
6
|
/** 注入为 `evaluator` 变量,可在生成的求值代码中使用 */
|
|
7
|
-
inject
|
|
7
|
+
inject?: unknown;
|
|
8
|
+
/** 求值代码是否为异步 @default false */
|
|
9
|
+
async?: boolean;
|
|
8
10
|
/** 生成求值代码,可用变量:`evaluator` `context` */
|
|
9
11
|
compile: (expression: string, type: TemplateType) => string;
|
|
10
12
|
};
|
|
@@ -26,7 +28,6 @@ export interface TemplateOptions {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export const defaultEvaluator: TemplateEvaluator = {
|
|
29
|
-
inject: undefined,
|
|
30
31
|
compile: (expression, type) => {
|
|
31
32
|
switch (type) {
|
|
32
33
|
case 'formula':
|
|
@@ -44,6 +45,16 @@ export const defaultEvaluator: TemplateEvaluator = {
|
|
|
44
45
|
export type TemplateFunction<T = unknown, C = Record<string, unknown>> = ((context?: C) => T) & {
|
|
45
46
|
source: string;
|
|
46
47
|
};
|
|
48
|
+
/** 创建模板 */
|
|
49
|
+
export function template<T = unknown, C = Record<string, unknown>>(
|
|
50
|
+
template: T,
|
|
51
|
+
options: TemplateOptions & { evaluator: { async: true } },
|
|
52
|
+
): TemplateFunction<Promise<T>, C>;
|
|
53
|
+
/** 创建模板 */
|
|
54
|
+
export function template<T = unknown, C = Record<string, unknown>>(
|
|
55
|
+
template: T,
|
|
56
|
+
options?: TemplateOptions,
|
|
57
|
+
): TemplateFunction<T, C>;
|
|
47
58
|
|
|
48
59
|
/** 创建模板 */
|
|
49
60
|
export function template<T = unknown, C = Record<string, unknown>>(
|
package/tests/parser.js
CHANGED
|
@@ -1,4 +1,89 @@
|
|
|
1
|
-
import { parseTemplate } from '../dist/parser.js';
|
|
1
|
+
import { parseTemplate, parseInterpolation } from '../dist/parser.js';
|
|
2
|
+
|
|
3
|
+
describe('parseInterpolation', () => {
|
|
4
|
+
it('should parse simple interpolation', () => {
|
|
5
|
+
expect(parseInterpolation('')).toEqual({
|
|
6
|
+
type: 'interpolation',
|
|
7
|
+
templates: [''],
|
|
8
|
+
values: [],
|
|
9
|
+
});
|
|
10
|
+
expect(parseInterpolation('hello')).toEqual({
|
|
11
|
+
type: 'interpolation',
|
|
12
|
+
templates: ['hello'],
|
|
13
|
+
values: [],
|
|
14
|
+
});
|
|
15
|
+
expect(parseInterpolation('hello $name!')).toEqual({
|
|
16
|
+
type: 'interpolation',
|
|
17
|
+
templates: ['hello ', '!'],
|
|
18
|
+
values: ['name'],
|
|
19
|
+
});
|
|
20
|
+
expect(parseInterpolation('$$name')).toEqual({
|
|
21
|
+
type: 'interpolation',
|
|
22
|
+
templates: ['$name'],
|
|
23
|
+
values: [],
|
|
24
|
+
});
|
|
25
|
+
expect(parseInterpolation('$name_is_long')).toEqual({
|
|
26
|
+
type: 'interpolation',
|
|
27
|
+
templates: ['', ''],
|
|
28
|
+
values: ['name_is_long'],
|
|
29
|
+
});
|
|
30
|
+
expect(parseInterpolation('$_001')).toEqual({
|
|
31
|
+
type: 'interpolation',
|
|
32
|
+
templates: ['', ''],
|
|
33
|
+
values: ['_001'],
|
|
34
|
+
});
|
|
35
|
+
expect(parseInterpolation('$001$a')).toEqual({
|
|
36
|
+
type: 'interpolation',
|
|
37
|
+
templates: ['', '', ''],
|
|
38
|
+
values: ['001', 'a'],
|
|
39
|
+
});
|
|
40
|
+
expect(parseInterpolation('$$name$a')).toEqual({
|
|
41
|
+
type: 'interpolation',
|
|
42
|
+
templates: ['$name', ''],
|
|
43
|
+
values: ['a'],
|
|
44
|
+
});
|
|
45
|
+
expect(parseInterpolation('$ $ $ $')).toEqual({
|
|
46
|
+
type: 'interpolation',
|
|
47
|
+
templates: ['$ $ $ $'],
|
|
48
|
+
values: [],
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('should parse range', () => {
|
|
52
|
+
it('should throw on bad range', () => {
|
|
53
|
+
expect(() => parseInterpolation('xxx', -1, 0)).toThrow('start out of range');
|
|
54
|
+
expect(() => parseInterpolation('xxx', 0, 4)).toThrow('length out of range');
|
|
55
|
+
expect(() => parseInterpolation('xxx', 1, 3)).toThrow('length out of range');
|
|
56
|
+
expect(() => parseInterpolation('xxx', 3, -1)).toThrow('length out of range');
|
|
57
|
+
});
|
|
58
|
+
it('should parse range', () => {
|
|
59
|
+
expect(parseInterpolation('abcde', 0, 3)).toEqual({
|
|
60
|
+
type: 'interpolation',
|
|
61
|
+
templates: ['abc'],
|
|
62
|
+
values: [],
|
|
63
|
+
});
|
|
64
|
+
expect(parseInterpolation('abcde', 1, 3)).toEqual({
|
|
65
|
+
type: 'interpolation',
|
|
66
|
+
templates: ['bcd'],
|
|
67
|
+
values: [],
|
|
68
|
+
});
|
|
69
|
+
expect(parseInterpolation('abcde', 2, 3)).toEqual({
|
|
70
|
+
type: 'interpolation',
|
|
71
|
+
templates: ['cde'],
|
|
72
|
+
values: [],
|
|
73
|
+
});
|
|
74
|
+
expect(parseInterpolation('abcde', 3, 1)).toEqual({
|
|
75
|
+
type: 'interpolation',
|
|
76
|
+
templates: ['d'],
|
|
77
|
+
values: [],
|
|
78
|
+
});
|
|
79
|
+
expect(parseInterpolation('abcde', 5, 0)).toEqual({
|
|
80
|
+
type: 'interpolation',
|
|
81
|
+
templates: [''],
|
|
82
|
+
values: [],
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
2
87
|
|
|
3
88
|
describe('parseTemplate', () => {
|
|
4
89
|
describe('should parse plain', () => {
|
|
@@ -66,8 +151,8 @@ describe('parseTemplate', () => {
|
|
|
66
151
|
});
|
|
67
152
|
expect(parseTemplate('$$001$a')).toEqual({
|
|
68
153
|
type: 'interpolation',
|
|
69
|
-
templates: ['
|
|
70
|
-
values: ['a'],
|
|
154
|
+
templates: ['', '', ''],
|
|
155
|
+
values: ['001', 'a'],
|
|
71
156
|
});
|
|
72
157
|
expect(parseTemplate('$$name$a')).toEqual({
|
|
73
158
|
type: 'interpolation',
|
package/tests/template.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomBytes, randomFillSync } from 'node:crypto';
|
|
2
|
+
import { setTimeout as timeout } from 'node:timers/promises';
|
|
2
3
|
import { template } from '@cloudpss/template';
|
|
3
4
|
|
|
4
5
|
describe('template', () => {
|
|
@@ -258,4 +259,50 @@ describe('template', () => {
|
|
|
258
259
|
expect(result).toEqual({ a: '1', b: '' });
|
|
259
260
|
});
|
|
260
261
|
});
|
|
262
|
+
describe('should work with custom evaluator', () => {
|
|
263
|
+
it('with formula', () => {
|
|
264
|
+
const obj = { a: '=a', b: '=b' };
|
|
265
|
+
const result = template(obj, {
|
|
266
|
+
evaluator: {
|
|
267
|
+
inject: { a: 'abcd', b: 11n },
|
|
268
|
+
compile: (expression) => `evaluator.${expression}`,
|
|
269
|
+
},
|
|
270
|
+
})({ a: 1 });
|
|
271
|
+
expect(result).toEqual({ a: 'abcd', b: 11n });
|
|
272
|
+
});
|
|
273
|
+
it('with interpolation', () => {
|
|
274
|
+
const obj = { a: '$$a', b: '$$b' };
|
|
275
|
+
const result = template(obj, {
|
|
276
|
+
evaluator: {
|
|
277
|
+
inject: { a: 'abcd', b: 11n },
|
|
278
|
+
compile: (expression) => `evaluator.${expression}`,
|
|
279
|
+
},
|
|
280
|
+
})({ a: 1 });
|
|
281
|
+
expect(result).toEqual({ a: 'abcd', b: '11' });
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
describe('should work with async evaluator', () => {
|
|
285
|
+
it('with formula', async () => {
|
|
286
|
+
const obj = { a: '=a', b: '=b' };
|
|
287
|
+
const result = template(obj, {
|
|
288
|
+
evaluator: {
|
|
289
|
+
inject: { a: 'abcd', b: 11n, timeout },
|
|
290
|
+
async: true,
|
|
291
|
+
compile: (expression) => `await evaluator.timeout(100, evaluator.${expression})`,
|
|
292
|
+
},
|
|
293
|
+
})({ a: 1 });
|
|
294
|
+
await expect(result).resolves.toEqual({ a: 'abcd', b: 11n });
|
|
295
|
+
});
|
|
296
|
+
it('with interpolation', async () => {
|
|
297
|
+
const obj = { a: '$$a', b: '$$b' };
|
|
298
|
+
const result = template(obj, {
|
|
299
|
+
evaluator: {
|
|
300
|
+
inject: { a: 'abcd', b: 11n, timeout },
|
|
301
|
+
async: true,
|
|
302
|
+
compile: (expression) => `await evaluator.timeout(100, evaluator.${expression})`,
|
|
303
|
+
},
|
|
304
|
+
})({ a: 1 });
|
|
305
|
+
await expect(result).resolves.toEqual({ a: 'abcd', b: '11' });
|
|
306
|
+
});
|
|
307
|
+
});
|
|
261
308
|
});
|