/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ const { readFileSync, writeFileSync, mkdirSync } = require('fs'); const { resolve, dirname } = require('path'); /** * Generator the Angular CLI workspace schema file. */ function generate(inPath, outPath) { // While on paper we could use quicktype for this. // Quicktype doesn't handle `patternProperties` and `oneOf` that well. const jsonSchema = readFileSync(inPath, 'utf8'); const nestedDefinitions = {}; const schemaParsed = JSON.parse(jsonSchema, (key, value) => { if (key === '$ref' && typeof value === 'string' && !value.startsWith('#')) { // Resolve $ref and camelize key const definitionKey = value .replace(/(\.json|src)/g, '') .split(/\\|\/|_|-|\./) .filter((p) => !!p) .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) .join(''); const nestedSchemaPath = resolve(dirname(inPath), value); const nestedSchema = readFileSync(nestedSchemaPath, 'utf8'); const nestedSchemaJson = JSON.parse(nestedSchema, (key, value) => { switch (key) { case '$ref': if (value.startsWith('#/definitions/')) { return value.replace('#/definitions/', `#/definitions/${definitionKey}/definitions/`); } else { throw new Error(`Error while resolving $ref ${value} in ${nestedSchemaPath}.`); } case '$id': case 'id': case '$schema': case 'required': case 'x-prompt': case 'x-user-analytics': return undefined; default: return value; } }); nestedDefinitions[definitionKey] = nestedSchemaJson; return `#/definitions/${definitionKey}`; } return key === '' ? { ...value, definitions: { ...value.definitions, ...nestedDefinitions, }, } : value; }); const buildWorkspaceDirectory = process.env['BUILD_WORKSPACE_DIRECTORY'] || '.'; outPath = resolve(buildWorkspaceDirectory, outPath); mkdirSync(dirname(outPath), { recursive: true }); writeFileSync( outPath, JSON.stringify( schemaParsed, (key, value) => { if (key === 'x-deprecated') { // Needed for IDEs, and will be replaced to 'deprecated' later on. This must be a boolean. // https://json-schema.org/draft/2020-12/json-schema-validation.html#name-deprecated return !!value; } return value; }, 2, ).replace(/"x-deprecated"/g, '"deprecated"'), ); } if (require.main === module) { const argv = process.argv.slice(2); if (argv.length !== 2) { console.error('Must include 2 arguments.'); process.exit(1); } const [inPath, outPath] = argv; try { generate(inPath, outPath); } catch (error) { console.error('An error happened:'); console.error(error); process.exit(127); } } exports.generate = generate;