From 88c32c7aa512532a67803d2c69308dccaea3e29d Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Wed, 4 Sep 2024 16:39:18 +0300 Subject: [PATCH 1/3] feat: add automatic install and build for plugins --- package.json | 1 + pnpm-lock.yaml | 94 +++++++++++++++++++++++++ src/cli/commands/plugin/init/action.ts | 30 ++++++-- src/cli/commands/plugin/init/command.ts | 7 ++ src/cli/commands/utils/helpers.ts | 68 +++++++++++++++++- src/types.ts | 4 ++ 6 files changed, 197 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 7e149e9..2c3d81d 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "chalk": "4.1.2", "commander": "12.1.0", "concurrently": "^8.2.2", + "execa": "^9.3.1", "get-latest-version": "5.1.0", "git-url-parse": "13.1.1", "nodemon": "^3.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a21a78..007da1b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: concurrently: specifier: ^8.2.2 version: 8.2.2 + execa: + specifier: ^9.3.1 + version: 9.3.1 get-latest-version: specifier: 5.1.0 version: 5.1.0 @@ -838,9 +841,16 @@ packages: cpu: [x64] os: [win32] + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -2037,6 +2047,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execa@9.3.1: + resolution: {integrity: sha512-gdhefCCNy/8tpH/2+ajP9IQc14vXchNdd0weyzSJEFURhRMGncQ+zKFxwjAufIewPEJm9BPOaJnvg2UtlH2gPQ==} + engines: {node: ^18.19.0 || >=20.5.0} + exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -2074,6 +2088,10 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -2193,6 +2211,10 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -2316,6 +2338,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + human-signals@8.0.0: + resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==} + engines: {node: '>=18.18.0'} + husky@9.0.11: resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} engines: {node: '>=18'} @@ -2522,6 +2548,10 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -2546,6 +2576,10 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + is-unicode-supported@2.0.0: + resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + engines: {node: '>=18'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -3172,6 +3206,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse-path@7.0.0: resolution: {integrity: sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==} @@ -3282,6 +3320,10 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-ms@9.1.0: + resolution: {integrity: sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==} + engines: {node: '>=18'} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -3684,6 +3726,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -4041,6 +4087,10 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + yup@0.32.9: resolution: {integrity: sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==} engines: {node: '>=10'} @@ -4968,8 +5018,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.14.1': optional: true + '@sec-ant/readable-stream@0.4.1': {} + '@sinclair/typebox@0.27.8': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -6585,6 +6639,21 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + execa@9.3.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.3 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.0 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 5.3.0 + pretty-ms: 9.1.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + exit@0.1.2: {} expect@29.7.0: @@ -6627,6 +6696,10 @@ snapshots: dependencies: bser: 2.1.1 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.0.0 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -6760,6 +6833,11 @@ snapshots: get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -6888,6 +6966,8 @@ snapshots: human-signals@5.0.0: {} + human-signals@8.0.0: {} + husky@9.0.11: {} iconv-lite@0.4.24: @@ -7054,6 +7134,8 @@ snapshots: is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -7076,6 +7158,8 @@ snapshots: is-unicode-supported@0.1.0: {} + is-unicode-supported@2.0.0: {} + is-weakmap@2.0.2: {} is-weakref@1.0.2: @@ -7897,6 +7981,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@4.0.0: {} + parse-path@7.0.0: dependencies: protocols: 2.0.1 @@ -7986,6 +8072,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.2.0 + pretty-ms@9.1.0: + dependencies: + parse-ms: 4.0.0 + process-nextick-args@2.0.1: {} progress-stream@2.0.0: @@ -8443,6 +8533,8 @@ snapshots: strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -8805,6 +8897,8 @@ snapshots: yocto-queue@1.0.0: {} + yoctocolors@2.1.1: {} + yup@0.32.9: dependencies: '@babel/runtime': 7.24.4 diff --git a/src/cli/commands/plugin/init/action.ts b/src/cli/commands/plugin/init/action.ts index b4be398..4769953 100644 --- a/src/cli/commands/plugin/init/action.ts +++ b/src/cli/commands/plugin/init/action.ts @@ -6,14 +6,18 @@ import gitUrlParse from 'git-url-parse'; import path from 'node:path'; import { outdent } from 'outdent'; -import { dirContainsStrapiProject, logInstructions } from '../../utils/helpers'; +import { + dirContainsStrapiProject, + logInstructions, + getPkgManager, + runBuild, + runInstall, +} from '../../utils/helpers'; import { gitIgnoreFile } from './files/gitIgnore'; -import type { CLIContext } from '../../../../types'; -import type { InitOptions, TemplateFile } from '@strapi/pack-up'; - -type ActionOptions = Pick; +import type { CLIContext, CommonCLIOptions } from '../../../../types'; +import type { TemplateFile } from '@strapi/pack-up'; // TODO: remove these when release versions are available const USE_RC_VERSIONS: string[] = [ @@ -29,7 +33,7 @@ let promptAnswers: { name: string; answer: string | boolean }[] = []; export default async ( packagePath: string, - { silent, debug }: ActionOptions, + { silent, debug, useNpm, usePnpm, useYarn, install }: CommonCLIOptions, { logger, cwd }: CLIContext ) => { try { @@ -59,6 +63,20 @@ export default async ( template, }); + const packageManager = getPkgManager( + { + useNpm, + usePnpm, + useYarn, + }, + isStrapiProject + ); + + if (install) { + await runInstall(packageManager, pluginPath); + await runBuild(packageManager, pluginPath); + } + if (isStrapiProject) { const pkgName = promptAnswers.find((option) => option.name === 'pkgName')?.answer; const language = promptAnswers.find((option) => option.name === 'typescript')?.answer diff --git a/src/cli/commands/plugin/init/command.ts b/src/cli/commands/plugin/init/command.ts index 725bf36..e3daedd 100644 --- a/src/cli/commands/plugin/init/command.ts +++ b/src/cli/commands/plugin/init/command.ts @@ -12,6 +12,13 @@ const command: StrapiCommand = ({ command: commanderCommand, ctx }) => { .argument('path', 'path to the plugin') .option('-d, --debug', 'Enable debugging mode with verbose logs', false) .option('--silent', "Don't log anything", false) + // Package manager options + .option('--use-npm', 'Use npm as the plugin package manager') + .option('--use-yarn', 'Use yarn as the plugin package manager') + .option('--use-pnpm', 'Use pnpm as the plugin package manager') + + // dependencies options + .option('--no-install', 'Do not install dependencies') .action((path, options) => { return action(path, options, ctx); }); diff --git a/src/cli/commands/utils/helpers.ts b/src/cli/commands/utils/helpers.ts index f2f2f35..d3a5bc4 100644 --- a/src/cli/commands/utils/helpers.ts +++ b/src/cli/commands/utils/helpers.ts @@ -1,8 +1,9 @@ import chalk from 'chalk'; +import { execa as execa_ } from 'execa'; import fs from 'fs'; import path from 'path'; -import type { CLIContext } from '../../../types'; +import type { CLIContext, CommonCLIOptions } from '../../../types'; export const runAction = (name: string, action: (...args: any[]) => Promise) => @@ -30,6 +31,50 @@ export const dirContainsStrapiProject = (dir: string) => { } }; +export const getPkgManager = (options: CommonCLIOptions, isStrapi: boolean) => { + // if we are in a strapi project we return it's package manager + if (isStrapi) { + // Check if each file exists + const hasPackageLock = fs.existsSync(path.join(process.cwd(), 'package-lock.json')); + const hasYarnLock = fs.existsSync(path.join(process.cwd(), 'yarn.lock')); + const hasPnpmLock = fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml')); + + if (hasPackageLock) { + return 'npm'; + } + if (hasYarnLock) { + return 'yarn'; + } + if (hasPnpmLock) { + return 'pnpm'; + } + } + + if (options.useNpm === true) { + return 'npm'; + } + + if (options.usePnpm === true) { + return 'pnpm'; + } + + if (options.useYarn === true) { + return 'yarn'; + } + + const userAgent = process.env.npm_config_user_agent || ''; + + if (userAgent.startsWith('yarn')) { + return 'yarn'; + } + + if (userAgent.startsWith('pnpm')) { + return 'pnpm'; + } + + return 'npm'; +}; + export const logInstructions = ( pluginName: string, { language, path: pluginPath }: { language: string; path: string } @@ -55,3 +100,24 @@ ${exportInstruction} { ${separator} `; }; + +export const runInstall = async (packageManager: 'yarn' | 'npm' | 'pnpm', pluginPath: string) => { + const execa = execa_({ + cwd: pluginPath, + verbose: 'full', + }); + + await execa`${packageManager} install`; +}; + +export const runBuild = async (packageManager: 'yarn' | 'npm' | 'pnpm', pluginPath: string) => { + const execa = execa_({ + pluginPath, + verbose: 'full', + }); + + if (packageManager === 'npm') { + await execa`${packageManager} run build`; + } + await execa`${packageManager} build`; +}; diff --git a/src/types.ts b/src/types.ts index 57466b0..389ca31 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,10 @@ import type { Command } from 'commander'; export interface CommonCLIOptions { silent?: boolean; debug?: boolean; + useNpm?: boolean; + useYarn?: boolean; + usePnpm?: boolean; + install?: boolean; } export interface CLIContext { From f56a12ff5ab537a50018bd11da19450b4c308817 Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Thu, 5 Sep 2024 11:23:07 +0300 Subject: [PATCH 2/3] fix: dynamically import execa package --- src/cli/commands/utils/helpers.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cli/commands/utils/helpers.ts b/src/cli/commands/utils/helpers.ts index d3a5bc4..9c5bcaa 100644 --- a/src/cli/commands/utils/helpers.ts +++ b/src/cli/commands/utils/helpers.ts @@ -1,5 +1,4 @@ import chalk from 'chalk'; -import { execa as execa_ } from 'execa'; import fs from 'fs'; import path from 'path'; @@ -102,7 +101,9 @@ ${separator} }; export const runInstall = async (packageManager: 'yarn' | 'npm' | 'pnpm', pluginPath: string) => { - const execa = execa_({ + const { execa: execaPkg } = await import('execa'); + + const execa = execaPkg({ cwd: pluginPath, verbose: 'full', }); @@ -111,13 +112,16 @@ export const runInstall = async (packageManager: 'yarn' | 'npm' | 'pnpm', plugin }; export const runBuild = async (packageManager: 'yarn' | 'npm' | 'pnpm', pluginPath: string) => { - const execa = execa_({ - pluginPath, + const { execa: execaPkg } = await import('execa'); + + const execa = execaPkg({ + cwd: pluginPath, verbose: 'full', }); if (packageManager === 'npm') { await execa`${packageManager} run build`; + return; } await execa`${packageManager} build`; }; From 4ad1eb65a2e41ae8c9700472247d0d5ea2c8c30e Mon Sep 17 00:00:00 2001 From: Bassel Kanso Date: Thu, 5 Sep 2024 11:27:48 +0300 Subject: [PATCH 3/3] chore: add changeset --- .changeset/spicy-knives-shout.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spicy-knives-shout.md diff --git a/.changeset/spicy-knives-shout.md b/.changeset/spicy-knives-shout.md new file mode 100644 index 0000000..f709679 --- /dev/null +++ b/.changeset/spicy-knives-shout.md @@ -0,0 +1,5 @@ +--- +'@strapi/sdk-plugin': minor +--- + +automate install and build for generated plugins