1+ import { fileURLToPath } from 'node:url' ;
12import chalk from 'chalk' ;
23import fs from 'fs' ;
34import { globSync } from 'glob' ;
@@ -7,6 +8,7 @@ import shell from 'shelljs';
78import YAML from 'js-yaml' ;
89import marky from 'marky' ;
910import { createRequire } from 'module' ;
11+ import { compile , toSafeIdentifier } from 'json-schema-to-typescript-lite' ;
1012
1113import fetchTranslations from './translations.js' ;
1214
@@ -187,7 +189,8 @@ function processData(options, type) {
187189 minifyJSON ( distDir + '/preset_defaults.json' , distDir + '/preset_defaults.min.json' ) ,
188190 minifyJSON ( distDir + '/deprecated.json' , distDir + '/deprecated.min.json' ) ,
189191 minifyJSON ( distDir + '/discarded.json' , distDir + '/discarded.min.json' ) ,
190- minifyJSON ( distDir + '/translations/' + sourceLocale + '.json' , distDir + '/translations/' + sourceLocale + '.min.json' )
192+ minifyJSON ( distDir + '/translations/' + sourceLocale + '.json' , distDir + '/translations/' + sourceLocale + '.min.json' ) ,
193+ generateTypeDefs ( distDir ) ,
191194 ] ;
192195
193196 if ( doFetchTranslations ) {
@@ -693,6 +696,87 @@ function generateIconsList(presets, fields, categories) {
693696 return Object . keys ( icons ) . sort ( ) ;
694697}
695698
699+ /** @param {string } string */
700+ const toPascalCase = string => string . replace ( / ( _ .| ^ .) / g, match => match . at ( - 1 ) . toUpperCase ( ) ) ;
701+
702+ /** @param {string } string */
703+ const createTypeIdentifier = ( string ) => toPascalCase ( toSafeIdentifier ( string ) ) ;
704+
705+
706+ /** @param {string } distDir */
707+ async function generateTypeDefs ( distDir ) {
708+ const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
709+ const inputFolder = path . join ( __dirname , '../schemas' ) ;
710+
711+ /**
712+ * Some generated files use plural names because they
713+ * export an object, e.g. `Fields = Record<string, Field>`
714+ */
715+ const KEY_MAP = {
716+ field : 'fields' ,
717+ preset : 'presets' ,
718+ preset_category : 'preset_categories' ,
719+ } ;
720+ const fileNames = globSync ( path . join ( inputFolder , '/**/*.json' ) , { posix : true } ) ;
721+
722+ for ( const fileName of fileNames ) {
723+ const key = path . parse ( fileName ) . name ;
724+ const pluralKey = KEY_MAP [ key ] ;
725+
726+ const mainExport = createTypeIdentifier ( pluralKey || key ) ;
727+
728+ const output = [ '' ] ;
729+
730+ if ( pluralKey ) {
731+ output . push (
732+ `export interface ${ createTypeIdentifier ( pluralKey ) } {` ,
733+ ` [id: string]: ${ createTypeIdentifier ( key ) } ` ,
734+ '}'
735+ ) ;
736+ }
737+
738+ output . push (
739+ `declare const json: ${ mainExport } ;` ,
740+ 'export default json;'
741+ ) ;
742+
743+ const fileContent = JSON . parse ( await fs . promises . readFile ( fileName , 'utf8' ) ) ;
744+ if ( key === 'field' ) delete fileContent . anyOf ;
745+
746+ const tsFile = await compile ( fileContent , mainExport , {
747+ additionalProperties : false ,
748+ cwd : path . join ( __dirname , '../schemas' ) ,
749+
750+ // ensure that the default export uses a consistent name
751+ customName : ( schema , fallback ) =>
752+ schema . $schema && schema . $id
753+ ? createTypeIdentifier ( path . parse ( schema . $id ) . name )
754+ : ( schema . $id || schema . title || fallback || '' ) ,
755+ } ) ;
756+
757+
758+ output . unshift ( tsFile ) ;
759+ output . push ( '' ) ;
760+ await fs . promises . writeFile (
761+ path . join ( distDir , `${ pluralKey || key } .d.json.ts` ) ,
762+ output . join ( '\n' ) ,
763+ ) ;
764+ }
765+
766+ // finally, create the index file which re-exports everything
767+ // as named types.
768+ const indexFile = fileNames
769+ . map ( ( fileName ) => {
770+ const key = path . parse ( fileName ) . name ;
771+ return `export type * from './${ KEY_MAP [ key ] || key } .d.json.ts';` ;
772+ } )
773+ . join ( '\n' ) ;
774+
775+ await fs . promises . writeFile (
776+ path . join ( distDir , 'index.d.ts' ) ,
777+ indexFile + '\n' ,
778+ ) ;
779+ }
696780
697781function validateCategoryPresets ( categories , presets ) {
698782 Object . keys ( categories ) . forEach ( id => {
0 commit comments