@optro/create-trello-powerup
Version:
Easily create Trello Power-Ups from the Command Line
263 lines (262 loc) • 14.3 kB
JavaScript
"use strict";
const tslib_1 = require("tslib");
const command_1 = require("@oclif/command");
const inquirer = tslib_1.__importStar(require("inquirer"));
const string_1 = require("./utility/string");
const path_1 = tslib_1.__importDefault(require("path"));
const child_process_1 = require("child_process");
const shell = tslib_1.__importStar(require("shelljs"));
const replace = tslib_1.__importStar(require("replace-in-file"));
const filenamify_1 = tslib_1.__importDefault(require("filenamify"));
const constants_1 = require("./utility/constants");
const fs_1 = require("./utility/fs");
class CreateTrelloPowerup extends command_1.Command {
async run() {
// Read Arguments
const { args, flags } = this.parse(CreateTrelloPowerup);
this.debug(args, flags);
// Startup Text
this.log();
this.log('┌' + '─'.repeat(27) + '┐');
this.log('│ Create Trello Power-Up 🚀 │');
this.log('└' + '─'.repeat(27) + '┘');
this.log();
this.log('Generate a new Trello Power-Up in minutes.\n');
this.log('Find more information in our step-by-step guide:');
this.log('» https://vendor.optro.cloud/build-trello-powerup');
this.log('\n');
if (!shell.which('git')) {
shell.echo('Missing Required Package: git');
shell.exit(1);
}
// Get Information from User
const parameters = await inquirer.prompt([
{
name: 'name',
message: '1. What is the Power-Up Name?',
type: 'input',
default: 'my-powerup',
when: (_answers => !args.powerupName),
validate: input => {
const folder = filenamify_1.default(input).replace(' ', '-');
if (fs_1.doesFolderExist(folder)) {
return 'This folder already exists - Try another name';
}
return true;
},
},
{
name: 'capabilities',
message: `${args.powerupName ? '1' : '2'}. What Capabilities should be enabled?`,
type: 'checkbox',
choices: [
{ name: 'Attachment Section', value: 'attachment-section' },
{ name: 'Attachment Thumbnail', value: 'attachment-thumbnail' },
{ name: 'Authorization Status', value: 'authorization-status' },
{ name: 'Board Button', value: 'board-buttons', checked: true },
{ name: 'Card Back Section', value: 'card-back-section', checked: true },
{ name: 'Card Badges', value: 'card-badges', checked: true },
{ name: 'Card Button', value: 'card-buttons', checked: true, disabled: 'Mandatory' },
{ name: 'Card Detail Badge', value: 'card-detail-badges', checked: true },
{ name: 'Card From URL', value: 'card-from-url' },
{ name: 'Format URL', value: 'format-url' },
{ name: 'List Action', value: 'list-actions', checked: true },
{ name: 'List Sorter', value: 'list-sorters', checked: true },
{ name: 'On Enable', value: 'on-enable', checked: true },
{ name: 'On Disable', value: 'on-disable', checked: true },
{ name: 'Remove Data', value: 'remove-data' },
{ name: 'Save Attachment', value: 'save-attachment' },
{ name: 'Show Authorization', value: 'show-authorization' },
{ name: 'Show Settings', value: 'show-settings', checked: true },
],
filter: input => {
return [...input, 'card-buttons'];
},
},
{
name: 'confirm',
message: `${args.powerupName ? '2' : '3'}. Confirm Power-Up generation?`,
type: 'confirm',
default: true,
},
]);
if (!parameters.confirm) {
this.error('User Cancelled Project Generation');
this.exit(0);
}
const folderName = (args.powerupName || filenamify_1.default(parameters.name)).toLowerCase().replace(/\s/gi, '-');
// Check if Directory Exists
if (fs_1.doesFolderExist(folderName)) {
this.error('The project folder specified already exists! Exiting.');
this.exit(1);
}
// 1. Clone the Template Repo
this.log('[1/4] Cloning Template...');
try {
await fs_1.downloadRepo(constants_1.TEMPLATE_REPO, path_1.default.join(process.cwd(), folderName));
fs_1.deleteFolder(path_1.default.join(process.cwd(), folderName, '.git'));
}
catch (error) {
this.error('A fatal error occurred during cloning template', error);
this.exit(1);
}
// 2. Delete Unused Folders
this.log('[2/4] Deleting Unused Resources...');
try {
const capabilitiesToRemove = constants_1.ALL_CAPABILITIES.filter(capability => !parameters.capabilities.includes(capability));
for (const capability of capabilitiesToRemove) {
fs_1.deleteFolder(path_1.default.join(process.cwd(), folderName, 'src', capability));
}
fs_1.deleteFolder(path_1.default.join(process.cwd(), folderName, '.github', 'ISSUE_TEMPLATE'));
fs_1.deleteFile(path_1.default.join(process.cwd(), 'webpack.config.ts'));
fs_1.deleteFile(path_1.default.join(process.cwd(), 'src', 'router.tsx'));
fs_1.deleteFile(path_1.default.join(process.cwd(), 'src', 'capabilities.ts'));
}
catch (error) {
this.error('A fatal error occurred while deleting unused resources', error);
this.exit(2);
}
// 3. Configure Dynamic Files
this.log('[3/4] Configuring Dynamic Files...');
try {
fs_1.copyFile(path_1.default.join(__dirname, '..', 'templates', 'webpack.config.ts'), path_1.default.join(process.cwd(), folderName, 'webpack.config.ts'));
fs_1.copyFile(path_1.default.join(__dirname, '..', 'templates', 'router.tsx'), path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'));
fs_1.copyFile(path_1.default.join(__dirname, '..', 'templates', 'capabilities.ts'), path_1.default.join(process.cwd(), folderName, 'src', 'capabilities.ts'));
const applicableCapabilities = constants_1.ALL_HTML_BACKED_CAPABILITIES.filter(c => parameters.capabilities.includes(c));
for (const capability of applicableCapabilities.reverse()) {
// 3.1 - Webpack Config File
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'webpack.config.ts'),
from: constants_1.WEBPACK_REPLACEMENT_STRING,
to: string_1.getWebpackHtmlPlugin(capability),
});
// 3.2 React Router File
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'),
from: constants_1.REACT_ROUTER_MODULE_REPLACEMENT_STRING,
to: string_1.getReactRouterRoute(capability),
});
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'),
from: constants_1.REACT_ROUTER_LOADER_REPLACEMENT_STRING,
to: string_1.getReactRouterLoader(capability),
});
}
for (const capability of parameters.capabilities) {
// 3.3 Capabilities File
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'capabilities.ts'),
from: constants_1.CAPABILITIES_IMPORT_REPLACEMENT_STRING,
to: string_1.getCapabilityImport(capability),
});
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'capabilities.ts'),
from: constants_1.CAPABILITIES_REPLACEMENT_STRING,
to: string_1.getCapabilityModule(capability),
});
}
// 3.4 Environmental Variables File
const powerupId = args.powerupId ? args.powerupId : 'UNSPECIFIED';
const apiKey = args.apiKey ? args.apiKey : 'UNSPECIFIED';
const licenseType = args.licenseType ? args.licenseType : 'UNSPECIFIED';
fs_1.writeToFile(path_1.default.join(process.cwd(), folderName, '.env'), string_1.getEnv(powerupId, folderName, apiKey, licenseType));
// 3.6 Monetization
// 3.6.1 Add Dependency for the Optro API Client
fs_1.addDependency(path_1.default.join(process.cwd(), folderName, 'package.json'), '@optro/api-client', '^1.0.3');
// 3.6.2 Add License Provider to Router
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'),
from: constants_1.REACT_ROUTER_IMPORT_REPLACEMENT_STRING,
to: string_1.getReactRouterMonetizationImport(),
});
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'),
from: constants_1.REACT_ROUTER_CLIENT_REPLACEMENT_STRING,
to: string_1.getReactRouterMonetizationClient(),
});
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'),
from: constants_1.REACT_ROUTER_CLIENT_PROVIDER_REPLACEMENT_STRING,
to: string_1.getReactRouterMonetizationProvider(),
});
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'router.tsx'),
from: constants_1.REACT_ROUTER_CLIENT_PROVIDER_CLOSE_REPLACEMENT_STRING,
to: string_1.getReactRouterMonetizationProviderClose(),
});
// 3.6.2 Add Example Licensed Feature (Colorized Cards)
if (parameters.capabilities.includes('card-buttons')) {
// 3.6.3 Add import for LicenseConditional to the Card Button component
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'card-button', 'CardButton.tsx'),
from: constants_1.CARD_BUTTON_CONDITIONAL_IMPORT_REPLACEMENT_STRING,
to: string_1.getCardButtonMonetizationImport(),
});
// 3.6.4 Add LicenseConditional to the Render Code
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'card-button', 'CardButton.tsx'),
from: constants_1.CARD_BUTTON_CONDITIONAL_START_REPLACEMENT_STRING,
to: string_1.getCardButtonMonetizationStartTag(),
});
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'card-button', 'CardButton.tsx'),
from: constants_1.CARD_BUTTON_CONDITIONAL_END_REPLACEMENT_STRING,
to: string_1.getCardButtonMonetizationEndTag(),
});
// 3.6.5 Add License Status Display
replace.replaceInFileSync({
files: path_1.default.join(process.cwd(), folderName, 'src', 'card-button', 'CardButton.tsx'),
from: constants_1.CARD_BUTTON_STATUS_REPLACEMENT_STRING,
to: string_1.getLicenseStatusTag(),
});
}
// 3.5 Cleanup Unused Dependencies
if (!parameters.capabilities.includes('card-back-section')) {
fs_1.deleteDependency(path_1.default.join(process.cwd(), folderName, 'package.json'), 'lottie-react');
}
if (!parameters.capabilities.includes('attachment-thumbnail')) {
fs_1.deleteDependency(path_1.default.join(process.cwd(), folderName, 'package.json'), 'unique-names-generator');
}
if (!parameters.capabilities.includes('card-buttons')) {
fs_1.deleteDependency(path_1.default.join(process.cwd(), folderName, 'package.json'), 'react-color');
}
}
catch (error) {
this.error('A fatal error occurred during configuring dynamic files', error);
this.exit(3);
}
// 4. Install Dependencies
this.log('[4/4] Installing Dependencies...');
try {
child_process_1.execSync(`yarn --cwd "${path_1.default.join(process.cwd(), folderName)}" install --silent`, { stdio: 'inherit' });
}
catch (error) {
this.error('A fatal error occurred during installing dependencies', error);
this.exit(4);
}
// Build complete
this.log();
this.log('┌' + '─'.repeat(29) + '┐');
this.log('│ Finished building Power-Up! │');
this.log('└' + '─'.repeat(29) + '┘');
this.log();
const doneParameters = await inquirer.prompt([
{
name: 'start',
message: 'Start the Power-Up in Development Mode (yarn watch)?',
type: 'confirm',
default: true,
},
]);
if (doneParameters.start) {
child_process_1.execSync(`yarn --cwd "${path_1.default.join(process.cwd(), folderName)}" watch`, { stdio: 'inherit' });
}
}
}
CreateTrelloPowerup.description = 'Easily create Trello Power-Ups from the Command Line';
CreateTrelloPowerup.flags = {
version: command_1.flags.version({ char: 'v' }),
help: command_1.flags.help({ char: 'h' }),
};
CreateTrelloPowerup.args = constants_1.INPUT_ARGUMENTS;
module.exports = CreateTrelloPowerup;