UNPKG

umbot

Version:

Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф

364 lines (335 loc) 13.3 kB
'use strict'; const fs = require('node:fs'); const { exec } = require('node:child_process'); const utils = require(__dirname + '/../utils.js').utils; /** * Класс, создающий пустой проект, или шаблон для готового проекта. */ class CreateController { /** * Создает пустой проект */ static T_DEFAULT = 'Default'; /** * Создает викторину */ static T_QUIZ = 'Quiz'; flags = []; #path; #name; /** * Параметры для создания приложения */ params; /** * Читает содержимое файла * @param {string} file Путь к файлу * @returns {string} Содержимое файла или пустая строка * @private */ _getFileContent(file) { let content = ''; if (file && utils.isFile(file)) { content = utils.fread(file); } return content; } /** * Формирует заголовок файла с информацией о создании * @returns {string} Заголовок файла * @private */ _getHeaderContent() { let headerContent = '/*\n'; headerContent += '/* Created by umbot\n'; headerContent += ' * Date: {{date}}\n'; headerContent += ' * Time: {{time}}\n'; headerContent += ' */\n\n'; return headerContent; } /** * Инициализирует параметры приложения * @param defaultParams * @returns {string} * @private */ _initParams(defaultParams) { let params; if (this.params && this.params.params) { params = { ...defaultParams, ...this.params.params }; } else { params = defaultParams; } let content = this._getHeaderContent(); content += "import { IAppParam } from 'umbot';\n\n"; content += 'export default function(): IAppParam {\n'; content += '\treturn '; content += JSON.stringify(params, null, '\t'); content += ';\n'; content += '}\n'; return content; } /** * Инициализирует конфигурации приложения * @param defaultConfig * @returns {string} * @private */ _initConfig(defaultConfig) { let config; if (this.params.config) { config = { ...defaultConfig, ...this.params.config }; } else { config = defaultConfig; } if (this.params.isEnv) { config.env = `./.env`; } let content = this._getHeaderContent(); content += "import { IAppConfig } from 'umbot';\n\n"; content += 'export default function (): IAppConfig {\n'; content += '\treturn '; content += JSON.stringify(config, null, '\t'); content += ';\n'; content += '}\n'; return content; } /** * Заменяет все вхождения подстрок в строке * @param {string|string[]} find Строка или массив строк для поиска * @param {string|string[]} replace Строка или массив строк для замены * @param {string} str Исходная строка * @returns {string} Строка с произведенными заменами * @private */ _replace(find, replace, str) { if (typeof find === 'string') { return str.replace(new RegExp(find, 'g'), replace); } else { let res = str; const maxReplace = replace.length - 1; find.forEach((f, i) => { let r = replace[i]; if (r === undefined) { r = replace[maxReplace]; } res = res.replace(new RegExp(f, 'g'), r); }); return res; } } /** * Генерирует файл из шаблона * @param {string} templateContent Содержимое шаблона * @param {string} fileName Имя файла для создания * @returns {string} Путь к созданному файлу * @private */ _generateFile(templateContent, fileName) { const find = [ '{{date}}', '{{time}}', '{{name}}', '{{className}}', '__className__', '{{}}', '{{hostname}}', '{{port}}', ]; const name = this.#name.substring(0, 1).toUpperCase() + this.#name.substring(1); const date = `${new Date().getDate().toString().padStart(2, '0')}.${(new Date().getMonth() + 1).toString().padStart(2, '0')}.${new Date().getFullYear()}`; const time = `${new Date().getHours().toString().padStart(2, '0')}:${new Date().getMinutes().toString().padStart(2, '0')}`; const replace = [ date, time, this.#name, name, name, '', '"' + (this.params?.hostname || '0.0.0.0') + '"', this.params?.port || 3000, ]; fileName = this._replace(find, replace, fileName); const content = this._replace(find, replace, templateContent); utils.fwrite(fileName, content); return fileName; } /** * Создает файл конфигурации проекта * @param {string} path Путь к шаблонам * @private */ _getConfigFile(path) { console.log('Создается файл с конфигурацией приложения: ...'); const configFile = `${this.#path}/src/config/{{name}}Config.ts`; let configContent; if (utils.isFile(`${path}/config/defaultConfig.js`)) { const config = require(`${path}/config/defaultConfig`); configContent = this._initConfig(config.config); } else { configContent = ''; } this._generateFile(configContent, configFile); console.log('Файл с конфигурацией успешно создан'); } /** * Создает файл параметров проекта * @param {string} path Путь к шаблонам * @param {string} type Тип приложения * @private */ _getParamsFile(path, type) { console.log('Создается файл с параметрами приложения: ...'); const paramsFile = `${this.#path}/src/config/{{name}}Params.ts`; let paramsContent; if (utils.isFile(`${path}/config/${type}Params.js`)) { const param = require(`${path}/config/${type}Params`); paramsContent = this._initParams(param.params); } else { paramsContent = ''; } this._generateFile(paramsContent, paramsFile); console.log('Файл с параметрами успешно создан'); } createDockerFile(path) { const standardPath = __dirname + '/../template'; const dockerFile = `${path}/Dockerfile`; const dockerContent = this._getFileContent(`${standardPath}/docker/Dockerfile.text`); this._generateFile(dockerContent, dockerFile); console.log('Dockerfile успешно создан'); } createDeployFile(path) { const standardPath = __dirname + '/../template'; const deployFile = `${path}/.github/workflows/deploy.yml`; fs.mkdirSync(`${path}/.github`); fs.mkdirSync(`${path}/.github/workflows`); const deployContent = this._getFileContent(`${standardPath}/github/deploy.yml`); this._generateFile(deployContent, deployFile); console.log('deploy.yml успешно создан'); } /**. * Создает структуру проекта * @param {string} type Тип проекта (Default или Quiz) * @private */ _create(type = CreateController.T_DEFAULT) { if (![CreateController.T_DEFAULT, CreateController.T_QUIZ].includes(type)) { console.warn( 'Не удалось создать проект, так как не удалось определить тип создаваемого приложения', ); } else { const standardPath = __dirname + '/../template'; const srcPath = `${this.#path}/src`; if (!utils.isDir(srcPath)) { fs.mkdirSync(srcPath); } const configFile = `${srcPath}/config`; if (!utils.isDir(configFile)) { fs.mkdirSync(configFile); } const typeToLower = type.toLowerCase(); this._getConfigFile(standardPath); this._getParamsFile(standardPath, typeToLower); if (!(this.flags.includes('--minimal') && type === CreateController.T_DEFAULT)) { let controllerFile = `${srcPath}/controller`; if (!utils.isDir(controllerFile)) { fs.mkdirSync(controllerFile); } console.log('Создается класс с логикой приложения: ...'); controllerFile += '/{{className}}Controller.ts'; const controllerContent = this._getFileContent( `${standardPath}/controller/${type}Controller.ts.text`, ); this._generateFile(controllerContent, controllerFile); console.log('Класс с логикой приложения успешно создан'); } console.log('Создается index файл: ...'); let path = 'index'; const mode = this.params?.mode; if (mode === 'dev') { path += 'Dev'; } else if (mode === 'dev-online') { path += 'DevOnline'; } else if (mode === 'build') { path += 'Build'; } if (this.flags.includes('--minimal') && type === CreateController.T_DEFAULT) { path += 'Min'; } const indexFile = `${srcPath}/index.ts`; const indexContent = this._getFileContent(`${standardPath}/${path}.ts.text`); this._generateFile(indexContent, indexFile); console.log('index.ts успешно создан'); const packageFile = `${this.#path}/package.json`; const packageContent = this._getFileContent(`${standardPath}/package.json.text`); this._generateFile(packageContent, packageFile); console.log('package.json успешно создан'); const tsconfigFile = `${this.#path}/tsconfig.json`; const tsconfigContent = this._getFileContent(`${standardPath}/tsconfig.json`); this._generateFile(tsconfigContent, tsconfigFile); console.log('tsconfig.json успешно создан'); if (this.flags.includes('--prod')) { this.createDeployFile(this.#path); this.createDockerFile(this.#path); } console.log(`Проект успешно создан, и находится в директории: ${this.#path}`); } } /** * Генерирует файл * @param fileName * @param content */ generateFile(fileName, content) { utils.fwrite(`${this.#path}/${fileName}`, content); console.log('.env файл успешно создан'); } /** * Форматирует проект через prettier */ format() { exec(`prettier.cmd --write ${this.#path}`, (error) => { if (error) { console.error(`exec error: ${error}`); } }); } /** * Инициализация параметров проекта * @param name Имя проекта * @param type Тип проекта * @public */ init(name = null, type = CreateController.T_DEFAULT) { const correctName = name?.replace(/\W/g, '_'); if (correctName) { this.#name = correctName; this.#path = ''; if (this.params && this.params.path) { this.#path = this.params.path; const paths = this.#path.split('/'); let path = ''; paths.forEach((dir) => { path += `${dir}/`; if (dir !== './' && path !== '../') { if (!utils.isDir(path)) { fs.mkdirSync(path); } } }); } else { this.#path += correctName; if (!utils.isDir(this.#path)) { fs.mkdirSync(this.#path); } } this._create(type); } else { console.error('Не указано имя проекта'); } } } /** * Контроллер для создания новых проектов и компонентов. */ exports.create = CreateController;