UNPKG

@bitrix/cli

Version:
291 lines (270 loc) 8.36 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var Logger = _interopDefault(require('@bitrix/logger')); var camelcase = _interopDefault(require('camelcase')); var os = _interopDefault(require('os')); var mustache = _interopDefault(require('mustache')); var fse = _interopDefault(require('fs-extra')); var minimist = _interopDefault(require('minimist')); var slash = _interopDefault(require('slash')); var glob = _interopDefault(require('fast-glob')); var path = _interopDefault(require('path')); var fs = _interopDefault(require('fs')); var inquirer = _interopDefault(require('inquirer')); var boxen = _interopDefault(require('boxen')); require('colors'); const appRoot = path.resolve(__dirname, '../'); const lockFile = path.resolve(os.homedir(), '.bitrix.lock'); function buildExtensionName(filePath, context) { const moduleExp = new RegExp('/(.[a-z0-9_-]+)/install/js/(.[a-z0-9_-]+)/'); const moduleRes = `${slash(filePath)}`.match(moduleExp); if (Array.isArray(moduleRes)) { const fragments = `${slash(context)}`.split(`${moduleRes[1]}/install/js/${moduleRes[2]}/`); return `${moduleRes[2]}.${fragments[fragments.length - 1].replace(/\/$/, '').split('/').join('.')}`; } const localExp = new RegExp('/local/js/(.[a-z0-9_-]+)/(.[a-z0-9_-]+)/'); const localRes = `${slash(filePath)}`.match(localExp); if (!Array.isArray(localRes)) { return path.basename(context); } const fragments = `${slash(context)}`.split(`/local/js/${localRes[1]}/`); return `${localRes[1]}.${fragments[fragments.length - 1].replace(/\/$/, '').split('/').join('.')}`; } function render({ input, output, data = {} }) { if (fs.existsSync(input)) { if (fs.existsSync(output)) { fs.unlinkSync(output); } const template = fs.readFileSync(input, 'utf-8'); fse.outputFileSync(output, mustache.render(template, data)); } } function buildNamespaceName({ root = '', extensionName } = {}) { if (typeof extensionName === 'string') { const namespace = extensionName.split('.').slice(0, -1).map(name => { if (name.length === 2) { return name.toUpperCase(); } return `${name.charAt(0).toUpperCase()}${name.slice(1)}`; }).join('.'); if (typeof root === 'string' && root !== '') { return `${root}.${namespace}`; } return namespace; } return root; } const templatePath = path.resolve(appRoot, 'src/templates'); const extensionTemplatePath = path.resolve(templatePath, 'extension'); const configTemplatePath = path.resolve(extensionTemplatePath, 'bundle.config.js'); const inputTemplatePath = path.resolve(extensionTemplatePath, 'input.js'); const defaultOptions = { test: true }; function createExtension(directory, options = defaultOptions) { if (typeof directory !== 'string') { throw new Error('directory is not string'); } if (!fs.existsSync(directory)) { throw new Error('directory is not exists'); } const extensionPath = path.resolve(directory, options.name.toLowerCase()); const inputPath = path.resolve(extensionPath, `src/${options.name}.js`); const outputPath = path.resolve(extensionPath, `dist/${options.name}.bundle.js`); const configPath = path.resolve(extensionPath, 'bundle.config.js'); const extensionName = buildExtensionName(inputPath, extensionPath); const namespaceName = buildNamespaceName({ root: 'BX', extensionName }); render({ input: inputTemplatePath, output: inputPath, data: { name: camelcase(options.name, { pascalCase: true }), nameLower: `${options.name}`.toLowerCase() } }); const additionalOptions = (() => { let acc = ''; if (options.browserslist) { acc += `\n\tbrowserslist: ${options.browserslist},`; } if (options.minification) { acc += `\n\tminification: ${options.minification},`; } if (options.sourceMaps === false) { acc += `\n\tsourceMaps: ${options.sourceMaps},`; } return acc; })(); render({ input: configTemplatePath, output: configPath, data: { input: slash(path.relative(extensionPath, inputPath)), output: slash(path.relative(extensionPath, outputPath)), namespace: namespaceName, additionalOptions } }); if (options.tests) { const testTemplatePath = path.resolve(extensionTemplatePath, 'test.js'); const testFilePath = path.resolve(extensionPath, `test/${options.name}/${options.name}.test.js`); render({ input: testTemplatePath, output: testFilePath, data: { name: camelcase(options.name, { pascalCase: true }), sourceName: options.name } }); } return { extensionName, functionName: camelcase(options.name, { pascalCase: true }) }; } var alias = { w: 'watch', p: 'path', m: 'modules', t: 'test', h: 'help', v: 'version', c: 'create', n: 'name', e: 'extensions' }; var argv = minimist(process.argv.slice(2), { alias }); function getDirectories(dir) { if (fs.existsSync(path.resolve(dir))) { const pattern = slash(path.resolve(dir, '**')); const options = { onlyDirectories: true, deep: 0 }; return glob.sync(pattern, options).map(dirPath => path.basename(dirPath)); } return []; } function isRepositoryRoot(dirPath) { const dirs = getDirectories(dirPath); return dirs.includes('main') && dirs.includes('fileman') && dirs.includes('iblock') && dirs.includes('ui') && dirs.includes('translate'); } var params = { get path() { return path.resolve(argv.path || process.cwd()); }, get modules() { const modules = (argv.modules || '').split(',').map(module => module.trim()).filter(module => !!module).map(module => path.resolve(this.path, module)); if (isRepositoryRoot(this.path) && modules.length === 0) { return getDirectories(this.path); } return modules; }, get extensions() { if (typeof argv.extensions === 'string') { return argv.extensions.split(',').map(module => module.trim()); } return []; }, get name() { return argv.name || argv._[1]; } }; async function ask(questions = []) { const answers = {}; if (!Array.isArray(questions) || !questions.length) { return answers; } const rawAnswers = await inquirer.prompt(questions); return Object.keys(rawAnswers).reduce((acc, item) => { const question = questions.find(currentQuestion => { return currentQuestion.name === item; }); answers[question.id || item] = rawAnswers[item]; return answers; }, answers); } const options = { padding: 1, margin: 1, align: 'left', borderColor: 'yellow', borderStyle: 'round' }; function box(content) { return boxen(content.replace(/^\s+|\s+$|\t/g, ''), options); } async function bitrixCreate() { const answers = await (() => { if (argv.y) { return { name: typeof argv._[1] === 'string' ? argv._[1] : '', tests: true, browserslist: true }; } return ask([{ name: 'Extension name', id: 'name', type: 'input', default: typeof argv._[1] === 'string' ? argv._[1] : '', validate: input => { if (typeof input === 'string' && input.length) { return true; } return 'Name should be not empty string'; } }, { name: 'Enable tests', id: 'tests', type: 'confirm', default: true }, { name: 'Use Browserslist', id: 'browserslist', type: 'confirm', default: true }, { name: 'Enable minification', id: 'minification', type: 'confirm', default: false }, { name: 'Enable sourceMaps', id: 'sourceMaps', type: 'confirm', default: true }]); })(); const extInfo = createExtension(params.path, answers); const info = box(` ${'Success!'.bold} Extension ${extInfo.extensionName} created Run ${`bitrix build -p ./${answers.name}`.bold} for build extension ${'Include extension in php'.bold} \\Bitrix\\Main\\UI\\Extension::load('${extInfo.extensionName}'); ${'or import in your js code'.bold} import {${extInfo.functionName}} from '${extInfo.extensionName}'; `); // eslint-disable-next-line return Logger.log(info); } module.exports = bitrixCreate;