Skip to content

Commit

Permalink
fix(cli): Install studio package if not found (#8058)
Browse files Browse the repository at this point in the history
* Automatically install studio package if not found

* Use existing package check/install code and rework the check in the cli command

* fix typo and comment auth reference
  • Loading branch information
Josh-Walker-GM authored Apr 19, 2023
1 parent 8265e59 commit 090ef33
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 1 deletion.
16 changes: 15 additions & 1 deletion packages/cli/src/commands/experimental/studio.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import terminalLink from 'terminal-link'

import { isModuleInstalled, installRedwoodModule } from '../../lib/packages'

export const command = 'studio'
export const description = 'Run the Redwood development studio'

export const builder = (yargs) => {
yargs.epilogue(
`Also see the ${terminalLink(
Expand All @@ -10,12 +13,23 @@ export const builder = (yargs) => {
)}`
)
}

export const handler = async () => {
try {
// Check the module is installed
if (!isModuleInstalled('@redwoodjs/studio')) {
console.log(
'The studio package is not installed, installing it for you, this may take a moment...'
)
await installRedwoodModule('@redwoodjs/studio')
console.log('Studio package installed successfully.')
}

// Import studio and start it
const { start } = await import('@redwoodjs/studio')
await start()
} catch (e) {
console.log('Error: Cannot start the development studio')
console.log('Cannot start the development studio')
console.log(e)
process.exit(1)
}
Expand Down
76 changes: 76 additions & 0 deletions packages/cli/src/lib/packages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import path from 'path'

import execa from 'execa'
import fs from 'fs-extra'

import { getPaths } from './index'

/**
* Installs a Redwood module into a user's project keeping the version consistent with that of \@redwoodjs/cli.
* If the module is already installed, this function does nothing.
* If no remote version can not be found which matches the local cli version then the latest canary version will be used.
*
* @param {string} module A redwoodjs module, e.g. \@redwoodjs/web
*/
export async function installRedwoodModule(module) {
const packageJsonPath = require.resolve('@redwoodjs/cli/package.json')
let { version } = fs.readJSONSync(packageJsonPath)

if (!isModuleInstalled(module)) {
const { stdout } = await execa.command(
`yarn npm info ${module} --fields versions --json`
)

// If the version includes a plus, like '4.0.0-rc.428+dd79f1726'
// (all @canary, @next, and @rc packages do), get rid of everything after the plus.
if (version.includes('+')) {
version = version.split('+')[0]
}

const versionIsPublished = JSON.parse(stdout).versions.includes(version)

if (!versionIsPublished) {
// Fallback to canary. This is most likely because it's a new package
version = 'canary'
}

// We use `version` to make sure we install the same version as the rest
// of the RW packages
await execa.command(`yarn add -D ${module}@${version}`, {
stdio: 'inherit',
cwd: getPaths().base,
})
}
}

/**
* Check if a user's project's package.json has a module listed as a dependency
* or devDependency. If not, check node_modules.
*
* @param {string} module
*/
export function isModuleInstalled(module) {
const { dependencies, devDependencies } = fs.readJSONSync(
path.join(getPaths().base, 'package.json')
)

const deps = {
...dependencies,
...devDependencies,
}

if (deps[module]) {
return true
}

// Check any of the places require would look for this module.
// This enables testing with `yarn rwfw project:copy`.
//
// We can't use require.resolve here because it caches the exception
// Making it impossible to require when we actually do install it...
return require.resolve
.paths(`${module}/package.json`)
.some((requireResolvePath) => {
return fs.existsSync(path.join(requireResolvePath, module))
})
}

0 comments on commit 090ef33

Please sign in to comment.