typia 4.2.0 → 4.2.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/lib/executable/TypiaSetupWizard.js +3 -3
- package/lib/executable/TypiaSetupWizard.js.map +1 -1
- package/lib/executable/setup/CommandExecutor.js +1 -1
- package/lib/executable/setup/CommandExecutor.js.map +1 -1
- package/lib/programmers/RandomProgrammer.js.map +1 -1
- package/lib/programmers/internal/stringify_dynamic_properties.js +1 -0
- package/lib/programmers/internal/stringify_dynamic_properties.js.map +1 -1
- package/lib/utils/PatternUtil.d.ts +1 -1
- package/lib/utils/PatternUtil.js +3 -1
- package/lib/utils/PatternUtil.js.map +1 -1
- package/package.json +1 -1
- package/src/executable/TypiaSetupWizard.ts +153 -149
- package/src/executable/setup/CommandExecutor.ts +8 -9
- package/src/factories/MetadataTagFactory.ts +355 -355
- package/src/factories/TypeFactory.ts +124 -124
- package/src/functional/$string.ts +50 -50
- package/src/programmers/RandomProgrammer.ts +1 -3
- package/src/programmers/TypiaProgrammer.ts +129 -129
- package/src/programmers/helpers/OptionPredicator.ts +15 -15
- package/src/programmers/internal/application_object.ts +165 -165
- package/src/programmers/internal/stringify_dynamic_properties.ts +171 -167
- package/src/transform.ts +27 -27
- package/src/transformers/ITransformOptions.ts +62 -62
- package/src/transformers/NodeTransformer.ts +13 -13
- package/src/utils/PatternUtil.ts +33 -30
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
import ts from "typescript";
|
|
2
|
-
|
|
3
|
-
export namespace TypeFactory {
|
|
4
|
-
export const resolve =
|
|
5
|
-
(checker: ts.TypeChecker) =>
|
|
6
|
-
(type: ts.Type): ts.Type | null =>
|
|
7
|
-
getReturnType(checker)(type)("toJSON");
|
|
8
|
-
|
|
9
|
-
export const isFunction = (type: ts.Type): boolean =>
|
|
10
|
-
getFunction(type) !== null;
|
|
11
|
-
|
|
12
|
-
const getFunction = (type: ts.Type) => {
|
|
13
|
-
const node = type.symbol?.declarations?.[0];
|
|
14
|
-
if (node === undefined) return null;
|
|
15
|
-
|
|
16
|
-
return ts.isFunctionLike(node)
|
|
17
|
-
? node
|
|
18
|
-
: ts.isPropertyAssignment(node) || ts.isPropertyDeclaration(node)
|
|
19
|
-
? ts.isFunctionLike(node.initializer)
|
|
20
|
-
? node.initializer
|
|
21
|
-
: null
|
|
22
|
-
: null;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const getReturnType =
|
|
26
|
-
(checker: ts.TypeChecker) =>
|
|
27
|
-
(type: ts.Type) =>
|
|
28
|
-
(name: string): ts.Type | null => {
|
|
29
|
-
// FIND TO-JSON METHOD
|
|
30
|
-
const symbol: ts.Symbol | undefined = type.getProperty(name);
|
|
31
|
-
if (!symbol) return null;
|
|
32
|
-
else if (!symbol.valueDeclaration) return null;
|
|
33
|
-
|
|
34
|
-
// GET FUNCTION DECLARATION
|
|
35
|
-
const functor: ts.Type = checker.getTypeOfSymbolAtLocation(
|
|
36
|
-
symbol,
|
|
37
|
-
symbol.valueDeclaration,
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
// RETURNS THE RETURN-TYPE
|
|
41
|
-
const signature: ts.Signature | undefined =
|
|
42
|
-
checker.getSignaturesOfType(functor, ts.SignatureKind.Call)[0];
|
|
43
|
-
return signature ? signature.getReturnType() : null;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export const getFullName =
|
|
47
|
-
(checker: ts.TypeChecker) =>
|
|
48
|
-
(type: ts.Type, symbol?: ts.Symbol): string => {
|
|
49
|
-
// PRIMITIVE
|
|
50
|
-
symbol ??= type.aliasSymbol ?? type.getSymbol();
|
|
51
|
-
if (symbol === undefined) return checker.typeToString(type);
|
|
52
|
-
|
|
53
|
-
// UNION OR INTERSECT
|
|
54
|
-
if (
|
|
55
|
-
type.aliasSymbol === undefined &&
|
|
56
|
-
type.isUnionOrIntersection()
|
|
57
|
-
) {
|
|
58
|
-
const joiner: string = type.isIntersection() ? " & " : " | ";
|
|
59
|
-
return type.types
|
|
60
|
-
.map((child) => getFullName(checker)(child))
|
|
61
|
-
.join(joiner);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
//----
|
|
65
|
-
// SPECIALIZATION
|
|
66
|
-
//----
|
|
67
|
-
const name: string = get_name(symbol);
|
|
68
|
-
|
|
69
|
-
// CHECK GENERIC
|
|
70
|
-
const generic: readonly ts.Type[] = type.aliasSymbol
|
|
71
|
-
? type.aliasTypeArguments || []
|
|
72
|
-
: checker.getTypeArguments(type as ts.TypeReference);
|
|
73
|
-
return generic.length
|
|
74
|
-
? name === "Promise"
|
|
75
|
-
? getFullName(checker)(generic[0]!)
|
|
76
|
-
: `${name}<${generic
|
|
77
|
-
.map((child) => getFullName(checker)(child))
|
|
78
|
-
.join(", ")}>`
|
|
79
|
-
: name;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const explore_name =
|
|
83
|
-
(decl: ts.Node) =>
|
|
84
|
-
(name: string): string =>
|
|
85
|
-
ts.isModuleBlock(decl)
|
|
86
|
-
? explore_name(decl.parent.parent)(
|
|
87
|
-
`${decl.parent.name.getFullText().trim()}.${name}`,
|
|
88
|
-
)
|
|
89
|
-
: name;
|
|
90
|
-
|
|
91
|
-
const get_name = (symbol: ts.Symbol): string => {
|
|
92
|
-
const parent = symbol.getDeclarations()?.[0]?.parent;
|
|
93
|
-
return parent
|
|
94
|
-
? explore_name(parent)(symbol.escapedName.toString())
|
|
95
|
-
: "__type";
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export const keyword = (
|
|
99
|
-
type:
|
|
100
|
-
| "void"
|
|
101
|
-
| "any"
|
|
102
|
-
| "unknown"
|
|
103
|
-
| "boolean"
|
|
104
|
-
| "number"
|
|
105
|
-
| "bigint"
|
|
106
|
-
| "string",
|
|
107
|
-
) => {
|
|
108
|
-
return ts.factory.createKeywordTypeNode(
|
|
109
|
-
type === "void"
|
|
110
|
-
? ts.SyntaxKind.VoidKeyword
|
|
111
|
-
: type === "any"
|
|
112
|
-
? ts.SyntaxKind.AnyKeyword
|
|
113
|
-
: type === "unknown"
|
|
114
|
-
? ts.SyntaxKind.UnknownKeyword
|
|
115
|
-
: type === "boolean"
|
|
116
|
-
? ts.SyntaxKind.BooleanKeyword
|
|
117
|
-
: type === "number"
|
|
118
|
-
? ts.SyntaxKind.NumberKeyword
|
|
119
|
-
: type === "bigint"
|
|
120
|
-
? ts.SyntaxKind.BigIntKeyword
|
|
121
|
-
: ts.SyntaxKind.StringKeyword,
|
|
122
|
-
);
|
|
123
|
-
};
|
|
124
|
-
}
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
export namespace TypeFactory {
|
|
4
|
+
export const resolve =
|
|
5
|
+
(checker: ts.TypeChecker) =>
|
|
6
|
+
(type: ts.Type): ts.Type | null =>
|
|
7
|
+
getReturnType(checker)(type)("toJSON");
|
|
8
|
+
|
|
9
|
+
export const isFunction = (type: ts.Type): boolean =>
|
|
10
|
+
getFunction(type) !== null;
|
|
11
|
+
|
|
12
|
+
const getFunction = (type: ts.Type) => {
|
|
13
|
+
const node = type.symbol?.declarations?.[0];
|
|
14
|
+
if (node === undefined) return null;
|
|
15
|
+
|
|
16
|
+
return ts.isFunctionLike(node)
|
|
17
|
+
? node
|
|
18
|
+
: ts.isPropertyAssignment(node) || ts.isPropertyDeclaration(node)
|
|
19
|
+
? ts.isFunctionLike(node.initializer)
|
|
20
|
+
? node.initializer
|
|
21
|
+
: null
|
|
22
|
+
: null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getReturnType =
|
|
26
|
+
(checker: ts.TypeChecker) =>
|
|
27
|
+
(type: ts.Type) =>
|
|
28
|
+
(name: string): ts.Type | null => {
|
|
29
|
+
// FIND TO-JSON METHOD
|
|
30
|
+
const symbol: ts.Symbol | undefined = type.getProperty(name);
|
|
31
|
+
if (!symbol) return null;
|
|
32
|
+
else if (!symbol.valueDeclaration) return null;
|
|
33
|
+
|
|
34
|
+
// GET FUNCTION DECLARATION
|
|
35
|
+
const functor: ts.Type = checker.getTypeOfSymbolAtLocation(
|
|
36
|
+
symbol,
|
|
37
|
+
symbol.valueDeclaration,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// RETURNS THE RETURN-TYPE
|
|
41
|
+
const signature: ts.Signature | undefined =
|
|
42
|
+
checker.getSignaturesOfType(functor, ts.SignatureKind.Call)[0];
|
|
43
|
+
return signature ? signature.getReturnType() : null;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const getFullName =
|
|
47
|
+
(checker: ts.TypeChecker) =>
|
|
48
|
+
(type: ts.Type, symbol?: ts.Symbol): string => {
|
|
49
|
+
// PRIMITIVE
|
|
50
|
+
symbol ??= type.aliasSymbol ?? type.getSymbol();
|
|
51
|
+
if (symbol === undefined) return checker.typeToString(type);
|
|
52
|
+
|
|
53
|
+
// UNION OR INTERSECT
|
|
54
|
+
if (
|
|
55
|
+
type.aliasSymbol === undefined &&
|
|
56
|
+
type.isUnionOrIntersection()
|
|
57
|
+
) {
|
|
58
|
+
const joiner: string = type.isIntersection() ? " & " : " | ";
|
|
59
|
+
return type.types
|
|
60
|
+
.map((child) => getFullName(checker)(child))
|
|
61
|
+
.join(joiner);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//----
|
|
65
|
+
// SPECIALIZATION
|
|
66
|
+
//----
|
|
67
|
+
const name: string = get_name(symbol);
|
|
68
|
+
|
|
69
|
+
// CHECK GENERIC
|
|
70
|
+
const generic: readonly ts.Type[] = type.aliasSymbol
|
|
71
|
+
? type.aliasTypeArguments || []
|
|
72
|
+
: checker.getTypeArguments(type as ts.TypeReference);
|
|
73
|
+
return generic.length
|
|
74
|
+
? name === "Promise"
|
|
75
|
+
? getFullName(checker)(generic[0]!)
|
|
76
|
+
: `${name}<${generic
|
|
77
|
+
.map((child) => getFullName(checker)(child))
|
|
78
|
+
.join(", ")}>`
|
|
79
|
+
: name;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const explore_name =
|
|
83
|
+
(decl: ts.Node) =>
|
|
84
|
+
(name: string): string =>
|
|
85
|
+
ts.isModuleBlock(decl)
|
|
86
|
+
? explore_name(decl.parent.parent)(
|
|
87
|
+
`${decl.parent.name.getFullText().trim()}.${name}`,
|
|
88
|
+
)
|
|
89
|
+
: name;
|
|
90
|
+
|
|
91
|
+
const get_name = (symbol: ts.Symbol): string => {
|
|
92
|
+
const parent = symbol.getDeclarations()?.[0]?.parent;
|
|
93
|
+
return parent
|
|
94
|
+
? explore_name(parent)(symbol.escapedName.toString())
|
|
95
|
+
: "__type";
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const keyword = (
|
|
99
|
+
type:
|
|
100
|
+
| "void"
|
|
101
|
+
| "any"
|
|
102
|
+
| "unknown"
|
|
103
|
+
| "boolean"
|
|
104
|
+
| "number"
|
|
105
|
+
| "bigint"
|
|
106
|
+
| "string",
|
|
107
|
+
) => {
|
|
108
|
+
return ts.factory.createKeywordTypeNode(
|
|
109
|
+
type === "void"
|
|
110
|
+
? ts.SyntaxKind.VoidKeyword
|
|
111
|
+
: type === "any"
|
|
112
|
+
? ts.SyntaxKind.AnyKeyword
|
|
113
|
+
: type === "unknown"
|
|
114
|
+
? ts.SyntaxKind.UnknownKeyword
|
|
115
|
+
: type === "boolean"
|
|
116
|
+
? ts.SyntaxKind.BooleanKeyword
|
|
117
|
+
: type === "number"
|
|
118
|
+
? ts.SyntaxKind.NumberKeyword
|
|
119
|
+
: type === "bigint"
|
|
120
|
+
? ts.SyntaxKind.BigIntKeyword
|
|
121
|
+
: ts.SyntaxKind.StringKeyword,
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In the past, name of `typia` was `typescript-json`, and supported
|
|
3
|
-
* JSON serialization by wrapping `fast-json-stringify. `typescript-json` was
|
|
4
|
-
* a helper library of `fast-json-stringify`, which can skip manual JSON schema
|
|
5
|
-
* definition just by putting pure TypeScript type.
|
|
6
|
-
*
|
|
7
|
-
* This `$string` function is a part of `fast-json-stringify` at that time, and
|
|
8
|
-
* still being used in `typia` for the string serialization.
|
|
9
|
-
*
|
|
10
|
-
* @internal
|
|
11
|
-
* @reference https://github.com/fastify/fast-json-stringify/blob/master/lib/serializer.js
|
|
12
|
-
* @blog https://dev.to/samchon/good-bye-typescript-is-ancestor-of-typia-20000x-faster-validator-49fi
|
|
13
|
-
*/
|
|
14
|
-
export const $string = (str: string): string => {
|
|
15
|
-
if (STR_ESCAPE.test(str) === false) return `"${str}"`;
|
|
16
|
-
|
|
17
|
-
const length: number = str.length;
|
|
18
|
-
if (length > 41) return JSON.stringify(str);
|
|
19
|
-
|
|
20
|
-
let result = "";
|
|
21
|
-
let last = -1;
|
|
22
|
-
let point = 255;
|
|
23
|
-
|
|
24
|
-
// eslint-disable-next-line
|
|
25
|
-
for (let i = 0; i < length; ++i) {
|
|
26
|
-
point = str.charCodeAt(i);
|
|
27
|
-
if (point < 32) {
|
|
28
|
-
return JSON.stringify(str);
|
|
29
|
-
}
|
|
30
|
-
if (point >= 0xd800 && point <= 0xdfff) {
|
|
31
|
-
// The current character is a surrogate.
|
|
32
|
-
return JSON.stringify(str);
|
|
33
|
-
}
|
|
34
|
-
if (
|
|
35
|
-
point === 0x22 || // '"'
|
|
36
|
-
point === 0x5c // '\'
|
|
37
|
-
) {
|
|
38
|
-
last === -1 && (last = 0);
|
|
39
|
-
result += str.slice(last, i) + "\\";
|
|
40
|
-
last = i;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
(last === -1 && '"' + str + '"') || '"' + result + str.slice(last) + '"'
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const STR_ESCAPE =
|
|
50
|
-
/[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/;
|
|
1
|
+
/**
|
|
2
|
+
* In the past, name of `typia` was `typescript-json`, and supported
|
|
3
|
+
* JSON serialization by wrapping `fast-json-stringify. `typescript-json` was
|
|
4
|
+
* a helper library of `fast-json-stringify`, which can skip manual JSON schema
|
|
5
|
+
* definition just by putting pure TypeScript type.
|
|
6
|
+
*
|
|
7
|
+
* This `$string` function is a part of `fast-json-stringify` at that time, and
|
|
8
|
+
* still being used in `typia` for the string serialization.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
* @reference https://github.com/fastify/fast-json-stringify/blob/master/lib/serializer.js
|
|
12
|
+
* @blog https://dev.to/samchon/good-bye-typescript-is-ancestor-of-typia-20000x-faster-validator-49fi
|
|
13
|
+
*/
|
|
14
|
+
export const $string = (str: string): string => {
|
|
15
|
+
if (STR_ESCAPE.test(str) === false) return `"${str}"`;
|
|
16
|
+
|
|
17
|
+
const length: number = str.length;
|
|
18
|
+
if (length > 41) return JSON.stringify(str);
|
|
19
|
+
|
|
20
|
+
let result = "";
|
|
21
|
+
let last = -1;
|
|
22
|
+
let point = 255;
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line
|
|
25
|
+
for (let i = 0; i < length; ++i) {
|
|
26
|
+
point = str.charCodeAt(i);
|
|
27
|
+
if (point < 32) {
|
|
28
|
+
return JSON.stringify(str);
|
|
29
|
+
}
|
|
30
|
+
if (point >= 0xd800 && point <= 0xdfff) {
|
|
31
|
+
// The current character is a surrogate.
|
|
32
|
+
return JSON.stringify(str);
|
|
33
|
+
}
|
|
34
|
+
if (
|
|
35
|
+
point === 0x22 || // '"'
|
|
36
|
+
point === 0x5c // '\'
|
|
37
|
+
) {
|
|
38
|
+
last === -1 && (last = 0);
|
|
39
|
+
result += str.slice(last, i) + "\\";
|
|
40
|
+
last = i;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
(last === -1 && '"' + str + '"') || '"' + result + str.slice(last) + '"'
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const STR_ESCAPE =
|
|
50
|
+
/[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/;
|
|
@@ -219,9 +219,7 @@ export namespace RandomProgrammer {
|
|
|
219
219
|
const expressions: ts.Expression[] = [];
|
|
220
220
|
if (meta.any)
|
|
221
221
|
expressions.push(
|
|
222
|
-
ts.factory.createStringLiteral(
|
|
223
|
-
"any type used...",
|
|
224
|
-
),
|
|
222
|
+
ts.factory.createStringLiteral("any type used..."),
|
|
225
223
|
);
|
|
226
224
|
|
|
227
225
|
// NULL COALESCING
|
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import ts from "typescript";
|
|
4
|
-
|
|
5
|
-
import { ImportTransformer } from "../transformers/ImportTransformer";
|
|
6
|
-
|
|
7
|
-
import transform from "../transform";
|
|
8
|
-
|
|
9
|
-
export namespace TypiaProgrammer {
|
|
10
|
-
export interface IProps {
|
|
11
|
-
input: string;
|
|
12
|
-
output: string;
|
|
13
|
-
project: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const build = async (
|
|
17
|
-
props: TypiaProgrammer.IProps,
|
|
18
|
-
): Promise<void> => {
|
|
19
|
-
props.input = path.resolve(props.input);
|
|
20
|
-
props.output = path.resolve(props.output);
|
|
21
|
-
|
|
22
|
-
if ((await is_directory(props.input)) === false)
|
|
23
|
-
throw new Error(
|
|
24
|
-
"Error on TypiaGenerator.generate(): input path is not a directory.",
|
|
25
|
-
);
|
|
26
|
-
else if (fs.existsSync(props.output) === false)
|
|
27
|
-
await fs.promises.mkdir(props.output, { recursive: true });
|
|
28
|
-
else if ((await is_directory(props.output)) === false) {
|
|
29
|
-
const parent: string = path.join(props.output, "..");
|
|
30
|
-
if ((await is_directory(parent)) === false)
|
|
31
|
-
throw new Error(
|
|
32
|
-
"Error on TypiaGenerator.generate(): output path is not a directory.",
|
|
33
|
-
);
|
|
34
|
-
await fs.promises.mkdir(props.output);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// CREATE PROGRAM
|
|
38
|
-
const { options: compilerOptions } = ts.parseJsonConfigFileContent(
|
|
39
|
-
ts.readConfigFile(props.project, ts.sys.readFile).config,
|
|
40
|
-
{
|
|
41
|
-
fileExists: ts.sys.fileExists,
|
|
42
|
-
readFile: ts.sys.readFile,
|
|
43
|
-
readDirectory: ts.sys.readDirectory,
|
|
44
|
-
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
45
|
-
},
|
|
46
|
-
path.dirname(props.project),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const program: ts.Program = ts.createProgram(
|
|
50
|
-
await (async () => {
|
|
51
|
-
const container: string[] = [];
|
|
52
|
-
await gather(props)(container)(props.input)(props.output);
|
|
53
|
-
return container;
|
|
54
|
-
})(),
|
|
55
|
-
compilerOptions,
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// DO TRANSFORM
|
|
59
|
-
const result: ts.TransformationResult<ts.SourceFile> = ts.transform(
|
|
60
|
-
program
|
|
61
|
-
.getSourceFiles()
|
|
62
|
-
.filter(
|
|
63
|
-
(file) =>
|
|
64
|
-
!file.isDeclarationFile &&
|
|
65
|
-
path.resolve(file.fileName).indexOf(props.input) !== -1,
|
|
66
|
-
),
|
|
67
|
-
[
|
|
68
|
-
ImportTransformer.transform(props.input)(props.output),
|
|
69
|
-
transform(
|
|
70
|
-
program,
|
|
71
|
-
((compilerOptions.plugins as any[]) ?? []).find(
|
|
72
|
-
(p: any) =>
|
|
73
|
-
p.transform === "typia/lib/transform" ||
|
|
74
|
-
p.transform === "../src/transform.ts",
|
|
75
|
-
) ?? {},
|
|
76
|
-
),
|
|
77
|
-
],
|
|
78
|
-
program.getCompilerOptions(),
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// ARCHIVE TRANSFORMED FILES
|
|
82
|
-
const printer: ts.Printer = ts.createPrinter({
|
|
83
|
-
newLine: ts.NewLineKind.LineFeed,
|
|
84
|
-
});
|
|
85
|
-
for (const file of result.transformed) {
|
|
86
|
-
const to: string = path
|
|
87
|
-
.resolve(file.fileName)
|
|
88
|
-
.replace(props.input, props.output);
|
|
89
|
-
|
|
90
|
-
const content: string = printer.printFile(file);
|
|
91
|
-
await fs.promises.writeFile(to, emend(content), "utf8");
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const emend = (content: string): string => {
|
|
96
|
-
if (
|
|
97
|
-
content.indexOf("typia.") === -1 ||
|
|
98
|
-
content.indexOf("import typia") !== -1 ||
|
|
99
|
-
content.indexOf("import * as typia") !== -1
|
|
100
|
-
)
|
|
101
|
-
return content;
|
|
102
|
-
return `import typia from "typia";\n\n${content}`;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const is_directory = async (current: string): Promise<boolean> => {
|
|
106
|
-
const stat: fs.Stats = await fs.promises.stat(current);
|
|
107
|
-
return stat.isDirectory();
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const gather =
|
|
111
|
-
(props: IProps) =>
|
|
112
|
-
(container: string[]) =>
|
|
113
|
-
(from: string) =>
|
|
114
|
-
async (to: string) => {
|
|
115
|
-
if (from === props.output) return;
|
|
116
|
-
else if (fs.existsSync(to) === false) await fs.promises.mkdir(to);
|
|
117
|
-
|
|
118
|
-
for (const file of await fs.promises.readdir(from)) {
|
|
119
|
-
const next: string = path.join(from, file);
|
|
120
|
-
const stat: fs.Stats = await fs.promises.stat(next);
|
|
121
|
-
|
|
122
|
-
if (stat.isDirectory()) {
|
|
123
|
-
await gather(props)(container)(next)(path.join(to, file));
|
|
124
|
-
continue;
|
|
125
|
-
} else if (file.substring(file.length - 3) === ".ts")
|
|
126
|
-
container.push(next);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
|
|
5
|
+
import { ImportTransformer } from "../transformers/ImportTransformer";
|
|
6
|
+
|
|
7
|
+
import transform from "../transform";
|
|
8
|
+
|
|
9
|
+
export namespace TypiaProgrammer {
|
|
10
|
+
export interface IProps {
|
|
11
|
+
input: string;
|
|
12
|
+
output: string;
|
|
13
|
+
project: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const build = async (
|
|
17
|
+
props: TypiaProgrammer.IProps,
|
|
18
|
+
): Promise<void> => {
|
|
19
|
+
props.input = path.resolve(props.input);
|
|
20
|
+
props.output = path.resolve(props.output);
|
|
21
|
+
|
|
22
|
+
if ((await is_directory(props.input)) === false)
|
|
23
|
+
throw new Error(
|
|
24
|
+
"Error on TypiaGenerator.generate(): input path is not a directory.",
|
|
25
|
+
);
|
|
26
|
+
else if (fs.existsSync(props.output) === false)
|
|
27
|
+
await fs.promises.mkdir(props.output, { recursive: true });
|
|
28
|
+
else if ((await is_directory(props.output)) === false) {
|
|
29
|
+
const parent: string = path.join(props.output, "..");
|
|
30
|
+
if ((await is_directory(parent)) === false)
|
|
31
|
+
throw new Error(
|
|
32
|
+
"Error on TypiaGenerator.generate(): output path is not a directory.",
|
|
33
|
+
);
|
|
34
|
+
await fs.promises.mkdir(props.output);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// CREATE PROGRAM
|
|
38
|
+
const { options: compilerOptions } = ts.parseJsonConfigFileContent(
|
|
39
|
+
ts.readConfigFile(props.project, ts.sys.readFile).config,
|
|
40
|
+
{
|
|
41
|
+
fileExists: ts.sys.fileExists,
|
|
42
|
+
readFile: ts.sys.readFile,
|
|
43
|
+
readDirectory: ts.sys.readDirectory,
|
|
44
|
+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
45
|
+
},
|
|
46
|
+
path.dirname(props.project),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const program: ts.Program = ts.createProgram(
|
|
50
|
+
await (async () => {
|
|
51
|
+
const container: string[] = [];
|
|
52
|
+
await gather(props)(container)(props.input)(props.output);
|
|
53
|
+
return container;
|
|
54
|
+
})(),
|
|
55
|
+
compilerOptions,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// DO TRANSFORM
|
|
59
|
+
const result: ts.TransformationResult<ts.SourceFile> = ts.transform(
|
|
60
|
+
program
|
|
61
|
+
.getSourceFiles()
|
|
62
|
+
.filter(
|
|
63
|
+
(file) =>
|
|
64
|
+
!file.isDeclarationFile &&
|
|
65
|
+
path.resolve(file.fileName).indexOf(props.input) !== -1,
|
|
66
|
+
),
|
|
67
|
+
[
|
|
68
|
+
ImportTransformer.transform(props.input)(props.output),
|
|
69
|
+
transform(
|
|
70
|
+
program,
|
|
71
|
+
((compilerOptions.plugins as any[]) ?? []).find(
|
|
72
|
+
(p: any) =>
|
|
73
|
+
p.transform === "typia/lib/transform" ||
|
|
74
|
+
p.transform === "../src/transform.ts",
|
|
75
|
+
) ?? {},
|
|
76
|
+
),
|
|
77
|
+
],
|
|
78
|
+
program.getCompilerOptions(),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// ARCHIVE TRANSFORMED FILES
|
|
82
|
+
const printer: ts.Printer = ts.createPrinter({
|
|
83
|
+
newLine: ts.NewLineKind.LineFeed,
|
|
84
|
+
});
|
|
85
|
+
for (const file of result.transformed) {
|
|
86
|
+
const to: string = path
|
|
87
|
+
.resolve(file.fileName)
|
|
88
|
+
.replace(props.input, props.output);
|
|
89
|
+
|
|
90
|
+
const content: string = printer.printFile(file);
|
|
91
|
+
await fs.promises.writeFile(to, emend(content), "utf8");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const emend = (content: string): string => {
|
|
96
|
+
if (
|
|
97
|
+
content.indexOf("typia.") === -1 ||
|
|
98
|
+
content.indexOf("import typia") !== -1 ||
|
|
99
|
+
content.indexOf("import * as typia") !== -1
|
|
100
|
+
)
|
|
101
|
+
return content;
|
|
102
|
+
return `import typia from "typia";\n\n${content}`;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const is_directory = async (current: string): Promise<boolean> => {
|
|
106
|
+
const stat: fs.Stats = await fs.promises.stat(current);
|
|
107
|
+
return stat.isDirectory();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const gather =
|
|
111
|
+
(props: IProps) =>
|
|
112
|
+
(container: string[]) =>
|
|
113
|
+
(from: string) =>
|
|
114
|
+
async (to: string) => {
|
|
115
|
+
if (from === props.output) return;
|
|
116
|
+
else if (fs.existsSync(to) === false) await fs.promises.mkdir(to);
|
|
117
|
+
|
|
118
|
+
for (const file of await fs.promises.readdir(from)) {
|
|
119
|
+
const next: string = path.join(from, file);
|
|
120
|
+
const stat: fs.Stats = await fs.promises.stat(next);
|
|
121
|
+
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
await gather(props)(container)(next)(path.join(to, file));
|
|
124
|
+
continue;
|
|
125
|
+
} else if (file.substring(file.length - 3) === ".ts")
|
|
126
|
+
container.push(next);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { ITransformOptions } from "../../transformers/ITransformOptions";
|
|
2
|
-
|
|
3
|
-
export namespace OptionPredicator {
|
|
4
|
-
export const numeric = (options: ITransformOptions): boolean =>
|
|
5
|
-
finite(options) || options.numeric === true;
|
|
6
|
-
|
|
7
|
-
export const functional = (options: ITransformOptions): boolean =>
|
|
8
|
-
options.functional === true;
|
|
9
|
-
|
|
10
|
-
export const finite = (options: ITransformOptions): boolean =>
|
|
11
|
-
options.finite === true;
|
|
12
|
-
|
|
13
|
-
export const undefined = (options: ITransformOptions): boolean =>
|
|
14
|
-
options.undefined !== false;
|
|
15
|
-
}
|
|
1
|
+
import { ITransformOptions } from "../../transformers/ITransformOptions";
|
|
2
|
+
|
|
3
|
+
export namespace OptionPredicator {
|
|
4
|
+
export const numeric = (options: ITransformOptions): boolean =>
|
|
5
|
+
finite(options) || options.numeric === true;
|
|
6
|
+
|
|
7
|
+
export const functional = (options: ITransformOptions): boolean =>
|
|
8
|
+
options.functional === true;
|
|
9
|
+
|
|
10
|
+
export const finite = (options: ITransformOptions): boolean =>
|
|
11
|
+
options.finite === true;
|
|
12
|
+
|
|
13
|
+
export const undefined = (options: ITransformOptions): boolean =>
|
|
14
|
+
options.undefined !== false;
|
|
15
|
+
}
|