Skip to content

Commit

Permalink
C3: Relax empty directory check (#4226)
Browse files Browse the repository at this point in the history
* C3: Relax empty directory check

* Adding tests

* Add pattern matching to allowedExistingFile check
  • Loading branch information
jculvey committed Oct 27, 2023
1 parent 4959ac2 commit 5810f81
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-news-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-cloudflare": patch
---

Relax empty directory check. Directories containing certain common config files and/or files created by an ide will be exempt from the pre-flight check
21 changes: 20 additions & 1 deletion packages/create-cloudflare/src/__tests__/common.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as command from "helpers/command";
import { describe, expect, test, vi } from "vitest";
import { isGitConfigured } from "../common";
import { isAllowedExistingFile, isGitConfigured } from "../common";
import { validateProjectDirectory } from "../common";

function promisify<T>(value: T) {
Expand Down Expand Up @@ -71,3 +71,22 @@ describe("validateProjectDirectory", () => {
expect(validateProjectDirectory("f".repeat(59), args)).toBeUndefined();
});
});

describe("isAllowedExistingFile", () => {
const allowed = [
"LICENSE",
"LICENSE.md",
"license",
".npmignore",
".git",
".DS_Store",
];
test.each(allowed)("%s", (val) => {
expect(isAllowedExistingFile(val)).toBe(true);
});

const disallowed = ["foobar", "potato"];
test.each(disallowed)("%s", (val) => {
expect(isAllowedExistingFile(val)).toBe(false);
});
});
54 changes: 51 additions & 3 deletions packages/create-cloudflare/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@ export const validateProjectDirectory = (
// Validate that the directory is non-existent or empty
const path = resolve(relativePath);
const existsAlready = existsSync(path);
const isEmpty = existsAlready && readdirSync(path).length === 0; // allow existing dirs _if empty_ to ensure c3 is non-destructive

if (existsAlready && !isEmpty) {
return `Directory \`${relativePath}\` already exists and is not empty. Please choose a new name.`;
if (existsAlready) {
for (const file of readdirSync(path)) {
if (!isAllowedExistingFile(file)) {
return `Directory \`${relativePath}\` already exists and contains files that might conflict. Please choose a new name.`;
}
}
}

// Ensure the name is valid per the pages schema
Expand All @@ -67,6 +70,51 @@ export const validateProjectDirectory = (
}
};

export const isAllowedExistingFile = (file: string) => {
// C3 shouldn't prevent a user from using an existing directory if it
// only contains benign config and/or other files from the following set
const allowedExistingFiles = new Set([
".DS_Store",
".git",
".gitattributes",
".gitignore",
".gitlab-ci.yml",
".hg",
".hgcheck",
".hgignore",
".idea",
".npmignore",
".travis.yml",
".vscode",
"Thumbs.db",
"docs",
"mkdocs.yml",
"npm-debug.log",
"yarn-debug.log",
"yarn-error.log",
"yarnrc.yml",
".yarn",
".gitkeep",
]);

if (allowedExistingFiles.has(file)) return true;

const allowedExistingPatters = [
/readme(\.md)?$/i,
/license(\.md)?$/i,
/\.iml$/,
/^npm-debug\.log/,
/^yarn-debug\.log/,
/^yarn-error\.log/,
];

for (const regex of allowedExistingPatters) {
if (regex.test(file)) return true;
}

return false;
};

export const setupProjectDirectory = (args: C3Args) => {
// Crash if the directory already exists
const path = resolve(args.projectName);
Expand Down

0 comments on commit 5810f81

Please sign in to comment.