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.

272 lines (247 loc) 12.9 kB
const child_process = require("child_process"); const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); const CONSTANTS = require('../constant'); const helper = require('../helper'); const chalk = require('chalk'); const buildComponentsListFile = (buildFolderName, packageVersion) => { 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; } }); }); fs.outputFileSync(path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/'), JSON.stringify(cmpIndexPathObj), { encoding: 'utf8' } ); fs.outputFileSync(path.join(projectPath, CONSTANTS.ENTRY_POINT_FILE).replace(/\\/g, '/'), 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; fs.outputFileSync(path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME_OUTPUT).replace(/\\/g, '/'), modifiedComponentMapFile, { encoding: 'utf8' } ); const tempList = getAggregateCompListForProject({ dirToLookUp: path.join(projectPath).replace(/\\/g, '/') }); if (tempList) { cmpntDefs.push(...tempList); fs.outputFileSync(path.join(projectPath, "../", buildFolderFullPath, 'componentsconfig.json').replace(/\\/g, '/'), JSON.stringify(cmpntDefs)); } const tempCompLocalizationList = getAggregateCompLocalizationForProject({ dirToLookUp: path.join(projectPath).replace(/\\/g, '/') }); if (tempCompLocalizationList) { cmpntLocalizations.push(...tempCompLocalizationList); fs.outputFileSync(path.join(projectPath, "../", buildFolderFullPath, 'componentslocalization.json').replace(/\\/g, '/'), JSON.stringify(cmpntLocalizations)); } 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, '/')); fs.unlink(path.join(projectPath, CONSTANTS.COMPONENT_LIST_FILE_NAME).replace(/\\/g, '/')); fs.unlink(path.join(projectPath, CONSTANTS.ENTRY_POINT_FILE).replace(/\\/g, '/')); }; 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); } }); 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; } if(!v3){ fs.outputFileSync(path.join(projectPath, '../', buildFolderFullPath, 'chunks', CONSTANTS.MANIFEST).replace(/\\/g, '/'), JSON.stringify(data)); } else { fs.outputFileSync(path.join(projectPath, '../', buildFolderFullPath, CONSTANTS.MANIFEST).replace(/\\/g, '/'), JSON.stringify(data)); } console.log(chalk.magenta("== Creating manifest for the library version - completed ==")); fs.unlink(path.join(projectPath, '../', buildFolderFullPath, CONSTANTS.STATS).replace(/\\/g, '/')); } 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 }