Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support biome.js as a linter / formatter option in the cli #2021

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b4c3f6a
docs: added appRouter CLI flag to docs
aidansunbury Oct 26, 2024
b519a8f
Merge branch 't3-oss:main' into main
aidansunbury Nov 9, 2024
e1dfc85
initial comments on edit locations
aidansunbury Nov 9, 2024
41d2811
working on being able to test initial version
aidansunbury Nov 10, 2024
bafb814
moved prettier related setup to eslint file so that it is not tied to…
aidansunbury Nov 10, 2024
d62917f
added package.json scripts for biome
aidansunbury Nov 10, 2024
2c1f7f1
added changeset
aidansunbury Nov 10, 2024
cbad9bc
fixed biome scripts and created addPackageScript function
aidansunbury Nov 15, 2024
897ae87
Merge branch 'main' into main
juliusmarminge Jan 12, 2025
e84958e
test adding lint and formating check to e2e tests
aidansunbury Jan 15, 2025
9ef765b
fix lint issues to be able to run updated e2e tests
aidansunbury Jan 15, 2025
4ed4df7
move check to correct directory to actually run tests
aidansunbury Jan 15, 2025
af52d73
make sure we can format and lint
aidansunbury Jan 15, 2025
d27c29f
run check and format in correct directory with eslint scaffolded
aidansunbury Jan 15, 2025
6ad3205
format scaffolded project after creation and check in e2e
aidansunbury Jan 15, 2025
08173ce
lint fix
aidansunbury Jan 15, 2025
9cfd0d2
revert e2e tests to ensure less than 256 matrix variations
aidansunbury Jan 15, 2025
8415dfa
remove extra parts of e2e
aidansunbury Jan 15, 2025
63534b4
add back space
aidansunbury Jan 15, 2025
59eec32
Merge branch 'main' into main
aidansunbury Jan 15, 2025
2ce9ea5
added biome.jsonc suggestions
aidansunbury Jan 16, 2025
29fdd74
Merge branch 'main' of https://github.com/aidansunbury/create-t3-app
aidansunbury Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/late-tips-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-t3-app": minor
---

Added support for biome.js as a formatter and linter
35 changes: 35 additions & 0 deletions cli/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ interface CliFlags {
appRouter: boolean;
/** @internal Used in CI. */
dbProvider: DatabaseProvider;
/** @internal Used in CI */
eslint: boolean;
/** @internal Used in CI */
biome: boolean;
}

interface CliResults {
Expand All @@ -62,6 +66,8 @@ const defaultOptions: CliResults = {
importAlias: "~/",
appRouter: false,
dbProvider: "sqlite",
eslint: false,
biome: false,
},
databaseProvider: "sqlite",
};
Expand Down Expand Up @@ -145,6 +151,16 @@ export const runCli = async (): Promise<CliResults> => {
"Explicitly tell the CLI to use the new Next.js app router",
(value) => !!value && value !== "false"
)
.option(
"--eslint [boolean]",
"Experimental: Boolean value if we should install eslint and prettier. Must be used in conjunction with `--CI`.",
(value) => !!value && value !== "false"
)
.option(
"--biome [boolean]",
"Experimental: Boolean value if we should install biome. Must be used in conjunction with `--CI`.",
(value) => !!value && value !== "false"
)
/** END CI-FLAGS */
.version(getVersion(), "-v, --version", "Display the version number")
.addHelpText(
Expand Down Expand Up @@ -183,13 +199,19 @@ export const runCli = async (): Promise<CliResults> => {
if (cliResults.flags.prisma) cliResults.packages.push("prisma");
if (cliResults.flags.drizzle) cliResults.packages.push("drizzle");
if (cliResults.flags.nextAuth) cliResults.packages.push("nextAuth");
if (cliResults.flags.eslint) cliResults.packages.push("eslint");
if (cliResults.flags.biome) cliResults.packages.push("biome");
if (cliResults.flags.prisma && cliResults.flags.drizzle) {
// We test a matrix of all possible combination of packages in CI. Checking for impossible
// combinations here and exiting gracefully is easier than changing the CI matrix to exclude
// invalid combinations. We are using an "OK" exit code so CI continues with the next combination.
logger.warn("Incompatible combination Prisma + Drizzle. Exiting.");
process.exit(0);
}
if (cliResults.flags.biome && cliResults.flags.eslint) {
logger.warn("Incompatible combination Biome + ESLint. Exiting.");
process.exit(0);
}
if (databaseProviders.includes(cliResults.flags.dbProvider) === false) {
logger.warn(
`Incompatible database provided. Use: ${databaseProviders.join(", ")}. Exiting.`
Expand Down Expand Up @@ -300,6 +322,17 @@ export const runCli = async (): Promise<CliResults> => {
initialValue: "sqlite",
});
},
linter: () => {
return p.select({
message:
"Would you like to use ESLint and Prettier or Biome for linting and formatting?",
options: [
{ value: "eslint", label: "ESLint/Prettier" },
{ value: "biome", label: "Biome" },
],
initialValue: "eslint",
});
},
...(!cliResults.flags.noGit && {
git: () => {
return p.confirm({
Expand Down Expand Up @@ -341,6 +374,8 @@ export const runCli = async (): Promise<CliResults> => {
if (project.authentication === "next-auth") packages.push("nextAuth");
if (project.database === "prisma") packages.push("prisma");
if (project.database === "drizzle") packages.push("drizzle");
if (project.linter === "eslint") packages.push("eslint");
if (project.linter === "biome") packages.push("biome");

return {
appName: project.name ?? cliResults.appName,
Expand Down
30 changes: 30 additions & 0 deletions cli/src/installers/biome.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import path from "path";
import fs from "fs-extra";

import { PKG_ROOT } from "~/consts.js";
import { type Installer } from "~/installers/index.js";
import { addPackageDependency } from "~/utils/addPackageDependency.js";
import { addPackageScript } from "~/utils/addPackageScript.js";

export const biomeInstaller: Installer = ({ projectDir }) => {
addPackageDependency({
projectDir,
dependencies: ["@biomejs/biome"],
devMode: true,
});

const extrasDir = path.join(PKG_ROOT, "template/extras");
const biomeConfigSrc = path.join(extrasDir, "config/biome.jsonc");
const biomeConfigDest = path.join(projectDir, "biome.jsonc");

fs.copySync(biomeConfigSrc, biomeConfigDest);

addPackageScript({
projectDir,
scripts: {
"format:unsafe": "biome check --write --unsafe .",
"format:write": "biome check --write .",
"format:check": "biome check .",
aidansunbury marked this conversation as resolved.
Show resolved Hide resolved
},
});
};
16 changes: 13 additions & 3 deletions cli/src/installers/dependencyVersionMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const dependencyVersionMap = {
// Drizzle
"drizzle-kit": "^0.24.0",
"drizzle-orm": "^0.33.0",
"eslint-plugin-drizzle": "^0.2.3",
mysql2: "^3.11.0",
"@planetscale/database": "^1.19.0",
postgres: "^3.4.4",
Expand All @@ -25,8 +24,6 @@ export const dependencyVersionMap = {
// TailwindCSS
tailwindcss: "^3.4.3",
postcss: "^8.4.39",
prettier: "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",

// tRPC
"@trpc/client": "^11.0.0-rc.446",
Expand All @@ -36,5 +33,18 @@ export const dependencyVersionMap = {
"@tanstack/react-query": "^5.50.0",
superjson: "^2.2.1",
"server-only": "^0.0.1",

// biome
"@biomejs/biome": "1.9.4",

// eslint / prettier
prettier: "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
eslint: "^8.57.0",
"eslint-config-next": "^15.0.1",
"eslint-plugin-drizzle": "^0.2.3",
"@types/eslint": "^8.56.10",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
} as const;
export type AvailableDependencies = keyof typeof dependencyVersionMap;
33 changes: 11 additions & 22 deletions cli/src/installers/drizzle.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import path from "path";
import fs from "fs-extra";
import { type PackageJson } from "type-fest";

import { PKG_ROOT } from "~/consts.js";
import { type Installer } from "~/installers/index.js";
import { addPackageDependency } from "~/utils/addPackageDependency.js";
import { type AvailableDependencies } from "./dependencyVersionMap.js";
import { addPackageScript } from "~/utils/addPackageScript.js";

export const drizzleInstaller: Installer = ({
projectDir,
packages,
scopedAppName,
databaseProvider,
}) => {
const devPackages: AvailableDependencies[] = [
"drizzle-kit",
"eslint-plugin-drizzle",
];

addPackageDependency({
projectDir,
dependencies: devPackages,
dependencies: ["drizzle-kit"],
devMode: true,
});
addPackageDependency({
Expand Down Expand Up @@ -75,24 +69,19 @@ export const drizzleInstaller: Installer = ({
);
const clientDest = path.join(projectDir, "src/server/db/index.ts");

// add db:* scripts to package.json
const packageJsonPath = path.join(projectDir, "package.json");

const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson;
packageJsonContent.scripts = {
...packageJsonContent.scripts,
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
};
addPackageScript({
projectDir,
scripts: {
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
},
});

fs.copySync(configFile, configDest);
fs.mkdirSync(path.dirname(schemaDest), { recursive: true });
fs.writeFileSync(schemaDest, schemaContent);
fs.writeFileSync(configDest, configContent);
fs.copySync(clientSrc, clientDest);
fs.writeJSONSync(packageJsonPath, packageJsonContent, {
spaces: 2,
});
};
52 changes: 51 additions & 1 deletion cli/src/installers/eslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,61 @@ import path from "path";
import fs from "fs-extra";

import { _initialConfig } from "~/../template/extras/config/_eslint.js";
import { PKG_ROOT } from "~/consts.js";
import { type Installer } from "~/installers/index.js";
import { addPackageDependency } from "~/utils/addPackageDependency.js";
import { addPackageScript } from "~/utils/addPackageScript.js";
import { type AvailableDependencies } from "./dependencyVersionMap.js";

// Also installs prettier
export const dynamicEslintInstaller: Installer = ({ projectDir, packages }) => {
const usingDrizzle = !!packages?.drizzle?.inUse;
const devPackages: AvailableDependencies[] = [
"prettier",
"eslint",
"eslint-config-next",
"@types/eslint",
"@typescript-eslint/eslint-plugin",
"@typescript-eslint/parser",
];

if (packages?.tailwind.inUse) {
devPackages.push("prettier-plugin-tailwindcss");
}
if (packages?.drizzle.inUse) {
devPackages.push("eslint-plugin-drizzle");
}

addPackageDependency({
projectDir,
dependencies: devPackages,
devMode: true,
});
const extrasDir = path.join(PKG_ROOT, "template/extras");

// Prettier
let prettierSrc: string;
if (packages?.tailwind.inUse) {
prettierSrc = path.join(extrasDir, "config/_tailwind.prettier.config.js");
} else {
prettierSrc = path.join(extrasDir, "config/_prettier.config.js");
}
const prettierDest = path.join(projectDir, "prettier.config.js");

fs.copySync(prettierSrc, prettierDest);

addPackageScript({
projectDir,
scripts: {
lint: "next lint",
"lint:fix": "next lint --fix",
check: "next lint && tsc --noEmit",
"format:write": 'prettier --write "**/*.{ts,tsx,js,jsx,mdx}" --cache',
"format:check": 'prettier --check "**/*.{ts,tsx,js,jsx,mdx}" --cache',
},
});

// eslint
const usingDrizzle = !!packages?.drizzle?.inUse;
const eslintConfig = getEslintConfig({ usingDrizzle });

// Convert config from _eslint.config.json to .eslintrc.cjs
Expand Down
8 changes: 7 additions & 1 deletion cli/src/installers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { prismaInstaller } from "~/installers/prisma.js";
import { tailwindInstaller } from "~/installers/tailwind.js";
import { trpcInstaller } from "~/installers/trpc.js";
import { type PackageManager } from "~/utils/getUserPkgManager.js";
import { biomeInstaller } from "./biome.js";
import { dbContainerInstaller } from "./dbContainer.js";
import { drizzleInstaller } from "./drizzle.js";
import { dynamicEslintInstaller } from "./eslint.js";
Expand All @@ -18,6 +19,7 @@ export const availablePackages = [
"trpc",
"envVariables",
"eslint",
"biome",
"dbContainer",
] as const;
export type AvailablePackages = (typeof availablePackages)[number];
Expand Down Expand Up @@ -83,7 +85,11 @@ export const buildPkgInstallerMap = (
installer: envVariablesInstaller,
},
eslint: {
inUse: true,
inUse: packages.includes("eslint"),
installer: dynamicEslintInstaller,
},
biome: {
inUse: packages.includes("biome"),
installer: biomeInstaller,
},
});
27 changes: 11 additions & 16 deletions cli/src/installers/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import path from "path";
import fs from "fs-extra";
import { type PackageJson } from "type-fest";

import { PKG_ROOT } from "~/consts.js";
import { type Installer } from "~/installers/index.js";
import { addPackageDependency } from "~/utils/addPackageDependency.js";
import { addPackageScript } from "~/utils/addPackageScript.js";

export const prismaInstaller: Installer = ({
projectDir,
Expand Down Expand Up @@ -65,21 +65,16 @@ export const prismaInstaller: Installer = ({
);
const clientDest = path.join(projectDir, "src/server/db.ts");

// add postinstall and push script to package.json
const packageJsonPath = path.join(projectDir, "package.json");

const packageJsonContent = fs.readJSONSync(packageJsonPath) as PackageJson;
packageJsonContent.scripts = {
...packageJsonContent.scripts,
postinstall: "prisma generate",
"db:push": "prisma db push",
"db:studio": "prisma studio",
"db:generate": "prisma migrate dev",
"db:migrate": "prisma migrate deploy",
};
addPackageScript({
projectDir,
scripts: {
postinstall: "prisma generate",
"db:push": "prisma db push",
"db:studio": "prisma studio",
"db:generate": "prisma migrate dev",
"db:migrate": "prisma migrate deploy",
},
});

fs.copySync(clientSrc, clientDest);
fs.writeJSONSync(packageJsonPath, packageJsonContent, {
spaces: 2,
});
};
Loading
Loading