UNPKG

@pega/constellation-dx-components-build-utils

Version:

This tool uses a 'v3' approach to group components in a library, create a component map, employ webpack, and load the library like Pega-generated components, constellation app-static.

316 lines (277 loc) 14 kB
const child_process = require("child_process"); const fs = require('fs'); const path = require('path'); const glob = require('glob'); const CONSTANTS = require('../constant'); const helper = require('../helper'); const chalk = require('chalk'); const ensureDirSync = (filePath) => { const dir = path.dirname(filePath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } }; const buildComponentsListFile = (buildFolderName, packageVersion, notClean) => { if (!notClean) helper.cleanUpBuildFolder(CONSTANTS.CURRENT_WORKING_DIR, buildFolderName, packageVersion); console.log(chalk.magenta("== Aggregating components to build - Components List - Started ==")); const projectPath = path.join(CONSTANTS.CURRENT_WORKING_DIR, CONSTANTS.PROJECT_ROOT).replace(/\\/g, '/'); const componentsDir = path.join(projectPath, CONSTANTS.COMPONENTS_DIR).replace(/\\/g, '/'); const componentsLists = fs.readdirSync(componentsDir).filter(function (file) { return fs.statSync(path.join(componentsDir, file).replace(/\\/g, '/')).isDirectory() && fs.existsSync(path.join(componentsDir, file, CONSTANTS.COMPONENT_CONFIG_FILE_NAME).replace(/\\/g, '/')); }); let cmpIndexPathObj = {}; componentsLists.forEach((component) => { CONSTANTS.ENTRY_POINT_FILES.forEach((indexFile) => { if (!cmpIndexPathObj[component] && fs.existsSync(path.join(componentsDir, component, indexFile))) { let indexPath = path.join(CONSTANTS.COMPONENTS_DIR, component, indexFile).replace(/\\/g, '/'); cmpIndexPathObj[component] = indexPath; } }); }); const componentListPath = path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/'); ensureDirSync(componentListPath); fs.writeFileSync(componentListPath, JSON.stringify(cmpIndexPathObj), { encoding: 'utf8' }); const entryPointPath = path.join(projectPath, CONSTANTS.ENTRY_POINT_FILE).replace(/\\/g, '/'); ensureDirSync(entryPointPath); fs.writeFileSync(entryPointPath, CONSTANTS.CONTENT_ENTRY_POINT_FILE, { encoding: 'utf8' }); console.log(chalk.green("== Aggregating components to build - Components List - Completed ==")); }; const buildComponentMapJsFile = (buildFolderFullPath) => { console.log(chalk.magenta("== Creating map from the list of components - Component Map - Started ==")); const cmpntDefs = []; const cmpntLocalizations = []; const data = fs.readFileSync(path.join(CONSTANTS.BUILD_TOOL_LIB_PATH, CONSTANTS.UTILS).replace(/\\/g, '/'), 'utf8'); const indexOfStartDelimeter = data.indexOf(CONSTANTS.START_DELIMETER); const lenthOfStartDelimeter = CONSTANTS.START_DELIMETER.length; const tillStartDelimeterData = data.substring(0, indexOfStartDelimeter + lenthOfStartDelimeter); const indexOfEndDelimeter = data.indexOf(CONSTANTS.END_DELIMETER); const fromEndDelimeterData = data.substring(indexOfEndDelimeter); // component_map let componentMapData = `\n const ComponentMap = {\n`; const projectPath = path.join(CONSTANTS.CURRENT_WORKING_DIR, CONSTANTS.PROJECT_ROOT).replace(/\\/g, '/'); const componentListJsonPath = path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/'); const componentListJSON = fs.readFileSync(componentListJsonPath, { encoding: 'utf8' }); const componentListJSONData = JSON.parse(componentListJSON); Object.keys(componentListJSONData).forEach((component) => { const componentName = component.split(":")[0]; const overrideQueryParam = component.split(":")[1]; const componentPath = path.join(projectPath, componentListJSONData[component]).replace(/\\/g, '/'); const parameterForLoadable = overrideQueryParam ? `'${componentName}','${overrideQueryParam}'` : `'${componentName}'`; componentMapData += `\t${componentName}: {\n\t modules: [\n\t\t loadable (() =>\n\t\t\t import(\n\t\t\t\t/* webpackChunkName: '${componentName}' */\n\t\t\t\t '${componentPath}'\n\t\t\t\t)\n\t\t\t, ${parameterForLoadable})\n\t\t]\n\t},\n`; }); // remove the last , componentMapData = componentMapData.slice(0, -2); componentMapData += `\n};\n`; const modifiedComponentMapFile = tillStartDelimeterData + componentMapData + fromEndDelimeterData; const componentListOutputPath = path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME_OUTPUT).replace(/\\/g, '/'); ensureDirSync(componentListOutputPath); fs.writeFileSync(componentListOutputPath, modifiedComponentMapFile, { encoding: 'utf8' }); const tempList = getAggregateCompListForProject({ dirToLookUp: path.join(projectPath).replace(/\\/g, '/') }); if (tempList) { cmpntDefs.push(...tempList); const componentsConfigPath = path.join(projectPath, "../", buildFolderFullPath, 'componentsconfig.json').replace(/\\/g, '/'); ensureDirSync(componentsConfigPath); fs.writeFileSync(componentsConfigPath, JSON.stringify(cmpntDefs), { encoding: 'utf8' }); } const tempCompLocalizationList = getAggregateCompLocalizationForProject({ dirToLookUp: path.join(projectPath).replace(/\\/g, '/') }); if (tempCompLocalizationList) { cmpntLocalizations.push(...tempCompLocalizationList); const componentsLocalizationPath = path.join(projectPath, "../", buildFolderFullPath, 'componentslocalization.json').replace(/\\/g, '/'); ensureDirSync(componentsLocalizationPath); fs.writeFileSync(componentsLocalizationPath, JSON.stringify(cmpntLocalizations), { encoding: 'utf8' }); } console.log(chalk.green("== Creating map from the list of components - Component Map - Completed ==")); }; const webpackBuild = (buildFolderFullPath, buildFolderName, packageVersion, tokenParams, appStaticSVCUrl, v3) => { console.log(chalk.magenta("==== Webpack - Starting ====")); const appStaticUrl = require('../helper').getAppstaticEnd(appStaticSVCUrl); const token = helper.getB2SToken(tokenParams); let orgId = ''; if (token) { orgId = helper.getPayloadFromTkn(token, 'customer_org'); } if (!orgId) { orgId = '{ORG_ID}'; } const projectPath = path.join(CONSTANTS.CURRENT_WORKING_DIR, CONSTANTS.PROJECT_ROOT).replace(/\\/g, '/'); const webpackPath = path.join(CONSTANTS.BUILD_TOOL_ROOT_PATH).replace(/\\/g, '/'); child_process.execSync(`npx webpack --mode=production --env buildFolderFullPath=${buildFolderFullPath} buildFolderName=${buildFolderName} packageVersion=${packageVersion} appStaticUrl=${appStaticUrl} orgId=${orgId} v3=${v3} --config ${webpackPath}/webpack.config.js`, { stdio: [0, 1, 2], cwd: path.join(CONSTANTS.CURRENT_WORKING_DIR).replace(/\\/g, '/'), }); console.log(chalk.magenta("==== Cleanup ====")); fs.unlink(path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME_OUTPUT).replace(/\\/g, '/'), (err) => { if (err) throw err; console.log(CONSTANTS.COMPONENT_LIST_FILE_NAME_OUTPUT + ' was deleted'); }); fs.unlink(path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/'), (err) => { if (err) throw err; console.log(CONSTANTS.COMPONENT_LIST_FILE_NAME + ' was deleted'); }); fs.unlink(path.join(projectPath, CONSTANTS.ENTRY_POINT_FILE).replace(/\\/g, '/'), (err) => { if (err) throw err; console.log(CONSTANTS.ENTRY_POINT_FILE + ' was deleted'); }); }; const getComponentAssets = (componentAssets) => { const assets = []; componentAssets.forEach(ele => { if (ele?.name && ele?.name.split('/').length == 2) { const fileName = ele?.name.split('/')[1]; assets.push(fileName); } else if (ele?.name && typeof ele?.name === "string") { assets.push(ele?.name); } }); return assets; } const buildManifestFile = (buildFolderFullPath, v3) => { console.log(chalk.magenta("== Creating manifest for the library version - Started ==")); const projectPath = path.join(CONSTANTS.CURRENT_WORKING_DIR, CONSTANTS.PROJECT_ROOT).replace(/\\/g, '/'); const data = {}; try { // read stats.json file const stats = fs.readFileSync(path.join(projectPath, '../', buildFolderFullPath, CONSTANTS.STATS).replace(/\\/g, '/'), 'utf-8'); const statsObj = JSON.parse(stats); const components = {}; Object.keys(statsObj?.namedChunkGroups).forEach(component => { if (component !== 'main') { const value = []; if (statsObj?.namedChunkGroups[component]?.assets?.length) { value.push(...getComponentAssets(statsObj.namedChunkGroups[component].assets)); } if (statsObj?.namedChunkGroups[component]?.auxiliaryAssets?.length) { value.push(...getComponentAssets(statsObj.namedChunkGroups[component].auxiliaryAssets)); } components[component] = value; } }); data.components = components; const { stdout } = child_process.spawnSync('npm', ['list', '--depth=0'], { encoding: 'utf-8' }); const npmListOutput = stdout; if (npmListOutput && npmListOutput.indexOf(CONSTANTS.COSMOS_PKG_PATTERN) !== -1) { const cosmosVersion = helper.getVersionFromListOutput(npmListOutput, CONSTANTS.COSMOS_PKG_PATTERN); data.versions = cosmosVersion; } let manifestPath; if (!v3) { manifestPath = path.join(projectPath, '../', buildFolderFullPath, 'chunks', CONSTANTS.MANIFEST).replace(/\\/g, '/'); } else { manifestPath = path.join(projectPath, '../', buildFolderFullPath, CONSTANTS.MANIFEST).replace(/\\/g, '/'); } ensureDirSync(manifestPath); fs.writeFileSync(manifestPath, JSON.stringify(data), { encoding: 'utf8' }); console.log(chalk.magenta("== Creating manifest for the library version - completed ==")); fs.unlink(path.join(projectPath, '../', buildFolderFullPath, CONSTANTS.STATS).replace(/\\/g, '/'), (err) => { if (err) throw err; console.log(CONSTANTS.STATS + ' was deleted'); }); } catch (e) { console.log('Warning in manifest file generation.'); console.log(e); } } /** * This function generate build for library * @param {string} libraryName optional library name, else it will take value from package.json name * @param {string} libraryVersion optional library version, else it will take value from package.json version * @param {string} tokenParam optional B2S token for appstatic service, else it will take value from constant.js B2STOKEN * @param {string} appStaticSVCUrl optional appstatic service url, else it will take value from constant.js APPSTATICURL */ const buildLib = (libraryName, libraryVersion, tokenParam, appStaticSVCUrl, v3) => { console.log(chalk.magenta("==== Library Build - Starting ====")); const { buildFolderFullPath, buildFolderName, packageVersion } = helper.getBuildFolderName(libraryName, libraryVersion); // build entry point and cmpt-list.json buildComponentsListFile(buildFolderName, packageVersion); // build component map buildComponentMapJsFile(buildFolderFullPath); // then build env webpackBuild(buildFolderFullPath, buildFolderName, packageVersion, tokenParam, appStaticSVCUrl, v3); // generate manifest file buildManifestFile(buildFolderFullPath, v3); }; const buildLibV3 = (libraryName, libraryVersion, tokenParam, appStaticSVCUrl) => { buildLib(libraryName, libraryVersion, tokenParam, appStaticSVCUrl, true); }; const getAggregateCompListForProject = (options) => { const { dirToLookUp } = options; const cmpntDefs = []; const componentList = glob.sync(path.join(dirToLookUp, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/')); if (!componentList || componentList.length === 0) { console.error(chalk.red('No component-list.json found')); return; } try { const compMap = fs.readFileSync(componentList[0], 'utf-8'); const compMapObj = JSON.parse(compMap); const definitions = glob.sync(`${dirToLookUp}/components/**/config.json`.replace(/\\/g, '/')); if (!definitions) { console.info(chalk.magenta(`No components found`)); return; } definitions.forEach((config) => { config = config.replace(/\\/g, '/'); let cmpntNamePath = config.replace(`${dirToLookUp}/components/`, '').replace('/config.json', ''); const cmpntName = Object.keys(compMapObj).find(key => compMapObj[key].includes(cmpntNamePath)); if (cmpntName) { const data = fs.readFileSync(config, 'utf-8'); try { const cfg = JSON.parse(data); cmpntDefs.push(cfg); } catch (ex) { console.log(ex); } } }); } catch (err) { console.log(err); } return cmpntDefs; }; const getAggregateCompLocalizationForProject = (options) => { const cmpntLocalizations = []; const { dirToLookUp } = options; const componentList = glob.sync(path.join(dirToLookUp, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/')); if (!componentList || componentList.length === 0) { console.error(chalk.red('No component-list.json found')); return; } try { const compMap = fs.readFileSync(componentList[0], 'utf-8'); const compMapObj = JSON.parse(compMap); const definitions = glob.sync(`${dirToLookUp}/components/**/localizations.json`.replace(/\\/g, '/')); if (!definitions || definitions.length === 0) { console.info(chalk.magenta(`No components localization found`)); return; } definitions.forEach((config) => { // get cmpnt name let cmpntNamePath = config.replace(`${dirToLookUp}/components/`.replace(/\\/g, '/'), '').replace('/localizations.json'.replace(/\\/g, '/'), ''); const cmpntName = Object.keys(compMapObj).find(key => compMapObj[key].includes(cmpntNamePath)); // read cmpnt localization file content if (cmpntName) { const data = fs.readFileSync(config, 'utf-8'); // key : value const cmpntLocalization = { [cmpntName]: JSON.parse(data) }; // push to [] cmpntLocalizations.push(cmpntLocalization); } }); } catch (err) { console.log(err); } // return return cmpntLocalizations; } module.exports = { buildLib, buildLibV3, buildComponentsListFile, buildComponentMapJsFile }