type-crafter 0.12.1 → 0.13.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/dist/index.js CHANGED
@@ -20548,23 +20548,29 @@ var YAML = /*#__PURE__*/Object.freeze({
20548
20548
  });
20549
20549
 
20550
20550
  function hasColorSupport() {
20551
- if (process.env.NO_COLOR !== undefined)
20551
+ if (typeof process.env.NO_COLOR !== 'undefined') {
20552
20552
  return false;
20553
- if (process.env.FORCE_COLOR !== undefined)
20553
+ }
20554
+ if (typeof process.env.FORCE_COLOR !== 'undefined') {
20554
20555
  return true;
20555
- if (process.env.COLORTERM === 'truecolor' || process.env.COLORTERM === '24bit')
20556
+ }
20557
+ if (process.env.COLORTERM === 'truecolor' || process.env.COLORTERM === '24bit') {
20556
20558
  return true;
20557
- if (process.env.CI)
20559
+ }
20560
+ if (typeof process.env.CI !== 'undefined') {
20558
20561
  return true;
20562
+ }
20559
20563
  const term = process.env.TERM ?? '';
20560
- if (term === 'dumb')
20564
+ if (term === 'dumb') {
20561
20565
  return false;
20566
+ }
20562
20567
  if (term.includes('256color') ||
20563
20568
  term.includes('color') ||
20564
20569
  term.includes('xterm') ||
20565
20570
  term.includes('screen') ||
20566
- term.includes('vt100'))
20571
+ term.includes('vt100')) {
20567
20572
  return true;
20573
+ }
20568
20574
  return process.stdout.isTTY ?? false;
20569
20575
  }
20570
20576
  const isColorSupported = hasColorSupport();
@@ -20579,8 +20585,9 @@ function bgRgb(r, g, b) {
20579
20585
  return `\x1b[48;2;${r};${g};${b}m`;
20580
20586
  }
20581
20587
  function colorize$2(text, ...codes) {
20582
- if (!isColorSupported)
20588
+ if (!isColorSupported) {
20583
20589
  return text;
20590
+ }
20584
20591
  return codes.join('') + text + RESET;
20585
20592
  }
20586
20593
  const BRAND$2 = {
@@ -20629,14 +20636,16 @@ function interpolateColor(start, end, t) {
20629
20636
  };
20630
20637
  }
20631
20638
  function gradientText(text, startColor, endColor) {
20632
- if (!isColorSupported)
20639
+ if (!isColorSupported) {
20633
20640
  return text;
20641
+ }
20634
20642
  // eslint-disable-next-line @typescript-eslint/no-misused-spread -- character-by-character gradient
20635
20643
  const chars = [...text];
20636
20644
  return chars
20637
20645
  .map((char, i) => {
20638
- if (char === ' ' || char === '\n')
20646
+ if (char === ' ' || char === '\n') {
20639
20647
  return char;
20648
+ }
20640
20649
  const t = chars.length > 1 ? i / (chars.length - 1) : 0;
20641
20650
  const color = interpolateColor(startColor, endColor, t);
20642
20651
  return rgb(color.r, color.g, color.b) + char;
@@ -20699,8 +20708,9 @@ function createSpinner(message) {
20699
20708
  spinnerFrame = 0;
20700
20709
  return {
20701
20710
  start: () => {
20702
- if (activeSpinner)
20711
+ if (activeSpinner) {
20703
20712
  clearInterval(activeSpinner);
20713
+ }
20704
20714
  const render = () => {
20705
20715
  clearSpinnerLine();
20706
20716
  const frame = isColorSupported
@@ -20749,8 +20759,9 @@ function createSpinner(message) {
20749
20759
  };
20750
20760
  }
20751
20761
  function badge(text, bgColor, fgColor = FG.white) {
20752
- if (!isColorSupported)
20762
+ if (!isColorSupported) {
20753
20763
  return `[${text}]`;
20764
+ }
20754
20765
  return `${bgColor}${fgColor}${BOLD} ${text} ${RESET}`;
20755
20766
  }
20756
20767
  function logError(header, message = null) {
@@ -20883,12 +20894,26 @@ async function getExpectedWrittenPath(basePath, fileName) {
20883
20894
  : path.join(process.cwd(), basePath, fileName);
20884
20895
  return filePath;
20885
20896
  }
20886
- async function writeFile(basePath, fileName, content) {
20897
+ async function safeReadContent(filePath) {
20898
+ try {
20899
+ return await fs.readFile(filePath, 'utf-8');
20900
+ }
20901
+ catch (e) {
20902
+ return '';
20903
+ }
20904
+ }
20905
+ async function writeFile(basePath, fileName, content, append = false) {
20887
20906
  const isAbsolutePath = path.isAbsolute(basePath);
20888
20907
  const filePath = isAbsolutePath
20889
20908
  ? path.join(basePath, fileName)
20890
20909
  : path.join(process.cwd(), basePath, fileName);
20891
- await fs.writeFile(filePath, content);
20910
+ if (append) {
20911
+ const existingData = await safeReadContent(filePath);
20912
+ await fs.writeFile(filePath, content + existingData);
20913
+ }
20914
+ else {
20915
+ await fs.writeFile(filePath, content);
20916
+ }
20892
20917
  }
20893
20918
  async function readYaml(filePath) {
20894
20919
  const fileData = await readFile(filePath, true, true);
@@ -20986,7 +21011,7 @@ function handleErrors(error) {
20986
21011
  }
20987
21012
  }
20988
21013
 
20989
- let config$2 = null;
21014
+ let config$3 = null;
20990
21015
  let specFileData = null;
20991
21016
  let objectSyntaxTemplate = null;
20992
21017
  let exporterModuleSyntaxTemplate = null;
@@ -20997,13 +21022,13 @@ let allOfSyntaxTemplate = null;
20997
21022
  const cachedReferencedTypes = new Map();
20998
21023
  let expectedOutputFiles = null;
20999
21024
  function setConfig(newConfig) {
21000
- config$2 = newConfig;
21025
+ config$3 = newConfig;
21001
21026
  }
21002
21027
  function getConfig() {
21003
- if (config$2 === null) {
21028
+ if (config$3 === null) {
21004
21029
  throw new RuntimeError('Failed to load configuration!');
21005
21030
  }
21006
- return config$2;
21031
+ return config$3;
21007
21032
  }
21008
21033
  function setSpecFileData(newSpecFileData) {
21009
21034
  specFileData = newSpecFileData;
@@ -21072,10 +21097,10 @@ function cacheReferenceType(referencePath, generatedData) {
21072
21097
  cachedReferencedTypes.set(referencePath, generatedData);
21073
21098
  }
21074
21099
  function getInputFilePath(absolutePath = true) {
21075
- if (config$2 === null) {
21100
+ if (config$3 === null) {
21076
21101
  throw new RuntimeError('Spec file path not set!');
21077
21102
  }
21078
- return absolutePath ? resolveFilePath(config$2.input) : config$2.input;
21103
+ return absolutePath ? resolveFilePath(config$3.input) : config$3.input;
21079
21104
  }
21080
21105
  function getAllOfTemplate() {
21081
21106
  if (allOfSyntaxTemplate === null) {
@@ -21155,16 +21180,19 @@ function getReferencedTypeModules(_referencedTypes, _writtenAt) {
21155
21180
  return [];
21156
21181
  }
21157
21182
  const expectedOutputFiles = Runtime.getExpectedOutputFiles();
21183
+ const modulePathConfig = Runtime.getConfig().language.modulePathConfig;
21158
21184
  const referencedTypeModules = {};
21159
21185
  for (const referenceType of referencedTypes) {
21160
21186
  const outputFile = expectedOutputFiles.get(referenceType);
21161
21187
  if (typeof outputFile !== 'undefined' &&
21162
21188
  resolveFilePath(outputFile.filePath) !== resolveFilePath(writtenAt)) {
21163
21189
  if (typeof referencedTypeModules[outputFile.modulePath] === 'undefined') {
21190
+ const rawRelativePath = generateRelativePath(writtenAt, outputFile.modulePath);
21164
21191
  referencedTypeModules[outputFile.modulePath] = {
21165
21192
  modulePath: outputFile.modulePath,
21166
- moduleRelativePath: generateRelativePath(writtenAt, outputFile.modulePath),
21167
- referencedTypes: [referenceType]
21193
+ moduleRelativePath: formatModulePath(rawRelativePath, writtenAt, modulePathConfig),
21194
+ referencedTypes: [referenceType],
21195
+ moduleName: outputFile.modulePath.split('/').pop() ?? ''
21168
21196
  };
21169
21197
  }
21170
21198
  else {
@@ -21191,6 +21219,21 @@ function toPascalCaseHelper(input) {
21191
21219
  }
21192
21220
  return toPascalCase(inputString);
21193
21221
  }
21222
+ function toSnakeCase(input) {
21223
+ return input
21224
+ .replace(/[-]/g, '_')
21225
+ .replace(/(?<upper>[A-Z]+)(?<next>[A-Z][a-z])/g, '$1_$2')
21226
+ .replace(/(?<lower>[a-z\d])(?<cap>[A-Z])/g, '$1_$2')
21227
+ .toLowerCase()
21228
+ .replace(/__+/g, '_');
21229
+ }
21230
+ function toSnakeCaseHelper(input) {
21231
+ const inputString = decodeString(input);
21232
+ if (inputString === null) {
21233
+ return input;
21234
+ }
21235
+ return toSnakeCase(inputString);
21236
+ }
21194
21237
  function refineJSONKey(input) {
21195
21238
  if (typeof input === 'string' && input.includes('-')) {
21196
21239
  return `'${input}'`;
@@ -21216,6 +21259,7 @@ function registerTemplateHelpers() {
21216
21259
  Handlebars.registerHelper('getReferencedTypes', getReferencedTypes);
21217
21260
  Handlebars.registerHelper('getReferencedTypeModules', getReferencedTypeModules);
21218
21261
  Handlebars.registerHelper('toPascalCase', toPascalCaseHelper);
21262
+ Handlebars.registerHelper('toSnakeCase', toSnakeCaseHelper);
21219
21263
  Handlebars.registerHelper('isNonEmptyArray', (value) => Array.isArray(value) && value.length === 0);
21220
21264
  Handlebars.registerHelper('eq', (value1, value2) => value1 === value2);
21221
21265
  Handlebars.registerHelper('notEq', (value1, value2) => value1 !== value2);
@@ -21223,6 +21267,7 @@ function registerTemplateHelpers() {
21223
21267
  Handlebars.registerHelper('jsonKey', refineJSONKey);
21224
21268
  Handlebars.registerHelper('variableName', refineVariableName);
21225
21269
  Handlebars.registerHelper('indexKey', refineIndexKey);
21270
+ Handlebars.registerHelper('stringify', (value) => JSON.stringify(value));
21226
21271
  Handlebars.registerHelper('not', (value) => {
21227
21272
  if (typeof value === 'boolean') {
21228
21273
  return !value;
@@ -21265,6 +21310,30 @@ function generateRelativePath(fromPath, toPath) {
21265
21310
  pathPrefix = pathPrefix === '' ? './' : pathPrefix;
21266
21311
  return pathPrefix + toPathArray.slice(diffIndex).join('/');
21267
21312
  }
21313
+ function formatModulePath(relativePath, writtenAt, config) {
21314
+ let path = relativePath.replace(/^\.\//, '');
21315
+ const isModuleFile = writtenAt.endsWith('/' + config.moduleFileName) ||
21316
+ writtenAt.endsWith('\\' + config.moduleFileName);
21317
+ let parentCount = config.fileBasedModules && !isModuleFile ? 1 : 0;
21318
+ while (path.startsWith('../')) {
21319
+ parentCount++;
21320
+ path = path.slice(3);
21321
+ }
21322
+ const moduleFileRegex = new RegExp(`\\/?${config.moduleFileName}$`);
21323
+ path = path.replace(moduleFileRegex, '').replace(/\/$/, '');
21324
+ const parentPart = parentCount > 0 ? Array(parentCount).fill(config.parentRef).join(config.separator) : '';
21325
+ if (path === '' && parentPart === '') {
21326
+ return config.selfRef;
21327
+ }
21328
+ if (path === '') {
21329
+ return parentPart;
21330
+ }
21331
+ const formattedPath = path.replace(/\//g, config.separator);
21332
+ if (parentPart === '') {
21333
+ return formattedPath;
21334
+ }
21335
+ return parentPart + config.separator + formattedPath;
21336
+ }
21268
21337
  // #region string utils
21269
21338
  function stripPrefix(value, prefix) {
21270
21339
  return value.startsWith(prefix) ? value.slice(prefix.length) : value;
@@ -21417,21 +21486,23 @@ function getPrimitiveType(typeName, typeInfo) {
21417
21486
  return result;
21418
21487
  }
21419
21488
  async function generateAdditionalPropertiesType(typeName, typeInfo, parentTypes) {
21420
- let result = null;
21421
21489
  const stringKeyType = getPrimitiveType(typeName, {
21422
21490
  ...placeholderTypeInfo,
21423
21491
  type: 'string'
21424
21492
  }).templateInput.type;
21425
21493
  if (typeof typeInfo.additionalProperties === 'boolean') {
21426
- result = {
21427
- keyType: stringKeyType,
21428
- valueType: getPrimitiveType(typeName, {
21429
- ...placeholderTypeInfo,
21430
- type: 'unknown'
21431
- }).templateInput.type,
21432
- valueTypeReferenced: false,
21433
- valuePrimitiveType: 'unknown',
21434
- valueComposerType: null
21494
+ return {
21495
+ templateInput: {
21496
+ keyType: stringKeyType,
21497
+ valueType: getPrimitiveType(typeName, {
21498
+ ...placeholderTypeInfo,
21499
+ type: 'unknown'
21500
+ }).templateInput.type,
21501
+ valueTypeReferenced: false,
21502
+ valuePrimitiveType: 'unknown',
21503
+ valueComposerType: null
21504
+ },
21505
+ primitives: new Set()
21435
21506
  };
21436
21507
  }
21437
21508
  else if (valueIsKeyedAdditionalProperties(typeInfo.additionalProperties)) {
@@ -21439,17 +21510,20 @@ async function generateAdditionalPropertiesType(typeName, typeInfo, parentTypes)
21439
21510
  const generatedValueType = await generateType(typeName + 'ValueType', valueTypeInfo, parentTypes);
21440
21511
  const isReferenced = valueTypeInfo.$ref !== null;
21441
21512
  const isArray = valueTypeInfo.type === 'array';
21442
- result = {
21443
- keyType: getPrimitiveType(typeName, {
21444
- ...placeholderTypeInfo,
21445
- type: typeInfo.additionalProperties.keyType
21446
- }).templateInput.type,
21447
- valueType: generatedValueType.templateInput.type,
21448
- valueTypeReferenced: isReferenced,
21449
- valuePrimitiveType: isArray ? 'array' : (valueTypeInfo.type ?? 'object'),
21450
- valueComposerType: 'composerType' in generatedValueType.templateInput
21451
- ? (generatedValueType.templateInput.composerType ?? null)
21452
- : null
21513
+ return {
21514
+ templateInput: {
21515
+ keyType: getPrimitiveType(typeName, {
21516
+ ...placeholderTypeInfo,
21517
+ type: typeInfo.additionalProperties.keyType
21518
+ }).templateInput.type,
21519
+ valueType: generatedValueType.templateInput.type,
21520
+ valueTypeReferenced: isReferenced,
21521
+ valuePrimitiveType: isArray ? 'array' : (valueTypeInfo.type ?? 'object'),
21522
+ valueComposerType: 'composerType' in generatedValueType.templateInput
21523
+ ? (generatedValueType.templateInput.composerType ?? null)
21524
+ : null
21525
+ },
21526
+ primitives: generatedValueType.primitives
21453
21527
  };
21454
21528
  }
21455
21529
  else if (valueIsTypeInfo(typeInfo.additionalProperties)) {
@@ -21457,17 +21531,20 @@ async function generateAdditionalPropertiesType(typeName, typeInfo, parentTypes)
21457
21531
  const generatedValueType = await generateType(typeName + 'ValueType', valueTypeInfo, parentTypes);
21458
21532
  const isReferenced = valueTypeInfo.$ref !== null;
21459
21533
  const isArray = valueTypeInfo.type === 'array';
21460
- result = {
21461
- keyType: stringKeyType,
21462
- valueType: generatedValueType.templateInput.type,
21463
- valueTypeReferenced: isReferenced,
21464
- valuePrimitiveType: isArray ? 'array' : (valueTypeInfo.type ?? 'object'),
21465
- valueComposerType: 'composerType' in generatedValueType.templateInput
21466
- ? (generatedValueType.templateInput.composerType ?? null)
21467
- : null
21534
+ return {
21535
+ templateInput: {
21536
+ keyType: stringKeyType,
21537
+ valueType: generatedValueType.templateInput.type,
21538
+ valueTypeReferenced: isReferenced,
21539
+ valuePrimitiveType: isArray ? 'array' : (valueTypeInfo.type ?? 'object'),
21540
+ valueComposerType: 'composerType' in generatedValueType.templateInput
21541
+ ? (generatedValueType.templateInput.composerType ?? null)
21542
+ : null
21543
+ },
21544
+ primitives: generatedValueType.primitives
21468
21545
  };
21469
21546
  }
21470
- return result;
21547
+ return null;
21471
21548
  }
21472
21549
  async function generateObjectType(typeName, typeInfo, parentTypes) {
21473
21550
  const templateInput = {
@@ -21556,9 +21633,10 @@ async function generateObjectType(typeName, typeInfo, parentTypes) {
21556
21633
  };
21557
21634
  }
21558
21635
  // Generating additional property types
21559
- const additionalProperties = await generateAdditionalPropertiesType(typeName + 'AdditionalProperty', typeInfo, parentTypes);
21560
- if (additionalProperties !== null) {
21561
- templateInput.additionalProperties = additionalProperties;
21636
+ const additionalPropertiesResult = await generateAdditionalPropertiesType(typeName + 'AdditionalProperty', typeInfo, parentTypes);
21637
+ if (additionalPropertiesResult !== null) {
21638
+ templateInput.additionalProperties = additionalPropertiesResult.templateInput;
21639
+ primitives.push(...additionalPropertiesResult.primitives);
21562
21640
  }
21563
21641
  const result = {
21564
21642
  content: Runtime.getObjectTemplate()(templateInput) +
@@ -21926,7 +22004,8 @@ function generateExpectedOutputFile() {
21926
22004
  // Generating expect writing types for referenced types
21927
22005
  let referredGroups = {};
21928
22006
  for (const generatedRefData of Runtime.getCachedReferencedTypes().values()) {
21929
- if (typeof generatedRefData.sourceFile !== 'undefined' &&
22007
+ if (generatedRefData.type !== 'local' &&
22008
+ typeof generatedRefData.sourceFile !== 'undefined' &&
21930
22009
  generatedRefData.completeSource !== Runtime.getInputFilePath() // Preventing new file creation for remote references from base file
21931
22010
  ) {
21932
22011
  const groupName = toPascalCase(generatedRefData.sourceFile.replace('.yaml', ''));
@@ -21981,7 +22060,7 @@ async function writeTypesToFiles(config, types, folderName = '') {
21981
22060
  }
21982
22061
  return result;
21983
22062
  }
21984
- async function writeTypesToFile(config, types, fileName = 'types') {
22063
+ async function writeTypesToFile(config, types, fileName) {
21985
22064
  const templateInput = {
21986
22065
  referencedTypes: [],
21987
22066
  primitives: [],
@@ -22011,7 +22090,13 @@ async function writeExporterModules(files, folder) {
22011
22090
  const exporterModuleContent = Runtime.getExporterModuleTemplate()({
22012
22091
  modules: [...files].map((file) => file.replace(Runtime.getConfig().output.fileExtension, ''))
22013
22092
  });
22014
- await writeFile(folder, Runtime.getConfig().language.exporterModuleName + Runtime.getConfig().output.fileExtension, exporterModuleContent);
22093
+ const config = Runtime.getConfig();
22094
+ // Merging the contents of exporter module & types file in case their names are same.
22095
+ const typesFileName = Runtime.getConfig().output.typesFileName;
22096
+ const moduleExporterFileName = Runtime.getConfig().language.exporterModuleName;
22097
+ const writingTypesFile = files.has(typesFileName + config.output.fileExtension);
22098
+ const appendContent = writingTypesFile && typesFileName === moduleExporterFileName;
22099
+ await writeFile(folder, Runtime.getConfig().language.exporterModuleName + Runtime.getConfig().output.fileExtension, exporterModuleContent, appendContent);
22015
22100
  }
22016
22101
  async function writeOutput(generationResult) {
22017
22102
  const config = Runtime.getConfig();
@@ -22022,20 +22107,21 @@ async function writeOutput(generationResult) {
22022
22107
  const writtenFiles = new Map();
22023
22108
  // pre compute all the folders and files that will be written
22024
22109
  Runtime.setExpectedOutputFiles(generateExpectedOutputFile());
22025
- // writing types to output directory
22110
+ // #region writing types to output directory
22026
22111
  let typesFilesWritten = null;
22027
22112
  if (Object.keys(generationResult.types).length > 0) {
22028
22113
  if (config.output.writerMode.types === 'Files') {
22029
22114
  typesFilesWritten = await writeTypesToFiles(config, generationResult.types);
22030
22115
  }
22031
22116
  else if (config.output.writerMode.types === 'SingleFile') {
22032
- typesFilesWritten = await writeTypesToFile(config, generationResult.types);
22117
+ typesFilesWritten = await writeTypesToFile(config, generationResult.types, Runtime.getConfig().output.typesFileName);
22033
22118
  }
22034
22119
  }
22035
22120
  if (typesFilesWritten !== null) {
22036
22121
  addValuesToMappedSet(writtenFiles, await getCompleteFolderPath(typesFilesWritten.folderName), typesFilesWritten.files);
22037
22122
  }
22038
- // writing grouped types to output directory
22123
+ // #endregion
22124
+ // #region writing grouped types to output directory
22039
22125
  if (config.output.writerMode.groupedTypes === 'FolderWithFiles') {
22040
22126
  for (const groupName in generationResult.groupedTypes) {
22041
22127
  let groupFilesWritten = null;
@@ -22058,12 +22144,13 @@ async function writeOutput(generationResult) {
22058
22144
  }
22059
22145
  }
22060
22146
  }
22147
+ // #endregion
22061
22148
  writtenFiles.forEach((files, folder) => {
22062
22149
  void writeExporterModules(files, folder);
22063
22150
  });
22064
22151
  }
22065
22152
 
22066
- async function config$1(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode) {
22153
+ async function config$2(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode) {
22067
22154
  const devMode = 'PRODUCTION'.includes('DEVELOPMENT');
22068
22155
  // PRODUCTION will be replaced with PRODUCTION when package is built.
22069
22156
  const directoryPrefix = devMode ? 'src/' : './';
@@ -22079,6 +22166,7 @@ async function config$1(inputFilePath, outputDirectory, typesWriterMode, grouped
22079
22166
  cleanWrite: true,
22080
22167
  fileExtension: '.ts',
22081
22168
  directory: outputDirectory,
22169
+ typesFileName: 'types',
22082
22170
  writerMode: {
22083
22171
  groupedTypes: groupedTypesWriterMode,
22084
22172
  types: typesWriterMode
@@ -22102,13 +22190,20 @@ async function config$1(inputFilePath, outputDirectory, typesWriterMode, grouped
22102
22190
  array: '~ItemType~[]',
22103
22191
  object: 'type',
22104
22192
  unknown: 'unknown'
22193
+ },
22194
+ modulePathConfig: {
22195
+ separator: '/',
22196
+ parentRef: '..',
22197
+ selfRef: '.',
22198
+ moduleFileName: 'index',
22199
+ fileBasedModules: false
22105
22200
  }
22106
22201
  }
22107
22202
  };
22108
22203
  return config;
22109
22204
  }
22110
22205
 
22111
- async function config(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode) {
22206
+ async function config$1(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode) {
22112
22207
  const devMode = 'PRODUCTION'.includes('DEVELOPMENT');
22113
22208
  const directoryPrefix = devMode ? 'src/' : './';
22114
22209
  const objectSyntax = await readFile(directoryPrefix + 'templates/typescript-with-decoders/object-syntax.hbs', devMode);
@@ -22123,6 +22218,7 @@ async function config(inputFilePath, outputDirectory, typesWriterMode, groupedTy
22123
22218
  cleanWrite: true,
22124
22219
  fileExtension: '.ts',
22125
22220
  directory: outputDirectory,
22221
+ typesFileName: 'types',
22126
22222
  writerMode: {
22127
22223
  groupedTypes: groupedTypesWriterMode,
22128
22224
  types: typesWriterMode
@@ -22146,6 +22242,66 @@ async function config(inputFilePath, outputDirectory, typesWriterMode, groupedTy
22146
22242
  array: '~ItemType~[]',
22147
22243
  object: 'type',
22148
22244
  unknown: 'unknown'
22245
+ },
22246
+ modulePathConfig: {
22247
+ separator: '/',
22248
+ parentRef: '..',
22249
+ selfRef: '.',
22250
+ moduleFileName: 'index',
22251
+ fileBasedModules: false
22252
+ }
22253
+ }
22254
+ };
22255
+ return config;
22256
+ }
22257
+
22258
+ async function config(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode) {
22259
+ const devMode = 'PRODUCTION'.includes('DEVELOPMENT');
22260
+ // PRODUCTION will be replaced with PRODUCTION when package is built.
22261
+ const directoryPrefix = devMode ? 'src/' : './';
22262
+ const objectSyntax = await readFile(directoryPrefix + 'templates/rust/object-syntax.hbs', devMode);
22263
+ const exporterModuleSyntax = await readFile(directoryPrefix + 'templates/rust/exporter-module-syntax.hbs', devMode);
22264
+ const typesFileSyntax = await readFile(directoryPrefix + 'templates/rust/types-file-syntax.hbs', devMode);
22265
+ const enumSyntax = await readFile(directoryPrefix + 'templates/rust/enum-syntax.hbs', devMode);
22266
+ const oneOfSyntax = await readFile(directoryPrefix + 'templates/rust/oneOf-syntax.hbs', devMode);
22267
+ const allOfSyntax = await readFile(directoryPrefix + 'templates/rust/allOf-syntax.hbs', devMode);
22268
+ const config = {
22269
+ input: inputFilePath,
22270
+ output: {
22271
+ cleanWrite: true,
22272
+ fileExtension: '.rs',
22273
+ directory: outputDirectory,
22274
+ typesFileName: 'mod',
22275
+ writerMode: {
22276
+ groupedTypes: groupedTypesWriterMode,
22277
+ types: typesWriterMode
22278
+ }
22279
+ },
22280
+ template: {
22281
+ objectSyntax,
22282
+ exporterModuleSyntax,
22283
+ typesFileSyntax,
22284
+ enumSyntax,
22285
+ oneOfSyntax,
22286
+ allOfSyntax
22287
+ },
22288
+ language: {
22289
+ exporterModuleName: 'mod',
22290
+ typeMapper: {
22291
+ string: { default: 'String' },
22292
+ number: { default: 'i32' },
22293
+ integer: { default: 'i32' },
22294
+ boolean: 'bool',
22295
+ array: 'Vec<~ItemType~>',
22296
+ object: 'type',
22297
+ unknown: 'String'
22298
+ },
22299
+ modulePathConfig: {
22300
+ separator: '::',
22301
+ parentRef: 'super',
22302
+ selfRef: 'self',
22303
+ moduleFileName: 'mod',
22304
+ fileBasedModules: true
22149
22305
  }
22150
22306
  }
22151
22307
  };
@@ -22183,9 +22339,12 @@ async function runner(language, inputFilePath, outputDirectory, _typesWriterMode
22183
22339
  let generatorConfig = null;
22184
22340
  switch (language.toLowerCase()) {
22185
22341
  case 'typescript':
22186
- generatorConfig = await config$1(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode);
22342
+ generatorConfig = await config$2(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode);
22187
22343
  break;
22188
22344
  case 'typescript-with-decoders':
22345
+ generatorConfig = await config$1(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode);
22346
+ break;
22347
+ case 'rust':
22189
22348
  generatorConfig = await config(inputFilePath, outputDirectory, typesWriterMode, groupedTypesWriterMode);
22190
22349
  break;
22191
22350
  default:
@@ -22215,7 +22374,7 @@ async function runner(language, inputFilePath, outputDirectory, _typesWriterMode
22215
22374
  }
22216
22375
  }
22217
22376
  greeting();
22218
- const program = new Command().version('0.12.1');
22377
+ const program = new Command().version('0.13.0');
22219
22378
  program
22220
22379
  .command('generate')
22221
22380
  .description('Generate types for your language from a type spec file')
@@ -1,2 +1,3 @@
1
1
  export * as typescript from './typescript';
2
2
  export * as typescriptWithDecoders from './typescript-with-decoders';
3
+ export * as rust from './rust';
@@ -0,0 +1,26 @@
1
+ {{#if description}}
2
+ /// {{{description}}}
3
+ {{/if}}
4
+ #[derive(Debug, Clone, Serialize, Deserialize)]
5
+ pub struct {{typeName}} {
6
+ {{#each compositions}}
7
+ {{#if (eq this.source 'referenced')}}
8
+ #[serde(flatten)]
9
+ pub {{toSnakeCase this.referencedType}}: {{this.referencedType}},
10
+ {{else if (eq this.source 'inline')}}
11
+ {{#if (eq this.dataType 'object')}}
12
+ #[serde(flatten)]
13
+ pub {{toSnakeCase this.templateInput.typeName}}: {{this.templateInput.typeName}},
14
+ {{else}}
15
+ pub {{toSnakeCase this.templateInput.typeName}}: {{{this.templateInput.type}}},
16
+ {{/if}}
17
+ {{/if}}
18
+ {{/each}}
19
+ }
20
+ {{#each compositions}}
21
+ {{#if (eq this.source 'inline')}}
22
+ {{#if (eq this.dataType 'object')}}
23
+ {{{this.content}}}
24
+ {{/if}}
25
+ {{/if}}
26
+ {{/each}}
@@ -0,0 +1,17 @@
1
+ {{#if description}}
2
+ /// {{{description}}}
3
+ {{/if}}
4
+ {{#if example}}
5
+ /// Example: {{{example}}}
6
+ {{/if}}
7
+ #[derive(Debug, Clone, Serialize, Deserialize)]
8
+ pub enum {{typeName}} {
9
+ {{#each values}}
10
+ {{#if (eq ../type 'string')}}
11
+ #[serde(rename = "{{this}}")]
12
+ {{{toPascalCase this}}},
13
+ {{else}}
14
+ Value{{{this}}},
15
+ {{/if}}
16
+ {{/each}}
17
+ }
@@ -0,0 +1,3 @@
1
+ {{#each modules}}
2
+ pub mod {{this}};
3
+ {{/each}}
@@ -0,0 +1,2 @@
1
+ import type { Configuration, TypesWriterMode, GroupedTypesWriterMode } from '$types';
2
+ export declare function config(inputFilePath: string, outputDirectory: string, typesWriterMode: TypesWriterMode, groupedTypesWriterMode: GroupedTypesWriterMode): Promise<Configuration>;
@@ -0,0 +1,23 @@
1
+ {{#if description}}
2
+ /// {{{description}}}
3
+ {{/if}}
4
+ {{#if example}}
5
+ /// Example: {{{example}}}
6
+ {{/if}}
7
+ #[derive(Debug, Clone, Serialize, Deserialize)]
8
+ pub struct {{typeName}} {
9
+ {{#each properties}}
10
+ {{#if this.description}}
11
+ /// {{{this.description}}}
12
+ {{/if}}
13
+ #[serde(rename = "{{@key}}")]
14
+ {{#unless this.required}}
15
+ #[serde(skip_serializing_if = "Option::is_none")]
16
+ {{/unless}}
17
+ pub {{toSnakeCase @key}}: {{#unless this.required}}Option<{{/unless}}{{{this.type}}}{{#unless this.required}}>{{/unless}},
18
+ {{/each}}
19
+ {{#if additionalProperties}}
20
+ #[serde(flatten)]
21
+ pub additional_properties: std::collections::HashMap<{{{additionalProperties.keyType}}}, {{{additionalProperties.valueType}}}>,
22
+ {{/if}}
23
+ }
@@ -0,0 +1,25 @@
1
+ {{#if description}}
2
+ /// {{{description}}}
3
+ {{/if}}
4
+ #[derive(Debug, Clone, Serialize, Deserialize)]
5
+ #[serde(untagged)]
6
+ pub enum {{typeName}} {
7
+ {{#each compositions}}
8
+ {{#if (eq this.source 'referenced')}}
9
+ {{this.referencedType}}({{this.referencedType}}),
10
+ {{else if (eq this.source 'inline')}}
11
+ {{#if this.templateInput.values}}
12
+ {{this.templateInput.typeName}}({{this.templateInput.typeName}}),
13
+ {{else if (eq this.dataType 'object')}}
14
+ {{this.templateInput.typeName}}({{this.templateInput.typeName}}),
15
+ {{else}}
16
+ {{this.templateInput.typeName}}({{{this.templateInput.type}}}),
17
+ {{/if}}
18
+ {{/if}}
19
+ {{/each}}
20
+ }
21
+ {{#each compositions}}
22
+ {{#if (eq this.source 'inline')}}
23
+ {{{this.content}}}
24
+ {{/if}}
25
+ {{/each}}
@@ -0,0 +1,8 @@
1
+ use serde::{Serialize, Deserialize};
2
+ {{#each (getReferencedTypeModules referencedTypes writtenAt)}}
3
+ {{#each this.referencedTypes}}
4
+ use {{{../moduleRelativePath}}}::{{this}}::{{this}};
5
+ {{/each}}
6
+ {{/each}}
7
+
8
+ {{{typesContent}}}
@@ -9,6 +9,7 @@ export type OutputConfig = {
9
9
  cleanWrite: boolean;
10
10
  fileExtension: string;
11
11
  directory: string;
12
+ typesFileName: string;
12
13
  writerMode: {
13
14
  groupedTypes: GroupedTypesWriterMode;
14
15
  types: TypesWriterMode;
@@ -24,9 +25,17 @@ export type Template = {
24
25
  oneOfSyntax: string;
25
26
  allOfSyntax: string;
26
27
  };
28
+ export type ModulePathConfig = {
29
+ separator: string;
30
+ parentRef: string;
31
+ selfRef: string;
32
+ moduleFileName: string;
33
+ fileBasedModules: boolean;
34
+ };
27
35
  export type LanguageConfig = {
28
36
  exporterModuleName: string;
29
37
  typeMapper: LanguageTypeMapper;
38
+ modulePathConfig: ModulePathConfig;
30
39
  };
31
40
  /**
32
41
  * @description Mappers for all the types supported by OpenAPI 3.0.0
@@ -122,6 +131,10 @@ export type AdditionalPropertiesTemplateInput = {
122
131
  valuePrimitiveType: string;
123
132
  valueComposerType: string | null;
124
133
  };
134
+ export type AdditionalPropertiesGenerationResult = {
135
+ templateInput: AdditionalPropertiesTemplateInput;
136
+ primitives: Set<string>;
137
+ };
125
138
  export type ExporterModuleTemplateInput = {
126
139
  modules: string[];
127
140
  };
@@ -135,6 +148,7 @@ export type ReferencedModule = {
135
148
  modulePath: string;
136
149
  moduleRelativePath: string;
137
150
  referencedTypes: string[];
151
+ moduleName: string;
138
152
  };
139
153
  export type EnumTemplateInput = TypeDescriptors & {
140
154
  typeName: string;
@@ -5,5 +5,5 @@ export declare function createFolder(folderPath: string): Promise<void>;
5
5
  export declare function deleteFolder(folderPath: string): Promise<void>;
6
6
  export declare function getCompleteFolderPath(folderName: string): Promise<string>;
7
7
  export declare function getExpectedWrittenPath(basePath: string, fileName: string): Promise<string>;
8
- export declare function writeFile(basePath: string, fileName: string, content: string): Promise<void>;
8
+ export declare function writeFile(basePath: string, fileName: string, content: string, append?: boolean): Promise<void>;
9
9
  export declare function readYaml(filePath: string): Promise<unknown>;
@@ -1,4 +1,4 @@
1
- import { type TypeInfo, type TypeDataType } from '$types';
1
+ import { type ModulePathConfig, 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';
@@ -9,12 +9,15 @@ export declare function getReferencedTypes(object: unknown): string[];
9
9
  export declare function getReferencedTypeModules(_referencedTypes: unknown, _writtenAt: string): unknown[];
10
10
  export declare function toPascalCase(input: string): string;
11
11
  export declare function toPascalCaseHelper(input: unknown): string | unknown;
12
+ export declare function toSnakeCase(input: string): string;
13
+ export declare function toSnakeCaseHelper(input: unknown): string | unknown;
12
14
  export declare function refineJSONKey(input: unknown): unknown;
13
15
  export declare function refineVariableName(input: unknown): unknown;
14
16
  export declare function refineIndexKey(input: unknown): unknown;
15
17
  export declare function registerTemplateHelpers(): void;
16
18
  export declare function readNestedValue(json: unknown, keyPath: string[]): JSONObject;
17
19
  export declare function generateRelativePath(fromPath: string, toPath: string): string;
20
+ export declare function formatModulePath(relativePath: string, writtenAt: string, config: ModulePathConfig): string;
18
21
  export declare function stripPrefix(value: string, prefix: string): string;
19
22
  export declare function isPrimitiveType(typeInfo: TypeInfo): boolean;
20
23
  export declare function isSimplePrimitiveType(type: TypeDataType | null): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-crafter",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
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",
@@ -49,12 +49,12 @@
49
49
  "@rollup/plugin-node-resolve": "^16.0.3",
50
50
  "@rollup/plugin-replace": "^6.0.3",
51
51
  "@rollup/plugin-typescript": "^12.3.0",
52
- "@types/node": "^25.2.0",
53
- "@typescript-eslint/eslint-plugin": "^8.54.0",
54
- "@typescript-eslint/parser": "^8.54.0",
52
+ "@types/node": "^25.2.3",
53
+ "@typescript-eslint/eslint-plugin": "^8.55.0",
54
+ "@typescript-eslint/parser": "^8.55.0",
55
55
  "commander": "^14.0.3",
56
- "eslint": "^9.39.2",
57
- "eslint-config-love": "^149.0.0",
56
+ "eslint": "^10.0.0",
57
+ "eslint-config-love": "^150.0.0",
58
58
  "eslint-config-prettier": "^10.1.8",
59
59
  "handlebars": "^4.7.8",
60
60
  "husky": "^9.1.7",
@@ -62,7 +62,7 @@
62
62
  "rollup": "^4.57.1",
63
63
  "rollup-plugin-copy": "^3.5.0",
64
64
  "tslib": "^2.8.1",
65
- "type-decoder": "^2.2.0",
65
+ "type-decoder": "^2.3.0",
66
66
  "typescript": "^5.9.3",
67
67
  "yaml": "^2.8.2"
68
68
  }