config2ts 2.4.0 → 2.5.0

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
@@ -8,28 +8,82 @@ run command: `npm install -g config2ts`
8
8
 
9
9
  # how to use
10
10
 
11
- config2ts -d config -o dist -m csv.ts
11
+ config2ts -d config -o dist -n csv.ts -a public
12
12
 
13
13
  # support type
14
14
 
15
- | csv field | typescript type |
16
- | :-------: | :-------------: |
17
- | Index | string |
18
- | String | string |
19
- | Number | number |
20
- | Boolean | boolean |
21
- | Enum | type |
22
- | EnumIndex | type |
23
- | String[] | string[] |
24
- | Number[] | number[] |
25
- | Enum[] | type[] |
15
+ | csv field | typescript type |
16
+ | :--------: | :---------------: |
17
+ | Index | string |
18
+ | String | string |
19
+ | Number | number |
20
+ | Boolean | boolean |
21
+ | Enum | type |
22
+ | EnumIndex | type |
23
+ | String[] | string[] |
24
+ | Number[] | number[] |
25
+ | Enum[] | type[] |
26
+ | Ref | namespace.Record |
27
+ | RefEnum | namespace.type |
28
+ | RefEnum[] | namespace.type[] |
29
+ | Object | type (Record) |
26
30
 
27
31
  * `Number` support Infinity and NaN
28
32
  * `Enum` support empty string type
29
33
  * `Enum[]` do not include empty string type
30
34
  * `EnumIndex` will generate index type, and will use `Enum` type to generate interface
35
+ * `Ref` / `RefEnum` reference another csv's Record/type by id
36
+ * `Object` parse `key:value,key:value` format, auto-infer value types (number/boolean/string), generates a dedicated `type` that merges all keys
31
37
 
32
- ## Usage
38
+ ## assets2ts
39
+
40
+ Scan assets directory and generate an `assets.ts` index file with nested resource tree.
41
+
42
+ - `-a, --assets <path>` set assets resource directory. default: `public`
43
+ - Output file: `assets.ts` (alongside the merged config file)
44
+ - Directory/filenames are converted to camelCase keys
45
+ - `type` field uses file extension (lowercase), e.g. `'png'`, `'mp3'`, `'svg'`
46
+ - Directories containing 2+ files of the same extension get a dedicated type (e.g. `PngAsset`, `Mp3Asset`) and `as Record<string, XxxAsset>` annotation
47
+ - Directories with ≤1 file or mixed extensions do not get a type annotation
48
+ - Nested directories are supported (e.g. `public/sub/image/`)
49
+
50
+ ### generated example
51
+
52
+ ```typescript
53
+ // Auto Generated by config2ts, DO NOT EDIT
54
+
55
+ export type Mp3Asset = { path: string; type: 'mp3' };
56
+ export type PngAsset = { path: string; type: 'png' };
57
+ export type SvgAsset = { path: string; type: 'svg' };
58
+
59
+ export const RES = {
60
+ public: {
61
+ image: {
62
+ adjustHorizontal: {path:'public/image/adjust-horizontal.png',type:'png'},
63
+ direction: {path:'public/image/direction.png',type:'png'}
64
+ } as Record<string, PngAsset>,
65
+ music: {
66
+ effect1: {path:'public/music/effect1.mp3',type:'mp3'},
67
+ effect2: {path:'public/music/effect2.mp3',type:'mp3'}
68
+ } as Record<string, Mp3Asset>,
69
+ singleFile: {
70
+ effect1: {path:'public/single-file/effect1.mp3',type:'mp3'}
71
+ }
72
+ }
73
+ };
74
+ ```
75
+
76
+ ### usage
77
+
78
+ ```typescript
79
+ import { RES } from "./assets";
80
+
81
+ const meta = RES.public.image.adjustHorizontal;
82
+ // meta.path → 'public/image/adjust-horizontal.png'
83
+ // meta.type → 'png'
84
+ ```
85
+
86
+ ## Options
33
87
 
34
88
  ```
35
89
  Options:
@@ -38,7 +92,14 @@ config2ts -d config -o dist -m csv.ts
38
92
  -V, --version output the version number
39
93
  -d, --dir <path> set convert path. default: ./
40
94
  -o, --outDir <path> set outDir path. default: ./
41
- -p, --prefix <prefix> set interface prefix. default: ""
42
- -s, --suffix <suffix> set interface suffix. default: "Csv"
43
- -m, --merge <name> merge all to one ts file. default: csv.ts
95
+ -n, --name <name> output file name. default: csv.ts
96
+ -a, --assets <path> set assets resource directory for asset index. default: public
44
97
  ```
98
+
99
+ ## supported file formats
100
+
101
+ | format | extension |
102
+ | :----: | :-------: |
103
+ | csv | .csv |
104
+ | ini | .ini |
105
+ | toml | .toml |
@@ -1,7 +1,8 @@
1
1
  type ConvertHandler = (str: string, moduleName: string) => string;
2
2
  export declare const Convert: Record<string, ConvertHandler>;
3
+ export declare function assets2ts(assetsDir: string): string;
3
4
  export declare function GetTsString(filePath: string): string;
4
5
  export declare function GetTsStringFromFileList(fileList: string[]): string;
5
6
  export declare function GetValidFileList(fileList: string[]): string[];
6
- export declare function startConvert(dir: string, outDir: string, merge: string): void;
7
+ export declare function startConvert(dir: string, outDir: string, merge: string, assetsDir?: string): void;
7
8
  export {};
package/lib/config2ts.js CHANGED
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.Convert = void 0;
37
+ exports.assets2ts = assets2ts;
37
38
  exports.GetTsString = GetTsString;
38
39
  exports.GetTsStringFromFileList = GetTsStringFromFileList;
39
40
  exports.GetValidFileList = GetValidFileList;
@@ -53,6 +54,7 @@ const RefPrefix = "Ref[";
53
54
  const RefSuffix = "]";
54
55
  const RefEnumPrefix = "RefEnum[";
55
56
  const RefEnumArraySuffix = "][]";
57
+ const AutoGenHeader = "// Auto Generated by config2ts, DO NOT EDIT\n\n";
56
58
  exports.Convert = {
57
59
  ini: function (str, moduleName) {
58
60
  const obj = toml.parse(str);
@@ -333,6 +335,123 @@ function GetFileExt(filePath) {
333
335
  const pathObject = path.parse(filePath);
334
336
  return pathObject.ext.slice(1);
335
337
  }
338
+ function scanDir(dir, basePath) {
339
+ const node = {};
340
+ let hasContent = false;
341
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
342
+ for (const entry of entries) {
343
+ const fullPath = path.join(dir, entry.name);
344
+ if (entry.isDirectory()) {
345
+ const key = changeCase.camelCase(entry.name);
346
+ const childNode = scanDir(fullPath, path.posix.join(basePath, entry.name));
347
+ if (childNode !== null) {
348
+ node[key] = childNode;
349
+ hasContent = true;
350
+ }
351
+ }
352
+ else if (entry.isFile()) {
353
+ const parsed = path.parse(entry.name);
354
+ const ext = parsed.ext.slice(1).toLowerCase();
355
+ const key = changeCase.camelCase(parsed.name);
356
+ const relPath = path.posix.join(basePath, entry.name);
357
+ node[key] = { path: relPath, type: ext };
358
+ hasContent = true;
359
+ }
360
+ }
361
+ return hasContent ? node : null;
362
+ }
363
+ function isLeafNode(value) {
364
+ return "path" in value && "type" in value;
365
+ }
366
+ function getUniformType(node) {
367
+ const values = Object.values(node);
368
+ if (values.length <= 1)
369
+ return null;
370
+ let uniformType = null;
371
+ for (const v of values) {
372
+ if (!isLeafNode(v))
373
+ return null;
374
+ if (uniformType === null) {
375
+ uniformType = v.type;
376
+ }
377
+ else if (uniformType !== v.type) {
378
+ return null;
379
+ }
380
+ }
381
+ return uniformType;
382
+ }
383
+ function collectUniformDirs(node, dirTypes, exts) {
384
+ const ut = getUniformType(node);
385
+ if (ut !== null) {
386
+ dirTypes.set(node, ut);
387
+ exts.add(ut);
388
+ return;
389
+ }
390
+ for (const v of Object.values(node)) {
391
+ if (!isLeafNode(v)) {
392
+ collectUniformDirs(v, dirTypes, exts);
393
+ }
394
+ }
395
+ }
396
+ function extToTypeName(ext) {
397
+ return ext.charAt(0).toUpperCase() + ext.slice(1) + "Asset";
398
+ }
399
+ function serializeAssetNode(node, indent, uniformDirs, extTypeMap) {
400
+ const entries = Object.entries(node);
401
+ if (entries.length === 0)
402
+ return `${indent}{}`;
403
+ const ut = uniformDirs.get(node);
404
+ const lines = [];
405
+ for (let i = 0; i < entries.length; i++) {
406
+ const [key, value] = entries[i];
407
+ const serializedKey = serializeField(key);
408
+ const comma = i < entries.length - 1 ? "," : "";
409
+ if (isLeafNode(value)) {
410
+ lines.push(`${indent}${serializedKey}: {path:${json5.stringify(value.path)},type:${json5.stringify(value.type)}}${comma}`);
411
+ }
412
+ else {
413
+ const childUt = uniformDirs.get(value);
414
+ const typeAnnotation = childUt ? ` as Record<string, ${extTypeMap.get(childUt)}>` : "";
415
+ lines.push(`${indent}${serializedKey}: {`);
416
+ lines.push(serializeAssetNode(value, indent + " ", uniformDirs, extTypeMap));
417
+ lines.push(`${indent}}${typeAnnotation}${comma}`);
418
+ }
419
+ }
420
+ return lines.join("\n");
421
+ }
422
+ function assets2ts(assetsDir) {
423
+ if (!fs.existsSync(assetsDir)) {
424
+ return "";
425
+ }
426
+ const dirName = path.basename(assetsDir);
427
+ const root = scanDir(assetsDir, dirName);
428
+ if (root === null) {
429
+ return "";
430
+ }
431
+ const uniformDirs = new Map();
432
+ const exts = new Set();
433
+ collectUniformDirs(root, uniformDirs, exts);
434
+ const extTypeMap = new Map();
435
+ const sortedExts = Array.from(exts).sort();
436
+ for (const ext of sortedExts) {
437
+ extTypeMap.set(ext, extToTypeName(ext));
438
+ }
439
+ let template = "";
440
+ for (const ext of sortedExts) {
441
+ const typeName = extTypeMap.get(ext);
442
+ template += `export type ${typeName} = { path: string; type: ${json5.stringify(ext)} };\n`;
443
+ }
444
+ if (sortedExts.length > 0)
445
+ template += "\n";
446
+ template += `export const RES = {\n`;
447
+ const rootUt = uniformDirs.get(root);
448
+ template += ` ${dirName}: {\n`;
449
+ template += serializeAssetNode(root, " ", uniformDirs, extTypeMap);
450
+ const rootAnnotation = rootUt ? ` as Record<string, ${extTypeMap.get(rootUt)}>` : "";
451
+ template += `\n }${rootAnnotation}\n`;
452
+ template += `};\n`;
453
+ return template;
454
+ }
336
455
  function GetTsString(filePath) {
337
456
  const pathObject = path.parse(filePath);
338
457
  const handle = exports.Convert[GetFileExt(filePath)];
@@ -359,12 +478,20 @@ function GetValidFileList(fileList) {
359
478
  return exports.Convert[GetFileExt(filePath)] != null;
360
479
  });
361
480
  }
362
- function startConvert(dir, outDir, merge) {
481
+ function startConvert(dir, outDir, merge, assetsDir) {
363
482
  const fileList = fs.readdirSync(dir);
364
483
  const fullFileList = GetValidFileList(fileList).map(function (filename) {
365
484
  return path.join(dir, filename);
366
485
  });
367
486
  const mergeFile = path.join(outDir, merge);
368
- fs.writeFileSync(mergeFile, GetTsStringFromFileList(fullFileList), { encoding: "utf-8" });
487
+ fs.writeFileSync(mergeFile, AutoGenHeader + GetTsStringFromFileList(fullFileList), { encoding: "utf-8" });
369
488
  console.log(`config2ts, ${fullFileList.length} config files, merge into: ${mergeFile}`);
489
+ if (assetsDir) {
490
+ const assetsOutput = assets2ts(assetsDir);
491
+ if (assetsOutput) {
492
+ const assetsFile = path.join(outDir, "assets.ts");
493
+ fs.writeFileSync(assetsFile, AutoGenHeader + assetsOutput, { encoding: "utf-8" });
494
+ console.log(`assets2ts, resource dir: ${assetsDir}, output: ${assetsFile}`);
495
+ }
496
+ }
370
497
  }
package/lib/main.js CHANGED
@@ -45,6 +45,7 @@ program
45
45
  .option("-n, --name <name>", "output file name", "csv.ts")
46
46
  .option("-d, --dir <path>", "set convert path", ".")
47
47
  .option("-o, --outDir <path>", "set outDir path")
48
+ .option("-a, --assets <path>", "set assets resource directory for asset index", "public")
48
49
  .parse(process.argv);
49
50
  const options = program.opts();
50
51
  console.log(options);
@@ -55,4 +56,8 @@ console.log("outDir:", outDir);
55
56
  if (!fs.existsSync(outDir)) {
56
57
  fs.mkdirSync(outDir, { recursive: true });
57
58
  }
58
- (0, config2ts_1.startConvert)(dir, outDir, options.name);
59
+ const assetsDir = options.assets ? path.resolve(options.assets) : undefined;
60
+ if (assetsDir) {
61
+ console.log("assetsDir:", assetsDir);
62
+ }
63
+ (0, config2ts_1.startConvert)(dir, outDir, options.name, assetsDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "config2ts",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "convert config to ts file.",
5
5
  "main": "lib/config2ts.js",
6
6
  "types": "lib/config2ts.d.ts",