@cloudpss/template 0.5.23 → 0.5.25

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 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
@@ -1,3 +1,3 @@
1
- export { parseTemplate } from './parser.js';
1
+ export { parseTemplate, parseInterpolation } from './parser.js';
2
2
  export { template } from './template/index.js';
3
3
  //# sourceMappingURL=index.js.map
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;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,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 Template = string | {
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
- var ParserStatus;
2
- (function (ParserStatus) {
3
- ParserStatus[ParserStatus["Text"] = 0] = "Text";
4
- ParserStatus[ParserStatus["ExpressionSimple"] = 1] = "ExpressionSimple";
5
- ParserStatus[ParserStatus["ExpressionComplex"] = 2] = "ExpressionComplex";
6
- })(ParserStatus || (ParserStatus = {}));
7
- Object.freeze(ParserStatus);
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 parseInterpolation("$hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
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 parseInterpolation(template) {
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 = ParserStatus.Text;
24
- for (let i = 1; i < template.length; i++) {
25
- const char = template[i];
26
- if (status === ParserStatus.Text) {
27
- if (char !== '$') {
28
- currentTemplate += char;
29
- continue;
30
- }
31
- if (i === template.length - 1) {
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
- const nextChar = template[i + 1];
37
- if (nextChar === '$') {
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 += char;
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 = ParserStatus.ExpressionComplex;
56
+ status = PARSER_STATUS_EXPRESSION_COMPLEX;
48
57
  expressionComplexDepth = 1;
49
58
  i++;
50
59
  continue;
51
60
  }
52
- if (/[a-zA-Z_]/.exec(nextChar)) {
53
- // Start of expression
61
+ if (isIdentifierChar(nextChar)) {
62
+ // Start of simple expression
54
63
  templates.push(currentTemplate);
55
64
  currentTemplate = '';
56
- status = ParserStatus.ExpressionSimple;
65
+ currentValue = nextChar;
66
+ status = PARSER_STATUS_EXPRESSION_SIMPLE;
67
+ i++;
57
68
  continue;
58
69
  }
59
70
  // Not an expression
60
- currentTemplate += char;
71
+ currentTemplate += INTERPOLATION_CHAR;
61
72
  continue;
62
73
  }
63
- if (status === ParserStatus.ExpressionSimple) {
64
- if (/[a-zA-Z0-9_]/.exec(char)) {
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 = ParserStatus.Text;
83
+ status = PARSER_STATUS_TEXT;
72
84
  i--;
73
85
  continue;
74
86
  }
75
- if (status === ParserStatus.ExpressionComplex) {
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 = ParserStatus.Text;
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 === ParserStatus.Text) {
105
+ if (status === PARSER_STATUS_TEXT) {
94
106
  templates.push(currentTemplate);
95
107
  }
96
- else if (status === ParserStatus.ExpressionSimple) {
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
- return parseInterpolation(template);
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
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 '(' + result + ')';
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
- result += '[' + this.buildString(key) + ']';
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), 'context', `
145
- if (context == null) context = {};
146
- return (${source});
147
- `).bind(undefined, ...params.map(([, value]) => value));
148
- result.source = source;
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":"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"}
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../src/template/compiler.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,9 @@ import type { TemplateType } from '../parser.js';
2
2
  /** 模板编译求值 */
3
3
  export type TemplateEvaluator = {
4
4
  /** 注入为 `evaluator` 变量,可在生成的求值代码中使用 */
5
- inject: unknown;
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>;
@@ -1,6 +1,5 @@
1
1
  import { TemplateCompiler } from './compiler.js';
2
2
  export const defaultEvaluator = {
3
- inject: undefined,
4
3
  compile: (expression, type) => {
5
4
  switch (type) {
6
5
  case 'formula':
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/template/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AA0BjD,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IAC/C,MAAM,EAAE,SAAS;IACjB,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;AAOF,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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudpss/template",
3
- "version": "0.5.23",
3
+ "version": "0.5.25",
4
4
  "author": "CloudPSS",
5
5
  "license": "MIT",
6
6
  "keywords": [
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
- enum ParserStatus {
18
- Text,
19
- ExpressionSimple,
20
- ExpressionComplex,
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
- Object.freeze(ParserStatus);
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 parseInterpolation("$hello $name! I am $age years old. I'll be ${age+1} next year. Give me $$100.")
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 parseInterpolation(template: string): Template {
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 = ParserStatus.Text;
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
- if (status === ParserStatus.Text) {
45
- if (char !== '$') {
46
- currentTemplate += char;
47
- continue;
48
- }
49
- if (i === template.length - 1) {
50
- // End of input
51
- currentTemplate += char;
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
- const nextChar = template[i + 1];
55
- if (nextChar === '$') {
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 += char;
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 = ParserStatus.ExpressionComplex;
86
+ status = PARSER_STATUS_EXPRESSION_COMPLEX;
66
87
  expressionComplexDepth = 1;
67
88
  i++;
68
89
  continue;
69
90
  }
70
- if (/[a-zA-Z_]/.exec(nextChar)) {
71
- // Start of expression
91
+ if (isIdentifierChar(nextChar)) {
92
+ // Start of simple expression
72
93
  templates.push(currentTemplate);
73
94
  currentTemplate = '';
74
- status = ParserStatus.ExpressionSimple;
95
+ currentValue = nextChar;
96
+ status = PARSER_STATUS_EXPRESSION_SIMPLE;
97
+ i++;
75
98
  continue;
76
99
  }
77
100
  // Not an expression
78
- currentTemplate += char;
101
+ currentTemplate += INTERPOLATION_CHAR;
79
102
  continue;
80
103
  }
81
- if (status === ParserStatus.ExpressionSimple) {
82
- if (/[a-zA-Z0-9_]/.exec(char)) {
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 = ParserStatus.Text;
113
+ status = PARSER_STATUS_TEXT;
90
114
  i--;
91
115
  continue;
92
116
  }
93
- if (status === ParserStatus.ExpressionComplex) {
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 = ParserStatus.Text;
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 === ParserStatus.Text) {
134
+ if (status === PARSER_STATUS_TEXT) {
111
135
  templates.push(currentTemplate);
112
- } else if (status === ParserStatus.ExpressionSimple) {
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
- return parseInterpolation(template);
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
  }
@@ -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 '(' + result + ')';
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
- result += '[' + this.buildString(key) + ']';
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
- 'context',
126
- `
127
- if (context == null) context = {};
128
- return (${source});
129
- `,
130
- ).bind(undefined, ...params.map(([, value]) => value)) as TemplateFunction;
131
- result.source = source;
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) {
@@ -4,7 +4,9 @@ import { TemplateCompiler } from './compiler.js';
4
4
  /** 模板编译求值 */
5
5
  export type TemplateEvaluator = {
6
6
  /** 注入为 `evaluator` 变量,可在生成的求值代码中使用 */
7
- inject: unknown;
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: ['$001', ''],
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
  });