forge-type 1.0.2 → 1.0.3
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/generator/interfaceGenerator.d.ts +4 -0
- package/dist/generator/interfaceGenerator.d.ts.map +1 -0
- package/dist/generator/interfaceGenerator.js +108 -0
- package/dist/generator/interfaceGenerator.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/infer/inferType.d.ts +2 -0
- package/dist/infer/inferType.d.ts.map +1 -0
- package/dist/infer/inferType.js +38 -0
- package/dist/infer/inferType.js.map +1 -0
- package/dist/storage/fileWriter.d.ts +3 -0
- package/dist/storage/fileWriter.d.ts.map +1 -0
- package/dist/storage/fileWriter.js +26 -0
- package/dist/storage/fileWriter.js.map +1 -0
- package/dist/utils/opt-func.d.ts +3 -0
- package/dist/utils/opt-func.d.ts.map +1 -0
- package/dist/utils/opt-func.js +12 -0
- package/dist/utils/opt-func.js.map +1 -0
- package/package.json +20 -8
- package/src/generator/interfaceGenerator.ts +0 -121
- package/src/index.ts +0 -17
- package/src/infer/inferType.ts +0 -45
- package/src/storage/fileWriter.ts +0 -22
- package/src/utils/opt-func.ts +0 -9
- package/tsconfig.json +0 -26
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaceGenerator.d.ts","sourceRoot":"","sources":["../../src/generator/interfaceGenerator.ts"],"names":[],"mappings":"AAgBA,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,MAAM,CAmER"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateInterface = generateInterface;
|
|
4
|
+
const inferType_1 = require("../infer/inferType");
|
|
5
|
+
const fileWriter_1 = require("../storage/fileWriter");
|
|
6
|
+
const opt_func_1 = require("../utils/opt-func");
|
|
7
|
+
const generatedInterfaces = new Set();
|
|
8
|
+
const generatedEnums = new Set();
|
|
9
|
+
const fieldValuesMap = {};
|
|
10
|
+
// Fields allowed as enums
|
|
11
|
+
const allowedEnumFields = [
|
|
12
|
+
'gender', 'role', 'status', 'accountType', 'subscription', 'membership', 'accessLevel',
|
|
13
|
+
'visibility', 'type', 'priority', 'category',
|
|
14
|
+
'paymentMethod', 'paymentStatus', 'deliveryStatus',
|
|
15
|
+
'level', 'mode', 'subscriptionStatus', 'confirmation'
|
|
16
|
+
];
|
|
17
|
+
function generateInterface(name, data, options) {
|
|
18
|
+
const allInterfaces = [];
|
|
19
|
+
const enumDefinitions = [];
|
|
20
|
+
function helper(name, data) {
|
|
21
|
+
let existingProps = {};
|
|
22
|
+
if (options?.modify) {
|
|
23
|
+
const existingContent = (0, fileWriter_1.readTypeFile)(name);
|
|
24
|
+
if (existingContent)
|
|
25
|
+
existingProps = parseInterface(existingContent);
|
|
26
|
+
}
|
|
27
|
+
const bodyLines = [];
|
|
28
|
+
Object.entries(data).forEach(([key, value]) => {
|
|
29
|
+
let typeStr = (0, inferType_1.inferType)(value, key);
|
|
30
|
+
const optional = value === null || value === undefined ? '?' : '';
|
|
31
|
+
// Handle nested arrays of objects
|
|
32
|
+
if (Array.isArray(value) && typeof value[0] === 'object' && value[0] !== null) {
|
|
33
|
+
const typeName = (0, opt_func_1.capitalize)((0, opt_func_1.singularize)(key));
|
|
34
|
+
if (!generatedInterfaces.has(typeName)) {
|
|
35
|
+
generatedInterfaces.add(typeName);
|
|
36
|
+
helper(typeName, value[0]);
|
|
37
|
+
Object.entries(value[0]).forEach(([k, v]) => {
|
|
38
|
+
if (typeof v === 'string')
|
|
39
|
+
detectEnum(k, v, enumDefinitions);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
typeStr = `${typeName}[]`;
|
|
43
|
+
}
|
|
44
|
+
// Handle nested objects
|
|
45
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
46
|
+
typeStr = `{ ${Object.entries(value)
|
|
47
|
+
.map(([k, v]) => {
|
|
48
|
+
if (typeof v === 'string')
|
|
49
|
+
detectEnum(k, v, enumDefinitions);
|
|
50
|
+
return `${k}: ${(0, inferType_1.inferType)(v, k)};`;
|
|
51
|
+
})
|
|
52
|
+
.join(' ')} }`;
|
|
53
|
+
}
|
|
54
|
+
// Top-level enums
|
|
55
|
+
if (typeof value === 'string') {
|
|
56
|
+
const enumType = detectEnum(key, value, enumDefinitions);
|
|
57
|
+
if (enumType)
|
|
58
|
+
typeStr = enumType;
|
|
59
|
+
}
|
|
60
|
+
existingProps[key] = typeStr;
|
|
61
|
+
bodyLines.push(` ${key}${optional}: ${typeStr};`);
|
|
62
|
+
});
|
|
63
|
+
// Optional fields from existing interface
|
|
64
|
+
Object.keys(existingProps).forEach(key => {
|
|
65
|
+
if (!(key in data))
|
|
66
|
+
bodyLines.push(` ${key}?: ${existingProps[key]};`);
|
|
67
|
+
});
|
|
68
|
+
const content = `export interface ${name} {\n${bodyLines.join('\n')}\n}`;
|
|
69
|
+
allInterfaces.push(content);
|
|
70
|
+
}
|
|
71
|
+
helper(name, data);
|
|
72
|
+
const finalContent = [...enumDefinitions, ...allInterfaces].join('\n\n');
|
|
73
|
+
(0, fileWriter_1.saveTypeFile)(name, finalContent);
|
|
74
|
+
return finalContent;
|
|
75
|
+
}
|
|
76
|
+
function parseInterface(content) {
|
|
77
|
+
const props = {};
|
|
78
|
+
content.split('\n').forEach(line => {
|
|
79
|
+
const match = line.trim().match(/^(\w+)\??:\s(.+);$/);
|
|
80
|
+
if (match) {
|
|
81
|
+
const [_, key, type] = match;
|
|
82
|
+
if (key && type)
|
|
83
|
+
props[key] = type;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return props;
|
|
87
|
+
}
|
|
88
|
+
function detectEnum(key, value, enumDefinitions) {
|
|
89
|
+
if (!allowedEnumFields.includes(key))
|
|
90
|
+
return null;
|
|
91
|
+
if (typeof value !== 'string')
|
|
92
|
+
return null;
|
|
93
|
+
if (!fieldValuesMap[key])
|
|
94
|
+
fieldValuesMap[key] = new Set();
|
|
95
|
+
fieldValuesMap[key].add(value);
|
|
96
|
+
const uniqueValues = Array.from(fieldValuesMap[key]);
|
|
97
|
+
if (uniqueValues.length > 1 && uniqueValues.length <= 10) {
|
|
98
|
+
const typeName = (0, opt_func_1.capitalize)(key);
|
|
99
|
+
if (!generatedEnums.has(typeName)) {
|
|
100
|
+
generatedEnums.add(typeName);
|
|
101
|
+
const unionType = uniqueValues.map(v => `"${v}"`).join(' | ');
|
|
102
|
+
enumDefinitions.push(`export type ${typeName} = ${unionType};`);
|
|
103
|
+
}
|
|
104
|
+
return typeName;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=interfaceGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaceGenerator.js","sourceRoot":"","sources":["../../src/generator/interfaceGenerator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE;;;;EAU/B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTypeFromData = createTypeFromData;
|
|
4
|
+
const interfaceGenerator_1 = require("./generator/interfaceGenerator");
|
|
5
|
+
const fileWriter_1 = require("./storage/fileWriter");
|
|
6
|
+
function createTypeFromData(name, data, options // pass { modify: true } to update existing interface
|
|
7
|
+
) {
|
|
8
|
+
const typeString = (0, interfaceGenerator_1.generateInterface)(name, data, options);
|
|
9
|
+
const filePath = (0, fileWriter_1.saveTypeFile)(name, typeString); // saves all generated interfaces (including nested)
|
|
10
|
+
return {
|
|
11
|
+
name,
|
|
12
|
+
filePath,
|
|
13
|
+
type: typeString,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAGA,gDAaC;AAhBD,uEAAkE;AAClE,qDAAmD;AAEnD,SAAgB,kBAAkB,CAChC,IAAY,EACZ,IAAS,EACT,OAA8B,CAAC,qDAAqD;;IAEpF,MAAM,UAAU,GAAG,IAAA,sCAAiB,EAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACzD,MAAM,QAAQ,GAAG,IAAA,yBAAY,EAAC,IAAI,EAAE,UAAU,CAAC,CAAA,CAAC,oDAAoD;IAEpG,OAAO;QACL,IAAI;QACJ,QAAQ;QACR,IAAI,EAAE,UAAU;KACjB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inferType.d.ts","sourceRoot":"","sources":["../../src/infer/inferType.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,CACvB,KAAK,EAAE,GAAG,EACV,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,CA8BR"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inferType = inferType;
|
|
4
|
+
const generated = new Set();
|
|
5
|
+
function inferType(value, key) {
|
|
6
|
+
if (value === null)
|
|
7
|
+
return 'null';
|
|
8
|
+
// ARRAY
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
if (value.length === 0)
|
|
11
|
+
return 'any[]';
|
|
12
|
+
const first = value[0];
|
|
13
|
+
if (typeof first === 'object' && first !== null) {
|
|
14
|
+
const typeName = capitalize(singularize(key || 'Item'));
|
|
15
|
+
// Return reference to type name
|
|
16
|
+
// Generation will be handled in generateInterface
|
|
17
|
+
return `${typeName}[]`;
|
|
18
|
+
}
|
|
19
|
+
return `${typeof first}[]`;
|
|
20
|
+
}
|
|
21
|
+
// OBJECT
|
|
22
|
+
if (typeof value === 'object') {
|
|
23
|
+
// Inline object
|
|
24
|
+
const entries = Object.entries(value)
|
|
25
|
+
.map(([k, v]) => `${k}: ${inferType(v, k)};`)
|
|
26
|
+
.join(' ');
|
|
27
|
+
return `{ ${entries} }`;
|
|
28
|
+
}
|
|
29
|
+
return typeof value;
|
|
30
|
+
}
|
|
31
|
+
// Helpers
|
|
32
|
+
function singularize(word) {
|
|
33
|
+
return word.endsWith('s') ? word.slice(0, -1) : word;
|
|
34
|
+
}
|
|
35
|
+
function capitalize(word) {
|
|
36
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=inferType.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inferType.js","sourceRoot":"","sources":["../../src/infer/inferType.ts"],"names":[],"mappings":";;AAEA,8BAiCC;AAnCD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAA;AAEnC,SAAgB,SAAS,CACvB,KAAU,EACV,GAAY;IAEZ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAEjC,QAAQ;IACR,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,OAAO,CAAA;QAEtC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAEtB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAChD,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC,CAAA;YAEvD,gCAAgC;YAChC,kDAAkD;YAClD,OAAO,GAAG,QAAQ,IAAI,CAAA;QACxB,CAAC;QAED,OAAO,GAAG,OAAO,KAAK,IAAI,CAAA;IAC5B,CAAC;IAED,SAAS;IACT,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,gBAAgB;QAChB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;aAC5C,IAAI,CAAC,GAAG,CAAC,CAAA;QACZ,OAAO,KAAK,OAAO,IAAI,CAAA;IACzB,CAAC;IAED,OAAO,OAAO,KAAK,CAAA;AACrB,CAAC;AAED,UAAU;AACV,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtD,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACrD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileWriter.d.ts","sourceRoot":"","sources":["../../src/storage/fileWriter.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAWzD;AAGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,iBAIxC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.saveTypeFile = saveTypeFile;
|
|
7
|
+
exports.readTypeFile = readTypeFile;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
function saveTypeFile(name, content) {
|
|
11
|
+
const dir = path_1.default.resolve(process.cwd(), 'types/');
|
|
12
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
13
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
const filePath = path_1.default.join(dir, `${name}.ts`);
|
|
16
|
+
fs_1.default.writeFileSync(filePath, content);
|
|
17
|
+
return filePath;
|
|
18
|
+
}
|
|
19
|
+
// Add helper to read existing interface
|
|
20
|
+
function readTypeFile(name) {
|
|
21
|
+
const filePath = path_1.default.join(process.cwd(), 'types', `${name}.ts`);
|
|
22
|
+
if (!fs_1.default.existsSync(filePath))
|
|
23
|
+
return null;
|
|
24
|
+
return fs_1.default.readFileSync(filePath, 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=fileWriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileWriter.js","sourceRoot":"","sources":["../../src/storage/fileWriter.ts"],"names":[],"mappings":";;;;;AAGA,oCAWC;AAGD,oCAIC;AArBD,4CAAmB;AACnB,gDAAuB;AAEvB,SAAgB,YAAY,CAAC,IAAY,EAAE,OAAe;IACxD,MAAM,GAAG,GAAG,cAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAA;IAEjD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,YAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,CAAA;IAC7C,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAEnC,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,wCAAwC;AACxC,SAAgB,YAAY,CAAC,IAAY;IACvC,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,IAAI,KAAK,CAAC,CAAA;IAChE,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IACzC,OAAO,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;AAC3C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opt-func.d.ts","sourceRoot":"","sources":["../../src/utils/opt-func.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,UAEvC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,UAEtC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ------------------- Helpers -------------------
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.singularize = singularize;
|
|
5
|
+
exports.capitalize = capitalize;
|
|
6
|
+
function singularize(word) {
|
|
7
|
+
return word.endsWith('s') ? word.slice(0, -1) : word;
|
|
8
|
+
}
|
|
9
|
+
function capitalize(word) {
|
|
10
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=opt-func.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opt-func.js","sourceRoot":"","sources":["../../src/utils/opt-func.ts"],"names":[],"mappings":";AAAA,kDAAkD;;AAElD,kCAEC;AAED,gCAEC;AAND,SAAgB,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtD,CAAC;AAED,SAAgB,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AACrD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forge-type",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Automatically generates TypeScript interfaces and types from JSON data, with enum detection.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
6
10
|
"scripts": {
|
|
7
11
|
"build": "tsc",
|
|
8
12
|
"dev": "ts-node src/index.ts",
|
|
9
13
|
"test": "ts-node src/test/test.ts",
|
|
10
14
|
"clean": "rm -rf dist types/generated"
|
|
11
15
|
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
"keywords": [
|
|
17
|
+
"typescript",
|
|
18
|
+
"types",
|
|
19
|
+
"interface",
|
|
20
|
+
"json",
|
|
21
|
+
"enum",
|
|
22
|
+
"generator"
|
|
23
|
+
],
|
|
24
|
+
"author": "Your Name",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {},
|
|
27
|
+
"devDependencies": {
|
|
16
28
|
"@types/node": "^25.0.3",
|
|
17
29
|
"ts-node": "^10.9.2",
|
|
18
30
|
"typescript": "^5.9.3"
|
|
19
31
|
}
|
|
20
|
-
}
|
|
32
|
+
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { inferType } from '../infer/inferType'
|
|
2
|
-
import { saveTypeFile, readTypeFile } from '../storage/fileWriter'
|
|
3
|
-
import { capitalize, singularize } from '../utils/opt-func'
|
|
4
|
-
|
|
5
|
-
const generatedInterfaces = new Set<string>()
|
|
6
|
-
const generatedEnums = new Set<string>()
|
|
7
|
-
const fieldValuesMap: Record<string, Set<string>> = {}
|
|
8
|
-
|
|
9
|
-
// Fields allowed as enums
|
|
10
|
-
const allowedEnumFields = [
|
|
11
|
-
'gender', 'role', 'status', 'accountType', 'subscription', 'membership', 'accessLevel',
|
|
12
|
-
'visibility', 'type', 'priority', 'category',
|
|
13
|
-
'paymentMethod', 'paymentStatus', 'deliveryStatus',
|
|
14
|
-
'level', 'mode', 'subscriptionStatus', 'confirmation'
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
export function generateInterface(
|
|
18
|
-
name: string,
|
|
19
|
-
data: any,
|
|
20
|
-
options?: { modify?: boolean }
|
|
21
|
-
): string {
|
|
22
|
-
const allInterfaces: string[] = []
|
|
23
|
-
const enumDefinitions: string[] = []
|
|
24
|
-
|
|
25
|
-
function helper(name: string, data: any) {
|
|
26
|
-
let existingProps: Record<string, string> = {}
|
|
27
|
-
|
|
28
|
-
if (options?.modify) {
|
|
29
|
-
const existingContent = readTypeFile(name)
|
|
30
|
-
if (existingContent) existingProps = parseInterface(existingContent)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const bodyLines: string[] = []
|
|
34
|
-
|
|
35
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
36
|
-
let typeStr = inferType(value, key)
|
|
37
|
-
const optional = value === null || value === undefined ? '?' : ''
|
|
38
|
-
|
|
39
|
-
// Handle nested arrays of objects
|
|
40
|
-
if (Array.isArray(value) && typeof value[0] === 'object' && value[0] !== null) {
|
|
41
|
-
const typeName = capitalize(singularize(key))
|
|
42
|
-
if (!generatedInterfaces.has(typeName)) {
|
|
43
|
-
generatedInterfaces.add(typeName)
|
|
44
|
-
helper(typeName, value[0])
|
|
45
|
-
|
|
46
|
-
Object.entries(value[0]).forEach(([k, v]) => {
|
|
47
|
-
if (typeof v === 'string') detectEnum(k, v, enumDefinitions)
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
typeStr = `${typeName}[]`
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Handle nested objects
|
|
54
|
-
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
55
|
-
typeStr = `{ ${Object.entries(value)
|
|
56
|
-
.map(([k, v]) => {
|
|
57
|
-
if (typeof v === 'string') detectEnum(k, v, enumDefinitions)
|
|
58
|
-
return `${k}: ${inferType(v, k)};`
|
|
59
|
-
})
|
|
60
|
-
.join(' ')} }`
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Top-level enums
|
|
64
|
-
if (typeof value === 'string') {
|
|
65
|
-
const enumType = detectEnum(key, value, enumDefinitions)
|
|
66
|
-
if (enumType) typeStr = enumType
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
existingProps[key] = typeStr
|
|
70
|
-
bodyLines.push(` ${key}${optional}: ${typeStr};`)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
// Optional fields from existing interface
|
|
74
|
-
Object.keys(existingProps).forEach(key => {
|
|
75
|
-
if (!(key in data)) bodyLines.push(` ${key}?: ${existingProps[key]};`)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
const content = `export interface ${name} {\n${bodyLines.join('\n')}\n}`
|
|
79
|
-
allInterfaces.push(content)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
helper(name, data)
|
|
83
|
-
|
|
84
|
-
const finalContent = [...enumDefinitions, ...allInterfaces].join('\n\n')
|
|
85
|
-
saveTypeFile(name, finalContent)
|
|
86
|
-
|
|
87
|
-
return finalContent
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function parseInterface(content: string): Record<string, string> {
|
|
91
|
-
const props: Record<string, string> = {}
|
|
92
|
-
content.split('\n').forEach(line => {
|
|
93
|
-
const match = line.trim().match(/^(\w+)\??:\s(.+);$/)
|
|
94
|
-
if (match) {
|
|
95
|
-
const [_, key, type] = match
|
|
96
|
-
if (key && type) props[key] = type
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
return props
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function detectEnum(key: string, value: string, enumDefinitions: string[]): string | null {
|
|
103
|
-
if (!allowedEnumFields.includes(key)) return null
|
|
104
|
-
if (typeof value !== 'string') return null
|
|
105
|
-
|
|
106
|
-
if (!fieldValuesMap[key]) fieldValuesMap[key] = new Set()
|
|
107
|
-
fieldValuesMap[key].add(value)
|
|
108
|
-
|
|
109
|
-
const uniqueValues = Array.from(fieldValuesMap[key])
|
|
110
|
-
if (uniqueValues.length > 1 && uniqueValues.length <= 10) {
|
|
111
|
-
const typeName = capitalize(key)
|
|
112
|
-
if (!generatedEnums.has(typeName)) {
|
|
113
|
-
generatedEnums.add(typeName)
|
|
114
|
-
const unionType = uniqueValues.map(v => `"${v}"`).join(' | ')
|
|
115
|
-
enumDefinitions.push(`export type ${typeName} = ${unionType};`)
|
|
116
|
-
}
|
|
117
|
-
return typeName
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return null
|
|
121
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { generateInterface } from './generator/interfaceGenerator'
|
|
2
|
-
import { saveTypeFile } from './storage/fileWriter'
|
|
3
|
-
|
|
4
|
-
export function createTypeFromData(
|
|
5
|
-
name: string,
|
|
6
|
-
data: any,
|
|
7
|
-
options?: { modify?: boolean } // pass { modify: true } to update existing interface
|
|
8
|
-
) {
|
|
9
|
-
const typeString = generateInterface(name, data, options)
|
|
10
|
-
const filePath = saveTypeFile(name, typeString) // saves all generated interfaces (including nested)
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
name,
|
|
14
|
-
filePath,
|
|
15
|
-
type: typeString,
|
|
16
|
-
}
|
|
17
|
-
}
|
package/src/infer/inferType.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
const generated = new Set<string>()
|
|
2
|
-
|
|
3
|
-
export function inferType(
|
|
4
|
-
value: any,
|
|
5
|
-
key?: string
|
|
6
|
-
): string {
|
|
7
|
-
if (value === null) return 'null'
|
|
8
|
-
|
|
9
|
-
// ARRAY
|
|
10
|
-
if (Array.isArray(value)) {
|
|
11
|
-
if (value.length === 0) return 'any[]'
|
|
12
|
-
|
|
13
|
-
const first = value[0]
|
|
14
|
-
|
|
15
|
-
if (typeof first === 'object' && first !== null) {
|
|
16
|
-
const typeName = capitalize(singularize(key || 'Item'))
|
|
17
|
-
|
|
18
|
-
// Return reference to type name
|
|
19
|
-
// Generation will be handled in generateInterface
|
|
20
|
-
return `${typeName}[]`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return `${typeof first}[]`
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// OBJECT
|
|
27
|
-
if (typeof value === 'object') {
|
|
28
|
-
// Inline object
|
|
29
|
-
const entries = Object.entries(value)
|
|
30
|
-
.map(([k, v]) => `${k}: ${inferType(v, k)};`)
|
|
31
|
-
.join(' ')
|
|
32
|
-
return `{ ${entries} }`
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return typeof value
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Helpers
|
|
39
|
-
function singularize(word: string) {
|
|
40
|
-
return word.endsWith('s') ? word.slice(0, -1) : word
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function capitalize(word: string) {
|
|
44
|
-
return word.charAt(0).toUpperCase() + word.slice(1)
|
|
45
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
2
|
-
import path from 'path'
|
|
3
|
-
|
|
4
|
-
export function saveTypeFile(name: string, content: string) {
|
|
5
|
-
const dir = path.resolve(process.cwd(), 'types/')
|
|
6
|
-
|
|
7
|
-
if (!fs.existsSync(dir)) {
|
|
8
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const filePath = path.join(dir, `${name}.ts`)
|
|
12
|
-
fs.writeFileSync(filePath, content)
|
|
13
|
-
|
|
14
|
-
return filePath
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Add helper to read existing interface
|
|
18
|
-
export function readTypeFile(name: string) {
|
|
19
|
-
const filePath = path.join(process.cwd(), 'types', `${name}.ts`)
|
|
20
|
-
if (!fs.existsSync(filePath)) return null
|
|
21
|
-
return fs.readFileSync(filePath, 'utf-8')
|
|
22
|
-
}
|
package/src/utils/opt-func.ts
DELETED
package/tsconfig.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"rootDir": "./src",
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
|
|
6
|
-
"module": "NodeNext",
|
|
7
|
-
"moduleResolution": "NodeNext",
|
|
8
|
-
"target": "ES2020",
|
|
9
|
-
|
|
10
|
-
"declaration": true,
|
|
11
|
-
"declarationMap": true,
|
|
12
|
-
"sourceMap": true,
|
|
13
|
-
|
|
14
|
-
"strict": true,
|
|
15
|
-
"noUncheckedIndexedAccess": true,
|
|
16
|
-
"exactOptionalPropertyTypes": true,
|
|
17
|
-
|
|
18
|
-
"esModuleInterop": true,
|
|
19
|
-
"resolveJsonModule": true,
|
|
20
|
-
|
|
21
|
-
"skipLibCheck": true,
|
|
22
|
-
"forceConsistentCasingInFileNames": true
|
|
23
|
-
},
|
|
24
|
-
"include": ["src"],
|
|
25
|
-
"exclude": ["node_modules", "dist"]
|
|
26
|
-
}
|