@mirascript/mirascript 0.1.42 → 0.1.44

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.
Files changed (39) hide show
  1. package/dist/{chunk-6NQMCZBG.js → chunk-CQ42UDMB.js} +148 -134
  2. package/dist/chunk-CQ42UDMB.js.map +6 -0
  3. package/dist/{chunk-GBWG7BQZ.js → chunk-VSUYGID4.js} +18 -12
  4. package/dist/chunk-VSUYGID4.js.map +6 -0
  5. package/dist/compiler/emit/constants.d.ts +3 -0
  6. package/dist/compiler/emit/constants.d.ts.map +1 -1
  7. package/dist/compiler/emit/index.d.ts +4 -1
  8. package/dist/compiler/emit/index.d.ts.map +1 -1
  9. package/dist/compiler/emit/sourcemap.d.ts +2 -1
  10. package/dist/compiler/emit/sourcemap.d.ts.map +1 -1
  11. package/dist/compiler/worker.js +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/subtle.js +2 -2
  14. package/dist/vm/lib/global/debug.d.ts +11 -35
  15. package/dist/vm/lib/global/debug.d.ts.map +1 -1
  16. package/dist/vm/lib/global/json.d.ts.map +1 -1
  17. package/dist/vm/lib/helpers.d.ts +1 -1
  18. package/dist/vm/lib/helpers.d.ts.map +1 -1
  19. package/dist/vm/lib/loader.d.ts.map +1 -1
  20. package/dist/vm/operations/helpers.d.ts.map +1 -1
  21. package/dist/vm/types/context.d.ts.map +1 -1
  22. package/dist/vm/types/function.d.ts +11 -7
  23. package/dist/vm/types/function.d.ts.map +1 -1
  24. package/dist/vm/types/wrapper.d.ts +0 -2
  25. package/dist/vm/types/wrapper.d.ts.map +1 -1
  26. package/package.json +5 -5
  27. package/src/compiler/emit/constants.ts +3 -0
  28. package/src/compiler/emit/index.ts +12 -5
  29. package/src/compiler/emit/sourcemap.ts +4 -3
  30. package/src/vm/lib/global/debug.ts +77 -61
  31. package/src/vm/lib/global/json.ts +0 -2
  32. package/src/vm/lib/helpers.ts +13 -12
  33. package/src/vm/lib/loader.ts +4 -14
  34. package/src/vm/operations/helpers.ts +2 -3
  35. package/src/vm/types/context.ts +1 -0
  36. package/src/vm/types/function.ts +48 -24
  37. package/src/vm/types/wrapper.ts +0 -4
  38. package/dist/chunk-6NQMCZBG.js.map +0 -6
  39. package/dist/chunk-GBWG7BQZ.js.map +0 -6
@@ -1,7 +1,7 @@
1
+ import { SourceMapGenerator } from 'source-map-js';
1
2
  import type { IRange } from '../diagnostic.js';
2
3
  import type { ScriptInput, TranspileOptions } from '../types.js';
3
- import { SourceMapGenerator } from 'source-map-js';
4
- import { SCRIPT_PREFIX } from './constants.js';
4
+ import type { ScriptPrefix } from './constants.js';
5
5
  import { toJsLiteral } from './consts.js';
6
6
 
7
7
  const ORIGIN = `mira://MiraScript/`;
@@ -38,6 +38,7 @@ let sourceId = 1;
38
38
  /** 创建源映射 */
39
39
  export function createSourceMap(
40
40
  source: ScriptInput | undefined,
41
+ scriptPrefix: ScriptPrefix,
41
42
  sourcemaps: readonly IRange[],
42
43
  codeLines: string[],
43
44
  functions: readonly number[],
@@ -82,7 +83,7 @@ export function createSourceMap(
82
83
  map.addMapping({
83
84
  generated: {
84
85
  line: SOURCE_OFFSET,
85
- column: SCRIPT_PREFIX.length - 'CpEnter();'.length,
86
+ column: scriptPrefix.length - 'CpEnter();'.length,
86
87
  },
87
88
  original: {
88
89
  line: 1,
@@ -62,18 +62,24 @@ function buildFormatString(
62
62
  }
63
63
  }
64
64
 
65
- /** 默认的格式化函数 */
66
- const printFormatter: PrintOptions['formatter'] = function (args) {
65
+ /** 默认解析函数 */
66
+ const printParser: PrintOptions['parser'] = function (args) {
67
67
  const [format, values] = buildFormatString(this, args);
68
68
  const templates: string[] = [];
69
69
  const formats: SerializeFormat[] = [];
70
70
  let valIndex = 0;
71
71
  if (format.includes('%')) {
72
72
  const parts = format.split(/(%[%\w])/g);
73
+ let prevIsTemplate = true;
73
74
  for (let i = 0; i < parts.length; i++) {
74
75
  if (i % 2 === 0) {
75
76
  // Regular string part
76
- templates.push(parts[i]!);
77
+ if (prevIsTemplate && templates.length) {
78
+ templates[templates.length - 1] += parts[i]!;
79
+ } else {
80
+ templates.push(parts[i]!);
81
+ }
82
+ prevIsTemplate = true;
77
83
  continue;
78
84
  }
79
85
  // Specifier part
@@ -85,8 +91,10 @@ const printFormatter: PrintOptions['formatter'] = function (args) {
85
91
  } else {
86
92
  templates[templates.length - 1] += specifier;
87
93
  }
94
+ prevIsTemplate = true;
88
95
  continue;
89
96
  }
97
+ prevIsTemplate = false;
90
98
  formats.push(specifier as SerializeFormat);
91
99
  valIndex++;
92
100
  }
@@ -97,6 +105,8 @@ const printFormatter: PrintOptions['formatter'] = function (args) {
97
105
  if (valIndex < values.length) {
98
106
  if (templates.length) {
99
107
  templates[templates.length - 1] += ' ';
108
+ } else {
109
+ templates.push('');
100
110
  }
101
111
  for (let i = valIndex; i < values.length; i++) {
102
112
  formats.push('');
@@ -111,69 +121,82 @@ const printFormatter: PrintOptions['formatter'] = function (args) {
111
121
  };
112
122
  };
113
123
 
114
- /** 默认的输出函数 */
115
- const createPrinter = (consoleMethod: (...args: unknown[]) => void): PrintOptions['printer'] => {
116
- return function ({ templates, formats, values }: FormatResult) {
117
- let format = '';
118
- const formattedValues: unknown[] = [];
119
- let needAwait = false;
120
- for (let i = 0; i < templates.length; i++) {
121
- format += templates[i]!;
122
- if (i < values.length) {
123
- const f = formats[i] ?? '';
124
- const v = values[i]!;
125
- const serialized = serializeValue(this, v, f);
126
- if (serialized == null) {
127
- format += f || (typeof v == 'string' ? '%s' : '%o');
128
- formattedValues.push(v);
129
- } else if (typeof serialized == 'string') {
130
- format += '%s';
131
- formattedValues.push(serialized);
132
- } else {
133
- needAwait = true;
134
- format += '%s';
135
- formattedValues.push(serialized);
136
- }
124
+ /** 默认格式化函数 */
125
+ const printFormatter: PrintOptions['formatter'] = function ({
126
+ templates,
127
+ formats,
128
+ values,
129
+ }): unknown[] | PromiseLike<unknown[]> {
130
+ let format = '';
131
+ const formattedValues: unknown[] = [];
132
+ let needAwait = false;
133
+ for (let i = 0; i < templates.length; i++) {
134
+ format += templates[i]!.replaceAll('%', '%%');
135
+ if (i < values.length) {
136
+ const f = formats[i] ?? '';
137
+ const v = values[i]!;
138
+ const serialized = serializeValue(this, v, f);
139
+ if (serialized == null) {
140
+ format += f || (typeof v == 'string' ? '%s' : '%o');
141
+ formattedValues.push(v);
142
+ } else if (typeof serialized == 'string') {
143
+ format += '%s';
144
+ formattedValues.push(serialized);
145
+ } else {
146
+ needAwait = true;
147
+ format += '%s';
148
+ formattedValues.push(serialized);
137
149
  }
138
150
  }
139
- if (!needAwait) {
140
- consoleMethod(format, ...formattedValues);
141
- } else {
142
- void Promise.all(formattedValues).then((resolvedValues) => {
143
- consoleMethod(format, ...resolvedValues);
144
- });
145
- }
151
+ }
152
+ if (!needAwait) {
153
+ return [format, ...formattedValues];
154
+ } else {
155
+ return Promise.all(formattedValues).then((resolvedValues) => {
156
+ return [format, ...resolvedValues];
157
+ });
158
+ }
159
+ };
160
+
161
+ /** 默认的输出选项 */
162
+ const createPrintOptions = (consoleMethod: (...args: unknown[]) => void): PrintOptions => {
163
+ return {
164
+ prefix: ['MiraScript'],
165
+ serializer: defaultSerializer,
166
+ parser: printParser,
167
+ formatter: printFormatter,
168
+ logger: consoleMethod,
146
169
  };
147
170
  };
148
171
 
149
172
  /** 打印输出选项 */
150
- interface PrintOptions {
173
+ type PrintOptions = {
151
174
  /** 输出时的固定前缀 */
152
- prefix: readonly [prefix: string, ...additional: readonly string[]];
175
+ prefix: readonly [prefix?: string, ...additional: readonly string[]];
153
176
  /**
154
177
  * 序列化函数
155
178
  * @param arg 要序列化的值
156
179
  * @param format 序列化格式
157
- * @returns 序列化后的字符串,或 null 表示直接将原值传递给控制台
180
+ * @returns 序列化后的字符串,或 null 表示直接将原值传递给 {@link printer}
158
181
  */
159
182
  serializer: (this: PrintOptions, arg: VmAny, format: SerializeFormat) => string | null | PromiseLike<string>;
160
- /** 格式化函数 */
161
- formatter: (this: PrintOptions, args: readonly VmAny[]) => FormatResult;
162
- /** 输出函数 */
163
- printer: (this: PrintOptions, format: FormatResult) => void;
164
- }
165
-
166
- const printOptions: PrintOptions = {
167
- prefix: ['MiraScript'] as readonly [prefix: string, ...additional: readonly string[]],
168
- serializer: defaultSerializer,
169
- formatter: printFormatter,
170
- printer: () => undefined,
183
+ /** 解析函数,解析输入并使用 {@link serializer} 序列化值,供 {@link formatter} 使用 */
184
+ parser: (this: PrintOptions, args: readonly VmAny[]) => FormatResult;
185
+ /** 格式化函数,进一步处理解析结果,供 {@link logger} 使用 */
186
+ formatter: (this: PrintOptions, format: FormatResult) => unknown[] | PromiseLike<unknown[]>;
187
+ /** 打印函数,打印格式化结果 */
188
+ logger: (this: PrintOptions, ...args: unknown[]) => void;
171
189
  };
172
190
 
191
+ /** 进行输出 */
192
+ function doPrint(opt: PrintOptions, args: readonly VmAny[]): void {
193
+ const formatResult = opt.parser(args);
194
+ void Promise.resolve(opt.formatter(formatResult)).then((printed) => opt.logger(...printed));
195
+ }
196
+
173
197
  export const debug_print = VmLib(
174
198
  (...args) => {
175
- const formatResult = debug_print.formatter(args);
176
- debug_print.printer(formatResult);
199
+ doPrint(debug_print, args);
177
200
  },
178
201
  {
179
202
  summary: '打印调试信息到控制台',
@@ -182,17 +205,13 @@ export const debug_print = VmLib(
182
205
  returnsType: 'nil',
183
206
  examples: ['debug_print("value:", 42);'],
184
207
  },
185
- {
186
- ...printOptions,
187
- // eslint-disable-next-line no-console
188
- printer: createPrinter(console.log),
189
- },
208
+ // eslint-disable-next-line no-console
209
+ createPrintOptions(console.log.bind(console)),
190
210
  );
191
211
 
192
212
  export const panic = VmLib(
193
213
  (message: VmAny) => {
194
- const formatResult = message === undefined ? panic.formatter([]) : panic.formatter([message]);
195
- panic.printer(formatResult);
214
+ doPrint(panic, message === undefined ? [] : [message]);
196
215
  const mgsStr = toString(message, null);
197
216
  const error = !mgsStr ? 'panic' : 'panic: ' + mgsStr;
198
217
  throw new VmError(error, undefined);
@@ -204,11 +223,8 @@ export const panic = VmLib(
204
223
  returnsType: 'never',
205
224
  examples: ['panic("boom");'],
206
225
  },
207
- {
208
- ...printOptions,
209
- // eslint-disable-next-line no-console
210
- printer: createPrinter(console.error),
211
- },
226
+ // eslint-disable-next-line no-console
227
+ createPrintOptions(console.error.bind(console)),
212
228
  );
213
229
 
214
230
  if (typeof location != 'undefined') {
@@ -21,7 +21,6 @@ export const to_json = VmLib(
21
21
  paramsType: { data: 'any' },
22
22
  returnsType: 'string',
23
23
  examples: ['to_json([1, 2, 3]) // "[1,2,3]"'],
24
- injectCp: true,
25
24
  },
26
25
  );
27
26
 
@@ -41,6 +40,5 @@ export const from_json = VmLib(
41
40
  paramsType: { json: 'string', fallback: 'any' },
42
41
  returnsType: 'any',
43
42
  examples: [`from_json('{"a":1}') // (a: 1)`],
44
- injectCp: true,
45
43
  },
46
44
  );
@@ -1,4 +1,3 @@
1
- import type { Writable } from 'type-fest';
2
1
  import { VM_ARRAY_MAX_LENGTH } from '../../helpers/constants.js';
3
2
  import { isNaN, NotNumber, entries, fromEntries, isSafeInteger, isFinite } from '../../helpers/utils.js';
4
3
  import { toBoolean, toNumber, toString } from '../../helpers/convert/index.js';
@@ -274,7 +273,7 @@ export function map(
274
273
  /** 库函数选项 */
275
274
  export type VmLibOption = Pick<
276
275
  VmFunctionOption,
277
- 'summary' | 'params' | 'paramsType' | 'returns' | 'returnsType' | 'examples' | 'injectCp' | 'deprecated'
276
+ 'summary' | 'params' | 'paramsType' | 'returns' | 'returnsType' | 'examples' | 'deprecated'
278
277
  >;
279
278
  /** 库函数 */
280
279
  export type VmLib<T extends VmFunctionLike | VmConst = VmFunctionLike> = (T extends VmFunctionLike ? T : { value: T }) &
@@ -287,15 +286,17 @@ export function VmLib<
287
286
  >(value: T, option: VmLibOption, properties?: P): VmLib<T> & P {
288
287
  if (isVmFunction(value)) throw new TypeError('Cannot create VmLib from a VmFunction');
289
288
 
290
- const ret = (typeof value == 'function' ? value : { value: value }) as unknown as Writable<VmLib<T>> & P;
289
+ // 后续在 wrapEntry 中会处理函数的包装
290
+ const ret = (typeof value == 'function' ? value : { __proto__: null, value: value }) as VmLib<T> & P;
291
+ Object.defineProperties(ret, {
292
+ summary: { enumerable: true, value: option.summary },
293
+ params: { enumerable: true, value: option.params },
294
+ paramsType: { enumerable: true, value: option.paramsType },
295
+ returns: { enumerable: true, value: option.returns },
296
+ returnsType: { enumerable: true, value: option.returnsType },
297
+ examples: { enumerable: true, value: option.examples },
298
+ deprecated: { enumerable: true, value: option.deprecated ?? undefined },
299
+ });
291
300
  Object.assign(ret, properties);
292
- ret.params = option.params;
293
- ret.paramsType = option.paramsType;
294
- ret.returns = option.returns;
295
- ret.returnsType = option.returnsType;
296
- ret.summary = option.summary;
297
- ret.examples = option.examples;
298
- ret.injectCp = option.injectCp ?? false;
299
- ret.deprecated = option.deprecated ?? undefined;
300
- return ret as VmLib<T> & P;
301
+ return ret;
301
302
  }
@@ -8,7 +8,7 @@ import {
8
8
  } from '../types/index.js';
9
9
  import { create, defineProperty, entries } from '../../helpers/utils.js';
10
10
 
11
- import type { VmLib, VmLibOption } from './helpers.js';
11
+ import type { VmLib } from './helpers.js';
12
12
 
13
13
  /** 内置模块 */
14
14
  class VmBuiltinModule<const T extends Record<string, VmImmutable> = Record<string, VmImmutable>> extends VmModule<T> {
@@ -46,20 +46,10 @@ export function wrapEntry<const T extends RawValue>(
46
46
  }
47
47
  if (value.name !== name) {
48
48
  // 如果函数名和导出名不一致,则重命名
49
- defineProperty(value, 'name', {
50
- value: name,
51
- configurable: true,
52
- });
49
+ defineProperty(value, 'name', { value: name });
53
50
  }
54
- return [
55
- VmFunction(value, {
56
- isLib: true,
57
- injectCp: true,
58
- fullName: `${module}.${name}`,
59
- ...(value as VmLibOption),
60
- }) as ToWrappedValue<T>,
61
- value.summary || undefined,
62
- ];
51
+ defineProperty(value, 'fullName', { enumerable: true, value: `${module}.${name}` });
52
+ return [VmFunction(value, value) as ToWrappedValue<T>, value.summary || undefined];
63
53
  }
64
54
 
65
55
  /** 创建模块 */
@@ -1,4 +1,4 @@
1
- import { create, defineProperty, isFinite } from '../../helpers/utils.js';
1
+ import { create, isFinite } from '../../helpers/utils.js';
2
2
  import { VM_ARRAY_MAX_LENGTH, VM_FUNCTION_ANONYMOUS_NAME } from '../../helpers/constants.js';
3
3
  import { isVmConst } from '../../helpers/types.js';
4
4
  import type { VmFunctionLike } from '../types/function.js';
@@ -25,8 +25,7 @@ export function $ElOpt(key: string, value: VmAny): VmConst {
25
25
 
26
26
  /** 构造函数和函数表达式 */
27
27
  export function $Fn<T extends VmFunctionLike>(name: string | null | undefined, fn: T): VmFunction<T> {
28
- defineProperty(fn, 'name', { value: name || VM_FUNCTION_ANONYMOUS_NAME });
29
- return VmFunction(fn, { isLib: false, injectCp: false });
28
+ return VmFunction(fn, { isLib: false, injectCp: false, name: name || VM_FUNCTION_ANONYMOUS_NAME });
30
29
  }
31
30
 
32
31
  /** 读取闭包上值 */
@@ -45,6 +45,7 @@ export function defineVmContextValue(
45
45
  let v: VmImmutable;
46
46
  if (typeof value == 'function') {
47
47
  v = VmFunction(value, {
48
+ name,
48
49
  isLib: true,
49
50
  fullName: `global.${name}`,
50
51
  });
@@ -2,9 +2,10 @@ import type { Writable } from 'type-fest';
2
2
  import type { DiagnosticCode } from '@mirascript/constants';
3
3
  import { defineProperty } from '../../helpers/utils.js';
4
4
  import { kVmFunction, VM_FUNCTION_ANONYMOUS_NAME } from '../../helpers/constants.js';
5
- import type { VmAny, VmValue } from './index.js';
5
+ import { isVmFunction, type VmAny, type VmValue } from './index.js';
6
6
  import { fromVmFunctionProxy } from './boundary.js';
7
7
  import { CpEnter, CpExit } from '../checkpoint.js';
8
+ import type { VmLib } from '../lib/helpers.js';
8
9
 
9
10
  /**
10
11
  * Mirascript 函数签名
@@ -18,7 +19,7 @@ export type VmFunctionLike = (...args: ReadonlyArray<VmValue | undefined>) => Vm
18
19
  export type VmFunction<T extends VmFunctionLike = VmFunctionLike> = T & { readonly [kVmFunction]: VmFunctionInfo };
19
20
 
20
21
  /** Mirascript 函数信息 */
21
- export interface VmFunctionInfo {
22
+ export type VmFunctionInfo = {
22
23
  /** 完整名称 */
23
24
  readonly fullName: string;
24
25
  /** 是否为库函数 */
@@ -39,17 +40,27 @@ export interface VmFunctionInfo {
39
40
  readonly original?: VmFunctionLike;
40
41
  /** 标记为弃用 */
41
42
  readonly deprecated?: { use?: string; message: DiagnosticCode };
42
- }
43
+ };
43
44
 
44
45
  /** Mirascript 函数创建选项 */
45
- export type VmFunctionOption = Partial<
46
- Omit<VmFunctionInfo, 'original'> & {
47
- readonly injectCp: boolean;
48
- }
49
- >;
46
+ export type VmFunctionOption = Partial<Omit<VmFunctionInfo, 'original'>> & {
47
+ /** 函数名称 */
48
+ readonly name?: string | null | undefined;
49
+ /** 是否注入检查点 */
50
+ readonly injectCp?: boolean;
51
+ };
52
+
53
+ const nameIfNotAnonymous = <T>({ name }: { name: string | undefined }, fallback: T): string | T => {
54
+ if (!name) return fallback;
55
+ if (name === VM_FUNCTION_ANONYMOUS_NAME) return fallback;
56
+ return name;
57
+ };
50
58
 
51
59
  /** 创建 Mirascript 函数 */
52
- export function VmFunction<T extends VmFunctionLike>(fn: T, option: VmFunctionOption = {}): VmFunction<T> {
60
+ export function VmFunction<T extends VmFunctionLike>(
61
+ fn: T,
62
+ option: VmFunctionOption | VmFunction | VmLib<T> = {},
63
+ ): VmFunction<T> {
53
64
  if (typeof fn != 'function') {
54
65
  throw new TypeError('Invalid function');
55
66
  }
@@ -57,19 +68,34 @@ export function VmFunction<T extends VmFunctionLike>(fn: T, option: VmFunctionOp
57
68
  const exists = fromVmFunctionProxy(fn);
58
69
  // 如果已经是 VmFunction,则直接返回
59
70
  if (exists) return exists;
71
+ let opt: VmFunctionOption;
72
+ if (isVmFunction(option)) {
73
+ opt = { ...option[kVmFunction], name: nameIfNotAnonymous(option, null) };
74
+ } else if (typeof option == 'function') {
75
+ opt = {
76
+ ...option,
77
+ isLib: true,
78
+ injectCp: false,
79
+ name: nameIfNotAnonymous(option, null),
80
+ };
81
+ } else {
82
+ opt = option;
83
+ }
60
84
 
61
- const info: Writable<VmFunctionInfo> = {
62
- fullName: option.fullName ?? (fn.name === VM_FUNCTION_ANONYMOUS_NAME ? '' : fn.name),
63
- isLib: option.isLib ?? false,
64
- summary: option.summary || undefined,
65
- params: option.params,
66
- paramsType: option.paramsType,
67
- returns: option.returns || undefined,
68
- returnsType: option.returnsType || undefined,
69
- examples: option.examples?.length ? option.examples : undefined,
70
- deprecated: option.deprecated ?? undefined,
85
+ const info: Writable<VmFunctionInfo & { __proto__: null }> = {
86
+ __proto__: null,
87
+ fullName: opt.fullName ?? nameIfNotAnonymous(fn, ''),
88
+ isLib: opt.isLib ?? false,
89
+ summary: opt.summary || undefined,
90
+ params: opt.params,
91
+ paramsType: opt.paramsType,
92
+ returns: opt.returns || undefined,
93
+ returnsType: opt.returnsType || undefined,
94
+ examples: opt.examples?.length ? opt.examples : undefined,
95
+ deprecated: opt.deprecated ?? undefined,
71
96
  };
72
- if (option.injectCp) {
97
+ const name = opt.name ?? fn.name;
98
+ if (opt.injectCp) {
73
99
  const original = fn;
74
100
  info.original = original;
75
101
  fn = ((...args) => {
@@ -81,10 +107,8 @@ export function VmFunction<T extends VmFunctionLike>(fn: T, option: VmFunctionOp
81
107
  CpExit();
82
108
  }
83
109
  }) as typeof fn;
84
- defineProperty(fn, 'name', { value: original.name });
85
110
  }
86
- defineProperty(fn, kVmFunction, {
87
- value: Object.freeze(info),
88
- });
111
+ defineProperty(fn, 'name', { value: name, configurable: true });
112
+ defineProperty(fn, kVmFunction, { value: Object.freeze(info) });
89
113
  return fn as VmFunction<T>;
90
114
  }
@@ -22,10 +22,6 @@ export abstract class VmWrapper<T extends object> {
22
22
  describe(key: string): string | undefined {
23
23
  return undefined;
24
24
  }
25
- /** Convert the object to JSON */
26
- toJSON(): undefined {
27
- return undefined;
28
- }
29
25
  /** 转为字符串 */
30
26
  toString(useBraces: boolean): string {
31
27
  const { type, tag } = this;