forge-type 1.0.1 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # TypeForge
1
+ # ForgeType
2
2
 
3
- TypeForge is a TypeScript utility library that **automatically generates types/interfaces** from backend JSON data.
3
+ ForgeType is a TypeScript utility library that **automatically generates types/interfaces** from backend JSON data.
4
4
  It also supports **enum detection** for short string fields and helps speed up frontend development by reducing boilerplate code.
5
5
 
6
6
  ## Features
@@ -0,0 +1,4 @@
1
+ export declare function generateInterface(name: string, data: any, options?: {
2
+ modify?: boolean;
3
+ }): string;
4
+ //# sourceMappingURL=interfaceGenerator.d.ts.map
@@ -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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"}
@@ -0,0 +1,8 @@
1
+ export declare function createTypeFromData(name: string, data: any, options?: {
2
+ modify?: boolean;
3
+ }): {
4
+ name: string;
5
+ filePath: string;
6
+ type: string;
7
+ };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export declare function inferType(value: any, key?: string): string;
2
+ //# sourceMappingURL=inferType.d.ts.map
@@ -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,3 @@
1
+ export declare function saveTypeFile(name: string, content: string): string;
2
+ export declare function readTypeFile(name: string): string | null;
3
+ //# sourceMappingURL=fileWriter.d.ts.map
@@ -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,3 @@
1
+ export declare function singularize(word: string): string;
2
+ export declare function capitalize(word: string): string;
3
+ //# sourceMappingURL=opt-func.d.ts.map
@@ -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.1",
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
- "author": "",
14
- "license": "ISC",
15
- "dependencies": {
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
- }
@@ -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
- }
@@ -1,9 +0,0 @@
1
- // ------------------- Helpers -------------------
2
-
3
- export function singularize(word: string) {
4
- return word.endsWith('s') ? word.slice(0, -1) : word
5
- }
6
-
7
- export function capitalize(word: string) {
8
- return word.charAt(0).toUpperCase() + word.slice(1)
9
- }
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
- }