UNPKG

@ui5/builder

Version:
181 lines (162 loc) 6.16 kB
import {spawn} from "node:child_process"; import fs from "graceful-fs"; import path from "node:path"; import {promisify} from "node:util"; const writeFile = promisify(fs.writeFile); import {createAdapter} from "@ui5/fs/resourceFactory"; import {fileURLToPath} from "node:url"; /** * @public * @module @ui5/builder/processors/jsdoc/jsdocGenerator */ /** * JSDoc generator * * @public * @function default * @static * * @param {object} parameters Parameters * @param {string} parameters.sourcePath Path of the source files to be processed * @param {string} parameters.targetPath Path to write any output files * @param {string} parameters.tmpPath Path to write temporary and debug files * @param {object} parameters.options Options * @param {string} parameters.options.projectName Project name * @param {string} parameters.options.namespace Namespace to build (e.g. <code>some/project/name</code>) * @param {string} parameters.options.version Project version * @param {Array} [parameters.options.variants=["apijson"]] JSDoc variants to be built * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with newly created resources */ export default async function jsdocGenerator( {sourcePath, targetPath, tmpPath, options: {projectName, namespace, version, variants}} = {} ) { if (!sourcePath || !targetPath || !tmpPath || !projectName || !namespace || !version) { throw new Error("[jsdocGenerator]: One or more mandatory parameters not provided"); } if (!variants || variants.length === 0) { variants = ["apijson"]; } const config = await jsdocGenerator._generateJsdocConfig({ targetPath, tmpPath, namespace, projectName, version, variants }); const configPath = await jsdocGenerator._writeJsdocConfig(tmpPath, config); await jsdocGenerator._buildJsdoc({ sourcePath, configPath }); const fsTarget = createAdapter({ fsBasePath: targetPath, virBasePath: "/" }); // create resources from the output files return Promise.all([ fsTarget.byPath(`/test-resources/${namespace}/designtime/api.json`) // fsTarget.byPath(`/libraries/${options.projectName}.js`) ]).then((res) => res.filter(($)=>$)); } /** * Generate jsdoc-config.json content * * @private * @param {object} parameters Parameters * @param {string} parameters.targetPath Path to write any output files * @param {string} parameters.tmpPath Path to write temporary and debug files * @param {string} parameters.projectName Project name * @param {string} parameters.version Project version * @param {string} parameters.namespace Namespace to use (e.g. <code>some/project/name</code>) * @param {Array} parameters.variants JSDoc variants to be built * @returns {string} jsdoc-config.json content string */ async function generateJsdocConfig({targetPath, tmpPath, namespace, projectName, version, variants}) { // Backlash needs to be escaped as double-backslash // This is not only relevant for win32 paths but also for // Unix directory names that contain a backslash in their name const backslashRegex = /\\/g; // Resolve path to this script to get the path to the JSDoc extensions folder const jsdocPath = path.normalize(import.meta.dirname); const pluginPath = path.join(jsdocPath, "lib", "ui5", "plugin.js").replace(backslashRegex, "\\\\"); const templatePath = path.join(jsdocPath, "lib", "ui5", "template").replace(backslashRegex, "\\\\"); const destinationPath = path.normalize(tmpPath).replace(backslashRegex, "\\\\"); const jsapiFilePath = path.join(targetPath, "libraries", projectName + ".js").replace(backslashRegex, "\\\\"); const apiJsonFolderPath = path.join(tmpPath, "dependency-apis").replace(backslashRegex, "\\\\"); const apiJsonFilePath = path.join(targetPath, "test-resources", path.normalize(namespace), "designtime", "api.json") .replace(backslashRegex, "\\\\"); // Note: While the projectName could also be used here, it is not ensured that it fits to // the library namespace. // As the "uilib" information is used to check for certain constraints it must be aligned with // the technical namespace that is used in the folder-structure, library.js and for the // sap.ui.base.Object based classes. const uilib = namespace.replace(/\//g, "."); const config = `{ "plugins": ["${pluginPath}"], "opts": { "recurse": true, "lenient": true, "template": "${templatePath}", "ui5": { "saveSymbols": true }, "destination": "${destinationPath}" }, "templates": { "ui5": { "variants": ${JSON.stringify(variants)}, "version": "${version}", "uilib": "${uilib}", "jsapiFile": "${jsapiFilePath}", "apiJsonFolder": "${apiJsonFolderPath}", "apiJsonFile": "${apiJsonFilePath}" } } }`; return config; } /** * Write jsdoc-config.json to file system * * @private * @param {string} targetDirPath Directory Path to write the jsdoc-config.json file to * @param {string} config jsdoc-config.json content * @returns {string} Full path to the written jsdoc-config.json file */ async function writeJsdocConfig(targetDirPath, config) { const configPath = path.join(targetDirPath, "jsdoc-config.json"); await writeFile(configPath, config); return configPath; } /** * Execute JSDoc build by spawning JSDoc as an external process * * @private * @param {object} parameters Parameters * @param {string} parameters.sourcePath Project resources (input for JSDoc generation) * @param {string} parameters.configPath Full path to jsdoc-config.json file * @returns {Promise<undefined>} */ async function buildJsdoc({sourcePath, configPath}) { const args = [ fileURLToPath(import.meta.resolve("jsdoc/jsdoc.js")), "-c", configPath, "--verbose", sourcePath ]; const exitCode = await new Promise((resolve /* , reject */) => { const child = spawn("node", args, { stdio: ["ignore", "ignore", "inherit"] }); child.on("close", resolve); }); if (exitCode !== 0) { throw new Error(`JSDoc reported an error, check the log for issues (exit code: ${exitCode})`); } } jsdocGenerator._generateJsdocConfig = generateJsdocConfig; jsdocGenerator._writeJsdocConfig = writeJsdocConfig; jsdocGenerator._buildJsdoc = buildJsdoc;