diff --git a/.changeset/six-weeks-grab.md b/.changeset/six-weeks-grab.md new file mode 100644 index 000000000000..1a7125331467 --- /dev/null +++ b/.changeset/six-weeks-grab.md @@ -0,0 +1,5 @@ +--- +'create-astro': patch +--- + +Add support for running in cloned empty git repository diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts index 186650d9e4e6..ee548f59d006 100644 --- a/packages/create-astro/src/index.ts +++ b/packages/create-astro/src/index.ts @@ -43,6 +43,46 @@ function isEmpty(dirPath: string) { return !fs.existsSync(dirPath) || fs.readdirSync(dirPath).length === 0; } +// Some existing files and directories can be safely ignored when checking if a directory is a valid project directory. +// https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/create-react-app/createReactApp.js#L907-L934 +const VALID_PROJECT_DIRECTORY_SAFE_LIST = [ + '.DS_Store', + '.git', + '.gitattributes', + '.gitignore', + '.gitlab-ci.yml', + '.hg', + '.hgcheck', + '.hgignore', + '.idea', + '.npmignore', + '.travis.yml', + '.yarn', + '.yarnrc.yml', + 'docs', + 'LICENSE', + 'mkdocs.yml', + 'Thumbs.db', + /\.iml$/, + /^npm-debug\.log/, + /^yarn-debug\.log/, + /^yarn-error\.log/, +]; + +function isValidProjectDirectory(dirPath: string) { + if (!fs.existsSync(dirPath)) { + return true; + } + + const conflicts = fs.readdirSync(dirPath).filter((content) => { + return !VALID_PROJECT_DIRECTORY_SAFE_LIST.some((safeContent) => { + return typeof safeContent === 'string' ? content === safeContent : safeContent.test(content); + }); + }); + + return conflicts.length === 0; +} + const { version } = JSON.parse( fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8') ); @@ -59,7 +99,7 @@ export async function main() { let cwd = args['_'][2] as string; - if (cwd && isEmpty(cwd)) { + if (cwd && isValidProjectDirectory(cwd)) { let acknowledgeProjectDir = ora({ color: 'green', text: `Using ${bold(cwd)} as project directory.`, @@ -67,10 +107,10 @@ export async function main() { acknowledgeProjectDir.succeed(); } - if (!cwd || !isEmpty(cwd)) { + if (!cwd || !isValidProjectDirectory(cwd)) { const notEmptyMsg = (dirPath: string) => `"${bold(dirPath)}" is not empty!`; - if (!isEmpty(cwd)) { + if (!isValidProjectDirectory(cwd)) { let rejectProjectDir = ora({ color: 'red', text: notEmptyMsg(cwd) }); rejectProjectDir.fail(); } @@ -81,7 +121,7 @@ export async function main() { message: 'Where would you like to create your new project?', initial: './my-astro-site', validate(value) { - if (!isEmpty(value)) { + if (!isValidProjectDirectory(value)) { return notEmptyMsg(value); } return true; @@ -143,8 +183,10 @@ export async function main() { // degit does not return an error when an invalid template is provided, as such we need to handle this manually // It's not very pretty, but to the user eye, we just return a nice message and nothing weird happened - if (isEmpty(cwd)) { - fs.rmdirSync(cwd); + if (isValidProjectDirectory(cwd)) { + if (isEmpty(cwd)) { + fs.rmdirSync(cwd); + } throw new Error(`Error: The provided template (${cyan(options.template)}) does not exist`); } } catch (err: any) { diff --git a/packages/create-astro/test/directory-step.test.js b/packages/create-astro/test/directory-step.test.js index 7c629598ad19..0031f97fd41e 100644 --- a/packages/create-astro/test/directory-step.test.js +++ b/packages/create-astro/test/directory-step.test.js @@ -4,6 +4,7 @@ import { PROMPT_MESSAGES, testDir, setup, promiseWithTimeout, timeout } from './ const inputs = { nonEmptyDir: './fixtures/select-directory/nonempty-dir', + nonEmptySafeDir: './fixtures/select-directory/nonempty-safe-dir', emptyDir: './fixtures/select-directory/empty-dir', nonexistentDir: './fixtures/select-directory/banana-dir', }; @@ -30,6 +31,16 @@ describe('[create-astro] select directory', function () { }); }); }); + it('should proceed on a non-empty safe directory', function () { + return promiseWithTimeout((resolve) => { + const { stdout } = setup([inputs.nonEmptySafeDir]); + stdout.on('data', (chunk) => { + if (chunk.includes(PROMPT_MESSAGES.template)) { + resolve(); + } + }); + }); + }); it('should proceed on an empty directory', async function () { const resolvedEmptyDirPath = path.resolve(testDir, inputs.emptyDir); if (!existsSync(resolvedEmptyDirPath)) { diff --git a/packages/create-astro/test/fixtures/select-directory/nonempty-safe-dir/.gitignore b/packages/create-astro/test/fixtures/select-directory/nonempty-safe-dir/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/create-astro/test/fixtures/select-directory/nonempty-safe-dir/module.iml b/packages/create-astro/test/fixtures/select-directory/nonempty-safe-dir/module.iml new file mode 100644 index 000000000000..e69de29bb2d1