Skip to content

Commit 3131e9e

Browse files
committed
feat: generate type definitions from the json schema
1 parent 54c2a7d commit 3131e9e

File tree

4 files changed

+264
-7
lines changed

4 files changed

+264
-7
lines changed

eslint.config.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export default [
3030
'indent': ['off', 4],
3131
'keyword-spacing': 'error',
3232
'linebreak-style': ['error', 'unix'],
33-
'no-await-in-loop': 'error',
3433
'no-caller': 'error',
3534
'no-catch-shadow': 'error',
3635
'no-console': 'warn',

lib/build.js

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ import fs from 'fs';
33
import { globSync } from 'glob';
44
import jsonschema from 'jsonschema';
55
import path from 'path';
6+
import { fileURLToPath } from 'url';
67
import shell from 'shelljs';
78
import YAML from 'js-yaml';
89
import marky from 'marky';
910
import { createRequire } from 'module';
11+
import { compileFromFile } from 'json-schema-to-typescript';
12+
import { toSafeString } from 'json-schema-to-typescript/dist/src/utils.js';
1013

1114
import fetchTranslations from './translations.js';
1215

@@ -187,7 +190,8 @@ function processData(options, type) {
187190
minifyJSON(distDir + '/preset_defaults.json', distDir + '/preset_defaults.min.json'),
188191
minifyJSON(distDir + '/deprecated.json', distDir + '/deprecated.min.json'),
189192
minifyJSON(distDir + '/discarded.json', distDir + '/discarded.min.json'),
190-
minifyJSON(distDir + '/translations/' + sourceLocale + '.json', distDir + '/translations/' + sourceLocale + '.min.json')
193+
minifyJSON(distDir + '/translations/' + sourceLocale + '.json', distDir + '/translations/' + sourceLocale + '.min.json'),
194+
generateTypeDefs(distDir),
191195
];
192196

193197
if (doFetchTranslations) {
@@ -693,6 +697,68 @@ function generateIconsList(presets, fields, categories) {
693697
return Object.keys(icons).sort();
694698
}
695699

700+
/** @param {string} distDir */
701+
async function generateTypeDefs(distDir) {
702+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
703+
const inputFolder = path.join(__dirname, '../schemas');
704+
705+
/**
706+
* Some generated files use plural names because they
707+
* export an object, e.g. `Fields = Record<string, Field>`
708+
*/
709+
const KEY_MAP = {
710+
field: 'fields',
711+
preset: 'presets',
712+
preset_category: 'preset_categories',
713+
};
714+
const fileNames = await fs.promises.readdir(inputFolder);
715+
716+
for (const fileName of fileNames) {
717+
const key = fileName.replace('.json', '');
718+
const pluralKey = KEY_MAP[key];
719+
720+
let base = `
721+
declare const json: ${toSafeString(pluralKey || key)};
722+
export default json;
723+
`;
724+
725+
if (pluralKey) {
726+
base += `
727+
export type ${toSafeString(pluralKey)} = {
728+
[id: string]: ${toSafeString(key)}
729+
};
730+
`;
731+
}
732+
733+
const tsFile = await compileFromFile(path.join(inputFolder, fileName), {
734+
additionalProperties: false,
735+
bannerComment: base,
736+
737+
// ensure that the default export uses a consistent name
738+
customName: (schema) =>
739+
schema.$schema && schema.$id ? path.parse(schema.$id).name : undefined,
740+
});
741+
742+
await fs.promises.writeFile(
743+
path.join(distDir, `${pluralKey || key}.d.json.ts`),
744+
tsFile,
745+
);
746+
}
747+
748+
// finally, create the index file which re-exports everything
749+
// as named types.
750+
const indexFile = fileNames
751+
.map((fileName) => {
752+
const key = fileName.replace('.json', '');
753+
return `export type * from './${KEY_MAP[key] || key}.d.json.ts';`;
754+
})
755+
.join('\n');
756+
757+
await fs.promises.writeFile(
758+
path.join(distDir, 'index.d.ts'),
759+
indexFile + '\n',
760+
);
761+
}
696762

697763
function validateCategoryPresets(categories, presets) {
698764
Object.keys(categories).forEach(id => {

0 commit comments

Comments
 (0)