1+ import { fileURLToPath } from 'node:url' ;
12import { styleText } from 'node:util' ;
23import fs from 'fs' ;
34import jsonschema from 'jsonschema' ;
@@ -6,6 +7,7 @@ import shell from 'shelljs';
67import YAML from 'js-yaml' ;
78import marky from 'marky' ;
89import { createRequire } from 'module' ;
10+ import { compile , toSafeIdentifier } from 'json-schema-to-typescript-lite' ;
911
1012import fetchTranslations from './translations.js' ;
1113
@@ -186,7 +188,8 @@ function processData(options, type) {
186188 minifyJSON ( distDir + '/preset_defaults.json' , distDir + '/preset_defaults.min.json' ) ,
187189 minifyJSON ( distDir + '/deprecated.json' , distDir + '/deprecated.min.json' ) ,
188190 minifyJSON ( distDir + '/discarded.json' , distDir + '/discarded.min.json' ) ,
189- minifyJSON ( distDir + '/translations/' + sourceLocale + '.json' , distDir + '/translations/' + sourceLocale + '.min.json' )
191+ minifyJSON ( distDir + '/translations/' + sourceLocale + '.json' , distDir + '/translations/' + sourceLocale + '.min.json' ) ,
192+ generateTypeDefs ( distDir ) ,
190193 ] ;
191194
192195 if ( doFetchTranslations ) {
@@ -746,6 +749,91 @@ function generateIconsList(presets, fields, categories) {
746749 return Object . keys ( icons ) . sort ( ) ;
747750}
748751
752+ /** @param {string } string */
753+ const toPascalCase = string => string . replace ( / ( _ .| ^ .) / g, match => match . at ( - 1 ) . toUpperCase ( ) ) ;
754+
755+ /** @param {string } string */
756+ const createTypeIdentifier = ( string ) => toPascalCase ( toSafeIdentifier ( string ) ) ;
757+
758+
759+ /** @param {string } distDir */
760+ async function generateTypeDefs ( distDir ) {
761+ const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
762+ const inputFolder = path . join ( __dirname , '../schemas' ) ;
763+
764+ /**
765+ * Some generated files use plural names because they
766+ * export an object, e.g. `Fields = Record<string, Field>`
767+ * @type {Record<string, string> }
768+ */
769+ const KEY_MAP = {
770+ field : 'fields' ,
771+ preset : 'presets' ,
772+ preset_category : 'preset_categories' ,
773+ } ;
774+ const fileNames = fs . globSync ( path . join ( inputFolder , '/**/*.json' ) ) ;
775+
776+ /** @param {string } fileName */
777+ async function processFile ( fileName ) {
778+ const key = path . parse ( fileName ) . name ;
779+ const pluralKey = KEY_MAP [ key ] ;
780+
781+ const mainExport = createTypeIdentifier ( pluralKey || key ) ;
782+
783+ const output = [ '' ] ;
784+
785+ if ( pluralKey ) {
786+ output . push (
787+ `export interface ${ createTypeIdentifier ( pluralKey ) } {` ,
788+ ` [id: string]: ${ createTypeIdentifier ( key ) } ` ,
789+ '}'
790+ ) ;
791+ }
792+
793+ output . push (
794+ `declare const json: ${ mainExport } ;` ,
795+ 'export default json;'
796+ ) ;
797+
798+ const fileContent = JSON . parse ( await fs . promises . readFile ( fileName , 'utf8' ) ) ;
799+ if ( key === 'field' ) delete fileContent . anyOf ;
800+
801+ const tsFile = await compile ( fileContent , mainExport , {
802+ additionalProperties : false ,
803+ ignoreMinAndMaxItems : true ,
804+ cwd : path . join ( __dirname , '../schemas' ) ,
805+
806+ // ensure that the default export uses a consistent name
807+ customName : ( schema , fallback ) =>
808+ schema . $schema && schema . $id
809+ ? createTypeIdentifier ( path . parse ( schema . $id ) . name )
810+ : ( schema . $id || schema . title || fallback || '' ) ,
811+ } ) ;
812+
813+
814+ output . unshift ( tsFile ) ;
815+ output . push ( '' ) ;
816+ await fs . promises . writeFile (
817+ path . join ( distDir , `${ pluralKey || key } .d.json.ts` ) ,
818+ output . join ( '\n' ) ,
819+ ) ;
820+ }
821+ await Promise . all ( fileNames . map ( processFile ) ) ;
822+
823+ // finally, create the index file which re-exports everything
824+ // as named types.
825+ const indexFile = fileNames
826+ . map ( ( fileName ) => {
827+ const key = path . parse ( fileName ) . name ;
828+ return `export type * from './${ KEY_MAP [ key ] || key } .d.json.ts';` ;
829+ } )
830+ . join ( '\n' ) ;
831+
832+ await fs . promises . writeFile (
833+ path . join ( distDir , 'index.d.ts' ) ,
834+ indexFile + '\n' ,
835+ ) ;
836+ }
749837
750838function validateCategoryPresets ( categories , presets ) {
751839 Object . keys ( categories ) . forEach ( id => {
0 commit comments