fork refine

This commit is contained in:
Stefan Pejcic
2024-02-05 10:23:04 +01:00
parent 3fffde9a8f
commit 8496a83edb
3634 changed files with 715528 additions and 2 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,58 @@
import { ProjectTypes } from "@definitions/projectTypes";
import { getProjectType } from "@utils/project";
import { 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 "@utils/package";
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's 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 devtoolsDefault = await isDevtoolsInstalled();
const devtools = params.devtools === "false" ? false : devtoolsDefault;
if (devtools) {
devtoolsRunner();
}
runScript(binPath, command);
};
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,366 @@
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 "start" 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([
"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 "start" 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([
"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("Next.js project type", () => {
const projectType = ProjectTypes.NEXTJS;
describe("getDev with empty args", () => {
test('should return array with only "start" 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 "start" 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 "start" 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 "start" 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("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 array with only "start" 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', () => {
expect(projectScripts[projectType].getStart([])).toEqual([]);
});
});
describe("getBuild with empty args", () => {
test('should return array with only "build" if args is empty', () => {
expect(projectScripts[projectType].getBuild([])).toEqual([]);
});
});
describe("getDev", () => {
test('should prepend "start" to the args array', () => {
const args = ["--arg1", "--arg2"];
expect(projectScripts[projectType].getDev(args)).toEqual([...args]);
});
});
describe("getStart", () => {
test('should prepend "start" to the args array', () => {
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([
...args,
]);
});
});
});

View File

@@ -0,0 +1,75 @@
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(
`${process.cwd()}/node_modules/.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 { 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,30 @@
import { ProjectTypes } from "@definitions/projectTypes";
import execa from "execa";
export const runScript = async (binPath: string, args: 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",
...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 { 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,34 @@
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 (projectType === ProjectTypes.REMIX && runner === "start") {
projectType = "remix-serve" as ProjectTypes;
}
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(", ")}`;
};