@platforma-sdk/tengo-builder 1.16.1 → 1.17.1
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/commands/build.d.ts.map +1 -1
- package/dist/compiler/compiler.d.ts.map +1 -1
- package/dist/compiler/compileroptions.d.ts +6 -0
- package/dist/compiler/compileroptions.d.ts.map +1 -0
- package/dist/compiler/main.d.ts +0 -1
- package/dist/compiler/main.d.ts.map +1 -1
- package/dist/compiler/package.d.ts +6 -0
- package/dist/compiler/package.d.ts.map +1 -1
- package/dist/compiler/source.d.ts +9 -4
- package/dist/compiler/source.d.ts.map +1 -1
- package/dist/compiler/template.d.ts +5 -0
- package/dist/compiler/template.d.ts.map +1 -1
- package/dist/compiler/test.artifacts.d.ts +9 -0
- package/dist/compiler/test.artifacts.d.ts.map +1 -1
- package/dist/compiler/util.d.ts +3 -0
- package/dist/compiler/util.d.ts.map +1 -1
- package/dist/index.js +28 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +575 -513
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/commands/build.ts +2 -1
- package/src/commands/check.ts +1 -1
- package/src/commands/dump/all.ts +1 -1
- package/src/commands/dump/assets.ts +1 -1
- package/src/commands/dump/libs.ts +1 -1
- package/src/commands/dump/software.ts +1 -1
- package/src/commands/dump/templates.ts +1 -1
- package/src/commands/dump/tests.ts +1 -1
- package/src/commands/test.ts +1 -1
- package/src/compiler/compiler.test.ts +51 -20
- package/src/compiler/compiler.ts +10 -4
- package/src/compiler/compileroptions.ts +51 -0
- package/src/compiler/main.ts +4 -19
- package/src/compiler/package.ts +21 -13
- package/src/compiler/source.test.ts +18 -4
- package/src/compiler/source.ts +94 -20
- package/src/compiler/template.ts +6 -0
- package/src/compiler/test.artifacts.ts +66 -8
- package/src/compiler/util.ts +21 -0
package/src/compiler/source.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import
|
|
2
|
+
import winston from 'winston';
|
|
3
|
+
import {
|
|
4
|
+
TypedArtifactName,
|
|
5
|
+
FullArtifactName,
|
|
6
|
+
ArtifactType,
|
|
7
|
+
CompileMode,
|
|
8
|
+
CompilerOption
|
|
9
|
+
} from './package';
|
|
3
10
|
import { ArtifactMap, createArtifactNameSet } from './artifactset';
|
|
4
11
|
|
|
5
12
|
// matches any valid name in tengo. Don't forget to use '\b' when needed to limit the boundaries!
|
|
@@ -30,6 +37,9 @@ const newImportAssetRE = (moduleName: string) => {
|
|
|
30
37
|
return functionCallRE(moduleName, 'importAsset');
|
|
31
38
|
};
|
|
32
39
|
|
|
40
|
+
const emptyLineRE = /^\s*$/;
|
|
41
|
+
const compilerOptionRE = /^\/\/tengo:[\w]/;
|
|
42
|
+
const wrongCompilerOptionRE = /^\s*\/\/\s*tengo:\s*./;
|
|
33
43
|
const singlelineCommentRE = /^\s*(\/\/)|(\/\*.*\*\/)/;
|
|
34
44
|
const multilineCommentStartRE = /^\s*\/\*/;
|
|
35
45
|
const multilineCommentEndRE = /\*\//;
|
|
@@ -39,6 +49,30 @@ const importNameRE = new RegExp(
|
|
|
39
49
|
);
|
|
40
50
|
const dependencyRE = /(?<pkgName>[^"]+)?:(?<depID>[^"]+)/; // use it to parse <moduleName> from importPattern or <templateName> акщь getTemplateID
|
|
41
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Parse compiler option string representation
|
|
54
|
+
* Compiler option line is a comment starting with '//tengo:', say
|
|
55
|
+
* //tengo:hash_override tralala
|
|
56
|
+
*
|
|
57
|
+
* The common compiler option syntax is:
|
|
58
|
+
* //tengo:<option name> [<option arg1> [<option arg 2> [...]]]
|
|
59
|
+
*/
|
|
60
|
+
const parseComplierOption = (opt: string): CompilerOption => {
|
|
61
|
+
const parts = opt.split(' ');
|
|
62
|
+
const namePart = parts[0].split(':');
|
|
63
|
+
if (namePart.length != 2) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
"compiler option format is wrong: expect to have option name after 'tengo:' prefix, like 'tengo:MyOption'"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const optName = namePart[1];
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
name: optName,
|
|
72
|
+
args: parts.slice(1)
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
42
76
|
export class ArtifactSource {
|
|
43
77
|
constructor(
|
|
44
78
|
/** The mode this artifact was built (dev or dist) */
|
|
@@ -50,42 +84,49 @@ export class ArtifactSource {
|
|
|
50
84
|
/** Path to source file where artifact came from */
|
|
51
85
|
public readonly srcName: string,
|
|
52
86
|
/** List of dependencies */
|
|
53
|
-
public readonly dependencies: TypedArtifactName[]
|
|
87
|
+
public readonly dependencies: TypedArtifactName[],
|
|
88
|
+
/** Additional compiler options detected in source code */
|
|
89
|
+
public readonly compilerOptions: CompilerOption[]
|
|
54
90
|
) {}
|
|
55
91
|
}
|
|
56
92
|
|
|
57
93
|
export function parseSourceFile(
|
|
94
|
+
logger: winston.Logger,
|
|
58
95
|
mode: CompileMode,
|
|
59
96
|
srcFile: string,
|
|
60
97
|
fullSourceName: FullArtifactName,
|
|
61
98
|
normalize: boolean
|
|
62
99
|
): ArtifactSource {
|
|
63
100
|
const src = readFileSync(srcFile).toString();
|
|
64
|
-
const { deps, normalized } = parseSourceData(src, fullSourceName, normalize);
|
|
101
|
+
const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);
|
|
65
102
|
|
|
66
|
-
return new ArtifactSource(mode, fullSourceName, normalized, srcFile, deps.array);
|
|
103
|
+
return new ArtifactSource(mode, fullSourceName, normalized, srcFile, deps.array, opts);
|
|
67
104
|
}
|
|
68
105
|
|
|
69
106
|
export function parseSource(
|
|
107
|
+
logger: winston.Logger,
|
|
70
108
|
mode: CompileMode,
|
|
71
109
|
src: string,
|
|
72
110
|
fullSourceName: FullArtifactName,
|
|
73
111
|
normalize: boolean
|
|
74
112
|
): ArtifactSource {
|
|
75
|
-
const { deps, normalized } = parseSourceData(src, fullSourceName, normalize);
|
|
113
|
+
const { deps, normalized, opts } = parseSourceData(logger, src, fullSourceName, normalize);
|
|
76
114
|
|
|
77
|
-
return new ArtifactSource(mode, fullSourceName, normalized, '', deps.array);
|
|
115
|
+
return new ArtifactSource(mode, fullSourceName, normalized, '', deps.array, opts);
|
|
78
116
|
}
|
|
79
117
|
|
|
80
118
|
function parseSourceData(
|
|
119
|
+
logger: winston.Logger,
|
|
81
120
|
src: string,
|
|
82
121
|
fullSourceName: FullArtifactName,
|
|
83
122
|
globalizeImports: boolean
|
|
84
123
|
): {
|
|
85
124
|
normalized: string;
|
|
86
125
|
deps: ArtifactMap<TypedArtifactName>;
|
|
126
|
+
opts: CompilerOption[];
|
|
87
127
|
} {
|
|
88
128
|
const dependencySet = createArtifactNameSet();
|
|
129
|
+
const optionList: CompilerOption[] = [];
|
|
89
130
|
|
|
90
131
|
// iterating over lines
|
|
91
132
|
const lines = src.split('\n');
|
|
@@ -97,15 +138,17 @@ function parseSourceData(
|
|
|
97
138
|
const processedLines: string[] = [];
|
|
98
139
|
let parserContext: sourceParserContext = {
|
|
99
140
|
isInCommentBlock: false,
|
|
100
|
-
|
|
141
|
+
canDetectOptions: true,
|
|
142
|
+
tplDepREs: new Map<string, [ArtifactType, RegExp][]>(),
|
|
143
|
+
lineNo: 0
|
|
101
144
|
};
|
|
102
145
|
|
|
103
|
-
let lineNo = 0;
|
|
104
146
|
for (const line of lines) {
|
|
105
|
-
lineNo++;
|
|
147
|
+
parserContext.lineNo++;
|
|
106
148
|
|
|
107
149
|
try {
|
|
108
150
|
const result = parseSingleSourceLine(
|
|
151
|
+
logger,
|
|
109
152
|
line,
|
|
110
153
|
parserContext,
|
|
111
154
|
fullSourceName.pkg,
|
|
@@ -117,48 +160,79 @@ function parseSourceData(
|
|
|
117
160
|
if (result.artifact) {
|
|
118
161
|
dependencySet.add(result.artifact);
|
|
119
162
|
}
|
|
163
|
+
if (result.option) {
|
|
164
|
+
optionList.push(result.option);
|
|
165
|
+
}
|
|
120
166
|
} catch (error: any) {
|
|
121
|
-
throw new Error(`[line ${lineNo}]: ${error.message}\n\t${line}`);
|
|
167
|
+
throw new Error(`[line ${parserContext.lineNo}]: ${error.message}\n\t${line}`);
|
|
122
168
|
}
|
|
123
169
|
}
|
|
124
170
|
|
|
125
171
|
return {
|
|
126
172
|
normalized: processedLines.join('\n'),
|
|
127
|
-
deps: dependencySet
|
|
173
|
+
deps: dependencySet,
|
|
174
|
+
opts: optionList
|
|
128
175
|
};
|
|
129
176
|
}
|
|
130
177
|
|
|
131
178
|
interface sourceParserContext {
|
|
132
179
|
isInCommentBlock: boolean;
|
|
180
|
+
canDetectOptions: boolean;
|
|
133
181
|
tplDepREs: Map<string, [ArtifactType, RegExp][]>;
|
|
182
|
+
lineNo: number;
|
|
134
183
|
}
|
|
135
184
|
|
|
136
185
|
function parseSingleSourceLine(
|
|
186
|
+
logger: winston.Logger,
|
|
137
187
|
line: string,
|
|
138
188
|
context: sourceParserContext,
|
|
139
189
|
localPackageName: string,
|
|
140
|
-
globalizeImports
|
|
190
|
+
globalizeImports?: boolean
|
|
141
191
|
): {
|
|
142
192
|
line: string;
|
|
143
193
|
context: sourceParserContext;
|
|
144
194
|
artifact: TypedArtifactName | undefined;
|
|
195
|
+
option: CompilerOption | undefined;
|
|
145
196
|
} {
|
|
146
197
|
if (context.isInCommentBlock) {
|
|
147
198
|
if (multilineCommentEndRE.exec(line)) {
|
|
148
199
|
context.isInCommentBlock = false;
|
|
149
200
|
}
|
|
150
|
-
return { line, context, artifact: undefined };
|
|
201
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (compilerOptionRE.exec(line)) {
|
|
205
|
+
if (!context.canDetectOptions) {
|
|
206
|
+
logger.error(
|
|
207
|
+
`[line ${context.lineNo}]: compiler option '//tengo:' was detected, but it cannot be applied as compiler options can be set only at the file header, before any code line'`
|
|
208
|
+
);
|
|
209
|
+
throw new Error("tengo compiler options ('//tengo:' comments) can be set only in file header");
|
|
210
|
+
}
|
|
211
|
+
return { line, context, artifact: undefined, option: parseComplierOption(line) };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (wrongCompilerOptionRE.exec(line) && context.canDetectOptions) {
|
|
215
|
+
logger.warn(
|
|
216
|
+
`[line ${context.lineNo}]: text simillar to compiler option ('//tengo:...') was detected, but it has wrong format. Leave it as is, if you did not mean to use a line as compiler option. Or format it to '//tengo:<option>' otherwise (no spaces between '//' and 'tengo', no spaces between ':' and option name)`
|
|
217
|
+
);
|
|
218
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
151
219
|
}
|
|
152
220
|
|
|
153
221
|
if (singlelineCommentRE.exec(line)) {
|
|
154
|
-
return { line, context, artifact: undefined };
|
|
222
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
155
223
|
}
|
|
156
224
|
|
|
157
225
|
if (multilineCommentStartRE.exec(line)) {
|
|
158
226
|
context.isInCommentBlock = true;
|
|
159
|
-
return { line, context, artifact: undefined };
|
|
227
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (emptyLineRE.exec(line)) {
|
|
231
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
160
232
|
}
|
|
161
233
|
|
|
234
|
+
context.canDetectOptions = false;
|
|
235
|
+
|
|
162
236
|
const importInstruction = importRE.exec(line);
|
|
163
237
|
|
|
164
238
|
if (importInstruction) {
|
|
@@ -171,7 +245,7 @@ function parseSingleSourceLine(
|
|
|
171
245
|
['software', newGetSoftwareInfoRE(iInfo.alias)]
|
|
172
246
|
]);
|
|
173
247
|
}
|
|
174
|
-
return { line, context, artifact: undefined };
|
|
248
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
175
249
|
}
|
|
176
250
|
|
|
177
251
|
if (
|
|
@@ -208,14 +282,14 @@ function parseSingleSourceLine(
|
|
|
208
282
|
const artifact = parseArtifactName(iInfo.module, 'library', localPackageName);
|
|
209
283
|
if (!artifact) {
|
|
210
284
|
// not a Platforma Tengo library import
|
|
211
|
-
return { line, context, artifact: undefined };
|
|
285
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
212
286
|
}
|
|
213
287
|
|
|
214
288
|
if (globalizeImports) {
|
|
215
289
|
line = line.replace(importInstruction[0], ` := import("${artifact.pkg}:${artifact.id}")`);
|
|
216
290
|
}
|
|
217
291
|
|
|
218
|
-
return { line, context, artifact };
|
|
292
|
+
return { line, context, artifact, option: undefined };
|
|
219
293
|
}
|
|
220
294
|
|
|
221
295
|
if (context.tplDepREs.size > 0) {
|
|
@@ -241,12 +315,12 @@ function parseSingleSourceLine(
|
|
|
241
315
|
line = line.replace(fnCall, `${fnName}("${artifact.pkg}:${artifact.id}")`);
|
|
242
316
|
}
|
|
243
317
|
|
|
244
|
-
return { line, context, artifact };
|
|
318
|
+
return { line, context, artifact, option: undefined };
|
|
245
319
|
}
|
|
246
320
|
}
|
|
247
321
|
}
|
|
248
322
|
|
|
249
|
-
return { line, context, artifact: undefined };
|
|
323
|
+
return { line, context, artifact: undefined, option: undefined };
|
|
250
324
|
}
|
|
251
325
|
|
|
252
326
|
interface ImportInfo {
|
package/src/compiler/template.ts
CHANGED
|
@@ -44,6 +44,12 @@ export interface TemplateData {
|
|
|
44
44
|
/** i.e. 1.2.3 */
|
|
45
45
|
version: string;
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Custom hash token of the template for deduplication purposes. Can be set with 'hash_override' compiler option.
|
|
49
|
+
* Dangerous! Remember: great power comes with great responsibility.
|
|
50
|
+
*/
|
|
51
|
+
hashOverride?: string;
|
|
52
|
+
|
|
47
53
|
/** i.e. @milaboratory/some-package:some-lib -> normalized library source code */
|
|
48
54
|
libs: Record<string, TemplateLibData>;
|
|
49
55
|
/** i.e. @milaboratory/some-package:some-lib -> to nested template data */
|
|
@@ -93,6 +93,18 @@ export {
|
|
|
93
93
|
}
|
|
94
94
|
`;
|
|
95
95
|
|
|
96
|
+
export const testLocalLib3Name: FullArtifactName = {
|
|
97
|
+
type: 'library',
|
|
98
|
+
pkg: 'current-package',
|
|
99
|
+
id: 'local-library-3',
|
|
100
|
+
version: '6.6.6'
|
|
101
|
+
};
|
|
102
|
+
export const testLocalLib3Src = `
|
|
103
|
+
export {
|
|
104
|
+
"some": "value"
|
|
105
|
+
}
|
|
106
|
+
`;
|
|
107
|
+
|
|
96
108
|
export const testLocalTpl1Name: FullArtifactName = {
|
|
97
109
|
type: 'template',
|
|
98
110
|
pkg: 'current-package',
|
|
@@ -120,19 +132,50 @@ export const testLocalTpl2SrcNormalized = `
|
|
|
120
132
|
lib := import("package1:other-lib-1")
|
|
121
133
|
`;
|
|
122
134
|
|
|
135
|
+
export const testLocalTpl3Name: FullArtifactName = {
|
|
136
|
+
type: 'template',
|
|
137
|
+
pkg: 'current-package',
|
|
138
|
+
id: 'local-template-3',
|
|
139
|
+
version: '1.2.3'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const testLocalTpl3Src = `
|
|
143
|
+
//tengo:hash_override CE0F6EDF-D97C-44E7-B16B-D661D4C799C1
|
|
144
|
+
|
|
145
|
+
a := "some instruction"
|
|
146
|
+
lib := import(":local-library-3")
|
|
147
|
+
`;
|
|
148
|
+
|
|
149
|
+
export const testLocalTpl3SrcWrongOverride = `
|
|
150
|
+
//tengo:hash_override broken-hash-override
|
|
151
|
+
|
|
152
|
+
a := "some instruction"
|
|
153
|
+
lib := import(":local-library-3")
|
|
154
|
+
`;
|
|
155
|
+
|
|
123
156
|
export const testLocalLib1: TestArtifactSource = {
|
|
124
157
|
fullName: testLocalLib1Name,
|
|
125
|
-
src: testLocalLib1Src
|
|
158
|
+
src: testLocalLib1Src
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const testLocalLib3: TestArtifactSource = {
|
|
162
|
+
fullName: testLocalLib3Name,
|
|
163
|
+
src: testLocalLib3Src
|
|
126
164
|
};
|
|
127
165
|
|
|
128
166
|
export const testLocalTpl1: TestArtifactSource = {
|
|
129
167
|
fullName: testLocalTpl1Name,
|
|
130
|
-
src: testLocalTpl1Src
|
|
168
|
+
src: testLocalTpl1Src
|
|
131
169
|
};
|
|
132
170
|
|
|
133
171
|
export const testLocalTpl2: TestArtifactSource = {
|
|
134
172
|
fullName: testLocalTpl2Name,
|
|
135
|
-
src: testLocalTpl2Src
|
|
173
|
+
src: testLocalTpl2Src
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const testLocalTpl3: TestArtifactSource = {
|
|
177
|
+
fullName: testLocalTpl3Name,
|
|
178
|
+
src: testLocalTpl3Src
|
|
136
179
|
};
|
|
137
180
|
|
|
138
181
|
export const testLocalPackage = [testLocalTpl1, testLocalLib1, testLocalTpl2];
|
|
@@ -183,21 +226,36 @@ export const testPackage1Tpl3Src = `
|
|
|
183
226
|
lib := import("package1:other-lib-1")
|
|
184
227
|
`;
|
|
185
228
|
|
|
186
|
-
export const testPackage1Tpl3CompiledBase64 =
|
|
229
|
+
export const testPackage1Tpl3CompiledBase64 =
|
|
230
|
+
'H4sIAAAAAAAAE22PQQqDMBREr/KZVQsxELsL9CZ/E+VjQ2MiJpUWyd2LglCo2xlm3syK4LsMu2Jy/dMNYmwqD5mb4LvGbHp0o8Ce2wp57mHBUd5TmgutHImIGDmNwrDEWFx4iWFwrByhsMicfYqwMLrVN9Sq/hhFxim4Is3tBxF8R3fy4wa68OkgxnVnHPntWFUon2mvD7pIHFJz2HppzwZ9AanB7OAUAQAA';
|
|
187
231
|
|
|
188
232
|
export const testPackage1Lib1: TestArtifactSource = {
|
|
189
233
|
fullName: testPackage1Lib1Name,
|
|
190
|
-
src: testPackage1Lib1Src
|
|
234
|
+
src: testPackage1Lib1Src
|
|
191
235
|
};
|
|
192
236
|
|
|
193
237
|
export const testPackage1Lib2: TestArtifactSource = {
|
|
194
238
|
fullName: testPackage1Lib2Name,
|
|
195
|
-
src: testPackage1Lib2Src
|
|
239
|
+
src: testPackage1Lib2Src
|
|
196
240
|
};
|
|
197
241
|
|
|
198
242
|
export const testPackage1Tpl3: TestArtifactSource = {
|
|
199
243
|
fullName: testPackage1Tpl3Name,
|
|
200
|
-
src: testPackage1Tpl3Src
|
|
244
|
+
src: testPackage1Tpl3Src
|
|
201
245
|
};
|
|
202
246
|
|
|
203
|
-
export const testPackage1 = [
|
|
247
|
+
export const testPackage1: TestArtifactSource[] = [
|
|
248
|
+
testPackage1Lib1,
|
|
249
|
+
testPackage1Lib2,
|
|
250
|
+
testPackage1Tpl3
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
export const testPackage2: TestArtifactSource[] = [testLocalLib3, testLocalTpl3];
|
|
254
|
+
|
|
255
|
+
export const testPackage2BrokenHash: TestArtifactSource[] = [
|
|
256
|
+
testLocalLib3,
|
|
257
|
+
{
|
|
258
|
+
fullName: testLocalTpl3Name,
|
|
259
|
+
src: testLocalTpl3SrcWrongOverride
|
|
260
|
+
}
|
|
261
|
+
];
|
package/src/compiler/util.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
+
import * as winston from 'winston';
|
|
3
4
|
|
|
4
5
|
export function assertNever(x: never): never {
|
|
5
6
|
throw new Error('Unexpected object: ' + x);
|
|
6
7
|
}
|
|
7
8
|
|
|
9
|
+
export function createLogger(level: string = 'debug'): winston.Logger {
|
|
10
|
+
return winston.createLogger({
|
|
11
|
+
level: level,
|
|
12
|
+
format: winston.format.printf(({ level, message }) => {
|
|
13
|
+
return `${level.padStart(6, ' ')}: ${message}`;
|
|
14
|
+
}),
|
|
15
|
+
transports: [
|
|
16
|
+
new winston.transports.Console({
|
|
17
|
+
stderrLevels: ['error', 'warn', 'info', 'debug'],
|
|
18
|
+
handleExceptions: true
|
|
19
|
+
})
|
|
20
|
+
]
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
8
24
|
export function findNodeModules(): string {
|
|
9
25
|
let currentDir = process.cwd();
|
|
10
26
|
|
|
@@ -36,3 +52,8 @@ export function pathType(path: string): PathType {
|
|
|
36
52
|
else throw err;
|
|
37
53
|
}
|
|
38
54
|
}
|
|
55
|
+
|
|
56
|
+
export function isUUID(uuid: string): boolean {
|
|
57
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
58
|
+
return uuidRegex.test(uuid.toLowerCase());
|
|
59
|
+
}
|