UNPKG

@tangelo/tangelo-configuration-toolkit

Version:

Tangelo Configuration Toolkit is a command-line toolkit which offers support for developing a Tangelo configuration.

313 lines (277 loc) 12.6 kB
const del = require('del'); const {exec} = require('child_process'); const fs = require('fs-extra'); const globby = require('globby'); const gulp = require('gulp'); const g_print = require('gulp-print'); const path = require('path'); const sass = require('gulp-sass')(require('sass')); const TclConfig = require('../../lib/tcl-config'); const wws = require('../../lib/worker-with-spinner'); g_print.setLogFunction(filepath => _write(filepath.nostyle)); const compileSass = () => new Promise((resolve, reject) => { _info('Compile sass to css:'); gulp.src('**/*.scss') .pipe(sass().on('error', error => reject(error))) .pipe(g_print.default()) .pipe(gulp.dest('.')) .on('end', () => { resolve(); _write(); }) ; }); const cmdExec = command => new Promise(resolve => { _info(`Executing command (${command}):`); const cp = exec(command); const log = msg => { const line = msg.replace(/\s+$/, ''); // remove excessive whitespace if (line) console.log(line); }; cp.stdout.setEncoding('utf8'); cp.stdout.on('data', data => { if (/Error|Input error/.test(data)) _warn(data); else log(data); }); cp.stderr.setEncoding('utf8'); cp.stderr.on('data', data => log(data.replace(/Browserslist: caniuse-lite .*/s, ''))); cp.on('close', () => { _write(); resolve(); }); }); module.exports = { init ([fdt, fontoVersion]) { return new Promise((resolve, reject) => { _info('Ensure symlink exists:'); const tdiCodebasePath = _settingsTdi().codebase === 'dual' && fs.existsSync('../project.tcl') ? '/tcl' : _settingsTdi().codebase === 'dual' ? '/traditional' : '' ; fs.symlink(path.join(`../../../tdi${tdiCodebasePath}/fonto/packages-shared`), 'packages-shared', 'dir', err => { if (err && err.code!=='EEXIST') reject(err.code==='EPERM' ? 'Start your console as admin for symlink creation!' : err); else _write('Done.\n'); resolve(); }); }) .then(() => cmdExec(`${fdt} editor upgrade --version ${fontoVersion} --non-interactive`)) .then(() => [fdt]) ; }, schema ([fdt]) { return cmdExec(`${fdt} editor schema compile ../schema --overwrite`) .then(() => { _info('Updating schema configuration:'); const {rootSchemas} = fs.readJsonSync('../schema/fonto.json'); const fontoManifest = fs.readJsonSync('config/fonto-manifest.json'); let jsonPaths = []; // remove all schema packages (sx-shell-*) from fonto-manifest.json Object.keys(fontoManifest.dependencies).filter(k => k.includes('sx-shell-')).forEach(k => { delete fontoManifest.dependencies[k]; }); // add dependency for sx-module to each package fonto-manifest.json only if sx-module exists // add schema packages in fonto.json to config/fonto-manifest.json const sxModuleExists = fs.existsSync('packages/sx-module'); Object.entries(rootSchemas).forEach(([, obj]) => { const pckPath = 'packages/' + obj.packageName; if (sxModuleExists) { const pckFontoManifest = fs.readJsonSync(pckPath + '/fonto-manifest.json', {throws: false}) || {dependencies: {}}; pckFontoManifest.dependencies['sx-module'] = 'packages/sx-module'; fs.outputJsonSync(pckPath + '/fonto-manifest.json', pckFontoManifest, {spaces: 2}); _write(`${pckPath}/fonto-manifest.json`); } fontoManifest.dependencies[obj.packageName] = pckPath; jsonPaths.push(`${pckPath}/src/assets/schemas/${obj.packageName}.json`); }); fs.outputJsonSync('config/fonto-manifest.json', fontoManifest, {spaces: 2}); _write('config/fonto-manifest.json'); fs.outputFileSync('config/schemaExperienceResolver.js', `import tanSchemaLocationToSchemaExperienceResolver from 'tan-base/src/schemaExperienceResolver.js';`+ `\n\n`+ `tanSchemaLocationToSchemaExperienceResolver(${JSON.stringify(rootSchemas, null, 2)});` ); _write('config/schemaExperienceResolver.js'); _write(); _info('Copying schema json files to build folders:'); // deploy-watch copies schema's to server directly, so update local builds to keep integrity jsonPaths.forEach(jsonPath => { fs.copySync(jsonPath, 'dev/assets/schemas/' + jsonPath.match(/[^/]*$/)[0]); fs.copySync(jsonPath, 'dist/assets/schemas/' + jsonPath.match(/[^/]*$/)[0]); }); _write('Done.\n'); }) .then(() => [fdt]) ; }, elements ([fdt]) { _info('Elements requiring custom configuration:'); const findElements = ({fdt, rootSchemas, tdiXsdPath}) => { // cannot use variables from outside this function const {execSync} = require('child_process'), fs = require('fs'); // get all elements defined in default schema let ignoreElements = []; fs.readdirSync(tdiXsdPath).forEach(filename => { const file = fs.readFileSync(tdiXsdPath + filename, {encoding: 'UTF-8'}); ignoreElements.push(...(file.match(/(?<=xs:element name=")[^"]+(?=")/g)||[])); }); ignoreElements = [...new Set(ignoreElements)]; // remove duplicates // execute "fdt elements" for each schema package, ignore default elements, and combine results const schemasPerElement = {}; Object.entries(rootSchemas).forEach(([, obj]) => { const data = execSync(`${fdt} elements --schema packages/${obj.packageName}/src/assets/schemas/${obj.packageName}.json -C name`, {encoding: 'UTF-8'}); const elements = data.replace(/(^.*?Name\*)|(Printed name\*.*$)/gs, '').split(/\s+/); const customElements = [...new Set(elements)].filter(e => e && !ignoreElements.includes(e)); customElements.forEach(e => { if (schemasPerElement[e]) schemasPerElement[e] = [...schemasPerElement[e], obj.packageName]; else schemasPerElement[e] = [obj.packageName]; }); }); const result = []; Object.entries(schemasPerElement).forEach(([e, s]) => result.push([e, s])); // convert object to array return result[0] ? result : 'No results.'; }; return wws( 'searching', findElements, { fdt, rootSchemas: fs.readJsonSync('../schema/fonto.json').rootSchemas, tdiXsdPath: path.join(_paths.repo, _paths.tdi, 'src/config/cmscustom/[customer]/[project]/schema/xsd/') }, result => { if (Array.isArray(result)) result.forEach(([e, s]) => _write(e, s, '\n')); else _write(result, '\n'); } ) .then(() => [fdt]) ; }, attributes ([fdt]) { _info('Attributes requiring custom configuration:'); const findAttributes = ({fdt, rootSchemas, tdiXsdPath}) => { // cannot use variables from outside this function const {execSync} = require('child_process'), fs = require('fs'); // get all attributes defined in default schema let ignoreAttributes = []; fs.readdirSync(tdiXsdPath).forEach(filename => { const file = fs.readFileSync(tdiXsdPath + filename, {encoding: 'UTF-8'}); ignoreAttributes.push(...(file.match(/(?<=xs:attribute name=")[^"]+(?=")/g)||[])); }); ignoreAttributes = [...new Set(ignoreAttributes)]; // remove duplicates // execute "fdt attributes" for each schema package, ignore default attributes, and combine results const schemasPerAttribute = {}; Object.entries(rootSchemas).forEach(([, obj]) => { const data = execSync(`${fdt} attributes --schema packages/${obj.packageName}/src/assets/schemas/${obj.packageName}.json`, {encoding: 'UTF-8'}); const attributes = data.replace(/(^.*?Default value\s+)|(\s+Printed name\*.*$)/gs, '').split(/\n\s+/).map(a => a.split(/\s+/)).map(a => a[0] + (a[2]==='required' ? ' (required)' : '') + (a[3]==='-' ? ' (no default)' : '') ); const customAttributes = [...new Set(attributes)].filter(e => e && !ignoreAttributes.includes(e.replace(/ .*/, ''))); customAttributes.forEach(e => { if (schemasPerAttribute[e]) schemasPerAttribute[e] = [...schemasPerAttribute[e], obj.packageName]; else schemasPerAttribute[e] = [obj.packageName]; }); }); const result = []; Object.entries(schemasPerAttribute).forEach(([e, s]) => result.push([e, s])); // convert object to array return result[0] ? result : 'No results.'; }; return wws( 'searching', findAttributes, { fdt, rootSchemas: fs.readJsonSync('../schema/fonto.json').rootSchemas, tdiXsdPath: path.join(_paths.repo, _paths.tdi, 'src/config/cmscustom/[customer]/[project]/schema/xsd/') }, result => { if (Array.isArray(result)) result.forEach(([e, s]) => _write(e, s, '\n')); else _write(result, '\n'); } ) .then(() => [fdt]) ; }, localize ([fdt]) { const templateFile = 'messages-template-packages.json'; const messagesFile = 'packages/localization/src/messages.nl.json'; const packagesDirs = fs.readdirSync('packages') .map(p => path.join('packages', p)) .filter(p => fs.lstatSync(p).isDirectory() && !p.match(/sx-shell-|localization/)) .join(' ') ; _info(`Ensure ${messagesFile} exists:`); if (!fs.existsSync(messagesFile)) fs.outputJsonSync(messagesFile, []); _write('Done.\n'); _info('Make temporary copies of json files:'); const attrCfgTmpFiles = globby.sync('packages/*/src/!(messages.|operations-)*.json'); attrCfgTmpFiles.forEach((p, i, a) => { _write(p); a[i] = path.join(path.dirname(p), 'operations-' + path.basename(p)); fs.copyFileSync(p, a[i]); }); _write(); return cmdExec(`${fdt} localization extract ${templateFile} --paths ` + packagesDirs) .then(() => cmdExec(`${fdt} localization update ${messagesFile} ${templateFile}`)) .then(() => fs.readJson(messagesFile)) .then(data => // compress meta object into string array "refs" and remove "operations-" prefix from paths data.map(msg => { msg.refs = msg.meta.map(r => Object.values(r).join(':').replace(/src\/operations-_/, 'src/_')); delete msg.meta; return msg; }) ) .then(data => fs.outputJson(messagesFile, data, {spaces: 2})) .then(() => { _info('Cleanup temp files:'); return del([templateFile, ...attrCfgTmpFiles]); }) .then(() => _write('Done.\n')) .then(() => [fdt]) ; }, build ([fdt]) { return compileSass() .then(() => { _info('Delete dist/assets folder:'); fs.removeSync('dist/assets'); _write('Done.\n'); }) .then(() => cmdExec(`${fdt} editor build`)) .then(() => { _info('Read project configuration and create json and css for Fonto:'); new TclConfig('..').outputJson('dist/assets').outputCss('dist'); _write('Done.\n'); }) .then(() => [fdt]) ; }, run ([fdt]) { return compileSass() .then(() => { _info('Read project configuration and create json and css for Fonto:'); new TclConfig('..').outputJson('dev/assets').outputCss('dev'); _write('Done.\n'); }) .then(() => { _info('Starting watch for tcl and sass files:'); gulp.watch(['**/*.scss', '../*.tcl', 'dev/index.html']) .on('all', (event, filepath) => { _write(event + ':', filepath); if (filepath.endsWith('.scss')) { gulp.src('**/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(gulp.dest('.')) ; } else if (filepath.endsWith('.html')) { new TclConfig('..').modifyFontoHtml('dev'); } else if (filepath.endsWith('.tcl')) { new TclConfig('..').outputJson('dev/assets').outputCss('dev'); } }); _write('Done.\n'); }) .then(() => cmdExec(`${fdt} editor run --port 8888 --write-to-disk`)) ; } };