From 336fdbb01eea5e293aaa6020242a858bcf58fd8c Mon Sep 17 00:00:00 2001 From: km1chno Date: Thu, 4 Jul 2024 10:47:34 +0200 Subject: [PATCH] feat: add jest recipe --- src/commands/react-native-ci-cli.ts | 21 ++++++++++------ src/extensions/addScript.ts | 17 +++++++++++++ src/extensions/cli-extension.ts | 17 ------------- src/extensions/dependencies.ts | 38 ++++++++++++++++++++++++++++ src/recipies/jest.ts | 39 +++++++++++++++++++++++++++++ src/recipies/lint.ts | 24 +++--------------- src/templates/jest.ejf | 14 +++++++++++ 7 files changed, 124 insertions(+), 46 deletions(-) create mode 100644 src/extensions/addScript.ts delete mode 100644 src/extensions/cli-extension.ts create mode 100644 src/extensions/dependencies.ts create mode 100644 src/recipies/jest.ts create mode 100644 src/templates/jest.ejf diff --git a/src/commands/react-native-ci-cli.ts b/src/commands/react-native-ci-cli.ts index d2d2a5d..8620c42 100644 --- a/src/commands/react-native-ci-cli.ts +++ b/src/commands/react-native-ci-cli.ts @@ -1,7 +1,8 @@ import { GluegunCommand } from 'gluegun' -import runLint from '../recipies/lint' import { intro, outro } from '@clack/prompts' import { SKIP_INTERACTIVE_COMMAND } from '../constants' +import runLint from '../recipies/lint' +import runJest from '../recipies/jest' const command: GluegunCommand = { name: 'react-native-ci-cli', @@ -10,19 +11,18 @@ const command: GluegunCommand = { if (toolbox.isGitDirty() == null) { outro('This is not a git repository. Exiting.') - return } if (toolbox.isGitDirty() == true) { outro('Please commit your changes before running this command. Exiting.') - return } const lintExecutor = await runLint(toolbox) + const jestExecutor = await runJest(toolbox) - const executors = [lintExecutor].filter(Boolean) + const executors = [lintExecutor, jestExecutor].filter(Boolean) if (executors.length === 0) { outro('Nothing to do here. Cheers! 🎉') @@ -31,13 +31,18 @@ const command: GluegunCommand = { outro("Let's roll") - const executorResults = await Promise.all( - executors.map((executor) => executor(toolbox)) + const executorResults = await executors.reduce( + (executorsChain, executor) => + executorsChain.then((executorResults) => + executor(toolbox).then((result) => [...executorResults, result]) + ), + Promise.resolve([]) ) - const usedFlags = (await executorResults).join(' --') + + const usedFlags = executorResults.join(' ') toolbox.print.success( - `We're all set. Next time you can use silent command: npx create-react-native-ci-cli --${SKIP_INTERACTIVE_COMMAND} ${usedFlags}` + `We're all set 🎉.\nNext time you can use silent command: npx create-react-native-ci-cli --${SKIP_INTERACTIVE_COMMAND} ${usedFlags}.` ) }, } diff --git a/src/extensions/addScript.ts b/src/extensions/addScript.ts new file mode 100644 index 0000000..75f6c4f --- /dev/null +++ b/src/extensions/addScript.ts @@ -0,0 +1,17 @@ +import { GluegunToolbox } from 'gluegun' + +module.exports = (toolbox: GluegunToolbox) => { + const { patching, print } = toolbox + + toolbox.addScript = async (name: string, command: string) => { + await patching.update('package.json', (config) => { + if (!config.scripts[name]) { + config.scripts[name] = command + + print.info(`✔ Added script "${name}": "${command}" to package.json.`) + } + + return config + }) + } +} diff --git a/src/extensions/cli-extension.ts b/src/extensions/cli-extension.ts deleted file mode 100644 index 7e6b5cd..0000000 --- a/src/extensions/cli-extension.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { GluegunToolbox } from 'gluegun' - -// add your CLI-specific functionality here, which will then be accessible -// to your commands -module.exports = (toolbox: GluegunToolbox) => { - toolbox.foo = () => { - toolbox.print.info('called foo extension') - } - - // enable this if you want to read configuration in from - // the current folder's package.json (in a "react-native-ci-cli" property), - // react-native-ci-cli.config.json, etc. - // toolbox.config = { - // ...toolbox.config, - // ...toolbox.config.loadConfig("react-native-ci-cli", process.cwd()) - // } -} diff --git a/src/extensions/dependencies.ts b/src/extensions/dependencies.ts new file mode 100644 index 0000000..5720845 --- /dev/null +++ b/src/extensions/dependencies.ts @@ -0,0 +1,38 @@ +import { GluegunToolbox } from 'gluegun' + +module.exports = (toolbox: GluegunToolbox) => { + const { filesystem, packageManager, print } = toolbox + + const isInstalled = (name: string, dev: boolean) => { + const packageJSON = filesystem.read('package.json', 'json') + return ( + (!dev && packageJSON?.dependencies[name]) || + (dev && packageJSON?.devDependencies[name]) + ) + } + + toolbox.dependencies = { + isInstalled, + + addDependency: async (name: string, ver: string, dev: boolean) => { + if (isInstalled(name, dev)) { + print.info(`${name} already installed, skipping adding dependency.`) + return + } + + const fullName = !ver ? name : `${name}@${ver}` + + const spinner = print.spin( + `📦 Installing ${fullName} as ${ + dev ? 'devDependency' : 'dependency' + }...` + ) + + await packageManager.add(fullName, { dev }) + + spinner.stop() + + print.info(`✔ Installed ${fullName}.`) + }, + } +} diff --git a/src/recipies/jest.ts b/src/recipies/jest.ts new file mode 100644 index 0000000..ae18964 --- /dev/null +++ b/src/recipies/jest.ts @@ -0,0 +1,39 @@ +import { confirm } from '@clack/prompts' +import { Toolbox } from 'gluegun/build/types/domain/toolbox' + +const COMMAND = 'jest' + +const execute = () => async (toolbox: Toolbox) => { + toolbox.dependencies.addDependency('jest', '', true) + + toolbox.addScript('test', 'jest') + + await toolbox.template.generate({ + template: 'jest.ejf', + target: `.github/workflows/jest.yml`, + }) + + toolbox.print.info('✔ Created Jest workflow.') + + return `--${COMMAND}` +} + +const run = async ( + toolbox: Toolbox +): Promise<(toolbox: Toolbox) => Promise | null> => { + if (toolbox.skipInteractiveForCommand(COMMAND)) { + return execute() + } + + const proceed = await confirm({ + message: 'Do you want to run Jest on your project on every PR?', + }) + + if (!proceed) { + return + } + + return execute() +} + +export default run diff --git a/src/recipies/lint.ts b/src/recipies/lint.ts index daf1ac0..f2fcb67 100644 --- a/src/recipies/lint.ts +++ b/src/recipies/lint.ts @@ -4,34 +4,16 @@ import { confirm } from '@clack/prompts' const COMMAND = 'lint' const execute = () => async (toolbox: Toolbox) => { - const packageJSON = toolbox.filesystem.read('package.json', 'json') + toolbox.dependencies.addDependency('eslint', '', true) - if (!packageJSON?.devDependencies?.eslint) { - const spinner = toolbox.print.spin('Installing ESLint...') - - await toolbox.packageManager.add('eslint', { dev: true }) - - spinner.stop() - - toolbox.print.info('Installed ESLint.') - } - - await toolbox.patching.update('package.json', (config) => { - if (!config.scripts.lint) { - config.scripts.lint = 'eslint "**/*.{js,jsx,ts,tsx}"' - - toolbox.print.info('Added ESLint script to package.json') - } - - return config - }) + toolbox.addScript('lint', 'eslint "**/*.{js,jsx,ts,tsx}"') await toolbox.template.generate({ template: 'lint.ejf', target: `.github/workflows/lint.yml`, }) - toolbox.print.info('Created ESLint workflow.') + toolbox.print.info('✔ Created ESLint workflow.') return `--${COMMAND}` } diff --git a/src/templates/jest.ejf b/src/templates/jest.ejf new file mode 100644 index 0000000..123f5bd --- /dev/null +++ b/src/templates/jest.ejf @@ -0,0 +1,14 @@ +name: Run Jest tests +on: [pull_request] + +jobs: + test: + name: Jest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: 📦 Install dependencies + run: yarn + + - name: 🎭 Run Jest + run: yarn test