From da977d1c98a08610f515e6de1b2f03a8ee5a0907 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 5 Mar 2020 14:56:46 +0100 Subject: [PATCH 1/5] 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/frontend/packages/cli/src/commands/createPlugin.test.ts b/frontend/packages/cli/src/commands/createPlugin.test.ts index a46d8ea8fb..684331e276 100644 --- a/frontend/packages/cli/src/commands/createPlugin.test.ts +++ b/frontend/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/frontend/packages/cli/src/commands/createPlugin.ts b/frontend/packages/cli/src/commands/createPlugin.ts index 189b34ba0e..cdfdca5ebb 100644 --- a/frontend/packages/cli/src/commands/createPlugin.ts +++ b/frontend/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); } }; From 0eaad36a1e7369089ac6ffbf6a1dd9c0dcc1b2c8 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 5 Mar 2020 15:41:16 +0100 Subject: [PATCH 2/5] Remove unnecessary chalking --- .../packages/cli/src/commands/createPlugin.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/packages/cli/src/commands/createPlugin.ts b/frontend/packages/cli/src/commands/createPlugin.ts index cdfdca5ebb..d7b7d9969e 100644 --- a/frontend/packages/cli/src/commands/createPlugin.ts +++ b/frontend/packages/cli/src/commands/createPlugin.ts @@ -39,9 +39,9 @@ export const createTemporaryPluginFolder = (tempDir: string) => { ); try { fs.mkdirSync(tempDir); - process.stdout.write(chalk.green(MARKER_SUCCESS)); + process.stdout.write(MARKER_SUCCESS); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); throw new Error( `Failed to create temporary plugin directory: ${e.message}`, ); @@ -129,7 +129,7 @@ export const addPluginDependencyToApp = ( 'utf-8', ); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(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(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); throw new Error( `Failed to import plugin in app: ${pluginsFile}: ${e.message}`, ); @@ -239,10 +239,10 @@ const cleanUp = (tempDir: string, id: string) => { process.stdout.write(chalk.green(` Cleaning up ${chalk.cyan(id)}`)); try { fs.rmdirSync(tempDir, { recursive: true }); - process.stdout.write(chalk.green(MARKER_SUCCESS)); + process.stdout.write(MARKER_SUCCESS); console.log(); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); console.log(); console.log(chalk.red(`Failed to cleanup: ${e.message}`)); } @@ -281,9 +281,9 @@ export const movePlugin = (tempDir: string, rootDir: string, id: string) => { ); try { fs.moveSync(tempDir, destination); - process.stdout.write(chalk.green(MARKER_SUCCESS)); + process.stdout.write(MARKER_SUCCESS); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); throw new Error(`Failed to move plugin: ${e.message}`); } }; From 9c1d95740cec8021a176f305e77cdec4e4ec7f4a Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 5 Mar 2020 14:56:46 +0100 Subject: [PATCH 3/5] 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); } }; From 8a836fb7ad113002f9553f4c3031b00927505d8b Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Thu, 5 Mar 2020 15:41:16 +0100 Subject: [PATCH 4/5] Remove unnecessary chalking --- packages/cli/src/commands/createPlugin.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/commands/createPlugin.ts b/packages/cli/src/commands/createPlugin.ts index cdfdca5ebb..d7b7d9969e 100644 --- a/packages/cli/src/commands/createPlugin.ts +++ b/packages/cli/src/commands/createPlugin.ts @@ -39,9 +39,9 @@ export const createTemporaryPluginFolder = (tempDir: string) => { ); try { fs.mkdirSync(tempDir); - process.stdout.write(chalk.green(MARKER_SUCCESS)); + process.stdout.write(MARKER_SUCCESS); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); throw new Error( `Failed to create temporary plugin directory: ${e.message}`, ); @@ -129,7 +129,7 @@ export const addPluginDependencyToApp = ( 'utf-8', ); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(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(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); throw new Error( `Failed to import plugin in app: ${pluginsFile}: ${e.message}`, ); @@ -239,10 +239,10 @@ const cleanUp = (tempDir: string, id: string) => { process.stdout.write(chalk.green(` Cleaning up ${chalk.cyan(id)}`)); try { fs.rmdirSync(tempDir, { recursive: true }); - process.stdout.write(chalk.green(MARKER_SUCCESS)); + process.stdout.write(MARKER_SUCCESS); console.log(); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); console.log(); console.log(chalk.red(`Failed to cleanup: ${e.message}`)); } @@ -281,9 +281,9 @@ export const movePlugin = (tempDir: string, rootDir: string, id: string) => { ); try { fs.moveSync(tempDir, destination); - process.stdout.write(chalk.green(MARKER_SUCCESS)); + process.stdout.write(MARKER_SUCCESS); } catch (e) { - process.stdout.write(chalk.red(MARKER_FAILURE)); + process.stdout.write(MARKER_FAILURE); throw new Error(`Failed to move plugin: ${e.message}`); } }; From 9117380ce98ff475d6a59064690564ab7632fde9 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 6 Mar 2020 11:14:11 +0100 Subject: [PATCH 5/5] Fix paths and add spinners --- packages/cli/package.json | 2 + .../cli/src/commands/createPlugin.test.ts | 8 +- packages/cli/src/commands/createPlugin.ts | 75 +++++++++++++------ yarn.lock | 46 +++++++++++- 4 files changed, 101 insertions(+), 30 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 49b50e2664..524a81420d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -18,6 +18,7 @@ "@types/html-webpack-plugin": "^3.2.2", "@types/inquirer": "^6.5.0", "@types/node": "^13.7.2", + "@types/ora": "^3.2.0", "@types/react-dev-utils": "^9.0.4", "@types/recursive-readdir": "^2.2.0", "@types/webpack": "^4.41.7", @@ -38,6 +39,7 @@ "handlebars": "^4.7.3", "html-webpack-plugin": "^3.2.0", "inquirer": "^7.0.4", + "ora": "^4.0.3", "react-dev-utils": "^10.2.0", "recursive-readdir": "^2.2.2", "replace-in-file": "^5.0.2", diff --git a/packages/cli/src/commands/createPlugin.test.ts b/packages/cli/src/commands/createPlugin.test.ts index 684331e276..bb823218fe 100644 --- a/packages/cli/src/commands/createPlugin.test.ts +++ b/packages/cli/src/commands/createPlugin.test.ts @@ -92,12 +92,12 @@ describe('createPlugin', () => { 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); + const pluginDir = 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}`); + movePlugin(tempDir, pluginDir, id); + expect(fs.existsSync(pluginDir)).toBe(true); + expect(pluginDir).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 d7b7d9969e..52c0e5debb 100644 --- a/packages/cli/src/commands/createPlugin.ts +++ b/packages/cli/src/commands/createPlugin.ts @@ -9,9 +9,10 @@ import { exec } from 'child_process'; import { resolve as resolvePath } from 'path'; import { realpathSync, existsSync } from 'fs'; import os from 'os'; +import ora from 'ora'; -const MARKER_SUCCESS = chalk.green(` ✓\n`); -const MARKER_FAILURE = chalk.red(` ✗\n`); +const MARKER_SUCCESS = chalk.green(` ✔︎\n`); +const MARKER_FAILURE = chalk.red(` ✘\n`); const checkExists = (rootDir: string, id: string) => { console.log(); @@ -20,14 +21,20 @@ const checkExists = (rootDir: string, id: string) => { const destination = path.join(rootDir, 'packages', 'plugins', id); if (fs.existsSync(destination)) { - console.log(chalk.red(` plugin ID\t${chalk.cyan(id)} already exists ✗`)); + console.log( + chalk.red( + ` plugin ID\t${chalk.cyan(id)} already exists${MARKER_FAILURE}`, + ), + ); throw new Error( `A plugin with the same name already exists: ${chalk.cyan( destination.replace(`${rootDir}/`, ''), )}\nPlease try again with a different plugin ID`, ); } - console.log(chalk.green(` plugin ID\t${chalk.cyan(id)} is available ✓`)); + console.log( + chalk.green(` plugin ID\t${chalk.cyan(id)} is available${MARKER_SUCCESS}`), + ); }; export const createTemporaryPluginFolder = (tempDir: string) => { @@ -186,10 +193,14 @@ export const createFromTemplateDir = async ( try { files = await recursive(templateFolder); console.log( - chalk.green(` reading\t${chalk.cyan(`${files.length} files`)} ✓`), + chalk.green( + ` reading\t${chalk.cyan(`${files.length} files`)}${MARKER_SUCCESS}`, + ), ); } catch (e) { - console.log(chalk.red(` reading\t${chalk.cyan('0')} files ✗`)); + console.log( + chalk.red(` reading\t${chalk.cyan('0')} files${MARKER_FAILURE}`), + ); throw new Error(`Failed to read files in template directory: ${e.message}`); } @@ -227,7 +238,7 @@ export const createFromTemplateDir = async ( }); }; -const cleanUp = (tempDir: string, id: string) => { +const cleanUp = async (tempDir: string, id: string) => { console.log( chalk.green( `It seems that something went wrong when creating the plugin 🤔 `, @@ -236,14 +247,16 @@ const cleanUp = (tempDir: string, id: string) => { 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)}`)); + const spinner = ora({ + prefixText: chalk.green(` Cleaning up\t${chalk.cyan(id)}`), + spinner: 'arc', + color: 'green', + }).start(); try { - fs.rmdirSync(tempDir, { recursive: true }); - process.stdout.write(MARKER_SUCCESS); - console.log(); + await fs.remove(tempDir); + spinner.succeed(); } catch (e) { - process.stdout.write(MARKER_FAILURE); - console.log(); + spinner.fail(); console.log(chalk.red(`Failed to cleanup: ${e.message}`)); } }; @@ -256,13 +269,17 @@ const buildPlugin = async (pluginFolder: string) => { const commands = ['yarn install', 'yarn build']; for (const command of commands) { - process.stdout.write(chalk.green(` executing\t${chalk.cyan(command)}`)); + const spinner = ora({ + prefixText: chalk.green(` executing\t${chalk.cyan(command)}`), + spinner: 'arc', + color: 'green', + }).start(); try { process.chdir(pluginFolder); await prom_exec(command, { timeout: 60000 }); - process.stdout.write(MARKER_SUCCESS); + spinner.succeed(); } catch (e) { - process.stdout.write(MARKER_FAILURE); + spinner.fail(); throw new Error( `Could not execute command ${chalk.cyan(command)}: ${e.message}`, ); @@ -270,11 +287,13 @@ const buildPlugin = async (pluginFolder: string) => { } }; -export const movePlugin = (tempDir: string, rootDir: string, id: string) => { +export const movePlugin = ( + tempDir: string, + destination: string, + id: string, +) => { console.log(); - console.log(chalk.green(` Finalizing the plugin:`)); - - const destination = path.join(rootDir, 'packages', 'plugins', id); + console.log(chalk.green(` Moving the plugin:`)); process.stdout.write( chalk.green(` moving\t${chalk.cyan(id)} to final location`), @@ -288,7 +307,7 @@ export const movePlugin = (tempDir: string, rootDir: string, id: string) => { } }; -const createPlugin = async (): Promise => { +const createPlugin = async () => { const questions: Question[] = [ { type: 'input', @@ -305,6 +324,14 @@ const createPlugin = async (): Promise => { const cliPackage = resolvePath(__dirname, '..', '..'); const templateFolder = resolvePath(cliPackage, 'templates', 'default-plugin'); const tempDir = path.join(os.tmpdir(), answers.id); + const pluginDir = path.join( + rootDir, + '..', + '..', + 'packages', + 'plugins', + answers.id, + ); console.log(); console.log(chalk.green('Creating the plugin...')); @@ -313,8 +340,8 @@ const createPlugin = async (): Promise => { checkExists(rootDir, answers.id); createTemporaryPluginFolder(tempDir); await createFromTemplateDir(templateFolder, tempDir, answers); - await buildPlugin(tempDir); - movePlugin(tempDir, rootDir, answers.id); + movePlugin(tempDir, pluginDir, answers.id); + await buildPlugin(pluginDir); if (existsSync(appPackage)) { addPluginDependencyToApp(rootDir, answers.id); @@ -334,7 +361,7 @@ const createPlugin = async (): Promise => { console.log(); console.log(`${chalk.red(e.message)}`); console.log(); - cleanUp(tempDir, answers.id); + await cleanUp(tempDir, answers.id); console.log(); console.log(`🔥 ${chalk.red('Failed to create plugin!')}`); console.log(); diff --git a/yarn.lock b/yarn.lock index f34beec44c..dbc8c8f9f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2308,6 +2308,17 @@ dependencies: "@babel/runtime" "^7.4.4" +"@material-ui/lab@4.0.0-alpha.45": + version "4.0.0-alpha.45" + resolved "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.45.tgz#6e1abbdd6e44b9ef7b3eff8ef892a3da5dc52f10" + integrity sha512-zT6kUU87SHsPukiu3tlWg8V6o0tGS38c1b/xst/kPqX6eLbfqrROyxhHn1A8ZtHmqga1AKQdv/1llQoG80Afww== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.7.1" + clsx "^1.0.4" + prop-types "^15.7.2" + react-is "^16.8.0" + "@material-ui/styles@^4.9.0": version "4.9.0" resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.0.tgz#10c31859f6868cfa9d3adf6b6c3e32c9d676bc76" @@ -3176,6 +3187,13 @@ resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/ora@^3.2.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@types/ora/-/ora-3.2.0.tgz#b2f65d1283a8f36d8b0f9ee767e0732a2f429362" + integrity sha512-jll99xUKpiFbIFZSQcxm4numfsLaOWBzWNaRk3PvTSE7BPqTzzOCFmS0mQ7m8qkTfmYhuYbehTGsxkvRLPC++w== + dependencies: + ora "*" + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -4966,6 +4984,11 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-spinners@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" + integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== + cli-table3@^0.5.0, cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -5057,7 +5080,7 @@ clone@^1.0.2: resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clsx@^1.0.2: +clsx@^1.0.2, clsx@^1.0.4: version "1.1.0" resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702" integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA== @@ -8979,6 +9002,11 @@ is-installed-globally@^0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-npm@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" @@ -12299,6 +12327,20 @@ optionator@^0.8.1, optionator@^0.8.3: type-check "~0.3.2" word-wrap "~1.2.3" +ora@*, ora@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/ora/-/ora-4.0.3.tgz#752a1b7b4be4825546a7a3d59256fa523b6b6d05" + integrity sha512-fnDebVFyz309A73cqCipVL1fBZewq4vwgSHfxh43vVy31mbyoQ8sCH3Oeaog/owYOs/lLlGVPCISQonTneg6Pg== + dependencies: + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-spinners "^2.2.0" + is-interactive "^1.0.0" + log-symbols "^3.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + original@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -16748,7 +16790,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=