#!/usr/bin/env node 'use strict'; // FIXME /* eslint-disable import/extensions */ const _ = require('lodash'); const path = require('path'); const resolveCwd = require('resolve-cwd'); const { yellow } = require('chalk'); const { Command, Option } = require('commander'); const inquirer = require('inquirer'); const program = new Command(); const packageJSON = require('../package.json'); const { promptEncryptionKey, confirmMessage, parseURL, forceOption, } = require('../lib/commands/utils/commander'); const { ifOptions, assertUrlHasProtocol, exitWith } = require('../lib/commands/utils/helpers'); const checkCwdIsStrapiApp = (name) => { const logErrorAndExit = () => { console.log( `You need to run ${yellow( `strapi ${name}` )} in a Strapi project. Make sure you are in the right directory.` ); process.exit(1); }; try { const pkgJSON = require(`${process.cwd()}/package.json`); if (!_.has(pkgJSON, 'dependencies.@strapi/strapi')) { logErrorAndExit(name); } } catch (err) { logErrorAndExit(name); } }; const getLocalScript = (name) => (...args) => { checkCwdIsStrapiApp(name); const cmdPath = resolveCwd.silent(`@strapi/strapi/lib/commands/${name}`); if (!cmdPath) { console.log( `Error loading the local ${yellow( name )} command. Strapi might not be installed in your "node_modules". You may need to run "yarn install".` ); process.exit(1); } const script = require(cmdPath); Promise.resolve() .then(() => { return script(...args); }) .catch((error) => { console.error(error); process.exit(1); }); }; // Initial program setup program.storeOptionsAsProperties(false).allowUnknownOption(true); program.helpOption('-h, --help', 'Display help for command'); program.addHelpCommand('help [command]', 'Display help for command'); // `$ strapi version` (--version synonym) program.version(packageJSON.version, '-v, --version', 'Output the version number'); program .command('version') .description('Output the version of Strapi') .action(() => { process.stdout.write(`${packageJSON.version}\n`); process.exit(0); }); // `$ strapi console` program .command('console') .description('Open the Strapi framework console') .action(getLocalScript('console')); // `$ strapi new` program .command('new ') .option('--no-run', 'Do not start the application after it is created') .option('--use-npm', 'Force usage of npm instead of yarn to create the project') .option('--debug', 'Display database connection errors') .option('--quickstart', 'Create quickstart app') .option('--dbclient ', 'Database client') .option('--dbhost ', 'Database host') .option('--dbport ', 'Database port') .option('--dbname ', 'Database name') .option('--dbusername ', 'Database username') .option('--dbpassword ', 'Database password') .option('--dbssl ', 'Database SSL') .option('--dbfile ', 'Database file path for sqlite') .option('--dbforce', 'Allow overwriting existing database content') .option('-ts, --typescript', 'Create a typescript project') .description('Create a new application') .action(require('../lib/commands/new')); // `$ strapi start` program .command('start') .description('Start your Strapi application') .action(getLocalScript('start')); // `$ strapi develop` program .command('develop') .alias('dev') .option('--no-build', 'Disable build') .option('--watch-admin', 'Enable watch', false) .option('--polling', 'Watch for file changes in network directories', false) .option('--browser ', 'Open the browser', true) .description('Start your Strapi application in development mode') .action(getLocalScript('develop')); // $ strapi generate program .command('generate') .description('Launch the interactive API generator') .action(() => { checkCwdIsStrapiApp('generate'); process.argv.splice(2, 1); require('@strapi/generators').runCLI(); }); // `$ strapi generate:template ` program .command('templates:generate ') .description('Generate template from Strapi project') .action(getLocalScript('generate-template')); program .command('build') .option('--no-optimization', 'Build the admin app without optimizing assets') .description('Build the strapi admin app') .action(getLocalScript('build')); // `$ strapi install` program .command('install [plugins...]') .description('Install a Strapi plugin') .action(getLocalScript('install')); // `$ strapi uninstall` program .command('uninstall [plugins...]') .description('Uninstall a Strapi plugin') .option('-d, --delete-files', 'Delete files', false) .action(getLocalScript('uninstall')); // `$ strapi watch-admin` program .command('watch-admin') .option('--browser ', 'Open the browser', true) .description('Start the admin development server') .action(getLocalScript('watchAdmin')); program .command('configuration:dump') .alias('config:dump') .description('Dump configurations of your application') .option('-f, --file ', 'Output file, default output is stdout') .option('-p, --pretty', 'Format the output JSON with indentation and line breaks', false) .action(getLocalScript('configurationDump')); program .command('configuration:restore') .alias('config:restore') .description('Restore configurations of your application') .option('-f, --file ', 'Input file, default input is stdin') .option('-s, --strategy ', 'Strategy name, one of: "replace", "merge", "keep"') .action(getLocalScript('configurationRestore')); // Admin program .command('admin:create-user') .alias('admin:create') .description('Create a new admin') .option('-e, --email ', 'Email of the new admin') .option('-p, --password ', 'Password of the new admin') .option('-f, --firstname ', 'First name of the new admin') .option('-l, --lastname ', 'Last name of the new admin') .action(getLocalScript('admin-create')); program .command('admin:reset-user-password') .alias('admin:reset-password') .description("Reset an admin user's password") .option('-e, --email ', 'The user email') .option('-p, --password ', 'New password for the user') .action(getLocalScript('admin-reset')); program .command('routes:list') .description('List all the application routes') .action(getLocalScript('routes/list')); program .command('middlewares:list') .description('List all the application middlewares') .action(getLocalScript('middlewares/list')); program .command('policies:list') .description('List all the application policies') .action(getLocalScript('policies/list')); program .command('content-types:list') .description('List all the application content-types') .action(getLocalScript('content-types/list')); program .command('hooks:list') .description('List all the application hooks') .action(getLocalScript('hooks/list')); program .command('services:list') .description('List all the application services') .action(getLocalScript('services/list')); program .command('controllers:list') .description('List all the application controllers') .action(getLocalScript('controllers/list')); // `$ strapi opt-out-telemetry` program .command('telemetry:disable') .description('Disable anonymous telemetry and metadata sending to Strapi analytics') .action(getLocalScript('opt-out-telemetry')); // `$ strapi opt-in-telemetry` program .command('telemetry:enable') .description('Enable anonymous telemetry and metadata sending to Strapi analytics') .action(getLocalScript('opt-in-telemetry')); program .command('ts:generate-types') .description(`Generate TypeScript typings for your schemas`) .option( '-o, --out-dir ', 'Specify a relative directory in which the schemas definitions will be generated' ) .option('-f, --file ', 'Specify a filename to store the schemas definitions') .option('--verbose', `Display more information about the types generation`, false) .option('-s, --silent', `Run the generation silently, without any output`, false) .action(getLocalScript('ts/generate-types')); if (process.env.STRAPI_EXPERIMENTAL === 'true') { // `$ strapi transfer` program .command('transfer') .description('Transfer data from one source to another') .addOption( new Option( '--from ', `URL of the remote Strapi instance to get data from` ).argParser(parseURL) ) .addOption( new Option( '--to ', `URL of the remote Strapi instance to send data to` ).argParser(parseURL) ) .addOption(forceOption) // Validate URLs .hook( 'preAction', ifOptions( (opts) => opts.from, (thisCommand) => assertUrlHasProtocol(thisCommand.opts().from, ['https:', 'http:']) ) ) .hook( 'preAction', ifOptions( (opts) => opts.to, (thisCommand) => assertUrlHasProtocol(thisCommand.opts().to, ['https:', 'http:']) ) ) .hook( 'preAction', ifOptions( (opts) => !opts.from && !opts.to, () => exitWith(1, 'At least one source (from) or destination (to) option must be provided') ) ) .hook( 'preAction', confirmMessage( 'The import will delete all data in the remote database. Are you sure you want to proceed?' ) ) .allowExcessArguments(false) .action(getLocalScript('transfer/transfer')); } // `$ strapi export` program .command('export') .description('Export data from Strapi to file') .addOption( new Option('--no-encrypt', `Disables 'aes-128-ecb' encryption of the output file`).default(true) ) .addOption(new Option('--no-compress', 'Disables gzip compression of output file').default(true)) .addOption( new Option( '-k, --key ', 'Provide encryption key in command instead of using the prompt' ) ) .addOption(new Option('-f, --file ', 'name to use for exported file (without extensions)')) .allowExcessArguments(false) .hook('preAction', promptEncryptionKey) .action(getLocalScript('transfer/export')); // `$ strapi import` program .command('import') .description('Import data from file to Strapi') .requiredOption( '-f, --file ', 'path and filename for the Strapi export file you want to import' ) .addOption( new Option( '-k, --key ', 'Provide encryption key in command instead of using the prompt' ) ) .addOption(forceOption) .allowExcessArguments(false) .hook('preAction', async (thisCommand) => { const opts = thisCommand.opts(); const ext = path.extname(String(opts.file)); // check extension to guess if we should prompt for key if (ext === '.enc') { if (!opts.key) { const answers = await inquirer.prompt([ { type: 'password', message: 'Please enter your decryption key', name: 'key', }, ]); if (!answers.key?.length) { exitWith(0, 'No key entered, aborting import.'); } opts.key = answers.key; } } }) .hook( 'preAction', confirmMessage( 'The import will delete all data in your database. Are you sure you want to proceed?' ) ) .action(getLocalScript('transfer/import')); program.parseAsync(process.argv);