Skip to content

Commit

Permalink
fix(remix-dev): Prioritize remix templates in CLI create (#2733)
Browse files Browse the repository at this point in the history
We've received a few issues where folks would step through the interactive CLI, select one of our templates and end up with an unexpected result, largely because we always prioritize local files when detecting the input type.

Since local files are likely going to be used less frequently than our built-in stacks and templates, this PR checks for those options first and ensures that those inputs are not matched to local files/directories to avoid conflicts. If a local file or forked repo is desired, the user can pass the full path/URL accordingly.

Closes: #2491
  • Loading branch information
chaance authored Apr 12, 2022
1 parent 8ddffea commit a04f3d1
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 15 deletions.
29 changes: 29 additions & 0 deletions packages/remix-dev/__tests__/create-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,35 @@ describe("the create command", () => {
expect(fse.existsSync(path.join(projectDir, "app/root.tsx"))).toBeTruthy();
});

it("prioritizes built-in templates when validating input", async () => {
let projectDir = await getProjectDir("built-in-template");

// create a local directory in our cwd with the same name as our chosen
// template and give it a package.json so we can check it against the one in
// our template
let dupedDir = path.join(process.cwd(), "express");
await fse.mkdir(dupedDir);
await fse.writeFile(
path.join(dupedDir, "package.json"),
'{ "name": "dummy" }'
);

await run([
"create",
projectDir,
"--template",
"express",
"--install",
"--typescript",
]);

expect(fse.existsSync(path.join(projectDir, "package.json"))).toBeTruthy();
let pkgJSON = JSON.parse(
fse.readFileSync(path.join(projectDir, "package.json"), "utf-8")
);
expect(pkgJSON.name).not.toBe("dummy");
});

it("runs remix.init script when installing dependencies", async () => {
let projectDir = await getProjectDir("remix-init-auto");
await run([
Expand Down
34 changes: 19 additions & 15 deletions packages/remix-dev/cli/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,14 +563,29 @@ export type TemplateType =
| "local";

export function detectTemplateType(template: string): TemplateType | null {
// 1. Check if the user passed a local file. If they hand us an explicit file
// 1. Prioritize Remix templates and stacks first. This ensures that inputs
// like `--template remix` always pull from our templates, which is almost
// always the desired behavior. If users maintain a fork either locally or
// in another repo they can pass the repo shorthand, URL or path instead.
// This also ensures that our interactive CLI always works as expected even
// if the user has another directory with the same name.
// https://github.com/remix-run/remix/issues/2491
if (isRemixTemplate(template)) {
return "template";
}

if (isRemixStack(template)) {
return "repoTemplate";
}

// 2. Check if the user passed a local file. If they hand us an explicit file
// URL, we'll validate it first. Otherwise we just ping the filesystem to
// see if the string references a filepath and, if not, move on.
if (template.startsWith("file://")) {
return "local";
}

// 2. Check if it's a path to a local directory.
// 3. Check if it's a path to a local directory.
try {
if (
fse.existsSync(
Expand All @@ -585,28 +600,17 @@ export function detectTemplateType(template: string): TemplateType | null {
// ignore FS errors and move on
}

// 3. check if it's one of the pre-built remix stacks
if (isRemixStack(template)) {
return "repoTemplate";
}

// 4. examples/<template> will use an example folder in the Remix repo
if (/^examples?\/[\w-]+$/.test(template)) {
return "example";
}

// 5. If the string contains no slashes, spaces, or special chars, we assume
// it is one of our remix-run/remix/templates.
if (/^[\w-]+$/.test(template)) {
return "template";
}

// 6. Handle GitHub repos (URLs or :org/:repo shorthand)
// 5. Handle GitHub repos (URLs or :org/:repo shorthand)
if (isValidGithubUrl(template) || isGithubRepoShorthand(template)) {
return "repo";
}

// 7. Any other valid URL should be treated as a tarball.
// 6. Any other valid URL should be treated as a tarball.
if (isUrl(template)) {
return "remoteTarball";
}
Expand Down

0 comments on commit a04f3d1

Please sign in to comment.