zcatalyst-cli
Version:
Command Line Tool for CATALYST
387 lines (386 loc) • 20.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const prompt_1 = __importDefault(require("../../../prompt"));
const logger_1 = require("../../../util_modules/logger");
const endpoints_1 = require("../../../endpoints");
const archiver_1 = __importDefault(require("../../../archiver"));
const option_1 = require("../../../util_modules/option");
const path_1 = __importStar(require("path"));
const runtime_store_1 = __importDefault(require("../../../runtime-store"));
const fs_1 = require("../../../util_modules/fs");
const ansi_colors_1 = require("ansi-colors");
const config_1 = require("../../../util_modules/config");
const console_1 = require("console");
const error_1 = __importDefault(require("../../../error"));
const constants_1 = require("../../../util_modules/constants");
const project_1 = require("../../../util_modules/project");
const toml_1 = require("../../../util_modules/toml");
const async_1 = require("../../../util_modules/fs/lib/async");
function getFrameworkOption(frameworks, source) {
return __awaiter(this, void 0, void 0, function* () {
let frameworkOpt = (0, option_1.getOptionValue)('framework');
if (frameworkOpt &&
frameworks.findIndex((framework) => framework.name === frameworkOpt) === -1) {
(0, logger_1.warning)('Given framework is not supported in Slate.');
frameworkOpt = '';
}
if (!frameworkOpt) {
const filteredFrameworks = frameworks.filter((framework) => framework.name !== 'other');
const { selectedFramework } = yield prompt_1.default.ask(prompt_1.default.question('selectedFramework', 'Select a framework to start with: ', {
type: 'list',
choices: filteredFrameworks.map(({ label, name }) => prompt_1.default.choice(label, { value: name, short: label }))
}));
frameworkOpt = selectedFramework;
}
return { name: frameworkOpt, source };
});
}
function validateAppName(appName, key) {
if (appName.length === 0) {
return `${key} name cannot be empty`;
}
if (appName.length > 45) {
return `${key} name cannot be more than 45 characters`;
}
if (!appName.match(/^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/)) {
return `${key} name must be alphanumeric and cannot contain consecutive hyphens or hyphens at the beginning or end.`;
}
return true;
}
function getAppName(existingSlates, frameworkOpt) {
return __awaiter(this, void 0, void 0, function* () {
let appName = (0, option_1.getOptionValue)('name');
if (!appName) {
const { name } = yield prompt_1.default.ask(prompt_1.default.question('name', 'Please provide the name for your app:', {
type: 'input',
default: frameworkOpt,
validate: (name) => {
if (existingSlates.findIndex((targ) => targ.name === name) !== -1)
return 'Slate already configured with this name.';
return validateAppName(name, 'App');
}
}));
appName = name;
}
else {
if (existingSlates.findIndex((targ) => targ.name === appName) !== -1) {
throw new error_1.default('Slate already configured with this name.');
}
else {
const isValidApp = validateAppName(appName, 'App');
if (isValidApp !== true) {
throw new error_1.default(isValidApp + '');
}
}
}
return appName;
});
}
function handleTemplate(appName, { templateName, frameworkName } = {}) {
return __awaiter(this, void 0, void 0, function* () {
const projectRoot = (0, project_1.getProjectRoot)();
const appPath = path_1.default.join(projectRoot, appName);
const folderExists = yield fs_1.ASYNC.dirExists(appPath);
const overwriteAns = folderExists
? yield prompt_1.default.ask(prompt_1.default.question('overwrite', `Directory ${(0, ansi_colors_1.underline)(appPath)} already exists. Overwrite?`, {
type: 'confirm',
default: false
}))
: { overwrite: true };
if (!overwriteAns.overwrite) {
(0, logger_1.warning)('Skipping slate template setup');
return appPath;
}
yield fs_1.ASYNC.deleteDir(appPath).catch(console_1.error);
const templatePath = 'slate-main' + (templateName ? `/templates/${templateName}` : `/examples/${frameworkName}`);
const responseZip = (yield (yield (0, endpoints_1.slateAPI)()).downloadTemplate());
yield new archiver_1.default()
.load(responseZip)
.extract(appPath, templatePath, {
ignoreLevel: 3
})
.finalize();
return appPath;
});
}
function addExistingSlate(existingSlates) {
return __awaiter(this, void 0, void 0, function* () {
yield prompt_1.default.register('file-path');
const projectRoot = (0, project_1.getProjectRoot)();
const { sourcePath } = yield prompt_1.default.ask(prompt_1.default.question('sourcePath', 'Please provide the source path of your slate service: ', {
type: 'file-path',
validate: (pth) => __awaiter(this, void 0, void 0, function* () {
const buildPath = (0, path_1.resolve)(projectRoot, pth.value);
if (existingSlates.findIndex((targ) => targ.source === buildPath) !== -1)
return 'Path is already linked.';
if (yield fs_1.ASYNC.dirExists(buildPath)) {
return true;
}
return 'Path does not exist';
}),
depth: 5,
empTxt: ' ',
rootPath: projectRoot,
ignoreFiles: true,
excludeDir: true,
exclude: ['**/node_modules', '**/.git', '**/.catalyst']
}));
prompt_1.default.deregister('file-path');
return sourcePath;
});
}
function detectFramework(source, frameworkList) {
return __awaiter(this, void 0, void 0, function* () {
const frameworkOpt = (0, option_1.getOptionValue)('framework');
if (frameworkOpt &&
frameworkList.findIndex((framework) => framework.name === frameworkOpt) === -1) {
return { name: frameworkOpt, source };
}
const packageJsonPath = (0, path_1.join)(source, constants_1.FILENAME.package_json);
const packageJsonExists = yield fs_1.ASYNC.fileExists(packageJsonPath);
if (!packageJsonExists) {
(0, logger_1.warning)('Unable to find the package.json file at the given source path');
return getFrameworkOption(frameworkList, source);
}
const data = yield fs_1.ASYNC.readJSONFile(packageJsonPath);
const dependencies = Object.assign(Object.assign({}, data === null || data === void 0 ? void 0 : data.dependencies), data === null || data === void 0 ? void 0 : data.devDependencies);
if (!dependencies) {
(0, logger_1.warning)('The package.json file does not contain dependencies');
return getFrameworkOption(frameworkList, source);
}
const dependencyNames = Object.keys(dependencies);
const matchesKeywordPattern = (dep, keyword) => {
if (!keyword)
return false;
const normalizedKeyword = keyword.toLowerCase().replace(/[^a-z0-9]/g, '');
const normalizedDep = dep.toLowerCase().replace(/[^a-z0-9]/g, '');
if (normalizedDep === normalizedKeyword) {
return true;
}
if (normalizedDep.startsWith(`${normalizedKeyword}-`) ||
normalizedDep.endsWith(`-${normalizedKeyword}`) ||
normalizedDep.startsWith(`@${normalizedKeyword}/`) ||
normalizedDep.endsWith(`/${normalizedKeyword}`) ||
normalizedDep === `@${normalizedKeyword}`) {
return true;
}
const segments = dep.split(/[-/@]/).map((s) => s.toLowerCase());
if (segments.includes(normalizedKeyword)) {
return true;
}
return false;
};
let bestMatch = null;
let maxMatches = 0;
const frameworkScores = [];
for (const framework of frameworkList) {
if (!framework.keywords || framework.keywords.length === 0 || framework.name === 'other') {
continue;
}
const matchingKeywords = framework.keywords.filter((keyword) => {
if (!keyword || keyword.trim() === '')
return false;
return dependencyNames.some((dep) => matchesKeywordPattern(dep, keyword));
});
const matchCount = matchingKeywords.length;
let score = matchCount;
const priorityKeywords = ['next', 'nuxt', 'gatsby', 'angular', 'vue', 'solid-js'];
const hasPriorityKeyword = matchingKeywords.some((keyword) => priorityKeywords.includes(keyword.toLowerCase()));
if (hasPriorityKeyword) {
score += 10;
}
if (matchCount > 1) {
score += matchCount * 2;
}
if (matchCount > 0) {
frameworkScores.push({ framework, score, matches: matchingKeywords });
}
if (score > maxMatches) {
maxMatches = score;
bestMatch = framework;
}
}
const perfectMatches = frameworkScores.filter(({ framework }) => framework.keywords.length > 0 &&
framework.keywords.every((keyword) => dependencyNames.some((dep) => matchesKeywordPattern(dep, keyword))));
if (perfectMatches.length > 0) {
const filteredPerfectMatches = perfectMatches.filter((a) => !perfectMatches.some((b) => b !== a &&
b.framework.keywords.length > a.framework.keywords.length &&
a.framework.keywords.every((k) => b.framework.keywords.includes(k))));
filteredPerfectMatches.sort((a, b) => b.framework.keywords.length - a.framework.keywords.length);
const best = filteredPerfectMatches[0];
return { name: best.framework.name, source };
}
if (!bestMatch) {
(0, logger_1.warning)('No matching framework found in package.json dependencies');
return getFrameworkOption(frameworkList, source);
}
return { name: bestMatch.name, source };
});
}
function getConfigDetails(frameworkOpt, frameworks) {
return __awaiter(this, void 0, void 0, function* () {
const slateConfigs = frameworks.find((framework) => frameworkOpt.toLowerCase() === framework.name);
if (frameworkOpt === 'static') {
let deploymentName = 'default';
({ deploymentName } = yield prompt_1.default.ask(prompt_1.default.question('deploymentName', 'Provide your Deployment Name:', {
type: 'input',
default: 'default',
validate: (name) => {
return validateAppName(name, 'Deployment');
}
})));
return { framework: 'static', deployment_name: deploymentName };
}
const slateConfigDetails = {
framework: frameworkOpt,
install_command: slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.install_command.value,
build_path: slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.output_dir.value,
build_command: slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.build_command.value,
deployment_name: 'default',
root_path: './'
};
let editDefaultConfig;
if (!(0, option_1.getOptionValue)('default')) {
if (slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings) {
(0, console_1.log)(`Auto-detected App Configuration (${(0, ansi_colors_1.bold)(frameworkOpt)}):\n` +
`${(0, ansi_colors_1.grey)((0, ansi_colors_1.bold)('Install Command: ') +
(slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.install_command.placeholder))}\n` +
`${(0, ansi_colors_1.grey)((0, ansi_colors_1.bold)('Build Command: ') + (slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.build_command.placeholder))}\n` +
`${(0, ansi_colors_1.grey)((0, ansi_colors_1.bold)('Build Path: ') + (slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.output_dir.placeholder))}\n` +
`${(0, ansi_colors_1.grey)((0, ansi_colors_1.bold)('Deployment Name: ') + slateConfigDetails.deployment_name)}`);
editDefaultConfig = yield prompt_1.default.ask(prompt_1.default.question('config', 'Do you want to modify these default configurations?', {
type: 'confirm',
default: false
}));
}
else {
(0, logger_1.warning)('Unable to detect the app configuration for the selected framework.');
}
}
if (!(slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings) || (editDefaultConfig === null || editDefaultConfig === void 0 ? void 0 : editDefaultConfig.config)) {
const config = yield prompt_1.default.ask(prompt_1.default.question('installCommand', 'Provide your Install Command:', {
type: 'input',
default: slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.install_command.value,
validate: (value) => __awaiter(this, void 0, void 0, function* () {
value = value.trim();
if (value === '')
return 'Please provide a valid install command';
else if (value.length > 50)
return 'Cannot exceed 50 characters.';
return true;
})
}), prompt_1.default.question('buildCommand', 'Provide your Build Command:', {
type: 'input',
default: slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.build_command.value,
validate: (value) => __awaiter(this, void 0, void 0, function* () {
value = value.trim();
if (value === '')
return 'Please provide a valid build command';
else if (value.length > 50)
return 'Cannot exceed 50 characters.';
return true;
})
}), prompt_1.default.question('buildPath', 'Provide your Build Path:', {
type: 'input',
default: slateConfigs === null || slateConfigs === void 0 ? void 0 : slateConfigs.settings.output_dir.value,
validate: (value) => __awaiter(this, void 0, void 0, function* () {
value = value.trim();
if (value === '')
return 'Please provide a valid build path';
else if (/[#%*$@?]/.test(value))
return 'Build path containing invalid characters.';
else if (value.length > 50)
return 'Cannot exceed 50 characters.';
return true;
})
}), prompt_1.default.question('deploymentName', 'Provide your Deployment Name:', {
type: 'input',
default: 'default',
validate: (name) => {
return validateAppName(name, 'Deployment');
}
}));
(slateConfigDetails.install_command = config.installCommand),
(slateConfigDetails.build_path = config.buildPath),
(slateConfigDetails.build_command = config.buildCommand),
(slateConfigDetails.deployment_name = config.deploymentName);
}
return slateConfigDetails || {};
});
}
exports.default = (add) => __awaiter(void 0, void 0, void 0, function* () {
const frameworks = yield (yield (0, endpoints_1.slateAPI)()).getFrameworks();
const templateName = (0, option_1.getOptionValue)('template');
const frameworkOpt = add
? yield detectFramework(yield addExistingSlate(config_1.slateConfig.raw() || []), frameworks)
: yield getFrameworkOption(frameworks);
const appName = yield getAppName(config_1.slateConfig.raw() || [], frameworkOpt.name);
const payload = {
name: appName,
source: frameworkOpt.source ||
(yield handleTemplate(appName, { templateName, frameworkName: frameworkOpt.name }))
};
const slateConfigDetails = yield getConfigDetails(frameworkOpt.name, frameworks);
if (frameworkOpt.name !== 'static') {
const slateRunConfig = (0, option_1.getOptionValue)('default')
? { devCommand: 'npm start' }
: yield prompt_1.default.ask(prompt_1.default.question('devCommand', 'Please provide your Development Command:', {
type: 'input',
default: 'npm start',
validate: (value) => __awaiter(void 0, void 0, void 0, function* () {
value = value.trim();
if (value === '')
return 'Please provide a valid development command';
else if (value.length > 50)
return 'Cannot exceed 50 characters.';
return true;
})
}));
yield fs_1.ASYNC.writeJSONFile((0, path_1.join)(payload.source, constants_1.FILENAME.cli_config), {
slate: {
dev_command: slateRunConfig.devCommand
}
});
}
const pth = path_1.default.join(payload.source, '.catalyst', constants_1.FILENAME.slate_config);
const warnContent = '# ⚠️ This file is automatically generated by the Catalyst CLI when you link or create a Slate app,and is used only for the initial deployment to the Catalyst console. \
\n# ⚠️ Please do not modify this file, as it is fully managed by the CLI.\n';
yield (0, async_1.ensureFile)(pth, true);
yield fs_1.ASYNC.writeFile(pth, warnContent + '\n' + (0, toml_1.convertTOML)(slateConfigDetails));
runtime_store_1.default.set('context.payload.slate.targets', [payload]);
});