diff --git a/lib/versioning/index.spec.ts b/lib/versioning/index.spec.ts index 8cb9aba9541ebe7f61783b0bd3640fb106fb8c15..9cebaf8a0fe11eb5bf3135a6fc65be2de3b1c2af 100644 --- a/lib/versioning/index.spec.ts +++ b/lib/versioning/index.spec.ts @@ -2,6 +2,12 @@ import * as allVersioning from '.'; import { getOptions } from '../config/definitions'; import { GenericVersioningApi, GenericVersion } from './loose/generic'; import * as semverVersioning from './semver'; +import { loadModules } from '../util/modules'; +import { + VersioningApi, + VersioningApiConstructor, + isVersioningApiConstructor, +} from './common'; const supportedSchemes = getOptions().find( option => option.name === 'versioning' @@ -11,6 +17,31 @@ describe('allVersioning.get(versioning)', () => { it('has api', () => { expect(Object.keys(allVersioning.get('semver')).sort()).toMatchSnapshot(); }); + it('validates', () => { + function validate( + module: VersioningApi | VersioningApiConstructor, + name: string + ): boolean { + // eslint-disable-next-line new-cap + const mod = isVersioningApiConstructor(module) ? new module() : module; + + // TODO: test required api + if (!mod.isValid || !mod.isVersion) { + fail(`Missing api on ${name}`); + } + + return true; + } + const vers = allVersioning.getVersionings(); + + const loadedVers = loadModules(__dirname); + expect(Array.from(vers.keys())).toEqual(Object.keys(loadedVers)); + + for (const name of vers.keys()) { + const ver = vers.get(name); + expect(validate(ver, name)).toBe(true); + } + }); it('should fallback to semver', () => { expect(allVersioning.get(undefined)).toBe( diff --git a/lib/versioning/index.ts b/lib/versioning/index.ts index f015d34f4ec7b635ab006bfa42d61812881a7bbc..66ca4d94039bfbcb61b22e951ac3112c491876e0 100644 --- a/lib/versioning/index.ts +++ b/lib/versioning/index.ts @@ -1,42 +1,26 @@ -import fs from 'fs'; import { logger } from '../logger'; import { VersioningApi, - VersioningApiConstructor, isVersioningApiConstructor, + VersioningApiConstructor, } from './common'; +import versionings from './api.generated'; export * from './common'; -const allVersioning: Record< +export const getVersioningList = (): string[] => Array.from(versionings.keys()); +/** + * Get versioning map. Can be used to dynamically add new versionig type + */ +export const getVersionings = (): Map< string, VersioningApi | VersioningApiConstructor -> = {}; - -const versioningList: string[] = []; - -export const getVersioningList = (): string[] => versioningList; - -const versionings = fs - .readdirSync(__dirname, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory() && !dirent.name.startsWith('_')) - .map(dirent => dirent.name) - .sort(); - -for (const versioning of versionings) { - try { - allVersioning[versioning] = require('./' + versioning).api; // eslint-disable-line - versioningList.push(versioning); - } catch (err) /* istanbul ignore next */ { - logger.fatal({ err }, `Can not load versioning "${versioning}".`); - process.exit(1); - } -} +> => versionings; export function get(versioning: string): VersioningApi { if (!versioning) { logger.debug('Missing versioning'); - return allVersioning.semver as VersioningApi; + return versionings.get('semver') as VersioningApi; } let versioningName: string; let versioningConfig: string; @@ -48,12 +32,11 @@ export function get(versioning: string): VersioningApi { } else { versioningName = versioning; } - const theVersioning = allVersioning[versioningName]; + const theVersioning = versionings.get(versioningName); if (!theVersioning) { logger.warn({ versioning }, 'Unknown versioning'); - return allVersioning.semver as VersioningApi; + return versionings.get('semver') as VersioningApi; } - // istanbul ignore if: needs an implementation if (isVersioningApiConstructor(theVersioning)) { // eslint-disable-next-line new-cap return new theVersioning(versioningConfig); diff --git a/tools/generate-imports.ts b/tools/generate-imports.ts index c3f470ddfaca7f768dc6113a4faa00cd9cbe36e0..5e1379890951439e924a5e00e03e1e868276a981 100644 --- a/tools/generate-imports.ts +++ b/tools/generate-imports.ts @@ -25,8 +25,41 @@ async function updateFile(file: string, code: string): Promise<void> { } newFiles.add(file); } + +async function generate({ + path, + types, + map = '', + excludes = [], +}: { + path: string; + types: string[]; + map?: string; + excludes?: string[]; +}): Promise<void> { + shell.echo(`> ${path}`); + let imports = ''; + let maps = ''; + for (const ds of findModules(`lib/${path}`).filter( + n => !excludes?.includes(n) + )) { + const name = _.camelCase(ds); + imports += `import * as ${name} from './${ds}';\n`; + maps += `api.set('${ds}', ${name}${map});\n`; + } + + const code = `import { ${types.join(', ')} } from './common'; + ${imports}\n + const api = new Map<string, ${types.join(' | ')}>(); + export default api; + ${maps}`; + + await updateFile(`lib/${path}/api.generated.ts`, code.replace(/^\s+/gm, '')); +} + (async () => { try { + // datasources shell.echo('> datasources'); let code = ` import { Datasource } from './common'; @@ -38,22 +71,15 @@ export default api; } await updateFile('lib/datasource/api.generated.ts', code); - shell.echo('> managers'); - let imports = ''; - let maps = ''; - for (const ds of findModules('lib/manager')) { - const name = _.camelCase(ds); - imports += `import * as ${name} from './${ds}';\n`; - maps += `api.set('${ds}', ${name});\n`; - } - - code = `import { ManagerApi } from './common'; -${imports} -const api = new Map<string, ManagerApi>(); -export default api; -${maps}`; + // managers + await generate({ path: 'manager', types: ['ManagerApi'] }); - await updateFile('lib/manager/api.generated.ts', code); + // versioning + await generate({ + path: 'versioning', + types: ['VersioningApi', 'VersioningApiConstructor'], + map: '.api', + }); await Promise.all( shell