This commit is contained in:
Stefan Pejcic
2024-11-07 19:03:37 +01:00
parent c6df945ed5
commit 09f9f9502d
2472 changed files with 620417 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
import { Command, Option } from "commander";
import { getProjectType } from "@utils/project";
import { projectScripts } from "../projectScripts";
import { runScript } from "../runScript";
import { updateNotifier } from "src/update-notifier";
import { getPlatformOptionDescription, getRunnerDescription } from "../utils";
import { ProjectTypes } from "@definitions/projectTypes";
const build = (program: Command) => {
return program
.command("build")
.description(getRunnerDescription("build"))
.allowUnknownOption(true)
.addOption(
new Option(
"-p, --platform <platform>",
getPlatformOptionDescription(),
).choices(
Object.values(ProjectTypes).filter(
(type) => type !== ProjectTypes.UNKNOWN,
),
),
)
.argument("[args...]")
.action(action);
};
const action = async (
args: string[],
{ platform }: { platform: ProjectTypes },
) => {
const projectType = getProjectType(platform);
const binPath = projectScripts[projectType].getBin("build");
const command = projectScripts[projectType].getBuild(args);
await updateNotifier();
try {
await runScript(binPath, command);
} catch (error) {
process.exit(1);
}
};
export default build;

View File

@@ -0,0 +1,66 @@
import { ProjectTypes } from "@definitions/projectTypes";
import { getDevtoolsEnvKeyByProjectType, getProjectType } from "@utils/project";
import { type Command, Option } from "commander";
import { updateNotifier } from "src/update-notifier";
import { devtoolsRunner } from "src/commands/devtools";
import { projectScripts } from "../projectScripts";
import { runScript } from "../runScript";
import { getPlatformOptionDescription, getRunnerDescription } from "../utils";
import { isDevtoolsInstalled } from "src/utils/package";
import { ENV } from "src/utils/env";
const dev = (program: Command) => {
return program
.command("dev")
.description(getRunnerDescription("dev"))
.allowUnknownOption(true)
.addOption(
new Option(
"-p, --platform <platform>",
getPlatformOptionDescription(),
).choices(
Object.values(ProjectTypes).filter(
(type) => type !== ProjectTypes.UNKNOWN,
),
),
)
.addOption(
new Option(
"-d, --devtools <devtools>",
"Start Refine Devtools server",
).default("true", "true if devtools is installed"),
)
.argument("[args...]")
.action(action);
};
const action = async (
args: string[],
{ platform, ...params }: { devtools: string; platform: ProjectTypes },
) => {
const projectType = getProjectType(platform);
const binPath = projectScripts[projectType].getBin("dev");
const command = projectScripts[projectType].getDev(args);
await updateNotifier();
const devtoolsPortEnvKey = getDevtoolsEnvKeyByProjectType(projectType);
const devtoolsDefault = await isDevtoolsInstalled();
const devtools = params.devtools === "false" ? false : devtoolsDefault;
if (devtools) {
devtoolsRunner({ exitOnError: false });
}
const envWithDevtoolsPort =
devtools && ENV.REFINE_DEVTOOLS_PORT
? { [devtoolsPortEnvKey]: ENV.REFINE_DEVTOOLS_PORT }
: undefined;
runScript(binPath, command, envWithDevtoolsPort);
};
export default dev;

View File

@@ -0,0 +1,6 @@
import start from "./start";
import dev from "./dev";
import build from "./build";
import run from "./run";
export { dev, start, build, run };

View File

@@ -0,0 +1,463 @@
import { projectScripts } from "./projectScripts";
import { ProjectTypes } from "@definitions/projectTypes";
describe("REACT_SCRIPT project type", () => {
const projectType = ProjectTypes.REACT_SCRIPT;
describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["start"]);
});
});
describe("getStart with empty args", () => {
test('should return array with only "start" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["start"]);
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["build"]);
});
});
describe("getDev", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"start",
...args,
]);
});
});
describe("getStart", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"start",
...args,
]);
});
});
describe("getBuild", () => {
test('should prepend "build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"build",
...args,
]);
});
});
});
describe("VITE project type", () => {
const projectType = ProjectTypes.VITE;
describe("getDev with empty args", () => {
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 "preview" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["preview"]);
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["build"]);
});
});
describe("getDev", () => {
test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"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 "build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"build",
...args,
]);
});
});
});
describe("NEXTJS project type", () => {
const projectType = ProjectTypes.NEXTJS;
describe("getDev with empty args", () => {
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', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["start"]);
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["build"]);
});
});
describe("getDev", () => {
test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
...args,
]);
});
});
describe("getStart", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"start",
...args,
]);
});
});
describe("getBuild", () => {
test('should prepend "build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"build",
...args,
]);
});
});
});
describe("REMIX project type", () => {
const projectType = ProjectTypes.REMIX;
describe("getDev with empty args", () => {
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 default", () => {
const logSpy = jest.spyOn(console, "warn");
expect(projectScripts[projectType].getStart([])).toEqual([
"./build/index.js",
]);
expect(logSpy).toHaveBeenCalled();
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["build"]);
});
});
describe("getDev", () => {
test('should prepend "dev" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"dev",
...args,
]);
});
});
describe("getStart", () => {
test("should return args", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([...args]);
});
});
describe("getBuild", () => {
test('should prepend "build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"build",
...args,
]);
});
});
});
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;
describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["start"]);
});
});
describe("getStart with empty args", () => {
test('should return array with only "start" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["start"]);
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["build"]);
});
});
describe("getDev", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"start",
...args,
]);
});
});
describe("getStart", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"start",
...args,
]);
});
});
describe("getBuild", () => {
test('should prepend "build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"build",
...args,
]);
});
});
});
describe("PARCEL project type", () => {
const projectType = ProjectTypes.PARCEL;
describe("getDev with empty args", () => {
test('should return array with only "start" if args is empty', () => {
expect(projectScripts[projectType].getDev([])).toEqual(["start"]);
});
});
describe("getStart with empty args", () => {
test('should return array with only "start" if args is empty', () => {
expect(projectScripts[projectType].getStart([])).toEqual(["start"]);
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual(["build"]);
});
});
describe("getDev", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([
"start",
...args,
]);
});
});
describe("getStart", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([
"start",
...args,
]);
});
});
describe("getBuild", () => {
test('should prepend "build" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([
"build",
...args,
]);
});
});
});
describe("UNKNOWN project type", () => {
const projectType = ProjectTypes.UNKNOWN;
describe("getDev with empty args", () => {
test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getDev([])).toEqual([]);
});
});
describe("getStart with empty args", () => {
test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getStart([])).toEqual([]);
});
});
describe("getBuild with empty args", () => {
test("should return empty array if args is empty", () => {
expect(projectScripts[projectType].getBuild([])).toEqual([]);
});
});
describe("getDev", () => {
test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([...args]);
});
});
describe("getStart", () => {
test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getStart(args)).toEqual([...args]);
});
});
describe("getBuild", () => {
test("should return the args array as is", () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getBuild(args)).toEqual([...args]);
});
});
});

View File

@@ -0,0 +1,110 @@
import { ProjectTypes } from "@definitions/projectTypes";
/**
* Map `Refine` cli commands to project script
*/
export const projectScripts = {
[ProjectTypes.REACT_SCRIPT]: {
getDev: (args: string[]) => ["start", ...args],
getStart: (args: string[]) => ["start", ...args],
getBuild: (args: string[]) => ["build", ...args],
getBin: () => require.resolve(".bin/react-scripts"),
},
[ProjectTypes.VITE]: {
getDev: (args: string[]) => ["dev", ...args],
getStart: (args: string[]) => ["preview", ...args],
getBuild: (args: string[]) => ["build", ...args],
getBin: () => require.resolve(".bin/vite"),
},
[ProjectTypes.NEXTJS]: {
getDev: (args: string[]) => ["dev", ...args],
getStart: (args: string[]) => ["start", ...args],
getBuild: (args: string[]) => ["build", ...args],
getBin: () => require.resolve(".bin/next"),
},
[ProjectTypes.REMIX]: {
getDev: (args: string[]) => ["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/index.js`
const hasArgs = args?.length;
if (hasArgs) {
return args;
}
// otherwise print a warning and use `./build/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/index.js` as default");
console.warn("Example: `refine start ./build/index.js`");
console.log();
return ["./build/index.js"];
},
getBuild: (args: string[]) => ["build", ...args],
getBin: (type?: "dev" | "start" | "build") => {
const binName = type === "start" ? "remix-serve" : "remix";
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],
getBuild: (args: string[]) => ["build", ...args],
getBin: () => require.resolve(".bin/craco"),
},
[ProjectTypes.PARCEL]: {
getDev: (args: string[]) => ["start", ...args],
getStart: (args: string[]) => ["start", ...args],
getBuild: (args: string[]) => ["build", ...args],
getBin: () => require.resolve(".bin/parcel"),
},
[ProjectTypes.UNKNOWN]: {
getDev: (args: string[]) => [...args],
getStart: (args: string[]) => [...args],
getBuild: (args: string[]) => [...args],
getBin: () => {
return "unknown";
},
},
};

View File

@@ -0,0 +1,47 @@
import { getPreferedPM, getScripts } from "@utils/package";
import chalk from "chalk";
import type { Command } from "commander";
import { runScript } from "../runScript";
const run = (program: Command) => {
return program
.command("run")
.description(
"Runs a defined package script. If no `command` is provided, it will list the available scripts",
)
.allowUnknownOption(true)
.argument("[command] [args...]")
.action(action);
};
const action = async (args: string[]) => {
const [script, ...restArgs] = args;
const scriptsInPackage = getScripts();
// Show available scripts when no script is provided
if (!script) {
console.log(`Available via ${chalk.blue("`refine run`")}:\n`);
for (const [key, value] of Object.entries(scriptsInPackage)) {
console.log(` ${key}`);
console.log(` ${chalk.dim(value)}`);
console.log();
}
return;
}
// Check if script exists in package.json
const isDefinedScript = Object.keys(scriptsInPackage).includes(script);
// If script is not defined, run from node_modules
if (!isDefinedScript) {
const binPath = `${process.cwd()}/node_modules/.bin/${script}`;
runScript(binPath, restArgs);
return;
}
const pm = await getPreferedPM();
runScript(pm.name, ["run", script, ...restArgs]);
};
export default run;

View File

@@ -0,0 +1,37 @@
import { ProjectTypes } from "@definitions/projectTypes";
import { ENV } from "@utils/env";
import execa from "execa";
export const runScript = async (
binPath: string,
args: string[],
env: Record<string, string> = {},
) => {
if (binPath === "unknown") {
const supportedProjectTypes = Object.values(ProjectTypes)
.filter((v) => v !== "unknown")
.join(", ");
console.error(
`We couldn't find executable for your project. Supported executables are ${supportedProjectTypes}.\nPlease use your own script directly. If you think this is an issue, please report it at: https://github.com/refinedev/refine/issues`,
);
return;
}
const execution = execa(binPath, args, {
stdio: "pipe",
windowsHide: false,
env: {
FORCE_COLOR: "true",
REFINE_NO_TELEMETRY: ENV.REFINE_NO_TELEMETRY,
...env,
...process.env,
},
});
execution.stdout?.pipe(process.stdout);
execution.stderr?.pipe(process.stderr);
return await execution;
};

View File

@@ -0,0 +1,42 @@
import { ProjectTypes } from "@definitions/projectTypes";
import { getProjectType } from "@utils/project";
import { type Command, Option } from "commander";
import { updateNotifier } from "src/update-notifier";
import { projectScripts } from "../projectScripts";
import { runScript } from "../runScript";
import { getPlatformOptionDescription, getRunnerDescription } from "../utils";
const start = (program: Command) => {
return program
.command("start")
.description(getRunnerDescription("start"))
.allowUnknownOption(true)
.addOption(
new Option(
"-p, --platform <platform>",
getPlatformOptionDescription(),
).choices(
Object.values(ProjectTypes).filter(
(type) => type !== ProjectTypes.UNKNOWN,
),
),
)
.argument("[args...]")
.action(action);
};
const action = async (
args: string[],
{ platform }: { platform: ProjectTypes },
) => {
const projectType = getProjectType(platform);
const binPath = projectScripts[projectType].getBin("start");
const command = projectScripts[projectType].getStart(args);
await updateNotifier();
runScript(binPath, command);
};
export default start;

View File

@@ -0,0 +1,42 @@
import { ProjectTypes } from "@definitions/projectTypes";
import { getProjectType } from "@utils/project";
import { projectScripts } from "../projectScripts";
export const getRunnerDescription = (runner: "dev" | "start" | "build") => {
let projectType = getProjectType();
let command: string[] = [];
switch (runner) {
case "dev":
command = projectScripts[projectType].getDev([""]);
break;
case "start":
command = projectScripts[projectType].getStart([""]);
break;
case "build":
command = projectScripts[projectType].getBuild([""]);
break;
}
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(
" ",
)}\`. Also accepts all the arguments \`${projectType}\` accepts.`;
};
export const getPlatformOptionDescription = () => {
return `Platform to run command on. \nex: ${Object.values(ProjectTypes).join(
", ",
)}`;
};