typia 4.2.0 → 4.2.1-dev.20230808

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.
@@ -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
+ }
@@ -1,165 +1,165 @@
1
- import { CommentFactory } from "../../factories/CommentFactory";
2
-
3
- import { IJsDocTagInfo } from "../../metadata/IJsDocTagInfo";
4
- import { Metadata } from "../../metadata/Metadata";
5
- import { MetadataObject } from "../../metadata/MetadataObject";
6
- import { IJsonComponents } from "../../schemas/IJsonComponents";
7
-
8
- import { PatternUtil } from "../../utils/PatternUtil";
9
-
10
- import { IJsonSchema } from "../../module";
11
- import { ApplicationProgrammer } from "../ApplicationProgrammer";
12
- import { JSON_COMPONENTS_PREFIX } from "./JSON_SCHEMA_PREFIX";
13
- import { application_schema } from "./application_schema";
14
- import { metadata_to_pattern } from "./metadata_to_pattern";
15
-
16
- /**
17
- * @internal
18
- */
19
- export const application_object =
20
- (options: ApplicationProgrammer.IOptions) =>
21
- (components: IJsonComponents) =>
22
- (obj: MetadataObject) =>
23
- (nullable: boolean): IJsonSchema.IReference => {
24
- const key: string =
25
- options.purpose === "ajv"
26
- ? obj.name
27
- : `${obj.name}${nullable ? ".Nullable" : ""}`;
28
- const $id: string = `${JSON_COMPONENTS_PREFIX}/schemas/${key}`;
29
- const output = { $ref: $id };
30
-
31
- // TEMPORARY ASSIGNMENT
32
- if (components.schemas?.[key] !== undefined) return output;
33
- components.schemas ??= {};
34
- components.schemas[key] = {} as any;
35
-
36
- // ITERATE PROPERTIES
37
- const properties: Record<string, any> = {};
38
- const extraMeta: ISuperfluous = {
39
- patternProperties: {},
40
- additionalProperties: undefined,
41
- };
42
- const required: string[] = [];
43
-
44
- for (const property of obj.properties) {
45
- if (
46
- // FUNCTIONAL TYPE
47
- property.value.functional === true &&
48
- property.value.nullable === false &&
49
- property.value.isRequired() === true &&
50
- property.value.size() === 0
51
- )
52
- continue;
53
- else if (property.jsDocTags.find((tag) => tag.name === "hidden"))
54
- continue; // THE HIDDEN TAG
55
-
56
- const key: string | null = property.key.getSoleLiteral();
57
- const schema: IJsonSchema | null = application_schema(options)(
58
- true,
59
- )(components)(property.value)({
60
- deprecated:
61
- property.jsDocTags.some(
62
- (tag) => tag.name === "deprecated",
63
- ) || undefined,
64
- title: (() => {
65
- const info: IJsDocTagInfo | undefined =
66
- property.jsDocTags.find((tag) => tag.name === "title");
67
- return info?.text?.length
68
- ? CommentFactory.merge(info.text)
69
- : undefined;
70
- })(),
71
- description: property.description ?? undefined,
72
- "x-typia-metaTags": property.tags.length
73
- ? property.tags
74
- : undefined,
75
- "x-typia-jsDocTags": property.jsDocTags.length
76
- ? property.jsDocTags
77
- : undefined,
78
- "x-typia-required": property.value.required,
79
- "x-typia-optional": property.value.optional,
80
- });
81
-
82
- if (schema === null) continue;
83
- else if (key !== null) {
84
- properties[key] = schema;
85
- if (property.value.isRequired() === true) required.push(key);
86
- } else {
87
- const pattern: string = metadata_to_pattern(true)(property.key);
88
- if (pattern === PatternUtil.STRING)
89
- extraMeta.additionalProperties = [property.value, schema];
90
- else
91
- extraMeta.patternProperties[pattern] = [
92
- property.value,
93
- schema,
94
- ];
95
- }
96
- }
97
-
98
- const extraProps = {
99
- additionalProperties: extraMeta.additionalProperties?.[1],
100
- patternProperties: (() => {
101
- if (Object.keys(extraMeta.patternProperties).length === 0)
102
- return undefined;
103
- const output: Record<string, IJsonSchema> = {};
104
- for (const [key, value] of Object.entries(
105
- extraMeta.patternProperties,
106
- ))
107
- output[key] = value[1];
108
- return output;
109
- })(),
110
- };
111
- const schema: IJsonComponents.IObject = {
112
- $id: options.purpose === "ajv" ? $id : undefined,
113
- // $recursiveAnchor:
114
- // (options.purpose === "ajv" && obj.recursive) || undefined,
115
- type: "object",
116
- properties,
117
- nullable: options.purpose === "swagger" ? nullable : undefined,
118
- required: required.length ? required : undefined,
119
- description: obj.description,
120
- "x-typia-jsDocTags": obj.jsDocTags,
121
- ...(options.purpose === "ajv"
122
- ? extraProps
123
- : {
124
- // swagger can't express patternProperties
125
- "x-typia-additionalProperties":
126
- extraProps.additionalProperties,
127
- "x-typia-patternProperties": extraProps.patternProperties,
128
- additionalProperties:
129
- join(options)(components)(extraMeta),
130
- }),
131
- };
132
- components.schemas[key] = schema;
133
- return output;
134
- };
135
-
136
- const join =
137
- (options: ApplicationProgrammer.IOptions) =>
138
- (components: IJsonComponents) =>
139
- (extra: ISuperfluous): IJsonSchema | undefined => {
140
- // LIST UP METADATA
141
- const elements: [Metadata, IJsonSchema][] = Object.values(
142
- extra.patternProperties || {},
143
- );
144
- if (extra.additionalProperties)
145
- elements.push(extra.additionalProperties);
146
-
147
- // SHORT RETURN
148
- if (elements.length === 0) return undefined;
149
- else if (elements.length === 1) return elements[0]![1]!;
150
-
151
- // MERGE METADATA AND GENERATE VULNERABLE SCHEMA
152
- const meta: Metadata = elements
153
- .map((tuple) => tuple[0])
154
- .reduce((x, y) => Metadata.merge(x, y));
155
- return (
156
- application_schema(options)(true)(components)(meta)({
157
- "x-typia-required": false,
158
- }) ?? undefined
159
- );
160
- };
161
-
162
- interface ISuperfluous {
163
- additionalProperties?: [Metadata, IJsonSchema];
164
- patternProperties: Record<string, [Metadata, IJsonSchema]>;
165
- }
1
+ import { CommentFactory } from "../../factories/CommentFactory";
2
+
3
+ import { IJsDocTagInfo } from "../../metadata/IJsDocTagInfo";
4
+ import { Metadata } from "../../metadata/Metadata";
5
+ import { MetadataObject } from "../../metadata/MetadataObject";
6
+ import { IJsonComponents } from "../../schemas/IJsonComponents";
7
+
8
+ import { PatternUtil } from "../../utils/PatternUtil";
9
+
10
+ import { IJsonSchema } from "../../module";
11
+ import { ApplicationProgrammer } from "../ApplicationProgrammer";
12
+ import { JSON_COMPONENTS_PREFIX } from "./JSON_SCHEMA_PREFIX";
13
+ import { application_schema } from "./application_schema";
14
+ import { metadata_to_pattern } from "./metadata_to_pattern";
15
+
16
+ /**
17
+ * @internal
18
+ */
19
+ export const application_object =
20
+ (options: ApplicationProgrammer.IOptions) =>
21
+ (components: IJsonComponents) =>
22
+ (obj: MetadataObject) =>
23
+ (nullable: boolean): IJsonSchema.IReference => {
24
+ const key: string =
25
+ options.purpose === "ajv"
26
+ ? obj.name
27
+ : `${obj.name}${nullable ? ".Nullable" : ""}`;
28
+ const $id: string = `${JSON_COMPONENTS_PREFIX}/schemas/${key}`;
29
+ const output = { $ref: $id };
30
+
31
+ // TEMPORARY ASSIGNMENT
32
+ if (components.schemas?.[key] !== undefined) return output;
33
+ components.schemas ??= {};
34
+ components.schemas[key] = {} as any;
35
+
36
+ // ITERATE PROPERTIES
37
+ const properties: Record<string, any> = {};
38
+ const extraMeta: ISuperfluous = {
39
+ patternProperties: {},
40
+ additionalProperties: undefined,
41
+ };
42
+ const required: string[] = [];
43
+
44
+ for (const property of obj.properties) {
45
+ if (
46
+ // FUNCTIONAL TYPE
47
+ property.value.functional === true &&
48
+ property.value.nullable === false &&
49
+ property.value.isRequired() === true &&
50
+ property.value.size() === 0
51
+ )
52
+ continue;
53
+ else if (property.jsDocTags.find((tag) => tag.name === "hidden"))
54
+ continue; // THE HIDDEN TAG
55
+
56
+ const key: string | null = property.key.getSoleLiteral();
57
+ const schema: IJsonSchema | null = application_schema(options)(
58
+ true,
59
+ )(components)(property.value)({
60
+ deprecated:
61
+ property.jsDocTags.some(
62
+ (tag) => tag.name === "deprecated",
63
+ ) || undefined,
64
+ title: (() => {
65
+ const info: IJsDocTagInfo | undefined =
66
+ property.jsDocTags.find((tag) => tag.name === "title");
67
+ return info?.text?.length
68
+ ? CommentFactory.merge(info.text)
69
+ : undefined;
70
+ })(),
71
+ description: property.description ?? undefined,
72
+ "x-typia-metaTags": property.tags.length
73
+ ? property.tags
74
+ : undefined,
75
+ "x-typia-jsDocTags": property.jsDocTags.length
76
+ ? property.jsDocTags
77
+ : undefined,
78
+ "x-typia-required": property.value.required,
79
+ "x-typia-optional": property.value.optional,
80
+ });
81
+
82
+ if (schema === null) continue;
83
+ else if (key !== null) {
84
+ properties[key] = schema;
85
+ if (property.value.isRequired() === true) required.push(key);
86
+ } else {
87
+ const pattern: string = metadata_to_pattern(true)(property.key);
88
+ if (pattern === PatternUtil.STRING)
89
+ extraMeta.additionalProperties = [property.value, schema];
90
+ else
91
+ extraMeta.patternProperties[pattern] = [
92
+ property.value,
93
+ schema,
94
+ ];
95
+ }
96
+ }
97
+
98
+ const extraProps = {
99
+ additionalProperties: extraMeta.additionalProperties?.[1],
100
+ patternProperties: (() => {
101
+ if (Object.keys(extraMeta.patternProperties).length === 0)
102
+ return undefined;
103
+ const output: Record<string, IJsonSchema> = {};
104
+ for (const [key, value] of Object.entries(
105
+ extraMeta.patternProperties,
106
+ ))
107
+ output[key] = value[1];
108
+ return output;
109
+ })(),
110
+ };
111
+ const schema: IJsonComponents.IObject = {
112
+ $id: options.purpose === "ajv" ? $id : undefined,
113
+ // $recursiveAnchor:
114
+ // (options.purpose === "ajv" && obj.recursive) || undefined,
115
+ type: "object",
116
+ properties,
117
+ nullable: options.purpose === "swagger" ? nullable : undefined,
118
+ required: required.length ? required : undefined,
119
+ description: obj.description,
120
+ "x-typia-jsDocTags": obj.jsDocTags,
121
+ ...(options.purpose === "ajv"
122
+ ? extraProps
123
+ : {
124
+ // swagger can't express patternProperties
125
+ "x-typia-additionalProperties":
126
+ extraProps.additionalProperties,
127
+ "x-typia-patternProperties": extraProps.patternProperties,
128
+ additionalProperties:
129
+ join(options)(components)(extraMeta),
130
+ }),
131
+ };
132
+ components.schemas[key] = schema;
133
+ return output;
134
+ };
135
+
136
+ const join =
137
+ (options: ApplicationProgrammer.IOptions) =>
138
+ (components: IJsonComponents) =>
139
+ (extra: ISuperfluous): IJsonSchema | undefined => {
140
+ // LIST UP METADATA
141
+ const elements: [Metadata, IJsonSchema][] = Object.values(
142
+ extra.patternProperties || {},
143
+ );
144
+ if (extra.additionalProperties)
145
+ elements.push(extra.additionalProperties);
146
+
147
+ // SHORT RETURN
148
+ if (elements.length === 0) return undefined;
149
+ else if (elements.length === 1) return elements[0]![1]!;
150
+
151
+ // MERGE METADATA AND GENERATE VULNERABLE SCHEMA
152
+ const meta: Metadata = elements
153
+ .map((tuple) => tuple[0])
154
+ .reduce((x, y) => Metadata.merge(x, y));
155
+ return (
156
+ application_schema(options)(true)(components)(meta)({
157
+ "x-typia-required": false,
158
+ }) ?? undefined
159
+ );
160
+ };
161
+
162
+ interface ISuperfluous {
163
+ additionalProperties?: [Metadata, IJsonSchema];
164
+ patternProperties: Record<string, [Metadata, IJsonSchema]>;
165
+ }
@@ -145,6 +145,10 @@ export const stringify_dynamic_properties = (
145
145
  );
146
146
  statements.push(condition);
147
147
  }
148
+ statements.push(
149
+ ts.factory.createReturnStatement(ts.factory.createStringLiteral("")),
150
+ );
151
+
148
152
  return output();
149
153
  };
150
154