Skip to content

Commit

Permalink
[C3] fix: make sure that all C3 projects include in their `.gitignore…
Browse files Browse the repository at this point in the history
…` the wrangler files
  • Loading branch information
dario-piotrowicz committed Feb 29, 2024
1 parent 65d0399 commit b132c3f
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 7 deletions.
9 changes: 9 additions & 0 deletions .changeset/stale-needles-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"create-cloudflare": patch
---

fix: make sure that all C3 projects include in their `.gitignore` the wrangler files

previously only the worker templates included in their `.gitignore` the wrangler files
(those being `.dev.vars` and `.wrangler`), make sure to instead include such files in
the `.gitignore` files of all the templates including the full stack ones
240 changes: 240 additions & 0 deletions packages/create-cloudflare/src/__tests__/templates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { existsSync, statSync } from "fs";
import { appendFile, readFile, writeFile } from "helpers/files";
import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import { addWranglerToGitIgnore } from "../templates";
import type { PathLike } from "fs";
import type { C3Context } from "types";

vi.mock("fs");
vi.mock("helpers/files");

describe("addWranglerToGitIgnore", () => {
const writeFileResults: {
file: string | undefined;
content: string | undefined;
} = { file: undefined, content: undefined };
const appendFileResults: {
file: string | undefined;
content: string | undefined;
} = { file: undefined, content: undefined };

beforeAll(() => {
vi.mocked(writeFile).mockImplementation((file: string, content: string) => {
writeFileResults.file = file;
writeFileResults.content = content;
});
vi.mocked(appendFile).mockImplementation(
(file: string, content: string) => {
appendFileResults.file = file;
appendFileResults.content = content;
}
);
});

beforeEach(() => {
vi.mocked(statSync).mockImplementation(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
(path: string) => ({
isDirectory() {
return path.endsWith(".git");
},
})
);
vi.mocked(existsSync).mockReset();
vi.mocked(readFile).mockReset();
appendFileResults.file = undefined;
appendFileResults.content = undefined;
writeFileResults.file = undefined;
writeFileResults.content = undefined;
});

test("should append the wrangler section to a standard gitignore file", () => {
mockGitIgnore(
"my-project/.gitignore",
`
node_modules
.vscode`
);
addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(appendFileResults.file).toMatchInlineSnapshot(
`"my-project/.gitignore"`
);
expect(appendFileResults.content).toMatchInlineSnapshot(`
"
# wrangler files
.wrangler
.dev.vars
"
`);
});

test("should not touch the gitignore file if it already contains all wrangler files", () => {
mockGitIgnore(
"my-project/.gitignore",
`
node_modules
.dev.vars
.vscode
.wrangler
`
);
addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(appendFileResults.file).toBeUndefined();
expect(appendFileResults.content).toBeUndefined();
});

test("should not touch the gitignore file if contains all wrangler files (and can cope with comments)", () => {
mockGitIgnore(
"my-project/.gitignore",
`
node_modules
.wrangler # This is for wrangler
.dev.vars # this is for wrangler and getPlatformProxy
.vscode
`
);
addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(appendFileResults.file).toBeUndefined();
expect(appendFileResults.content).toBeUndefined();
});

test("should append to the gitignore file the missing wrangler files when some is already present (without including the section heading)", () => {
mockGitIgnore(
"my-project/.gitignore",
`
node_modules
.dev.vars
.vscode`
);
addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(appendFileResults.file).toMatchInlineSnapshot(
`"my-project/.gitignore"`
);
expect(appendFileResults.content).toMatchInlineSnapshot(`
"
.wrangler
"
`);
});

test("when it appends to the gitignore file it includes an empty line only if there wasn't one already", () => {
mockGitIgnore(
"my-project/.gitignore",
`
node_modules
.dev.vars
.vscode
`
);
addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(appendFileResults.file).toMatchInlineSnapshot(
`"my-project/.gitignore"`
);
expect(appendFileResults.content).toMatchInlineSnapshot(`
"
.wrangler
"
`);
});

test("should create the gitignore file if it didn't exist already", () => {
// let's mock a gitignore file to be read by readFile
mockGitIgnore("my-project/.gitignore", "");
// but let's pretend that it doesn't exist
vi.mocked(existsSync).mockImplementation(() => false);

addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

// writeFile wrote the (empty) gitignore file
expect(writeFileResults.file).toMatchInlineSnapshot(
`"my-project/.gitignore"`
);
expect(writeFileResults.content).toMatchInlineSnapshot(`""`);

// and the correct lines were then added to it
expect(appendFileResults.file).toMatchInlineSnapshot(
`"my-project/.gitignore"`
);
expect(appendFileResults.content).toMatchInlineSnapshot(`
"
# wrangler files
.wrangler
.dev.vars
"
`);
});

test("should not create the gitignore file the project doesn't use git", () => {
// let's mock a gitignore file to be read by readFile
mockGitIgnore("my-project/.gitignore", "");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
vi.mocked(statSync).mockImplementation(() => ({
isDirectory() {
return false;
},
}));
vi.mocked(existsSync).mockImplementation(() => false);

addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(writeFileResults.file).toBeUndefined();
expect(writeFileResults.content).toBeUndefined();
});

test("should not add the .wrangler entry if a .wrangler/ is already included)", () => {
mockGitIgnore(
"my-project/.gitignore",
`
node_modules
.wrangler/ # This is for wrangler
.vscode
`
);
addWranglerToGitIgnore({
project: { path: "my-project" },
} as unknown as C3Context);

expect(appendFileResults.file).toMatchInlineSnapshot(
`"my-project/.gitignore"`
);
expect(appendFileResults.content).toMatchInlineSnapshot(`
"
.dev.vars
"
`);
});

function mockGitIgnore(path: string, content: string) {
vi.mocked(existsSync).mockImplementation(
(filePath: PathLike) => filePath === path
);
vi.mocked(readFile).mockImplementation((filePath: string) =>
filePath === path ? content.replace(/\n\s*/g, "\n") : ""
);
}
});
3 changes: 3 additions & 0 deletions packages/create-cloudflare/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from "./common";
import { createProject } from "./pages";
import {
addWranglerToGitIgnore,
copyTemplateFiles,
selectTemplate,
updatePackageName,
Expand Down Expand Up @@ -147,6 +148,8 @@ const configure = async (ctx: C3Context) => {
await template.configure({ ...ctx });
}

addWranglerToGitIgnore(ctx);

await updatePackageScripts(ctx);

await offerGit(ctx);
Expand Down
66 changes: 64 additions & 2 deletions packages/create-cloudflare/src/templates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { existsSync } from "fs";
import { existsSync, statSync } from "fs";
import { cp, mkdtemp, rename } from "fs/promises";
import { tmpdir } from "os";
import { join, resolve } from "path";
Expand All @@ -9,7 +9,14 @@ import { spinner } from "@cloudflare/cli/interactive";
import deepmerge from "deepmerge";
import degit from "degit";
import { C3_DEFAULTS } from "helpers/cli";
import { readJSON, usesTypescript, writeJSON } from "helpers/files";
import {
appendFile,
readFile,
readJSON,
usesTypescript,
writeFile,
writeJSON,
} from "helpers/files";
import { validateTemplateUrl } from "./validators";
import type { C3Args, C3Context, PackageJson } from "types";

Expand Down Expand Up @@ -469,3 +476,58 @@ export const getCopyFilesDestinationDir = (

return copyFiles.destinationDir(ctx);
};

export const addWranglerToGitIgnore = (ctx: C3Context) => {
const gitStat = statSync(`${ctx.project.path}/.git`);
if (!gitStat.isDirectory()) {
// there is no .git directory so the project is likely not using git
return;
}

const gitIgnorePath = `${ctx.project.path}/.gitignore`;

const s = spinner();
s.start("Adding Wrangler files to the .gitignore file");

const fileExisted = existsSync(gitIgnorePath);

if (!fileExisted) {
writeFile(gitIgnorePath, "");
}

const existingGitIgnoreContent = readFile(gitIgnorePath);

const wranglerGitIgnoreFiles = [".wrangler", ".dev.vars"] as const;
const wranglerGitIgnoreFilesToAdd = wranglerGitIgnoreFiles.filter(
(file) =>
!existingGitIgnoreContent.match(
new RegExp(`\n${file}${file === ".wrangler" ? "/?" : ""}\\s+(#'*)?`)
)
);

if (wranglerGitIgnoreFilesToAdd.length === 0) {
s.stop(`The .gitignore file already includes all the wrangler files`);
return;
}

const linesToAppend = [
"",
...(!existingGitIgnoreContent.match(/\n\s*$/) ? [""] : []),
];

if (wranglerGitIgnoreFilesToAdd.length === wranglerGitIgnoreFiles.length) {
linesToAppend.push("# wrangler files");
}

wranglerGitIgnoreFilesToAdd.forEach((line) => linesToAppend.push(line));

linesToAppend.push("");

appendFile(gitIgnorePath, linesToAppend.join("\n"));

s.stop(
`${brandColor(fileExisted ? "updated" : "created")} ${dim(
".gitignore file"
)}`
);
};
5 changes: 0 additions & 5 deletions packages/wrangler/templates/gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,3 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*

# wrangler project

.dev.vars
.wrangler/

0 comments on commit b132c3f

Please sign in to comment.