@effect-app/eslint-codegen-model 0.59.0 → 0.59.2
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/.eslintignore +4 -0
- package/.eslintrc.cjs +11 -0
- package/CHANGELOG.md +12 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/presets/model.d.ts.map +1 -1
- package/dist/presets/model.js +36 -64
- package/package.json +2 -2
- package/src/presets/model.ts +38 -78
- package/src/compiler.ts +0 -246
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/presets/model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/presets/model.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAqBnD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAiDA,CAAA"}
|
package/dist/presets/model.js
CHANGED
|
@@ -27,100 +27,72 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.model = void 0;
|
|
30
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
30
31
|
const generator_1 = __importDefault(require("@babel/generator"));
|
|
31
32
|
const parser_1 = require("@babel/parser");
|
|
32
33
|
const fs = __importStar(require("fs"));
|
|
33
|
-
const compiler_1 = require("../compiler");
|
|
34
34
|
function normalise(str) {
|
|
35
35
|
try {
|
|
36
36
|
return (0, generator_1.default)((0, parser_1.parse)(str, { sourceType: "module", plugins: ["typescript"] }))
|
|
37
37
|
.code;
|
|
38
38
|
// .replace(/'/g, `"`)
|
|
39
39
|
// .replace(/\/index/g, "")
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// .replace(/([\n\s]+ \|)/g, " |").replaceAll(": |", ":")
|
|
41
|
+
// .replaceAll(/[\s\n]+\|/g, " |")
|
|
42
|
+
// .replaceAll("\n", ";")
|
|
43
|
+
// .replaceAll(" ", "")
|
|
44
44
|
// TODO: remove all \n and whitespace?
|
|
45
45
|
}
|
|
46
46
|
catch (e) {
|
|
47
47
|
return str;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
const model = ({ meta, options }, context) => {
|
|
52
|
-
if (!context.parserOptions.project) {
|
|
53
|
-
console.warn(`${meta.filename}: Cannot run ESLint Model plugin, because no TS Compiler is enabled`);
|
|
54
|
-
return meta.existingContent;
|
|
55
|
-
}
|
|
56
|
-
const writeFullTypes = !!options.writeFullTypes;
|
|
50
|
+
const model = ({ meta }) => {
|
|
57
51
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// checks and reads the file
|
|
52
|
+
const targetContent = fs.readFileSync(meta.filename).toString();
|
|
53
|
+
const processed = [];
|
|
61
54
|
const sourcePath = meta.filename;
|
|
62
55
|
if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {
|
|
63
56
|
throw Error(`Source path is not a file: ${sourcePath}`);
|
|
64
57
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// // node.modifiers.filter(
|
|
84
|
-
// // (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword
|
|
85
|
-
// // ).length > 0
|
|
86
|
-
// // )
|
|
87
|
-
// // .filter((node) => !!node.name)
|
|
88
|
-
// // .filter((node) => node.parameters.length >= 2)
|
|
89
|
-
// // .filter((node) => node.name!.getText(sourceFile).endsWith("_"))
|
|
90
|
-
// // .map((node) => ({
|
|
91
|
-
// // functionName: node.name!.getText(sourceFile),
|
|
92
|
-
// // typeParameters: node.typeParameters || ts.factory.createNodeArray(),
|
|
93
|
-
// // parameters: node.parameters || ts.factory.createNodeArray(),
|
|
94
|
-
// // type: node.type!,
|
|
95
|
-
// // implemented: !!node.body,
|
|
96
|
-
// // jsDoc: getJSDoc(node)
|
|
97
|
-
// // }))
|
|
98
|
-
// // .filter((decl) => exclude.indexOf(decl.functionName) === -1)
|
|
99
|
-
// // create the actual AST nodes
|
|
100
|
-
// const nodes = dataFirstDeclarations.map(createPipeableFunctionDeclaration)
|
|
101
|
-
// const expectedContent = nodes.map((node) => printNode(node, sourceFile)).join("\n")
|
|
102
|
-
const pn = (0, compiler_1.processNode)(program.getTypeChecker(), sourceFile, writeFullTypes);
|
|
103
|
-
let abc = [];
|
|
104
|
-
// TODO: must return void, cannot use getChildren() etc, or it wont work, no idea why!
|
|
105
|
-
sourceFile.forEachChild(c => { abc = abc.concat(pn(c)); });
|
|
58
|
+
const clss = targetContent.matchAll(/^export class (\w+)[^{]*(Extended(Tagged)?Class)|ExtendedTaggedRequest/g);
|
|
59
|
+
const them = [];
|
|
60
|
+
for (const cls of clss) {
|
|
61
|
+
let modelName = null;
|
|
62
|
+
if (cls && cls[1]) {
|
|
63
|
+
modelName = cls[1];
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
continue;
|
|
67
|
+
if (processed.includes(modelName))
|
|
68
|
+
continue;
|
|
69
|
+
processed.push(modelName);
|
|
70
|
+
them.push([
|
|
71
|
+
`export namespace ${modelName} {`,
|
|
72
|
+
` export class From extends S.FromClass<typeof ${modelName}>() {}`,
|
|
73
|
+
"}"
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
106
76
|
const expectedContent = [
|
|
107
77
|
"//",
|
|
108
78
|
`/* eslint-disable */`,
|
|
109
|
-
...
|
|
79
|
+
...them.flat().filter((x) => !!x),
|
|
110
80
|
`/* eslint-enable */`,
|
|
111
81
|
"//"
|
|
112
|
-
]
|
|
82
|
+
]
|
|
83
|
+
.join("\n");
|
|
113
84
|
// do not re-emit in a different style, or a loop will occur
|
|
114
|
-
if (normalise(meta.existingContent) === normalise(expectedContent))
|
|
85
|
+
if (normalise(meta.existingContent) === normalise(expectedContent)) {
|
|
115
86
|
return meta.existingContent;
|
|
87
|
+
}
|
|
116
88
|
return expectedContent;
|
|
117
89
|
}
|
|
118
90
|
catch (e) {
|
|
119
|
-
return ("/** Got exception: "
|
|
120
|
-
("stack" in e ? e.stack : "")
|
|
121
|
-
JSON.stringify(e)
|
|
122
|
-
"*/");
|
|
91
|
+
return ("/** Got exception: "
|
|
92
|
+
+ ("stack" in e ? e.stack : "")
|
|
93
|
+
+ JSON.stringify(e)
|
|
94
|
+
+ "*/");
|
|
123
95
|
}
|
|
124
96
|
};
|
|
125
97
|
exports.model = model;
|
|
126
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
98
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJlc2V0cy9tb2RlbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLHVEQUF1RDtBQUN2RCxpRUFBdUM7QUFDdkMsMENBQXFDO0FBRXJDLHVDQUF3QjtBQUV4QixTQUFTLFNBQVMsQ0FBQyxHQUFXO0lBQzVCLElBQUksQ0FBQztRQUNILE9BQU8sSUFBQSxtQkFBUSxFQUNiLElBQUEsY0FBSyxFQUFDLEdBQUcsRUFBRSxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBUSxDQUNyRTthQUNFLElBQUksQ0FBQTtRQUNQLHNCQUFzQjtRQUN0QiwyQkFBMkI7UUFDM0IseURBQXlEO1FBQ3pELGtDQUFrQztRQUNsQyx5QkFBeUI7UUFDekIsdUJBQXVCO1FBQ3ZCLHNDQUFzQztJQUN4QyxDQUFDO0lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNYLE9BQU8sR0FBRyxDQUFBO0lBQ1osQ0FBQztBQUNILENBQUM7QUFFTSxNQUFNLEtBQUssR0FFYixDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRTtJQUNoQixJQUFJLENBQUM7UUFDSCxNQUFNLGFBQWEsR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQTtRQUUvRCxNQUFNLFNBQVMsR0FBYSxFQUFFLENBQUE7UUFFOUIsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQTtRQUNoQyxJQUFJLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQztZQUNwRSxNQUFNLEtBQUssQ0FBQyw4QkFBOEIsVUFBVSxFQUFFLENBQUMsQ0FBQTtRQUN6RCxDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQUcsYUFBYSxDQUFDLFFBQVEsQ0FBQyx5RUFBeUUsQ0FBQyxDQUFBO1FBQzlHLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQTtRQUNmLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7WUFDdkIsSUFBSSxTQUFTLEdBQUcsSUFBSSxDQUFBO1lBQ3BCLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUNsQixTQUFTLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFBO1lBQ3BCLENBQUM7O2dCQUFNLFNBQVE7WUFDZixJQUFJLFNBQVMsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDO2dCQUFFLFNBQVE7WUFDM0MsU0FBUyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQTtZQUV6QixJQUFJLENBQUMsSUFBSSxDQUFDO2dCQUNSLG9CQUFvQixTQUFTLElBQUk7Z0JBQ2pDLGtEQUFrRCxTQUFTLFFBQVE7Z0JBQ25FLEdBQUc7YUFDSixDQUFDLENBQUE7UUFDSixDQUFDO1FBQ0QsTUFBTSxlQUFlLEdBQUc7WUFDdEIsSUFBSTtZQUNKLHNCQUFzQjtZQUN0QixHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQWUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDOUMscUJBQXFCO1lBQ3JCLElBQUk7U0FDTDthQUNFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQTtRQUViLDREQUE0RDtRQUM1RCxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssU0FBUyxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7WUFDbkUsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFBO1FBQzdCLENBQUM7UUFDRCxPQUFPLGVBQWUsQ0FBQTtJQUN4QixDQUFDO0lBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUNYLE9BQU8sQ0FDTCxxQkFBcUI7Y0FDbkIsQ0FBQyxPQUFPLElBQUssQ0FBUyxDQUFDLENBQUMsQ0FBRSxDQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Y0FDL0MsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7Y0FDakIsSUFBSSxDQUNQLENBQUE7SUFDSCxDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBbkRZLFFBQUEsS0FBSyxTQW1EakIifQ==
|
package/package.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"name": "@effect-app/eslint-codegen-model",
|
|
3
3
|
"description": "Contains eslint helpers",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.59.
|
|
5
|
+
"version": "0.59.2",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@babel/generator": "7.23.6",
|
|
8
8
|
"@babel/parser": "7.24.0",
|
|
9
|
-
"@typescript-eslint/utils": "7.1.
|
|
9
|
+
"@typescript-eslint/utils": "7.1.1",
|
|
10
10
|
"eslint-plugin-codegen": "0.17.0",
|
|
11
11
|
"glob": "8.1.0",
|
|
12
12
|
"io-ts": "2.2.21",
|
package/src/presets/model.ts
CHANGED
|
@@ -1,116 +1,76 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import generate from "@babel/generator"
|
|
2
3
|
import { parse } from "@babel/parser"
|
|
3
4
|
import type { Preset } from "eslint-plugin-codegen"
|
|
4
5
|
import * as fs from "fs"
|
|
5
|
-
|
|
6
|
+
|
|
6
7
|
function normalise(str: string) {
|
|
7
8
|
try {
|
|
8
9
|
return generate(
|
|
9
10
|
parse(str, { sourceType: "module", plugins: ["typescript"] }) as any
|
|
10
11
|
)
|
|
11
12
|
.code
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
// .replace(/'/g, `"`)
|
|
14
|
+
// .replace(/\/index/g, "")
|
|
15
|
+
// .replace(/([\n\s]+ \|)/g, " |").replaceAll(": |", ":")
|
|
16
|
+
// .replaceAll(/[\s\n]+\|/g, " |")
|
|
17
|
+
// .replaceAll("\n", ";")
|
|
18
|
+
// .replaceAll(" ", "")
|
|
19
|
+
// TODO: remove all \n and whitespace?
|
|
19
20
|
} catch (e) {
|
|
20
21
|
return str
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
import { ESLintUtils } from "@typescript-eslint/utils"
|
|
25
25
|
export const model: Preset<{
|
|
26
26
|
writeFullTypes?: boolean
|
|
27
|
-
}> = ({ meta
|
|
28
|
-
if (!context.parserOptions.project) {
|
|
29
|
-
console.warn(`${meta.filename}: Cannot run ESLint Model plugin, because no TS Compiler is enabled`)
|
|
30
|
-
return meta.existingContent
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const writeFullTypes = !!options.writeFullTypes
|
|
34
|
-
|
|
27
|
+
}> = ({ meta }) => {
|
|
35
28
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
const targetContent = fs.readFileSync(meta.filename).toString()
|
|
30
|
+
|
|
31
|
+
const processed: string[] = []
|
|
38
32
|
|
|
39
|
-
// checks and reads the file
|
|
40
33
|
const sourcePath = meta.filename
|
|
41
34
|
if (!fs.existsSync(sourcePath) || !fs.statSync(sourcePath).isFile()) {
|
|
42
35
|
throw Error(`Source path is not a file: ${sourcePath}`)
|
|
43
36
|
}
|
|
44
|
-
// const cfgFile = ts.findConfigFile(sourcePath, (fn) => fs.existsSync(fn))
|
|
45
|
-
// if (!cfgFile) {
|
|
46
|
-
// throw new Error("No TS config file found")
|
|
47
|
-
// }
|
|
48
|
-
|
|
49
|
-
// const cfg = ts.readConfigFile(cfgFile, (fn) => fs.readFileSync(fn, "utf-8"))
|
|
50
|
-
// const basePath = path.dirname(cfgFile); // equal to "getDirectoryPath" from ts, at least in our case.
|
|
51
|
-
// const parsedConfig = ts.parseJsonConfigFileContent(cfg.config, ts.sys, basePath);
|
|
52
|
-
|
|
53
|
-
// const program = ts.createProgram([sourcePath], parsedConfig.options)
|
|
54
37
|
|
|
38
|
+
const clss = targetContent.matchAll(/^export class (\w+)[^{]*(Extended(Tagged)?Class)|ExtendedTaggedRequest/g)
|
|
39
|
+
const them = []
|
|
40
|
+
for (const cls of clss) {
|
|
41
|
+
let modelName = null
|
|
42
|
+
if (cls && cls[1]) {
|
|
43
|
+
modelName = cls[1]
|
|
44
|
+
} else continue
|
|
45
|
+
if (processed.includes(modelName)) continue
|
|
46
|
+
processed.push(modelName)
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
sourcePath,
|
|
63
|
-
)!
|
|
64
|
-
|
|
65
|
-
// collect data-first declarations
|
|
66
|
-
// const dataFirstDeclarations = sourceFile.statements
|
|
67
|
-
// .filter(ts.isFunctionDeclaration)
|
|
68
|
-
// // .filter(
|
|
69
|
-
// // (node) =>
|
|
70
|
-
// // node.modifiers &&
|
|
71
|
-
// // node.modifiers.filter(
|
|
72
|
-
// // (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword
|
|
73
|
-
// // ).length > 0
|
|
74
|
-
// // )
|
|
75
|
-
// // .filter((node) => !!node.name)
|
|
76
|
-
// // .filter((node) => node.parameters.length >= 2)
|
|
77
|
-
// // .filter((node) => node.name!.getText(sourceFile).endsWith("_"))
|
|
78
|
-
// // .map((node) => ({
|
|
79
|
-
// // functionName: node.name!.getText(sourceFile),
|
|
80
|
-
// // typeParameters: node.typeParameters || ts.factory.createNodeArray(),
|
|
81
|
-
// // parameters: node.parameters || ts.factory.createNodeArray(),
|
|
82
|
-
// // type: node.type!,
|
|
83
|
-
// // implemented: !!node.body,
|
|
84
|
-
// // jsDoc: getJSDoc(node)
|
|
85
|
-
// // }))
|
|
86
|
-
// // .filter((decl) => exclude.indexOf(decl.functionName) === -1)
|
|
87
|
-
|
|
88
|
-
// // create the actual AST nodes
|
|
89
|
-
// const nodes = dataFirstDeclarations.map(createPipeableFunctionDeclaration)
|
|
90
|
-
// const expectedContent = nodes.map((node) => printNode(node, sourceFile)).join("\n")
|
|
91
|
-
|
|
92
|
-
const pn = processNode(program.getTypeChecker(), sourceFile, writeFullTypes)
|
|
93
|
-
let abc: (string[] | undefined)[] = []
|
|
94
|
-
// TODO: must return void, cannot use getChildren() etc, or it wont work, no idea why!
|
|
95
|
-
sourceFile.forEachChild(c => {abc = abc.concat(pn(c))})
|
|
48
|
+
them.push([
|
|
49
|
+
`export namespace ${modelName} {`,
|
|
50
|
+
` export class From extends S.FromClass<typeof ${modelName}>() {}`,
|
|
51
|
+
"}"
|
|
52
|
+
])
|
|
53
|
+
}
|
|
96
54
|
const expectedContent = [
|
|
97
55
|
"//",
|
|
98
|
-
`/* eslint-disable */`,
|
|
99
|
-
...
|
|
56
|
+
`/* eslint-disable */`,
|
|
57
|
+
...them.flat().filter((x): x is string => !!x),
|
|
100
58
|
`/* eslint-enable */`,
|
|
101
59
|
"//"
|
|
102
|
-
]
|
|
60
|
+
]
|
|
61
|
+
.join("\n")
|
|
103
62
|
|
|
104
63
|
// do not re-emit in a different style, or a loop will occur
|
|
105
|
-
if (normalise(meta.existingContent) === normalise(expectedContent))
|
|
64
|
+
if (normalise(meta.existingContent) === normalise(expectedContent)) {
|
|
106
65
|
return meta.existingContent
|
|
66
|
+
}
|
|
107
67
|
return expectedContent
|
|
108
68
|
} catch (e) {
|
|
109
69
|
return (
|
|
110
|
-
"/** Got exception: "
|
|
111
|
-
("stack" in (e as any) ? (e as any).stack : "")
|
|
112
|
-
JSON.stringify(e)
|
|
113
|
-
"*/"
|
|
70
|
+
"/** Got exception: "
|
|
71
|
+
+ ("stack" in (e as any) ? (e as any).stack : "")
|
|
72
|
+
+ JSON.stringify(e)
|
|
73
|
+
+ "*/"
|
|
114
74
|
)
|
|
115
75
|
}
|
|
116
76
|
}
|
package/src/compiler.ts
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import ts, { SyntaxKind } from "typescript"
|
|
2
|
-
|
|
3
|
-
const sortUnion = (a: string, b: string) => {
|
|
4
|
-
if (a !== "null" && a!== "undefined" && (b === "null" || b === "undefined")) {
|
|
5
|
-
return -1
|
|
6
|
-
}
|
|
7
|
-
if (b !== "null" && b !== "undefined" && (a === "null" || a === "undefined")) {
|
|
8
|
-
return 1
|
|
9
|
-
}
|
|
10
|
-
if(a < b) { return -1; }
|
|
11
|
-
if(a > b) { return 1; }
|
|
12
|
-
return 0;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const sortAlpha = (a: string, b: string) => {
|
|
16
|
-
if(a < b) { return -1; }
|
|
17
|
-
if(a > b) { return 1; }
|
|
18
|
-
return 0;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// TODO: we don't support string literals with spaces in them currently.
|
|
22
|
-
const rx = /(([^\s\<\>\,\[\(]+)? \| ([^\s\<\>\,\]\)]+))+/
|
|
23
|
-
|
|
24
|
-
function sortIt(str: string) {
|
|
25
|
-
return str.split(" | ").sort(sortUnion).join(" | ")
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const debug = false // true
|
|
29
|
-
|
|
30
|
-
export function processNode(tc: ts.TypeChecker, root: ts.Node, writeFullTypes = false) {
|
|
31
|
-
const processed: any[] = []
|
|
32
|
-
return (n: ts.Node) => {
|
|
33
|
-
if (/*ts.isClassDeclaration(n) || ts.isTypeAliasDeclaration(n)*/ true) {
|
|
34
|
-
|
|
35
|
-
let modelName = null
|
|
36
|
-
if (ts.isClassDeclaration(n)) {
|
|
37
|
-
if (n.getText().match(/(Extended(Tagged)?Class)|ExtendedTaggedRequest/)) {
|
|
38
|
-
modelName = (n.name as any)?.escapedText
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (!modelName) {
|
|
42
|
-
const constructorName = (n as any).name?.escapedText
|
|
43
|
-
// TODO: Remove requirement
|
|
44
|
-
if (!constructorName?.endsWith("Constructor")) {
|
|
45
|
-
//console.log("$$$constructorName doesnt end with Constructor", constructorName)
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
modelName = constructorName.replace("Constructor", "")
|
|
49
|
-
}
|
|
50
|
-
if (processed.includes(modelName)) { return }
|
|
51
|
-
processed.push(modelName)
|
|
52
|
-
|
|
53
|
-
if (!writeFullTypes) {
|
|
54
|
-
return [
|
|
55
|
-
`export namespace ${modelName} {`,
|
|
56
|
-
` export class From extends S.FromClass<typeof ${modelName}>() {}`,
|
|
57
|
-
// ` export const From: FromOps = { $: {} }`,
|
|
58
|
-
// ` export interface FromAspects {}`,
|
|
59
|
-
"}",
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
//console.log("$$$ constructorName", constructorName)
|
|
64
|
-
|
|
65
|
-
const t = tc.getTypeAtLocation(n)
|
|
66
|
-
|
|
67
|
-
const result = { encoded: [] as string[], parsed: [] as string[] }
|
|
68
|
-
const unions: Record<string, string> = {}
|
|
69
|
-
|
|
70
|
-
//console.log("$$$ fields", t.getProperties().map(x => x.escapedName))
|
|
71
|
-
t.getProperties().forEach((c) => {
|
|
72
|
-
const method = c.name
|
|
73
|
-
if (method === "encoded" || method === "parsed") {
|
|
74
|
-
//console.log("$$$ method", method)
|
|
75
|
-
//console.log(c.members)
|
|
76
|
-
const tt = tc.getTypeOfSymbolAtLocation(c, n)
|
|
77
|
-
// const s = tc.getReturnTypeOfSignature(tt.getCallSignatures()[0])
|
|
78
|
-
|
|
79
|
-
// const type = tc.getReturnTypeOfSignature(s! as any /* TODO */)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
tt.getProperties().forEach(p => {
|
|
83
|
-
const isLookup = debug && p.escapedName === "carrier"
|
|
84
|
-
|
|
85
|
-
//kind = 207,
|
|
86
|
-
//arguments[0].escapedText === "HosterRole"
|
|
87
|
-
//console.log("$$$p", p.escapedName)
|
|
88
|
-
//if (p.escapedName === "opposite") {
|
|
89
|
-
//console.log("$$$ a union!", p.declarations?.map(x => x.forEachChild(c => {
|
|
90
|
-
|
|
91
|
-
// TODO: have to find nullable, array, set, map, etc.
|
|
92
|
-
// TODO: "From"
|
|
93
|
-
// but also should find fully custom sets like PurchaseOrderModulesSet - we should be able to just use those directly, incl PurchaseOrderModulesSet.From
|
|
94
|
-
// for now just skip them?
|
|
95
|
-
p.declarations?.map(x => x.forEachChild(c => {
|
|
96
|
-
if (isLookup) {
|
|
97
|
-
console.log("$$$ lookup", c.kind, c)
|
|
98
|
-
}
|
|
99
|
-
if (c.kind === SyntaxKind.CallExpression) { // 207 -- SyntaxKind.ElementAccessExpression) {
|
|
100
|
-
let it = (c as any).arguments[0]
|
|
101
|
-
//const isState = p.escapedName === "state"
|
|
102
|
-
if (isLookup) {
|
|
103
|
-
console.log("$$$ state it", it)
|
|
104
|
-
}
|
|
105
|
-
const isNullable = it.expression?.escapedText === "nullable"
|
|
106
|
-
const isIt = it.arguments && it.arguments[0] //it.expression?.escapedText === "nullable"
|
|
107
|
-
if (isIt) {
|
|
108
|
-
//console.log("$$ nullable", it.arguments[0])
|
|
109
|
-
// TODO: usually the union is on the last input, we need to support all elements individually however
|
|
110
|
-
it = it.arguments[it.arguments.length - 1]
|
|
111
|
-
}
|
|
112
|
-
//console.log("$args", it)
|
|
113
|
-
//tc.getTypeAtLocation(it)
|
|
114
|
-
const tt = tc.getTypeAtLocation(c) //tc.getTypeOfSymbolAtLocation(it.parent, n)
|
|
115
|
-
const typeDecl = tc.typeToString(
|
|
116
|
-
tt,
|
|
117
|
-
root,
|
|
118
|
-
ts.TypeFormatFlags.NoTruncation
|
|
119
|
-
//ts.TypeFormatFlags.None
|
|
120
|
-
//ts.TypeFormatFlags.AddUndefined |
|
|
121
|
-
// | ts.TypeFormatFlags.NoTypeReduction
|
|
122
|
-
// | ts.TypeFormatFlags.MultilineObjectLiterals
|
|
123
|
-
//| ts.TypeFormatFlags.InTypeAlias
|
|
124
|
-
| ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope // prevents import(*)
|
|
125
|
-
// | ts.TypeFormatFlags.UseStructuralFallback
|
|
126
|
-
)
|
|
127
|
-
if (isLookup) {
|
|
128
|
-
console.log("$$ type", typeDecl)
|
|
129
|
-
}
|
|
130
|
-
const matches = typeDecl.match(rx)
|
|
131
|
-
if (isLookup) {
|
|
132
|
-
console.log("$$ matches", matches)
|
|
133
|
-
}
|
|
134
|
-
const isOptional = typeDecl.match(/\>, "optional"/)
|
|
135
|
-
if (matches) {
|
|
136
|
-
let replaced = matches[0]!.replace(rx, (match) => sortIt(match))
|
|
137
|
-
replaced = sortIt(isOptional ? isNullable ? replaced.replace(" | null", " | undefined | null") : replaced + " | undefined" : replaced)
|
|
138
|
-
//console.log("$$ replaced", replaced, it.escapedText, matches)
|
|
139
|
-
// if (it.escapedText === "TaskState") {
|
|
140
|
-
// console.log("Help", it)
|
|
141
|
-
// }
|
|
142
|
-
if (isLookup) {
|
|
143
|
-
console.log("$$$ replaced", it.escapedText, replaced)
|
|
144
|
-
}
|
|
145
|
-
if (it.escapedText && !it.escapedText.endsWith("Set") /* skip the "Set" problem */ && replaced.replace(" | null", "").includes("|")) {
|
|
146
|
-
const replacement = it.escapedText + (isNullable ? " | null" : "") + (isOptional ? " | undefined" : "")
|
|
147
|
-
// if (it.escapedText === "TaskState") {
|
|
148
|
-
// console.log("$$$", { replaced, replacement })
|
|
149
|
-
// unions[replaced] = replacement
|
|
150
|
-
// } else {
|
|
151
|
-
unions[replaced] = replacement
|
|
152
|
-
if (isLookup) {
|
|
153
|
-
console.log("$$ repl", { replaced, replacement})
|
|
154
|
-
}
|
|
155
|
-
//}
|
|
156
|
-
} else {
|
|
157
|
-
// if (isIt) {
|
|
158
|
-
// console.log("$$ no name found", it.escapedText)
|
|
159
|
-
// }
|
|
160
|
-
// console.log("$$ no name found??", it)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
//c.kind === 346 ? console.log(c) : null
|
|
166
|
-
//console.log((c as any).flowNode?.node?.name)
|
|
167
|
-
}))
|
|
168
|
-
//}
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
if (debug && Object.keys(unions).length) {
|
|
172
|
-
console.log("$$$ unions to replace", unions)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const typeDecl = tc.typeToString(
|
|
176
|
-
tt,
|
|
177
|
-
root,
|
|
178
|
-
ts.TypeFormatFlags.NoTruncation
|
|
179
|
-
//ts.TypeFormatFlags.None
|
|
180
|
-
//ts.TypeFormatFlags.AddUndefined |
|
|
181
|
-
// | ts.TypeFormatFlags.NoTypeReduction
|
|
182
|
-
// | ts.TypeFormatFlags.MultilineObjectLiterals
|
|
183
|
-
//| ts.TypeFormatFlags.InTypeAlias
|
|
184
|
-
| ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope // prevents import(*)
|
|
185
|
-
// | ts.TypeFormatFlags.UseStructuralFallback
|
|
186
|
-
)
|
|
187
|
-
const str = typeDecl === "{}" ? [] :
|
|
188
|
-
// drop leading { and trailing }
|
|
189
|
-
typeDecl.substring(2, typeDecl.length - 2)
|
|
190
|
-
.split(";")
|
|
191
|
-
.map(l => l.trim())
|
|
192
|
-
// todo; skip the first split, as its the property
|
|
193
|
-
.map(l => l.replace(rx, (match) => {
|
|
194
|
-
const rpl = sortIt(match)
|
|
195
|
-
//if (debug) { console.log("Searching for", rpl, { unions}) }
|
|
196
|
-
if (rpl.endsWith(" | undefined")) {
|
|
197
|
-
const sub = unions[rpl.replace(" | undefined", "")]
|
|
198
|
-
return sub ? sub + " | undefined" : unions[rpl] ?? rpl
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const sub = unions[rpl]
|
|
202
|
-
return (sub ? sub : rpl)
|
|
203
|
-
})
|
|
204
|
-
.replaceAll(" Array<", " ReadonlyArray<") // .replaceAll(/(Array|Set|Map)\</", "ReadonlyArray<") //
|
|
205
|
-
.replaceAll(" Set<", " ReadonlySet<")
|
|
206
|
-
.replaceAll(" Map<", " ReadonlyMap<")
|
|
207
|
-
.replaceAll("(Array<", "(ReadonlyArray<") // .replaceAll(/(Array|Set|Map)\</", "ReadonlyArray<") //
|
|
208
|
-
.replaceAll("(Set<", "(ReadonlySet<")
|
|
209
|
-
.replaceAll("(Map<", "(ReadonlyMap<")
|
|
210
|
-
.replaceAll(" Array.Array<", " ReadonlyArray<") // .replaceAll(/(Array|Set|Map)\</", "ReadonlyArray<") //
|
|
211
|
-
.replaceAll(" Set.Set<", " ReadonlySet<")
|
|
212
|
-
.replaceAll(" Map.Map<", " ReadonlyMap<")
|
|
213
|
-
)
|
|
214
|
-
// we sort for now, because otherwise we have sometimes multiple times changing back and forth between editor and console.
|
|
215
|
-
.sort(sortAlpha)
|
|
216
|
-
// Taken care of by "ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope"
|
|
217
|
-
//.replaceAll(/import\("[^"]+"\)\./g, "")
|
|
218
|
-
|
|
219
|
-
result[method] = str
|
|
220
|
-
}
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
if (!("parsed" in result)) {
|
|
224
|
-
throw new Error("No parsed result")
|
|
225
|
-
}
|
|
226
|
-
if (!("encoded" in result)) {
|
|
227
|
-
throw new Error("No encoded result")
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const encoded = result.encoded.filter(x => !!x)
|
|
232
|
-
const parsed = result.parsed.filter(x => !!x)
|
|
233
|
-
|
|
234
|
-
return [
|
|
235
|
-
`export interface ${modelName} {${parsed.length ? "\n" + parsed.map(l => " " + l).join("\n") + "\n" : ""}}`,
|
|
236
|
-
`export namespace ${modelName} {`,
|
|
237
|
-
` export interface From {${encoded.length ? "\n" + encoded.map(l => " " + l).join("\n") + "\n " : ""}}`,
|
|
238
|
-
` export const From: FromOps = {}`,
|
|
239
|
-
// ` export const From: FromOps = { $: {} }`,
|
|
240
|
-
// ` export interface FromAspects {}`,
|
|
241
|
-
` export interface FromOps {}`,
|
|
242
|
-
"}",
|
|
243
|
-
]
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|