diff --git a/packages/strapi/bin/strapi.js b/packages/strapi/bin/strapi.js index 686edf3109..5cadf35e39 100755 --- a/packages/strapi/bin/strapi.js +++ b/packages/strapi/bin/strapi.js @@ -230,11 +230,10 @@ program .option('-p, --password ', 'New password for the user') .action(getLocalScript('admin-reset')); -// `$ strapi export-template ` +// `$ strapi export:template ` program - .command('export-template') - .arguments('') + .command('export:template ') .description('Export project as Strapi template') - .action(getLocalScript('exportTemplate')); + .action(getLocalScript('export-template')); program.parseAsync(process.argv); diff --git a/packages/strapi/lib/commands/__tests__/export-template.test.js b/packages/strapi/lib/commands/__tests__/export-template.test.js new file mode 100644 index 0000000000..a0c61937fd --- /dev/null +++ b/packages/strapi/lib/commands/__tests__/export-template.test.js @@ -0,0 +1,92 @@ +'use strict'; +jest.mock('fs-extra', () => ({ + ensureDir: jest.fn(() => Promise.resolve()), + copy: jest.fn(() => Promise.resolve()), + pathExists: jest.fn(() => Promise.resolve()), +})); + +const { resolve, join } = require('path'); +const fse = require('fs-extra'); +const inquirer = require('inquirer'); + +const exportTemplate = require('../export-template'); + +describe('export:template command', () => { + beforeEach(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.clearAllMocks(); + }); + + it('creates a new template directory', async () => { + fse.pathExists.mockReturnValue(false); + const directory = '../test-dir'; + const templatePath = resolve(directory); + const contentPath = join(templatePath, 'template'); + + await exportTemplate(directory); + + expect(fse.pathExists).toHaveBeenCalledWith(contentPath); + expect(fse.ensureDir).toHaveBeenCalledWith(contentPath); + }); + + it.each(['api', 'components', 'config/functions/bootstrap.js', 'data'])( + 'copies folder %s', + async item => { + const directory = '../test-dir'; + const templatePath = resolve(directory); + const contentPath = join(templatePath, 'template'); + + await exportTemplate(directory); + + expect(fse.copy).toHaveBeenCalledWith(join(process.cwd(), item), join(contentPath, item)); + } + ); + + describe('handles prompt input', () => { + it('updates directory if confirmed', async () => { + fse.pathExists.mockReturnValue(true); + const mockInquiry = jest + .spyOn(inquirer, 'prompt') + .mockImplementationOnce(() => ({ confirm: true })); + const directory = '../test-dir'; + const templatePath = resolve(directory); + const contentPath = join(templatePath, 'template'); + + await exportTemplate(directory); + + expect(fse.pathExists).toHaveBeenCalledWith(contentPath); + expect(mockInquiry).toHaveBeenLastCalledWith( + expect.objectContaining({ message: expect.any(String), name: 'confirm', type: 'confirm' }) + ); + expect(fse.ensureDir).toHaveBeenCalled(); + expect(fse.copy).toHaveBeenCalled(); + }); + + it('exits if not confirmed', async () => { + fse.pathExists.mockReturnValue(true); + jest.spyOn(console, 'error').mockImplementation(() => {}); + const mockInquiry = jest + .spyOn(inquirer, 'prompt') + .mockImplementationOnce(() => ({ confirm: false })); + + const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => { + throw new Error('exit'); + }); + const directory = '../test-dir'; + const templatePath = resolve(directory); + const contentPath = join(templatePath, 'template'); + + await exportTemplate(directory).catch(err => { + expect(err).toEqual(new Error('exit')); + }); + + expect(fse.pathExists).toHaveBeenCalledWith(contentPath); + expect(mockInquiry).toHaveBeenLastCalledWith( + expect.objectContaining({ message: expect.any(String), name: 'confirm', type: 'confirm' }) + ); + expect(mockExit).toHaveBeenCalledWith(0); + expect(fse.ensureDir).not.toHaveBeenCalled(); + expect(fse.copy).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/strapi/lib/commands/export-template.js b/packages/strapi/lib/commands/export-template.js new file mode 100644 index 0000000000..f50d9e8397 --- /dev/null +++ b/packages/strapi/lib/commands/export-template.js @@ -0,0 +1,65 @@ +'use strict'; + +const { resolve, join, basename } = require('path'); +const fse = require('fs-extra'); +const chalk = require('chalk'); +const inquirer = require('inquirer'); + +// All directories that a template could need +const TEMPLATE_CONTENT = ['api', 'components', 'config/functions/bootstrap.js', 'data']; + +async function createTemplate(templatePath) { + // Get path to template directory: strapi-template-/template + const contentPath = join(templatePath, 'template'); + let successMessage = 'create'; + + // Check if the correct template directory structure exists + const exists = await fse.pathExists(contentPath); + const templateBase = basename(templatePath); + + if (exists) { + // Confirm the user wants to update the existing template + const inquiry = await inquirer.prompt({ + type: 'confirm', + name: 'confirm', + message: `${chalk.yellow(templateBase)} already exists. Do you want to replace it?`, + }); + + if (!inquiry.confirm) { + process.exit(0); + } + + successMessage = 'update'; + } + + // Create/update the template + await fse.ensureDir(contentPath); + console.log(`${chalk.cyan(successMessage)}: ${templatePath}`); +} + +async function copyContent(templatePath) { + const contentPath = join(templatePath, 'template'); + + TEMPLATE_CONTENT.forEach(async item => { + try { + await fse.copy(join(process.cwd(), item), join(contentPath, item)); + const templateBase = basename(templatePath); + const currentProjectBase = basename(process.cwd()); + console.log( + `${chalk.green( + 'success' + )}: copy ${currentProjectBase}/${item} => ${templateBase}/template/${item}` + ); + } catch (error) { + console.error(`${chalk.red('error')}: ${error.message}`); + } + }); +} + +module.exports = async function exportTemplate(directory) { + const dir = directory.startsWith('.') ? directory : `../${directory}`; + const templatePath = resolve(dir); + + await createTemplate(templatePath); + await copyContent(templatePath); +}; diff --git a/packages/strapi/lib/commands/exportTemplate.js b/packages/strapi/lib/commands/exportTemplate.js deleted file mode 100644 index b54c9ac3ce..0000000000 --- a/packages/strapi/lib/commands/exportTemplate.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -const { resolve, join, basename } = require('path'); -const fse = require('fs-extra'); -const chalk = require('chalk'); -const inquirer = require('inquirer'); - -// All directories that a template could need -const DIRECTORIES = ['api', 'components', 'config', 'data']; - -async function createTemplate(templatePath) { - // Get path to template directory: strapi-template-/template - const contentPath = join(templatePath, 'template'); - - try { - let successMessage = 'create'; - // Check if the correct template directory structure exists - const exists = await fse.pathExists(contentPath); - const templateBase = basename(templatePath); - - if (exists) { - // Confirm the user wants to update the existing template - const inquiry = await inquirer.prompt({ - type: 'confirm', - name: 'confirm', - message: `${chalk.yellow(templateBase)} already exists. Do you want to replace it?`, - }); - - if (!inquiry.confirm) { - process.exit(0); - } - - successMessage = 'update'; - } - - // Create/update the template - await fse.ensureDir(contentPath); - - console.log(`${chalk.cyan(successMessage)}: ${templatePath}`); - } catch (error) { - console.error(`${chalk.red('error')}: ${error.message}`); - } -} - -async function copyContent(templatePath) { - const contentPath = join(templatePath, 'template'); - - DIRECTORIES.forEach(async directory => { - try { - await fse.copy(join(process.cwd(), directory), join(contentPath, directory)); - - const templateBase = basename(templatePath); - const currentProjectBase = basename(process.cwd()); - console.log( - `${chalk.green( - 'success' - )}: copy ${currentProjectBase}/${directory} => ${templateBase}/template/${directory}` - ); - } catch (error) { - console.error(`${chalk.red('error')}: ${error.message}`); - } - }); -} - -module.exports = async function exportTemplate(name) { - // Create the template directory - const templatePath = resolve(`../strapi-template-${name}`); - await createTemplate(templatePath); - // Copy content from current Strapi project to template directory - await copyContent(templatePath); -};