feat: improve monorepo dev experience

This commit is contained in:
2023-10-30 17:31:40 +01:00
parent f65fb52def
commit 8466976b3c
59 changed files with 157 additions and 2662 deletions

View File

@@ -31,6 +31,7 @@
},
"dependencies": {
"@openstapps/tsup-plugin": "workspace:*",
"@types/json-schema": "7.0.14",
"deepmerge": "4.3.1",
"ts-json-schema-generator": "1.4.0"
},
@@ -41,7 +42,6 @@
"@types/chai": "4.3.5",
"@types/fs-extra": "9.0.13",
"@types/glob": "8.0.1",
"@types/json-schema": "7.0.14",
"@types/mocha": "10.0.1",
"@types/mustache": "4.2.2",
"@types/node": "18.15.3",
@@ -58,7 +58,6 @@
},
"tsup": {
"entry": [
"src/app.ts",
"src/index.ts"
],
"sourcemap": true,

View File

@@ -0,0 +1,5 @@
declare module 'schema:*' {
import {JSONSchema7} from 'json-schema';
const schema: JSONSchema7;
export default schema;
}

View File

@@ -1,10 +1,11 @@
import {createGenerator, SchemaGenerator} from 'ts-json-schema-generator';
import {createGenerator} from 'ts-json-schema-generator';
import {getValidatableTypes} from './get-validatable-types.js';
import {JSONSchema7} from 'json-schema';
/**
* Compile the JSON schema for a path
*/
export function compileSchema(path: string, tsconfig: string): ReturnType<SchemaGenerator['createSchema']> {
export function compileSchema(path: string, tsconfig: string): [schma: JSONSchema7, type: string] {
const generator = createGenerator({
path,
tsconfig,
@@ -23,5 +24,11 @@ export function compileSchema(path: string, tsconfig: string): ReturnType<Schema
Object.assign(fullSchema.definitions, generator.createSchema(schema).definitions);
}
return fullSchema;
const schemaTypes = `import {JSONSchema7} from 'json-schema';\n\nexport interface SchemaMap {\n${[
...schemaNames,
]
.map(schemaName => ` '${schemaName}': core.${schemaName};`)
.join('\n')}\n}\n\nconst schema: JSONSchema7;\nexport default schema;`;
return [fullSchema, schemaTypes];
}

View File

@@ -5,7 +5,7 @@ import {ts} from 'ts-json-schema-generator';
*/
export function getValidatableTypes(program: ts.Program) {
const checker = program.getTypeChecker();
const declarationNames: string[] = [];
const declarationNames = new Set<string>();
for (const sourceFile of program.getSourceFiles()) {
const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile);
@@ -16,7 +16,7 @@ export function getValidatableTypes(program: ts.Program) {
const name = ts.getNameOfDeclaration(declaration);
if (name && validatableTags.length > 0) {
declarationNames.push(name.getText());
declarationNames.add(name.getText());
}
}
}

View File

@@ -1,10 +1,44 @@
import {compileSchema} from './generator/compile-schema.js';
import {generateFiles, Plugin, PluginContext} from '@openstapps/tsup-plugin';
import {JSONSchema7} from 'json-schema';
import {Plugin as EsbuildPlugin} from 'esbuild';
import {createGenerator} from 'ts-json-schema-generator';
export type SchemaConsumer = (
this: PluginContext,
schema: ReturnType<typeof compileSchema>,
) => Record<string, string | Buffer>;
export type SchemaConsumer = (this: PluginContext, schema: JSONSchema7) => Record<string, string | Buffer>;
export const jsonSchema: EsbuildPlugin = {
name: 'json-schema',
setup(build) {
const fileRegex = /^schema:/;
const namespace = 'json-schema-ns';
const schemas = new Map<string, string>();
build.onResolve({filter: fileRegex}, ({path, importer}) => {
const [from, name] = path.replace(fileRegex, '').split('#', 1);
return {
path: `${from === 'file' ? importer : from}#${name}`,
namespace,
};
});
build.onLoad({filter: /.*/, namespace}, ({path}) => {
if (!schemas.has(path)) {
const [sourcePath, schemaName] = path.split('#', 1);
const generator = createGenerator({
path: sourcePath,
extraTags: ['elasticsearch'],
skipTypeCheck: true,
});
schemas.set(path, JSON.stringify(generator.createSchema(schemaName)));
}
return {
contents: schemas.get(path),
loader: 'json',
};
});
},
};
/**
* TSUp plugin for generating JSONSchema files
@@ -18,12 +52,17 @@ export function jsonSchemaPlugin(
return {
name: 'json-schema-generator',
async buildEnd() {
let schema: ReturnType<typeof compileSchema>;
let schema: JSONSchema7;
await generateFiles('JSON-Schema', async function () {
schema = compileSchema((this.options.entry as string[])[0], this.options.tsconfig!);
const [jsonSchema, types] = compileSchema(
(this.options.entry as string[])[0],
this.options.tsconfig!,
);
schema = jsonSchema;
return {
[schemaName]: JSON.stringify(schema),
[schemaName]: JSON.stringify(jsonSchema),
[`${schemaName.replace(/\.json$/, '')}.d.ts`]: types,
};
}).call(this);
@@ -35,3 +74,5 @@ export function jsonSchemaPlugin(
},
};
}
export {compileSchema} from './generator/compile-schema.js';

View File

@@ -0,0 +1,84 @@
/* eslint-disable unicorn/prefer-module */
/*
* Copyright (C) 2018-2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Logger} from 'packages/logger/lib/index.js';
import {expect} from 'chai';
import {Converter} from '../../core-tools/src/schema.js';
import path from 'path';
import {fileURLToPath} from 'url';
process.on('unhandledRejection', (error: unknown) => {
if (error instanceof Error) {
void Logger.error('UNHANDLED REJECTION', error.stack);
}
process.exit(1);
});
describe('Schema', function () {
this.timeout(40_000);
this.slow(10_000);
it('should create schema', function () {
const converter = new Converter(
path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'src', 'resources'),
);
const schema = converter.getSchema('Foo', '0.0.1');
expect(schema).to.be.deep.equal({
$id: 'https://core.stapps.tu-berlin.de/v0.0.1/lib/schema/Foo.json',
$schema: 'http://json-schema.org/draft-07/schema#',
additionalProperties: false,
definitions: {
FooType: {
description: 'This is a simple type declaration for usage in the Foo interface.',
const: 'Foo',
type: 'string',
},
SCFoo: {
additionalProperties: false,
description:
'This is a simple interface declaration for testing the schema generation and validation.',
properties: {
lorem: {
description: 'Dummy parameter',
enum: ['lorem', 'ipsum'],
type: 'string',
},
type: {
$ref: '#/definitions/FooType',
description: 'String literal type property',
},
},
required: ['lorem', 'type'],
type: 'object',
},
},
description: 'This is a simple interface declaration for testing the schema generation and validation.',
properties: {
lorem: {
description: 'Dummy parameter',
enum: ['lorem', 'ipsum'],
type: 'string',
},
type: {
$ref: '#/definitions/FooType',
description: 'String literal type property',
},
},
required: ['lorem', 'type'],
type: 'object',
});
});
});