Skip to content

Commit

Permalink
feat(cli): add Remix SPA mode support (refinedev#6246) (fixes refined…
Browse files Browse the repository at this point in the history
…ev#6127)

Co-authored-by: Ali Emir Şen <[email protected]>
  • Loading branch information
2 people authored and emrecancorapci committed Sep 4, 2024
1 parent 74bc6ea commit 0befe4c
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 21 deletions.
11 changes: 11 additions & 0 deletions .changeset/wicked-cherries-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@refinedev/cli": patch
---

feat: added scripts for Remix SPA Mode

It is now possible to execute the Remix SPA Mode script by selecting it from the platform options.

Two new project types are added `remix-vite` and `remix-spa`. `remix-vite` is Remix + Vite and `remix-spa` is Remix + Vite SPA Mode. While `remix-vite` type can be inferred from the project configuration without needing to specify it in the command, `remix-spa` type needs to be specified explicitly.

[Resolves #6127](https://github.com/refinedev/refine/issues/6127)
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ export const AntDesignIntegration: Integration = {
const description = "Setup Ant Design with Refine";
let disabled;

if ([ProjectTypes.NEXTJS, ProjectTypes.REMIX].includes(projectType)) {
if (
[
ProjectTypes.NEXTJS,
ProjectTypes.REMIX,
ProjectTypes.REMIX_VITE,
ProjectTypes.REMIX_SPA,
].includes(projectType)
) {
disabled =
"Automatic setup only available Vite for now. See the documentation for manual installation: https://refine.dev/docs/ui-integrations/ant-design/introduction/#installation";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ export const ReactRouterIntegration: Integration = {
disabled = `Can't be used with Next.js. https://nextjs.org/docs/app/building-your-application/routing`;
}

if (projectType === ProjectTypes.REMIX) {
if (
projectType === ProjectTypes.REMIX ||
projectType === ProjectTypes.REMIX_VITE ||
projectType === ProjectTypes.REMIX_SPA
) {
disabled = `Can't be used with Remix. https://remix.run/docs/en/main/discussion/routes`;
}

Expand Down
139 changes: 122 additions & 17 deletions packages/cli/src/commands/runner/projectScripts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ describe("REACT_SCRIPT project type", () => {
});
});

describe("Vite project type", () => {
describe("VITE project type", () => {
const projectType = ProjectTypes.VITE;

describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
test('should return array with only "dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["dev"]);
});
});

describe("getStart with empty args", () => {
test('should return array with only "start" if args is empty', () => {
test('should return array with only "preview" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["preview"]);
});
});
Expand All @@ -75,7 +75,7 @@ describe("Vite project type", () => {
});

describe("getDev", () => {
test('should prepend "start" to the args array', () => {
test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
Expand All @@ -85,7 +85,7 @@ describe("Vite project type", () => {
});

describe("getStart", () => {
test('should prepend "start" to the args array', () => {
test('should prepend "preview" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"preview",
Expand All @@ -105,11 +105,11 @@ describe("Vite project type", () => {
});
});

describe("Next.js project type", () => {
describe("NEXTJS project type", () => {
const projectType = ProjectTypes.NEXTJS;

describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
test('should return array with only "dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["dev"]);
});
});
Expand All @@ -127,7 +127,7 @@ describe("Next.js project type", () => {
});

describe("getDev", () => {
test('should prepend "start" to the args array', () => {
test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
Expand Down Expand Up @@ -157,11 +157,11 @@ describe("Next.js project type", () => {
});
});

describe("Remix project type", () => {
describe("REMIX project type", () => {
const projectType = ProjectTypes.REMIX;

describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
test('should return array with only "dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["dev"]);
});
});
Expand All @@ -183,7 +183,7 @@ describe("Remix project type", () => {
});

describe("getDev", () => {
test('should prepend "start" to the args array', () => {
test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
Expand All @@ -210,6 +210,111 @@ describe("Remix project type", () => {
});
});

describe("REMIX_VITE project type", () => {
const projectType = ProjectTypes.REMIX_VITE;

describe("getDev with empty args", () => {
test('should return array with only "vite:dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["vite:dev"]);
});
});

describe("getStart with empty args", () => {
test("should return default", () => {
const logSpy = jest.spyOn(console, "warn");
expect(projectScripts[projectType].getStart([])).toEqual([
"./build/server/index.js",
]);
expect(logSpy).toHaveBeenCalled();
});
});

describe("getBuild with empty args", () => {
test('should return array with only "vite:build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["vite:build"]);
});
});

describe("getDev", () => {
test('should prepend "vite:dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"vite:dev",
...args,
]);
});
});

describe("getStart", () => {
test("should return args", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([...args]);
});
});

describe("getBuild", () => {
test('should prepend "vite:build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"vite:build",
...args,
]);
});
});
});

describe("REMIX_SPA project type", () => {
const projectType = ProjectTypes.REMIX_SPA;

describe("getDev with empty args", () => {
test('should return array with only "vite:dev" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["vite:dev"]);
});
});

describe("getStart with empty args", () => {
test('should return array with only "preview" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["preview"]);
});
});

describe("getBuild with empty args", () => {
test('should return array with only "vite:build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["vite:build"]);
});
});

describe("getDev", () => {
test('should prepend "vite:dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"vite:dev",
...args,
]);
});
});

describe("getStart", () => {
test('should prepend "preview" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"preview",
...args,
]);
});
});

describe("getBuild", () => {
test('should prepend "vite:build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"vite:build",
...args,
]);
});
});
});

describe("CRACO project type", () => {
const projectType = ProjectTypes.CRACO;

Expand Down Expand Up @@ -318,39 +423,39 @@ describe("UNKNOWN project type", () => {
const projectType = ProjectTypes.UNKNOWN;

describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getDev([])).toEqual([]);
});
});

describe("getStart with empty args", () => {
test('should return array with only "start" if args is empty', () => {
test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getStart([])).toEqual([]);
});
});

describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getBuild([])).toEqual([]);
});
});

describe("getDev", () => {
test('should prepend "start" to the args array', () => {
test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([...args]);
});
});

describe("getStart", () => {
test('should prepend "start" to the args array', () => {
test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([...args]);
});
});

describe("getBuild", () => {
test('should prepend "build" to the args array', () => {
test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([...args]);
});
Expand Down
37 changes: 37 additions & 0 deletions packages/cli/src/commands/runner/projectScripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,43 @@ export const projectScripts = {
return require.resolve(`.bin/${binName}`);
},
},
[ProjectTypes.REMIX_VITE]: {
getDev: (args: string[]) => ["vite:dev", ...args],
getStart: (args: string[]) => {
// remix-serve accepts a path to the entry file as an argument
// if we have arguments, we will pass them to remix-serve and do nothing.
// ex: `refine start ./build/server/index.js`
const hasArgs = args?.length;
if (hasArgs) {
return args;
}

// otherwise print a warning and use `./build/server/index.js` as default
console.log();
console.warn(
"🚨 Remix requires a path to the entry file. Please provide it as an argument to `refine start` command in package.json scripts",
);
console.warn("Refine will use `./build/server/index.js` as default");
console.warn("Example: `refine start ./build/server/index.js`");
console.log();

return ["./build/server/index.js"];
},
getBuild: (args: string[]) => ["vite:build", ...args],
getBin: (type?: "dev" | "start" | "build") => {
const binName = type === "start" ? "remix-serve" : "remix";
return require.resolve(`.bin/${binName}`);
},
},
[ProjectTypes.REMIX_SPA]: {
getDev: (args: string[]) => ["vite:dev", ...args],
getStart: (args: string[]) => ["preview", ...args],
getBuild: (args: string[]) => ["vite:build", ...args],
getBin: (type?: "dev" | "start" | "build") => {
const binName = type === "start" ? "vite" : "remix";
return require.resolve(`.bin/${binName}`);
},
},
[ProjectTypes.CRACO]: {
getDev: (args: string[]) => ["start", ...args],
getStart: (args: string[]) => ["start", ...args],
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/commands/runner/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ export const getRunnerDescription = (runner: "dev" | "start" | "build") => {
break;
}

if (projectType === ProjectTypes.REMIX && runner === "start") {
projectType = "remix-serve" as ProjectTypes;
if (runner === "start") {
switch (projectType) {
case ProjectTypes.REMIX:
case ProjectTypes.REMIX_VITE:
projectType = "remix-serve" as ProjectTypes;
break;
case ProjectTypes.REMIX_SPA:
projectType = ProjectTypes.VITE;
break;
}
}

return `It runs: \`${projectType} ${command.join(
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/definitions/projectTypes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export enum ProjectTypes {
REACT_SCRIPT = "react-scripts",
REMIX = "remix",
REMIX_VITE = "remix-vite",
REMIX_SPA = "remix-spa",
NEXTJS = "nextjs",
VITE = "vite",
CRACO = "craco",
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/utils/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export const getProjectType = (platform?: ProjectTypes): ProjectTypes => {
dependencies.includes("@remix-run/react") ||
devDependencies.includes("@remix-run/react")
) {
// check for remix-vite
if (dependencies.includes("vite") || devDependencies.includes("vite")) {
return ProjectTypes.REMIX_VITE;
}

return ProjectTypes.REMIX;
}

Expand Down
10 changes: 10 additions & 0 deletions packages/cli/src/utils/resource/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ it("should get provider path", () => {
alias: "~/providers",
});

expect(getProviderPath(ProjectTypes.REMIX_VITE)).toEqual({
path: "app/providers",
alias: "~/providers",
});

expect(getProviderPath(ProjectTypes.REMIX_SPA)).toEqual({
path: "app/providers",
alias: "~/providers",
});

expect(getProviderPath(ProjectTypes.VITE)).toEqual({
path: "src/providers",
alias: "providers",
Expand Down
Loading

0 comments on commit 0befe4c

Please sign in to comment.