UNPKG

@ui5/ts-interface-generator

Version:

Generator for TypeScript type definitions for custom UI5 controls implemented in TypeScript

125 lines 6.38 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.main = main; exports.getProgramInfo = getProgramInfo; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const typescript_1 = __importDefault(require("typescript")); const interfaceGenerationHelper_1 = require("./interfaceGenerationHelper"); const typeScriptEnvironment_1 = require("./typeScriptEnvironment"); const addSourceExports_1 = require("./addSourceExports"); const preferences_1 = __importDefault(require("./preferences")); const loglevel_1 = __importDefault(require("loglevel")); loglevel_1.default.setDefaultLevel("info"); // main entry point function main(args) { const watchMode = args.watch; preferences_1.default.set({ jsdoc: args.jsdoc, }); const level = args.loglevel; if (level === "error" || level === "warn" || level === "info" || level === "debug" || level === "trace") { loglevel_1.default.setDefaultLevel(level); loglevel_1.default.info(`Log level set to: ${level}`); } let tsconfig = args.config; let logFound = ""; if (!tsconfig || !fs_1.default.existsSync(tsconfig) || fs_1.default.lstatSync(tsconfig).isDirectory()) { // eslint-disable-next-line @typescript-eslint/unbound-method tsconfig = typescript_1.default.findConfigFile("./", typescript_1.default.sys.fileExists, "tsconfig.json"); if (!tsconfig) { throw new Error("Could not find a valid 'tsconfig.json'. Please specify it using the '--config' parameter."); } else { logFound = " (automatically found, as none was given)"; } } loglevel_1.default.info(`Using the following TypeScript configuration file${logFound}: ${tsconfig}`); (0, typeScriptEnvironment_1.initialize)(tsconfig, onTSProgramUpdate, { watchMode }); } /** * Extracts known local exports and relevant source files from program. * * @param program * @param typeChecker */ function getProgramInfo(program, typeChecker) { // this block collects all path mappings from the compiler configuration, so we can find out the logical name for a concrete file path const paths = program.getCompilerOptions().paths; const allPathMappings = []; // contains target/sourcePattern couples; built as array, not a map, to make sure duplicate targets for different patterns work as well if (paths) { for (let sourcePattern in paths) { // e.g. 'com/myorg/myUI5Library/*': [ 'src-ts/com/myorg/myUI5Library\*' ] const targets = paths[sourcePattern]; sourcePattern = path_1.default.normalize(sourcePattern); // e.g. 'com\myorg\myUI5Library\*' if (sourcePattern === "*") { sourcePattern = "."; } else if (sourcePattern.endsWith("*")) { sourcePattern = sourcePattern.slice(0, -1); } for (let i = 0; i < targets.length; i++) { let target = path_1.default.normalize(targets[i]); // e.g. 'src-ts\com\myorg\myUI5Library\*' if (target === "*") { target = "."; } else if (target.endsWith("*")) { target = target.slice(0, -1); } allPathMappings.push({ target, sourcePattern }); } } } const allRelevantSourceFiles = []; const allKnownLocalExports = {}; // path mappings are relative to the "baseUrl", so find out what it is let basePath = path_1.default.normalize(program.getCurrentDirectory()); // e.g. 'c:\SAPDevelop\git\ui5-typescript-control-library\' const options = program.getCompilerOptions(); if (options.baseUrl) { basePath = path_1.default.normalize(options.baseUrl); } // loop all files, filter for relevant ones, and extract knowledge about all their module exports program .getSourceFiles() .filter((sourceFile) => { return (sourceFile.fileName.indexOf("@types") === -1 && sourceFile.fileName.indexOf("node_modules/") === -1 && !program.isSourceFileFromExternalLibrary(sourceFile)); // do not generate interfaces for dependencies; the last check is needed because source files inside node_modules do not have "node_modules" in their file name when symlinked; probably this last check would also be sufficient (and better) than guessing via name }) .forEach((sourceFile) => { allRelevantSourceFiles.push(sourceFile); (0, addSourceExports_1.addSourceExports)(sourceFile, basePath, typeChecker, allPathMappings, allKnownLocalExports); // extract all local exports }); return { allRelevantSourceFiles, allKnownLocalExports }; } /** * Whenever the code changes, this is called, in oder to re-generate the interfaces * * * TODO: can we use the knowledge about the changed files to limit the scope here? * A Problem is that also a change in a different file could influence the generation of an unchanged file. E.g. when an update of the * UI5 type definitions changes the base class of sap.m.Button to something where API methods are not generated, then * the next generation run would no longer create a *.gen.d.ts file for Controls deriving from Button (ok, extreme example...) * @param program * @param typeChecker * @param changedFiles * @param allKnownGlobals */ function onTSProgramUpdate(program, typeChecker, changedFiles, // is an empty array in non-watch case; is at least one file in watch case - but overall not reliable! allKnownGlobals) { const { allRelevantSourceFiles, allKnownLocalExports } = getProgramInfo(program, typeChecker); // now actually generate the interface files for all source files allRelevantSourceFiles.forEach((sourceFile) => { (0, interfaceGenerationHelper_1.generateInterfaces)(sourceFile, typeChecker, Object.assign(allKnownLocalExports, allKnownGlobals)); // don't modify the ambient globals here, as they might have a different lifecycle and we don't want to keep adding the same properties again }); } //# sourceMappingURL=generateTSInterfacesAPI.js.map