From 9c1d95740cec8021a176f305e77cdec4e4ec7f4a Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 5 Mar 2020 14:56:46 +0100 Subject: [PATCH] Use temporary folder for plugin creation to be able to clean up easy --- .../cli/src/commands/createPlugin.test.ts | 45 ++++-- packages/cli/src/commands/createPlugin.ts | 132 +++++++++--------- 2 files changed, 99 insertions(+), 78 deletions(-) diff --git a/packages/cli/src/commands/createPlugin.test.ts b/packages/cli/src/commands/createPlugin.test.ts index a46d8ea8fb..684331e276 100644 --- a/packages/cli/src/commands/createPlugin.test.ts +++ b/packages/cli/src/commands/createPlugin.test.ts @@ -5,29 +5,32 @@ import del from 'del'; import { createFileFromTemplate, createFromTemplateDir, - createPluginFolder, + createTemporaryPluginFolder, + movePlugin, } from './createPlugin'; describe('createPlugin', () => { describe('createPluginFolder', () => { - it('should create a plugin directory in the correct place', () => { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); + it('should create a temporary plugin directory in the correct place', () => { + const id = 'testPlugin'; + const tempDir = path.join(os.tmpdir(), id); try { - const pluginFolder = createPluginFolder(tempDir, 'foo'); - expect(fs.existsSync(pluginFolder)).toBe(true); - expect(pluginFolder).toMatch(/packages\/plugins\/foo/); + createTemporaryPluginFolder(tempDir); + expect(fs.existsSync(tempDir)).toBe(true); + expect(tempDir).toMatch(id); } finally { del.sync(tempDir, { force: true }); } }); - it('should not create a plugin directory if it already exists', () => { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); + it('should not create a temporary plugin directory if it already exists', () => { + const id = 'testPlugin'; + const tempDir = path.join(os.tmpdir(), id); try { - const pluginFolder = createPluginFolder(tempDir, 'foo'); - expect(fs.existsSync(pluginFolder)).toBe(true); - expect(() => createPluginFolder(tempDir, 'foo')).toThrowError( - /A plugin with the same name already exists/, + createTemporaryPluginFolder(tempDir); + expect(fs.existsSync(tempDir)).toBe(true); + expect(() => createTemporaryPluginFolder(tempDir)).toThrowError( + /Failed to create temporary plugin directory/, ); } finally { del.sync(tempDir, { force: true }); @@ -83,4 +86,22 @@ describe('createPlugin', () => { } }); }); + + describe('movePlugin', () => { + it('should move the temporary plugin directory to its final place', () => { + const id = 'testPlugin'; + const tempDir = path.join(os.tmpdir(), id); + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-')); + const pluginFolder = path.join(rootDir, 'packages', 'plugins', id); + try { + createTemporaryPluginFolder(tempDir); + movePlugin(tempDir, rootDir, id); + expect(fs.existsSync(pluginFolder)).toBe(true); + expect(pluginFolder).toMatch(`/packages\/plugins\/${id}`); + } finally { + del.sync(tempDir, { force: true }); + del.sync(rootDir, { force: true }); + } + }); + }); }); diff --git a/packages/cli/src/commands/createPlugin.ts b/packages/cli/src/commands/createPlugin.ts index 189b34ba0e..cdfdca5ebb 100644 --- a/packages/cli/src/commands/createPlugin.ts +++ b/packages/cli/src/commands/createPlugin.ts @@ -8,42 +8,42 @@ import { promisify } from 'util'; import { exec } from 'child_process'; import { resolve as resolvePath } from 'path'; import { realpathSync, existsSync } from 'fs'; +import os from 'os'; const MARKER_SUCCESS = chalk.green(` ✓\n`); const MARKER_FAILURE = chalk.red(` ✗\n`); -export const createPluginFolder = (rootDir: string, id: string): string => { +const checkExists = (rootDir: string, id: string) => { console.log(); - console.log(chalk.green(' Creating the plugin directory:')); + console.log(chalk.green(' Checking if the plugin already exists:')); const destination = path.join(rootDir, 'packages', 'plugins', id); if (fs.existsSync(destination)) { - console.log( - chalk.red( - ` failed:\t ✗ ${chalk.cyan(destination.replace(`${rootDir}/`, ''))}`, - ), - ); + console.log(chalk.red(` plugin ID\t${chalk.cyan(id)} already exists ✗`)); throw new Error( `A plugin with the same name already exists: ${chalk.cyan( destination.replace(`${rootDir}/`, ''), - )}\nPlease try again with a different Plugin ID`, + )}\nPlease try again with a different plugin ID`, ); } + console.log(chalk.green(` plugin ID\t${chalk.cyan(id)} is available ✓`)); +}; + +export const createTemporaryPluginFolder = (tempDir: string) => { + console.log(); + console.log(chalk.green(' Creating a temporary plugin directory:')); process.stdout.write( - chalk.green( - ` creating\t${chalk.cyan(destination.replace(`${rootDir}/`, ''))}`, - ), + chalk.green(` creating\t${chalk.cyan('temporary')} directory`), ); try { - fs.mkdirSync(destination, { recursive: true }); - process.stdout.write(chalk.green(' ✓\n')); - return destination; + fs.mkdirSync(tempDir); + process.stdout.write(chalk.green(MARKER_SUCCESS)); } catch (e) { - process.stdout.write(chalk.red(` ✗\n`)); + process.stdout.write(chalk.red(MARKER_FAILURE)); throw new Error( - `Failed to create plugin directory: ${destination}: ${e.message}`, + `Failed to create temporary plugin directory: ${e.message}`, ); } }; @@ -129,7 +129,7 @@ export const addPluginDependencyToApp = ( 'utf-8', ); } catch (e) { - process.stdout.write(chalk.red(` ✗\n`)); + process.stdout.write(chalk.red(MARKER_FAILURE)); throw new Error( `Failed to add plugin as dependency in app: ${packageFile}: ${e.message}`, ); @@ -164,7 +164,7 @@ export const addPluginToApp = (rootDir: string, pluginName: string) => { try { addExportStatement(pluginsFile, pluginExport); } catch (e) { - process.stdout.write(chalk.red(` ✗\n`)); + process.stdout.write(chalk.red(MARKER_FAILURE)); throw new Error( `Failed to import plugin in app: ${pluginsFile}: ${e.message}`, ); @@ -183,14 +183,13 @@ export const createFromTemplateDir = async ( let files = []; - process.stdout.write(chalk.green(` reading\t`)); try { files = await recursive(templateFolder); - process.stdout.write( - chalk.green(`${chalk.cyan(`${files.length} files`)} ✓\n`), + console.log( + chalk.green(` reading\t${chalk.cyan(`${files.length} files`)} ✓`), ); } catch (e) { - console.log(chalk.red(` ✗ 0 files\n`)); + console.log(chalk.red(` reading\t${chalk.cyan('0')} files ✗`)); throw new Error(`Failed to read files in template directory: ${e.message}`); } @@ -228,42 +227,24 @@ export const createFromTemplateDir = async ( }); }; -const cleanUp = async (rootDir: string, id: string) => { - const destination = path.join(rootDir, 'packages', 'plugins', id); - - const questions: Question[] = [ - { - type: 'confirm', - name: 'cleanup', - message: chalk.yellow( - `It seems that something went wrong when creating the plugin 🤔\nDo you want to remove the following directory and all the files in it:\n${chalk.cyan( - destination, - )}`, - ), - }, - ]; - const answers: Answers = await inquirer.prompt(questions); - - if (answers.cleanup) { +const cleanUp = (tempDir: string, id: string) => { + console.log( + chalk.green( + `It seems that something went wrong when creating the plugin 🤔 `, + ), + ); + console.log( + chalk.green('We are going to clean up, and then you can try again.'), + ); + process.stdout.write(chalk.green(` Cleaning up ${chalk.cyan(id)}`)); + try { + fs.rmdirSync(tempDir, { recursive: true }); + process.stdout.write(chalk.green(MARKER_SUCCESS)); console.log(); - console.log(chalk.green(`🧹 Cleaning up...`)); + } catch (e) { + process.stdout.write(chalk.red(MARKER_FAILURE)); console.log(); - console.log(chalk.green(` Removing plugin:`)); - process.stdout.write( - chalk.green( - ` deleting\t${chalk.cyan(destination.replace(`${rootDir}/`, ''))}`, - ), - ); - try { - // Not using recursion here, so only empty directories can be removed - fs.rmdirSync(destination); - process.stdout.write(MARKER_SUCCESS); - console.log(); - } catch (e) { - process.stdout.write(MARKER_FAILURE); - console.log(); - console.log(chalk.red(`Failed to cleanup: ${e.message}`)); - } + console.log(chalk.red(`Failed to cleanup: ${e.message}`)); } }; @@ -289,6 +270,24 @@ const buildPlugin = async (pluginFolder: string) => { } }; +export const movePlugin = (tempDir: string, rootDir: string, id: string) => { + console.log(); + console.log(chalk.green(` Finalizing the plugin:`)); + + const destination = path.join(rootDir, 'packages', 'plugins', id); + + process.stdout.write( + chalk.green(` moving\t${chalk.cyan(id)} to final location`), + ); + try { + fs.moveSync(tempDir, destination); + process.stdout.write(chalk.green(MARKER_SUCCESS)); + } catch (e) { + process.stdout.write(chalk.red(MARKER_FAILURE)); + throw new Error(`Failed to move plugin: ${e.message}`); + } +}; + const createPlugin = async (): Promise => { const questions: Question[] = [ { @@ -305,14 +304,17 @@ const createPlugin = async (): Promise => { const appPackage = resolvePath(rootDir, 'packages', 'app'); const cliPackage = resolvePath(__dirname, '..', '..'); const templateFolder = resolvePath(cliPackage, 'templates', 'default-plugin'); + const tempDir = path.join(os.tmpdir(), answers.id); + + console.log(); + console.log(chalk.green('Creating the plugin...')); try { - console.log(); - console.log(chalk.green('🧩 Creating the plugin...')); - - const destinationFolder = createPluginFolder(rootDir, answers.id); - await createFromTemplateDir(templateFolder, destinationFolder, answers); - await buildPlugin(destinationFolder); + checkExists(rootDir, answers.id); + createTemporaryPluginFolder(tempDir); + await createFromTemplateDir(templateFolder, tempDir, answers); + await buildPlugin(tempDir); + movePlugin(tempDir, rootDir, answers.id); if (existsSync(appPackage)) { addPluginDependencyToApp(rootDir, answers.id); @@ -328,16 +330,14 @@ const createPlugin = async (): Promise => { ), ); console.log(); - - return destinationFolder; } catch (e) { console.log(); console.log(`${chalk.red(e.message)}`); console.log(); + cleanUp(tempDir, answers.id); + console.log(); console.log(`🔥 ${chalk.red('Failed to create plugin!')}`); console.log(); - - await cleanUp(rootDir, answers.id); } };