diff --git a/packages/new-design/lib/config.mjs b/packages/new-design/lib/config.mjs index 45e4a4aa707..bf8d931a478 100644 --- a/packages/new-design/lib/config.mjs +++ b/packages/new-design/lib/config.mjs @@ -171,7 +171,6 @@ yarn-error.log* 'shared/components/workbench/inputs/design-option-pct-deg.mjs', 'shared/components/workbench/inputs/measurement.mjs', 'shared/components/workbench/measurements/index.mjs', - 'shared/components/workbench/measurements/non-human.mjs', 'shared/components/workbench/draft/circle.mjs', 'shared/components/workbench/draft/defs.mjs', 'shared/components/workbench/draft/error.mjs', diff --git a/packages/new-design/lib/utils.mjs b/packages/new-design/lib/utils.mjs index f3fab68c334..1a84ae9d7fe 100644 --- a/packages/new-design/lib/utils.mjs +++ b/packages/new-design/lib/utils.mjs @@ -1,5 +1,5 @@ import { config } from './config.mjs' -import { mkdir, readFile, writeFile, copyFile } from 'node:fs/promises' +import { mkdir, readFile, writeFile, copyFile, open, opendir } from 'node:fs/promises' import { join, dirname, relative } from 'path' import mustache from 'mustache' import rdir from 'recursive-readdir' @@ -91,15 +91,62 @@ export const getChoices = async () => { initial: 0, }) - const { name } = - template === 'tutorial' - ? { name: 'tutorial' } - : await prompts({ - type: 'text', - name: 'name', - message: 'What name would you like the design to have? 🏷️ ([a-z] only)', - validate: validateDesignName, - }) + let finalName = false // we're going to use this to track whether we stay in the naming loop + let overwrite = true // should we overwrite existing files? + const cwd = process.cwd() + let name // name will go here + + // while we're not finalized on a name + while (finalName === false) { + // request a name + name = + template === 'tutorial' && name === undefined + ? 'tutorial' + : ( + await prompts({ + type: 'text', + name: 'name', + message: 'What name would you like the design to have? 🏷️ ([a-z] only)', + validate: validateDesignName, + }) + ).name + + // check whether a folder with that name already exists + config.dest = join(cwd, name) + try { + const dir = await opendir(config.dest) + dir.close() + } catch { + // the folder didn't exist, so we're good to go + finalName = true + break + } + + // the folder did exist, so now we need to ask what to do + const { nextStep } = await prompts({ + type: 'select', + name: 'nextStep', + message: + 'It looks like you already have a design by that name in progress. What should we do?', + choices: [ + { title: 'Rename', value: 'rename', description: 'Choose a new name for this design' }, + { title: 'Overwrite', value: 'overwrite', description: 'Overwrite the existing design' }, + { + title: 'Re-initialize', + value: 'reinit', + description: + "Bring in a fresh workbench, but don't overwrite existing design files (useful for updating to the latest dev environment)", + }, + ], + }) + + // if they said rename, we loop again. otherwise + if (nextStep !== 'rename') { + finalName = true + // set the overwrite choice + overwrite = nextStep === 'overwrite' + } + } const { manager } = await prompts({ type: 'select', @@ -112,7 +159,7 @@ export const getChoices = async () => { initial: 0, }) - return { template, name, manager } + return { template, name, manager, overwrite } } const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1) @@ -128,9 +175,28 @@ const ensureDir = async (file, suppress = false) => { } // Helper method to copy template files -const copyFileOrTemplate = async (fromRootOrTemplate, toRoot, relativeDest, templateVars) => { +const copyFileOrTemplate = async ( + fromRootOrTemplate, + toRoot, + relativeDest, + templateVars, + overwrite = true +) => { const to = join(toRoot, relativeDest) + // if the file shouldn't be overwritten, open it to see if it exists + if (!overwrite) { + try { + // if the file doesn't exist, this will throw an error + const fd = await open(to) + fd.close() + // we only reach this return if the file exists, which means we're safe to leave + return + } catch { + // don't do anything with the error because it just means the file wasn't there and we can continue + } + } + await ensureDir(to) if (templateVars) { @@ -148,6 +214,7 @@ const copyPackageJson = async (config, choices) => { config.relativeFiles.templates['package.json'], 'utf-8' ) + await copyFileOrTemplate(packageJsonTemplate, config.dest, 'package.json', { name: choices.name, tag: config.tag, @@ -165,11 +232,17 @@ const copyIndexFile = async (config, choices) => { ? config.templateData.parts.map((p) => p.part) : config.templateData.parts // write the file - await copyFileOrTemplate(indexTemplate, config.dest, `${designSrcDir}/index.mjs`, { - name: choices.name, - Name: capitalize(choices.name), - parts: partNames, - }) + await copyFileOrTemplate( + indexTemplate, + config.dest, + `${designSrcDir}/index.mjs`, + { + name: choices.name, + Name: capitalize(choices.name), + parts: partNames, + }, + choices.overwrite + ) } // Template the part files @@ -213,7 +286,8 @@ const copyPartFiles = async (config, choices) => { partTemplate, config.dest, `${designSrcDir}/${templateArgs.part}.mjs`, - templateArgs + templateArgs, + choices.overwrite ) }) } @@ -271,7 +345,7 @@ const downloadLabFiles = async (config) => { // Helper method to initialize a git repository const initGitRepo = async (config, choices) => { - await writeFile(join(config.dest, '.gitignore'), config.gitignore, 'utf-8') + await copyFileOrTemplate(config.gitignore, config.dest, '.gitignore', {}, choices.overwrite) return execa( `git init -b main && git add . && git commit -m ":tada: Initialized ${choices.name} repository"`, @@ -345,8 +419,6 @@ export const createEnvironment = async (choices) => { shared: join(newDesignDir, `shared`), } - config.dest = join(process.cwd(), choices.name) - // Create target directory await mkdir(config.dest, { recursive: true }) @@ -427,7 +499,7 @@ export const createEnvironment = async (choices) => { chalk.white.dim(' | This does not stop you from developing your design'), }) } catch (err) { - /* no git no worries */ + console.log(err) } // All done. Show tips