@bitrix/cli
Version:
Bitrix CLI tools
291 lines (270 loc) • 8.36 kB
JavaScript
;
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;