@mirascript/cli 0.1.40

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/cli.js ADDED
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env node
2
+
3
+ import program from './dist/index.js';
4
+
5
+ await program.parseAsync();
package/dist/index.js ADDED
@@ -0,0 +1,240 @@
1
+ // src/index.ts
2
+ import { program as program3 } from "@commander-js/extra-typings";
3
+ import pkg from "#package.json" with { type: "json" };
4
+
5
+ // src/commands/run.ts
6
+ import { readFile, stat } from "node:fs/promises";
7
+ import { pathToFileURL } from "node:url";
8
+ import { InvalidArgumentError, program } from "@commander-js/extra-typings";
9
+ import { compileSync, configCheckpoint } from "@mirascript/mirascript";
10
+
11
+ // src/utils/execute.ts
12
+ import styles2 from "ansi-styles";
13
+ import supportsColor2 from "supports-color";
14
+ import { compile, createVmContext } from "@mirascript/mirascript";
15
+ import { lib } from "@mirascript/mirascript/subtle";
16
+
17
+ // src/utils/print.ts
18
+ import styles from "ansi-styles";
19
+ import supportsColor from "supports-color";
20
+ import {
21
+ serialize,
22
+ serializeNumber,
23
+ serializeBoolean,
24
+ serializeNil,
25
+ operations
26
+ } from "@mirascript/mirascript/subtle";
27
+ var noColor = !supportsColor.stdout;
28
+ var options = {
29
+ maxDepth: 3,
30
+ serializeNil: noColor ? void 0 : () => styles.gray.open + serializeNil() + styles.gray.close,
31
+ serializeBoolean: noColor ? void 0 : (v) => styles.blue.open + serializeBoolean(v) + styles.blue.close,
32
+ serializeNumber: noColor ? void 0 : (v) => styles.yellow.open + serializeNumber(v) + styles.yellow.close,
33
+ serializeStringQuote: noColor ? void 0 : (v, open) => {
34
+ const q = styles.dim.open + v + styles.dim.close;
35
+ if (open) {
36
+ return styles.green.open + q;
37
+ } else {
38
+ return q + styles.green.close;
39
+ }
40
+ },
41
+ serializeStringEscape: noColor ? void 0 : (v) => styles.bold.open + v + styles.bold.close,
42
+ serializePropName: noColor ? void 0 : (v) => styles.whiteBright.open + String(v) + styles.whiteBright.close,
43
+ serializeFunction: noColor ? operations.$ToString : (v) => styles.cyan.open + operations.$ToString(v) + styles.cyan.close,
44
+ serializeModule: noColor ? (v, depth, options2) => {
45
+ return operations.$ToString(v) + " " + options2.serializeRecord(v.value, depth, options2);
46
+ } : (v, depth, options2) => {
47
+ return styles.magenta.open + operations.$ToString(v) + styles.magenta.close + " " + options2.serializeRecord(v.value, depth, options2);
48
+ }
49
+ };
50
+ function print(value, depth = 3) {
51
+ if (value === void 0) {
52
+ if (noColor) return "<uninitialized>";
53
+ return styles.gray.open + "<uninitialized>" + styles.gray.close;
54
+ }
55
+ return serialize(value, { ...options, maxDepth: depth });
56
+ }
57
+
58
+ // src/utils/execute.ts
59
+ var { panic, debug_print } = lib;
60
+ panic.serializer = debug_print.serializer = (arg, format2) => {
61
+ if (format2 === "%o" || format2 === "%O" || !format2) {
62
+ return print(arg);
63
+ }
64
+ return null;
65
+ };
66
+ async function execute(script, template, variables, fileName) {
67
+ try {
68
+ const f = await compile(script, { input_mode: template ? "Template" : "Script", sourceMap: true, fileName });
69
+ const r = f(createVmContext(variables));
70
+ if (template) {
71
+ console.log(r);
72
+ } else {
73
+ console.log(print(r));
74
+ }
75
+ } catch (ex) {
76
+ const { stack } = ex;
77
+ if (supportsColor2.stderr) {
78
+ console.error(styles2.red.open + stack + styles2.red.close);
79
+ } else {
80
+ console.error(stack);
81
+ }
82
+ process.exitCode = 2;
83
+ }
84
+ }
85
+
86
+ // src/commands/run.ts
87
+ var DEFAULT_TIMEOUT = 3e3;
88
+ program.command("run", { isDefault: true }).description("执行 MiraScript 脚本,默认命令").option(
89
+ "-v, --variable <key=value>",
90
+ "设置全局变量,可以多次使用",
91
+ (v, p) => {
92
+ p = { ...p };
93
+ const i = v.indexOf("=");
94
+ if (i < 0) {
95
+ p[v] = true;
96
+ } else {
97
+ const key = v.slice(0, i).trim();
98
+ const value = v.slice(i + 1).trim();
99
+ try {
100
+ const pv = compileSync(`return (${value});`)();
101
+ p[key] = pv ?? value;
102
+ } catch {
103
+ p[key] = value;
104
+ }
105
+ }
106
+ return p;
107
+ },
108
+ {}
109
+ ).option("-t, --template", "使用模板模式").option(
110
+ "--timeout <ms>",
111
+ "脚本执行超时时间(毫秒,0 表示不超时)",
112
+ (v) => {
113
+ const ms = Number.parseFloat(v);
114
+ if (Number.isNaN(ms) || ms < 0) {
115
+ throw new InvalidArgumentError("超时时间必须是非负整数");
116
+ }
117
+ return ms;
118
+ },
119
+ DEFAULT_TIMEOUT
120
+ ).option("--no-template", "使用脚本模式").option("-e, --eval <script>", "要执行的脚本").argument("[script]", "要执行的脚本文件路径(如果提供了 -e 则忽略此参数)").action(async (script, opt) => {
121
+ configCheckpoint(opt.timeout || Number.POSITIVE_INFINITY);
122
+ if (opt.eval != null) {
123
+ const template = !!opt.template;
124
+ await execute(opt.eval, template, opt.variable, template ? "eval.miratpl" : "eval.mira");
125
+ return;
126
+ }
127
+ if (script) {
128
+ try {
129
+ const s = await stat(script);
130
+ if (!s.isFile()) {
131
+ console.error(`脚本路径不是文件: ${script}`);
132
+ process.exitCode = 2;
133
+ return;
134
+ }
135
+ } catch (ex) {
136
+ if (ex.code === "ENOENT") {
137
+ console.error(`脚本文件不存在: ${script}`);
138
+ process.exitCode = 2;
139
+ } else if (ex.code === "EACCES") {
140
+ console.error(`权限不足: ${ex.message}`);
141
+ process.exitCode = 3;
142
+ } else {
143
+ console.error(`无法访问脚本文件: ${ex.message}`);
144
+ process.exitCode = 1;
145
+ }
146
+ return;
147
+ }
148
+ const context = await readFile(script, "utf8");
149
+ const template = opt.template ?? script.endsWith(".miratpl");
150
+ await execute(context, template, opt.variable, pathToFileURL(script).href);
151
+ return;
152
+ }
153
+ program.help({ error: true });
154
+ });
155
+
156
+ // src/commands/format.ts
157
+ import fs from "node:fs/promises";
158
+ import { program as program2 } from "@commander-js/extra-typings";
159
+ import { loadModule } from "@mirascript/bindings/wasm";
160
+ var templateConfig = null;
161
+ var scriptConfig = null;
162
+ async function getConfig(templateMode) {
163
+ const mod = await loadModule();
164
+ if (templateMode) {
165
+ if (!templateConfig) {
166
+ templateConfig = new mod.wasm.Config();
167
+ templateConfig.input_mode = mod.wasm.InputMode.Template;
168
+ templateConfig.trivia = true;
169
+ templateConfig.diagnostic_position_encoding = mod.wasm.DiagnosticPositionEncoding.Utf8;
170
+ }
171
+ return templateConfig;
172
+ } else {
173
+ if (!scriptConfig) {
174
+ scriptConfig = new mod.wasm.Config();
175
+ scriptConfig.input_mode = mod.wasm.InputMode.Script;
176
+ scriptConfig.trivia = true;
177
+ scriptConfig.diagnostic_position_encoding = mod.wasm.DiagnosticPositionEncoding.Utf8;
178
+ }
179
+ return scriptConfig;
180
+ }
181
+ }
182
+ async function format(input, templateMode) {
183
+ if (!input) return "";
184
+ const mod = await loadModule();
185
+ const compiler = new mod.wasm.MonacoCompiler(input, await getConfig(templateMode));
186
+ try {
187
+ if (!compiler.parse()) return void 0;
188
+ const formatted = compiler.format();
189
+ if (!formatted) return void 0;
190
+ return formatted;
191
+ } finally {
192
+ compiler.free();
193
+ }
194
+ }
195
+ var command = program2.command("format");
196
+ command.description("格式化 MiraScript 脚本").option("-w, --write", "直接修改文件").option("-t, --template", "对无法推断类型的文件使用模板模式").argument("<script...>", '要格式化的脚本文件路径或 glob,输入 "-" 表示从标准输入读取').action(async (script, opt) => {
197
+ if (script.length === 0) {
198
+ command.help({ error: true });
199
+ }
200
+ if (script.length === 1 && script[0] === "-") {
201
+ let input = "";
202
+ for await (const chunk of process.stdin) {
203
+ input += chunk;
204
+ }
205
+ const output = await format(input, !!opt.template);
206
+ if (output == null) {
207
+ console.error("格式化失败");
208
+ process.exit(1);
209
+ }
210
+ process.stdout.write(output);
211
+ return;
212
+ }
213
+ for await (const file of fs.glob(script)) {
214
+ const input = await fs.readFile(file, "utf8");
215
+ const output = await format(input, !!opt.template);
216
+ if (opt.write) {
217
+ if (output == null) {
218
+ console.error(`格式化失败: ${file}`);
219
+ continue;
220
+ }
221
+ if (output === input) {
222
+ console.warn(`文件未更改: ${file}`);
223
+ continue;
224
+ }
225
+ await fs.writeFile(file, output, "utf8");
226
+ console.warn(`已格式化文件: ${file}`);
227
+ } else {
228
+ process.stdout.write(`// File: ${file}
229
+ `);
230
+ process.stdout.write((output ?? input) + "\n");
231
+ }
232
+ }
233
+ });
234
+
235
+ // src/index.ts
236
+ var index_default = program3.name(pkg.name.split("/").pop()).version(pkg.version).description(pkg.description);
237
+ export {
238
+ index_default as default
239
+ };
240
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/commands/run.ts", "../src/utils/execute.ts", "../src/utils/print.ts", "../src/commands/format.ts"],
4
+ "mappings": "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",
5
+ "names": ["program", "styles", "supportsColor", "options", "format", "supportsColor", "styles", "program", "program"]
6
+ }
@@ -0,0 +1,38 @@
1
+ import esbuild from 'esbuild';
2
+ import { parseArgs } from 'node:util';
3
+
4
+ const args = parseArgs({
5
+ options: {
6
+ watch: {
7
+ type: 'boolean',
8
+ default: false,
9
+ },
10
+ },
11
+ });
12
+
13
+ const context = await esbuild.context({
14
+ sourcemap: true,
15
+ sourcesContent: false,
16
+ format: 'esm',
17
+ charset: 'utf8',
18
+ entryPoints: ['./src/index.ts'],
19
+ outdir: './dist',
20
+ target: 'esnext',
21
+ bundle: true,
22
+ packages: 'external',
23
+ external: ['#package.json'],
24
+ minify: false,
25
+ treeShaking: true,
26
+ splitting: true,
27
+ });
28
+
29
+ await context.rebuild();
30
+ if (args.values.watch) {
31
+ await context.watch();
32
+ process.on('SIGINT', async () => {
33
+ await context.dispose();
34
+ process.exit(0);
35
+ });
36
+ } else {
37
+ await context.dispose();
38
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@mirascript/cli",
3
+ "version": "0.1.40",
4
+ "author": "CloudPSS",
5
+ "license": "MIT",
6
+ "description": "Cli for MiraScript, an expression based scripting language.",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "bin": {
10
+ "mirascript": "./cli.js"
11
+ },
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "@mirascript/dev": "./src/index.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "imports": {
21
+ "#package.json": "./package.json"
22
+ },
23
+ "dependencies": {
24
+ "@cloudpss/worker": "^0.1.10",
25
+ "@commander-js/extra-typings": "^14.0.0",
26
+ "ansi-styles": "^6.2.3",
27
+ "commander": "^14.0.2",
28
+ "supports-color": "^10.2.2",
29
+ "@mirascript/bindings": "~0.1.40",
30
+ "@mirascript/mirascript": "~0.1.40"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^25.0.10",
34
+ "type-fest": "^5.4.1"
35
+ },
36
+ "scripts": {
37
+ "watch": "pnpm build --watch",
38
+ "build": "pnpm clean && node ./esbuild.config.js",
39
+ "mirascript": "node --enable-source-maps ./cli.js",
40
+ "clean": "rimraf dist"
41
+ }
42
+ }
@@ -0,0 +1,90 @@
1
+ /* eslint-disable no-console */
2
+ import fs from 'node:fs/promises';
3
+ import { program } from '@commander-js/extra-typings';
4
+ import { loadModule, type BcModule } from '@mirascript/bindings/wasm';
5
+
6
+ let templateConfig: BcModule.WasmConfig | null = null;
7
+ let scriptConfig: BcModule.WasmConfig | null = null;
8
+
9
+ /** 获取配置 */
10
+ async function getConfig(templateMode: boolean): Promise<BcModule.WasmConfig> {
11
+ const mod = await loadModule();
12
+ if (templateMode) {
13
+ if (!templateConfig) {
14
+ templateConfig = new mod.wasm.Config();
15
+ templateConfig.input_mode = mod.wasm.InputMode.Template;
16
+ templateConfig.trivia = true;
17
+ templateConfig.diagnostic_position_encoding = mod.wasm.DiagnosticPositionEncoding.Utf8;
18
+ }
19
+ return templateConfig;
20
+ } else {
21
+ if (!scriptConfig) {
22
+ scriptConfig = new mod.wasm.Config();
23
+ scriptConfig.input_mode = mod.wasm.InputMode.Script;
24
+ scriptConfig.trivia = true;
25
+ scriptConfig.diagnostic_position_encoding = mod.wasm.DiagnosticPositionEncoding.Utf8;
26
+ }
27
+ return scriptConfig;
28
+ }
29
+ }
30
+
31
+ /** 格式化 */
32
+ async function format(input: string, templateMode: boolean): Promise<string | undefined> {
33
+ if (!input) return '';
34
+
35
+ const mod = await loadModule();
36
+ const compiler = new mod.wasm.MonacoCompiler(input, await getConfig(templateMode));
37
+ try {
38
+ if (!compiler.parse()) return undefined;
39
+ const formatted = compiler.format();
40
+ if (!formatted) return undefined;
41
+ return formatted;
42
+ } finally {
43
+ compiler.free();
44
+ }
45
+ }
46
+
47
+ const command = program.command('format');
48
+ command
49
+ .description('格式化 MiraScript 脚本')
50
+ .option('-w, --write', '直接修改文件')
51
+ .option('-t, --template', '对无法推断类型的文件使用模板模式')
52
+ .argument('<script...>', '要格式化的脚本文件路径或 glob,输入 "-" 表示从标准输入读取')
53
+ .action(async (script, opt) => {
54
+ if (script.length === 0) {
55
+ command.help({ error: true });
56
+ }
57
+ if (script.length === 1 && script[0] === '-') {
58
+ // 从标准输入读取
59
+ let input = '';
60
+ for await (const chunk of process.stdin) {
61
+ input += chunk;
62
+ }
63
+ const output = await format(input, !!opt.template);
64
+ if (output == null) {
65
+ console.error('格式化失败');
66
+ process.exit(1);
67
+ }
68
+ process.stdout.write(output);
69
+ return;
70
+ }
71
+ for await (const file of fs.glob(script)) {
72
+ const input = await fs.readFile(file, 'utf8');
73
+ const output = await format(input, !!opt.template);
74
+ if (opt.write) {
75
+ if (output == null) {
76
+ console.error(`格式化失败: ${file}`);
77
+ continue;
78
+ }
79
+ if (output === input) {
80
+ console.warn(`文件未更改: ${file}`);
81
+ continue;
82
+ }
83
+ await fs.writeFile(file, output, 'utf8');
84
+ console.warn(`已格式化文件: ${file}`);
85
+ } else {
86
+ process.stdout.write(`// File: ${file}\n`);
87
+ process.stdout.write((output ?? input) + '\n');
88
+ }
89
+ }
90
+ });
@@ -0,0 +1,85 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { InvalidArgumentError, program } from '@commander-js/extra-typings';
4
+ import { compileSync, configCheckpoint, type VmValue } from '@mirascript/mirascript';
5
+ import { execute } from '../utils/execute.js';
6
+
7
+ const DEFAULT_TIMEOUT = 3000;
8
+
9
+ /* eslint-disable no-console */
10
+ program
11
+ .command('run', { isDefault: true })
12
+ .description('执行 MiraScript 脚本,默认命令')
13
+ .option(
14
+ '-v, --variable <key=value>',
15
+ '设置全局变量,可以多次使用',
16
+ (v, p) => {
17
+ p = { ...p };
18
+ const i = v.indexOf('=');
19
+ if (i < 0) {
20
+ p[v] = true;
21
+ } else {
22
+ const key = v.slice(0, i).trim();
23
+ const value = v.slice(i + 1).trim();
24
+ try {
25
+ const pv = compileSync(`return (${value});`)();
26
+ p[key] = pv ?? value;
27
+ } catch {
28
+ p[key] = value;
29
+ }
30
+ }
31
+ return p;
32
+ },
33
+ {} as Record<string, VmValue>,
34
+ )
35
+ .option('-t, --template', '使用模板模式')
36
+ .option(
37
+ '--timeout <ms>',
38
+ '脚本执行超时时间(毫秒,0 表示不超时)',
39
+ (v) => {
40
+ const ms = Number.parseFloat(v);
41
+ if (Number.isNaN(ms) || ms < 0) {
42
+ throw new InvalidArgumentError('超时时间必须是非负整数');
43
+ }
44
+ return ms;
45
+ },
46
+ DEFAULT_TIMEOUT,
47
+ )
48
+ .option('--no-template', '使用脚本模式')
49
+ .option('-e, --eval <script>', '要执行的脚本')
50
+ .argument('[script]', '要执行的脚本文件路径(如果提供了 -e 则忽略此参数)')
51
+ .action(async (script, opt) => {
52
+ configCheckpoint(opt.timeout || Number.POSITIVE_INFINITY);
53
+ if (opt.eval != null) {
54
+ const template = !!opt.template;
55
+ await execute(opt.eval, template, opt.variable, template ? 'eval.miratpl' : 'eval.mira');
56
+ return;
57
+ }
58
+ if (script) {
59
+ try {
60
+ const s = await stat(script);
61
+ if (!s.isFile()) {
62
+ console.error(`脚本路径不是文件: ${script}`);
63
+ process.exitCode = 2;
64
+ return;
65
+ }
66
+ } catch (ex) {
67
+ if ((ex as NodeJS.ErrnoException).code === 'ENOENT') {
68
+ console.error(`脚本文件不存在: ${script}`);
69
+ process.exitCode = 2;
70
+ } else if ((ex as NodeJS.ErrnoException).code === 'EACCES') {
71
+ console.error(`权限不足: ${(ex as NodeJS.ErrnoException).message}`);
72
+ process.exitCode = 3;
73
+ } else {
74
+ console.error(`无法访问脚本文件: ${(ex as NodeJS.ErrnoException).message}`);
75
+ process.exitCode = 1;
76
+ }
77
+ return;
78
+ }
79
+ const context = await readFile(script, 'utf8');
80
+ const template = opt.template ?? script.endsWith('.miratpl');
81
+ await execute(context, template, opt.variable, pathToFileURL(script).href);
82
+ return;
83
+ }
84
+ program.help({ error: true });
85
+ });
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { program } from '@commander-js/extra-typings';
2
+ import pkg from '#package.json' with { type: 'json' };
3
+
4
+ import './commands/run.js';
5
+ import './commands/format.js';
6
+
7
+ export default program.name(pkg.name.split('/').pop()!).version(pkg.version).description(pkg.description);
@@ -0,0 +1,41 @@
1
+ /* eslint-disable no-console */
2
+ import styles from 'ansi-styles';
3
+ import supportsColor from 'supports-color';
4
+ import { compile, createVmContext, type VmValue } from '@mirascript/mirascript';
5
+ import { lib } from '@mirascript/mirascript/subtle';
6
+ import { print } from './print.js';
7
+
8
+ const { panic, debug_print } = lib;
9
+
10
+ panic.serializer = debug_print.serializer = (arg, format) => {
11
+ if (format === '%o' || format === '%O' || !format) {
12
+ return print(arg);
13
+ }
14
+ return null;
15
+ };
16
+
17
+ /** 执行脚本 */
18
+ export async function execute(
19
+ script: string,
20
+ template: boolean,
21
+ variables: Record<string, VmValue>,
22
+ fileName: string,
23
+ ): Promise<void> {
24
+ try {
25
+ const f = await compile(script, { input_mode: template ? 'Template' : 'Script', sourceMap: true, fileName });
26
+ const r = f(createVmContext(variables));
27
+ if (template) {
28
+ console.log(r);
29
+ } else {
30
+ console.log(print(r));
31
+ }
32
+ } catch (ex) {
33
+ const { stack } = ex as Error;
34
+ if (supportsColor.stderr) {
35
+ console.error(styles.red.open + stack + styles.red.close);
36
+ } else {
37
+ console.error(stack);
38
+ }
39
+ process.exitCode = 2;
40
+ }
41
+ }
@@ -0,0 +1,57 @@
1
+ import styles from 'ansi-styles';
2
+ import supportsColor from 'supports-color';
3
+ import type { VmAny, VmRecord } from '@mirascript/mirascript';
4
+ import {
5
+ serialize,
6
+ serializeNumber,
7
+ serializeBoolean,
8
+ serializeNil,
9
+ type SerializeOptions,
10
+ operations,
11
+ } from '@mirascript/mirascript/subtle';
12
+
13
+ const noColor = !supportsColor.stdout;
14
+
15
+ const options: Partial<SerializeOptions> = {
16
+ maxDepth: 3,
17
+ serializeNil: noColor ? undefined : () => styles.gray.open + serializeNil() + styles.gray.close,
18
+ serializeBoolean: noColor ? undefined : (v) => styles.blue.open + serializeBoolean(v) + styles.blue.close,
19
+ serializeNumber: noColor ? undefined : (v) => styles.yellow.open + serializeNumber(v) + styles.yellow.close,
20
+ serializeStringQuote: noColor
21
+ ? undefined
22
+ : (v, open) => {
23
+ const q = styles.dim.open + v + styles.dim.close;
24
+ if (open) {
25
+ return styles.green.open + q;
26
+ } else {
27
+ return q + styles.green.close;
28
+ }
29
+ },
30
+ serializeStringEscape: noColor ? undefined : (v) => styles.bold.open + v + styles.bold.close,
31
+ serializePropName: noColor ? undefined : (v) => styles.whiteBright.open + String(v) + styles.whiteBright.close,
32
+ serializeFunction: noColor
33
+ ? operations.$ToString
34
+ : (v) => styles.cyan.open + operations.$ToString(v) + styles.cyan.close,
35
+ serializeModule: noColor
36
+ ? (v, depth, options) => {
37
+ return operations.$ToString(v) + ' ' + options.serializeRecord(v.value as VmRecord, depth, options);
38
+ }
39
+ : (v, depth, options) => {
40
+ return (
41
+ styles.magenta.open +
42
+ operations.$ToString(v) +
43
+ styles.magenta.close +
44
+ ' ' +
45
+ options.serializeRecord(v.value as VmRecord, depth, options)
46
+ );
47
+ },
48
+ };
49
+
50
+ /** 序列化值 */
51
+ export function print(value: VmAny, depth = 3): string {
52
+ if (value === undefined) {
53
+ if (noColor) return '<uninitialized>';
54
+ return styles.gray.open + '<uninitialized>' + styles.gray.close;
55
+ }
56
+ return serialize(value, { ...options, maxDepth: depth });
57
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ // 如果使用其他版本 TS,使用类似 "extends": "@cloudpss/tsconfig/4.2" 引入
3
+ "extends": "../../tsconfig.json",
4
+
5
+ "compilerOptions": {
6
+ "noEmit": true
7
+ }
8
+ }