type-crafter 0.13.0 → 0.13.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/dist/index.js CHANGED
@@ -21188,11 +21188,13 @@ function getReferencedTypeModules(_referencedTypes, _writtenAt) {
21188
21188
  resolveFilePath(outputFile.filePath) !== resolveFilePath(writtenAt)) {
21189
21189
  if (typeof referencedTypeModules[outputFile.modulePath] === 'undefined') {
21190
21190
  const rawRelativePath = generateRelativePath(writtenAt, outputFile.modulePath);
21191
+ const moduleName = outputFile.modulePath.split('/').pop() ?? '';
21191
21192
  referencedTypeModules[outputFile.modulePath] = {
21192
21193
  modulePath: outputFile.modulePath,
21193
21194
  moduleRelativePath: formatModulePath(rawRelativePath, writtenAt, modulePathConfig),
21194
21195
  referencedTypes: [referenceType],
21195
- moduleName: outputFile.modulePath.split('/').pop() ?? ''
21196
+ moduleName,
21197
+ fileBasedModules: moduleName === (modulePathConfig?.moduleFileName ?? '')
21196
21198
  };
21197
21199
  }
21198
21200
  else {
@@ -21234,6 +21236,20 @@ function toSnakeCaseHelper(input) {
21234
21236
  }
21235
21237
  return toSnakeCase(inputString);
21236
21238
  }
21239
+ function toCamelCase(input) {
21240
+ const pascal = toPascalCase(input);
21241
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
21242
+ }
21243
+ function formatCase(input, fontCase) {
21244
+ switch (fontCase) {
21245
+ case 'snake_case':
21246
+ return toSnakeCase(input);
21247
+ case 'PascalCase':
21248
+ return toPascalCase(input);
21249
+ case 'camelCase':
21250
+ return toCamelCase(input);
21251
+ }
21252
+ }
21237
21253
  function refineJSONKey(input) {
21238
21254
  if (typeof input === 'string' && input.includes('-')) {
21239
21255
  return `'${input}'`;
@@ -21252,6 +21268,19 @@ function refineIndexKey(input) {
21252
21268
  }
21253
21269
  return input;
21254
21270
  }
21271
+ function escapeReservedWord(input) {
21272
+ if (typeof input !== 'string') {
21273
+ return input;
21274
+ }
21275
+ const config = Runtime.getConfig().language.reservedKeywords;
21276
+ if (typeof config === 'undefined') {
21277
+ return input;
21278
+ }
21279
+ if (config.words.includes(input)) {
21280
+ return config.prefix + input;
21281
+ }
21282
+ return input;
21283
+ }
21255
21284
  function registerTemplateHelpers() {
21256
21285
  Handlebars.registerHelper('getOptionalKeys', getOptionalKeys);
21257
21286
  Handlebars.registerHelper('getRequiredKeys', getRequiredKeys);
@@ -21267,6 +21296,7 @@ function registerTemplateHelpers() {
21267
21296
  Handlebars.registerHelper('jsonKey', refineJSONKey);
21268
21297
  Handlebars.registerHelper('variableName', refineVariableName);
21269
21298
  Handlebars.registerHelper('indexKey', refineIndexKey);
21299
+ Handlebars.registerHelper('escapeReservedWord', escapeReservedWord);
21270
21300
  Handlebars.registerHelper('stringify', (value) => JSON.stringify(value));
21271
21301
  Handlebars.registerHelper('not', (value) => {
21272
21302
  if (typeof value === 'boolean') {
@@ -21491,18 +21521,19 @@ async function generateAdditionalPropertiesType(typeName, typeInfo, parentTypes)
21491
21521
  type: 'string'
21492
21522
  }).templateInput.type;
21493
21523
  if (typeof typeInfo.additionalProperties === 'boolean') {
21524
+ const unknownType = getPrimitiveType(typeName, {
21525
+ ...placeholderTypeInfo,
21526
+ type: 'unknown'
21527
+ });
21494
21528
  return {
21495
21529
  templateInput: {
21496
21530
  keyType: stringKeyType,
21497
- valueType: getPrimitiveType(typeName, {
21498
- ...placeholderTypeInfo,
21499
- type: 'unknown'
21500
- }).templateInput.type,
21531
+ valueType: unknownType.templateInput.type,
21501
21532
  valueTypeReferenced: false,
21502
21533
  valuePrimitiveType: 'unknown',
21503
21534
  valueComposerType: null
21504
21535
  },
21505
- primitives: new Set()
21536
+ primitives: unknownType.primitives
21506
21537
  };
21507
21538
  }
21508
21539
  else if (valueIsKeyedAdditionalProperties(typeInfo.additionalProperties)) {
@@ -21582,8 +21613,7 @@ async function generateObjectType(typeName, typeInfo, parentTypes) {
21582
21613
  const referencedType = await generateReferencedType(propertyName, propertyDetails, parentTypes);
21583
21614
  recursivePropertyName = referencedType.templateInput.typeName;
21584
21615
  languageDataType = recursivePropertyName;
21585
- references.push(...referencedType.references);
21586
- primitives.push(...referencedType.primitives);
21616
+ references.push(recursivePropertyName);
21587
21617
  isReferenced = true;
21588
21618
  }
21589
21619
  else if (enumValues !== null) {
@@ -21697,6 +21727,18 @@ async function generateArrayType(typeName, typeInfo, parentTypes) {
21697
21727
  }
21698
21728
  else {
21699
21729
  arrayItemsType = await generateType(typeName + 'Item', typeInfo.items, parentTypes);
21730
+ // For referenced types (e.g. enums via $ref), use the type name as the item type
21731
+ // identifier, consistent with how inline enums are handled above.
21732
+ // Also only keep the direct reference — the referenced type handles its own imports.
21733
+ // Create new objects to avoid mutating cached data from generateReferencedType.
21734
+ if (typeInfo.items.$ref !== null) {
21735
+ const refItemTypeName = arrayItemsType.templateInput.typeName;
21736
+ arrayItemsType = {
21737
+ ...arrayItemsType,
21738
+ templateInput: { ...arrayItemsType.templateInput, type: refItemTypeName },
21739
+ references: new Set([refItemTypeName])
21740
+ };
21741
+ }
21700
21742
  }
21701
21743
  if (typeof arrayItemsType.templateInput?.type === 'undefined') {
21702
21744
  throw new InvalidSpecFileError('Invalid array type for: ' + typeName);
@@ -21962,28 +22004,41 @@ async function generator(specFileData) {
21962
22004
  }
21963
22005
  }
21964
22006
  result.groupedTypes = groupedTypes;
22007
+ // remove self references from grouped types
22008
+ for (const groupName in result.groupedTypes) {
22009
+ for (const typeName in result.groupedTypes[groupName]) {
22010
+ result.groupedTypes[groupName][typeName].references.delete(typeName);
22011
+ }
22012
+ }
21965
22013
  return result;
21966
22014
  }
21967
22015
 
22016
+ function formatModuleName(name) {
22017
+ const moduleNameCase = Runtime.getConfig().language.modulePathConfig.moduleNameCase;
22018
+ return typeof moduleNameCase !== 'undefined' ? formatCase(name, moduleNameCase) : name;
22019
+ }
21968
22020
  function generateTypesOutputFiles(types, groupName = null) {
21969
22021
  const config = Runtime.getConfig();
21970
22022
  const extension = config.output.fileExtension;
21971
22023
  const outputDir = config.output.directory;
21972
22024
  const moduleFileName = config.language.exporterModuleName;
21973
22025
  const writerMode = groupName === null ? config.output.writerMode.types : config.output.writerMode.groupedTypes;
22026
+ const formattedGroupName = groupName !== null ? formatModuleName(groupName) : null;
21974
22027
  const result = new Map();
21975
22028
  for (const typeName in types) {
21976
22029
  const typeProperties = types[typeName];
21977
22030
  if (typeof typeProperties !== 'undefined') {
22031
+ const formattedTypeName = formatModuleName(typeName);
21978
22032
  result.set(typeName, {
21979
22033
  modulePath: outputDir +
21980
22034
  '/' +
21981
- (writerMode === 'FolderWithFiles' ? (groupName ?? '') + '/' : '') +
21982
- moduleFileName,
22035
+ (formattedGroupName
22036
+ ? formattedGroupName + (writerMode === 'FolderWithFiles' ? '/' + moduleFileName : '')
22037
+ : moduleFileName),
21983
22038
  filePath: outputDir +
21984
22039
  (writerMode === 'Files' || writerMode === 'FolderWithFiles'
21985
- ? '/' + (groupName ?? '') + typeName
21986
- : '/' + (groupName ?? 'types')),
22040
+ ? '/' + (formattedGroupName ?? '') + formattedTypeName
22041
+ : '/' + (formattedGroupName ?? 'types')),
21987
22042
  extension
21988
22043
  });
21989
22044
  }
@@ -22041,7 +22096,7 @@ async function writeTypesToFiles(config, types, folderName = '') {
22041
22096
  const typeNames = Object.keys(types);
22042
22097
  for (const typeName in types) {
22043
22098
  const typeData = types[typeName];
22044
- const file = typeName + config.output.fileExtension;
22099
+ const file = formatModuleName(typeName) + config.output.fileExtension;
22045
22100
  const references = filterReferences
22046
22101
  ? [...types[typeName].references].filter((x) => !typeNames.includes(x))
22047
22102
  : [...types[typeName].references];
@@ -22125,11 +22180,12 @@ async function writeOutput(generationResult) {
22125
22180
  if (config.output.writerMode.groupedTypes === 'FolderWithFiles') {
22126
22181
  for (const groupName in generationResult.groupedTypes) {
22127
22182
  let groupFilesWritten = null;
22128
- await createFolderWithBasePath(config.output.directory, groupName);
22183
+ const formattedGroupName = formatModuleName(groupName);
22184
+ await createFolderWithBasePath(config.output.directory, formattedGroupName);
22129
22185
  addValuesToMappedSet(writtenFiles, await getCompleteFolderPath(config.output.directory), [
22130
- groupName
22186
+ formattedGroupName
22131
22187
  ]);
22132
- groupFilesWritten = await writeTypesToFiles(config, generationResult.groupedTypes[groupName], groupName);
22188
+ groupFilesWritten = await writeTypesToFiles(config, generationResult.groupedTypes[groupName], formattedGroupName);
22133
22189
  if (groupFilesWritten !== null) {
22134
22190
  addValuesToMappedSet(writtenFiles, await getCompleteFolderPath(groupFilesWritten.folderName), groupFilesWritten.files);
22135
22191
  }
@@ -22138,7 +22194,7 @@ async function writeOutput(generationResult) {
22138
22194
  else if (config.output.writerMode.groupedTypes === 'SingleFile') {
22139
22195
  for (const groupName in generationResult.groupedTypes) {
22140
22196
  let groupFilesWritten = null;
22141
- groupFilesWritten = await writeTypesToFile(config, generationResult.groupedTypes[groupName], groupName);
22197
+ groupFilesWritten = await writeTypesToFile(config, generationResult.groupedTypes[groupName], formatModuleName(groupName));
22142
22198
  if (groupFilesWritten !== null) {
22143
22199
  addValuesToMappedSet(writtenFiles, await getCompleteFolderPath(groupFilesWritten.folderName), groupFilesWritten.files);
22144
22200
  }
@@ -22301,7 +22357,64 @@ async function config(inputFilePath, outputDirectory, typesWriterMode, groupedTy
22301
22357
  parentRef: 'super',
22302
22358
  selfRef: 'self',
22303
22359
  moduleFileName: 'mod',
22304
- fileBasedModules: true
22360
+ fileBasedModules: true,
22361
+ moduleNameCase: 'snake_case'
22362
+ },
22363
+ reservedKeywords: {
22364
+ prefix: 'r#',
22365
+ words: [
22366
+ 'as',
22367
+ 'break',
22368
+ 'const',
22369
+ 'continue',
22370
+ 'crate',
22371
+ 'else',
22372
+ 'enum',
22373
+ 'extern',
22374
+ 'false',
22375
+ 'fn',
22376
+ 'for',
22377
+ 'if',
22378
+ 'impl',
22379
+ 'in',
22380
+ 'let',
22381
+ 'loop',
22382
+ 'match',
22383
+ 'mod',
22384
+ 'move',
22385
+ 'mut',
22386
+ 'pub',
22387
+ 'ref',
22388
+ 'return',
22389
+ 'self',
22390
+ 'Self',
22391
+ 'static',
22392
+ 'struct',
22393
+ 'super',
22394
+ 'trait',
22395
+ 'true',
22396
+ 'type',
22397
+ 'unsafe',
22398
+ 'use',
22399
+ 'where',
22400
+ 'while',
22401
+ 'async',
22402
+ 'await',
22403
+ 'dyn',
22404
+ 'abstract',
22405
+ 'become',
22406
+ 'box',
22407
+ 'do',
22408
+ 'final',
22409
+ 'macro',
22410
+ 'override',
22411
+ 'priv',
22412
+ 'typeof',
22413
+ 'unsized',
22414
+ 'virtual',
22415
+ 'yield',
22416
+ 'try'
22417
+ ]
22305
22418
  }
22306
22419
  }
22307
22420
  };
@@ -22374,7 +22487,7 @@ async function runner(language, inputFilePath, outputDirectory, _typesWriterMode
22374
22487
  }
22375
22488
  }
22376
22489
  greeting();
22377
- const program = new Command().version('0.13.0');
22490
+ const program = new Command().version('0.13.2');
22378
22491
  program
22379
22492
  .command('generate')
22380
22493
  .description('Generate types for your language from a type spec file')
@@ -6,13 +6,13 @@ pub struct {{typeName}} {
6
6
  {{#each compositions}}
7
7
  {{#if (eq this.source 'referenced')}}
8
8
  #[serde(flatten)]
9
- pub {{toSnakeCase this.referencedType}}: {{this.referencedType}},
9
+ pub {{escapeReservedWord (toSnakeCase this.referencedType)}}: {{this.referencedType}},
10
10
  {{else if (eq this.source 'inline')}}
11
11
  {{#if (eq this.dataType 'object')}}
12
12
  #[serde(flatten)]
13
- pub {{toSnakeCase this.templateInput.typeName}}: {{this.templateInput.typeName}},
13
+ pub {{escapeReservedWord (toSnakeCase this.templateInput.typeName)}}: {{this.templateInput.typeName}},
14
14
  {{else}}
15
- pub {{toSnakeCase this.templateInput.typeName}}: {{{this.templateInput.type}}},
15
+ pub {{escapeReservedWord (toSnakeCase this.templateInput.typeName)}}: {{{this.templateInput.type}}},
16
16
  {{/if}}
17
17
  {{/if}}
18
18
  {{/each}}
@@ -10,11 +10,13 @@ pub struct {{typeName}} {
10
10
  {{#if this.description}}
11
11
  /// {{{this.description}}}
12
12
  {{/if}}
13
+ {{#unless (eq @key (toSnakeCase @key))}}
13
14
  #[serde(rename = "{{@key}}")]
15
+ {{/unless}}
14
16
  {{#unless this.required}}
15
17
  #[serde(skip_serializing_if = "Option::is_none")]
16
18
  {{/unless}}
17
- pub {{toSnakeCase @key}}: {{#unless this.required}}Option<{{/unless}}{{{this.type}}}{{#unless this.required}}>{{/unless}},
19
+ pub {{escapeReservedWord (toSnakeCase @key)}}: {{#unless this.required}}Option<{{/unless}}{{{this.type}}}{{#unless this.required}}>{{/unless}},
18
20
  {{/each}}
19
21
  {{#if additionalProperties}}
20
22
  #[serde(flatten)]
@@ -1,7 +1,11 @@
1
1
  use serde::{Serialize, Deserialize};
2
2
  {{#each (getReferencedTypeModules referencedTypes writtenAt)}}
3
3
  {{#each this.referencedTypes}}
4
- use {{{../moduleRelativePath}}}::{{this}}::{{this}};
4
+ {{#if ../fileBasedModules}}
5
+ use {{{../moduleRelativePath}}}::{{toSnakeCase this}}::{{this}};
6
+ {{else}}
7
+ use {{{../moduleRelativePath}}}::{{this}};
8
+ {{/if}}
5
9
  {{/each}}
6
10
  {{/each}}
7
11
 
@@ -25,17 +25,24 @@ export type Template = {
25
25
  oneOfSyntax: string;
26
26
  allOfSyntax: string;
27
27
  };
28
+ export type FontCase = 'snake_case' | 'PascalCase' | 'camelCase';
28
29
  export type ModulePathConfig = {
29
30
  separator: string;
30
31
  parentRef: string;
31
32
  selfRef: string;
32
33
  moduleFileName: string;
33
34
  fileBasedModules: boolean;
35
+ moduleNameCase?: FontCase;
36
+ };
37
+ export type ReservedKeywordsConfig = {
38
+ words: string[];
39
+ prefix: string;
34
40
  };
35
41
  export type LanguageConfig = {
36
42
  exporterModuleName: string;
37
43
  typeMapper: LanguageTypeMapper;
38
44
  modulePathConfig: ModulePathConfig;
45
+ reservedKeywords?: ReservedKeywordsConfig;
39
46
  };
40
47
  /**
41
48
  * @description Mappers for all the types supported by OpenAPI 3.0.0
@@ -149,6 +156,7 @@ export type ReferencedModule = {
149
156
  moduleRelativePath: string;
150
157
  referencedTypes: string[];
151
158
  moduleName: string;
159
+ fileBasedModules: boolean;
152
160
  };
153
161
  export type EnumTemplateInput = TypeDescriptors & {
154
162
  typeName: string;
@@ -1,4 +1,4 @@
1
- import { type ModulePathConfig, type TypeInfo, type TypeDataType } from '$types';
1
+ import { type ModulePathConfig, type FontCase, type TypeInfo, type TypeDataType } from '$types';
2
2
  import { type JSONObject } from 'type-decoder';
3
3
  export * from './file-system';
4
4
  export * from './logger';
@@ -11,9 +11,12 @@ export declare function toPascalCase(input: string): string;
11
11
  export declare function toPascalCaseHelper(input: unknown): string | unknown;
12
12
  export declare function toSnakeCase(input: string): string;
13
13
  export declare function toSnakeCaseHelper(input: unknown): string | unknown;
14
+ export declare function toCamelCase(input: string): string;
15
+ export declare function formatCase(input: string, fontCase: FontCase): string;
14
16
  export declare function refineJSONKey(input: unknown): unknown;
15
17
  export declare function refineVariableName(input: unknown): unknown;
16
18
  export declare function refineIndexKey(input: unknown): unknown;
19
+ export declare function escapeReservedWord(input: unknown): unknown;
17
20
  export declare function registerTemplateHelpers(): void;
18
21
  export declare function readNestedValue(json: unknown, keyPath: string[]): JSONObject;
19
22
  export declare function generateRelativePath(fromPath: string, toPath: string): string;
@@ -1,3 +1,4 @@
1
1
  import { type TypeFilePath, type Types } from '$types';
2
+ export declare function formatModuleName(name: string): string;
2
3
  export declare function generateTypesOutputFiles(types: Types | null, groupName?: string | null): Map<string, TypeFilePath>;
3
4
  export declare function generateExpectedOutputFile(): Map<string, TypeFilePath>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-crafter",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "A tool to generate types from a yaml schema for any language",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -15,8 +15,8 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "dev": "tsc --watch",
18
- "format:all": "npx prettier --write .",
19
- "lint:all": "eslint src",
18
+ "format": "npx prettier --write .",
19
+ "lint": "eslint src",
20
20
  "clean:output": "rm -rf dist",
21
21
  "build": "npm run clean:output && rollup --config rollup.config.js"
22
22
  },