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(cli): add 'no src directory' option #1939

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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/curvy-geckos-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-t3-app": major
---

Adds a new option to the CLI to avoid including the source directory during project creation.
13 changes: 13 additions & 0 deletions cli/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface CliFlags {
noInstall: boolean;
default: boolean;
importAlias: string;
srcDirectory: boolean;

/** @internal Used in CI. */
CI: boolean;
Expand Down Expand Up @@ -52,6 +53,7 @@ const defaultOptions: CliResults = {
flags: {
noGit: false,
noInstall: false,
srcDirectory: false,
default: false,
CI: false,
tailwind: false,
Expand Down Expand Up @@ -86,6 +88,11 @@ export const runCli = async (): Promise<CliResults> => {
"Explicitly tell the CLI to not run the package manager's install command",
false
)
.option(
"--srcDirectory",
"Explicitly tell the CLI to create a 'src' directory in the project",
false
)
.option(
"-y, --default",
"Bypass the CLI and use all default options to bootstrap a new t3-app",
Expand Down Expand Up @@ -253,6 +260,11 @@ export const runCli = async (): Promise<CliResults> => {
message: "Will you be using Tailwind CSS for styling?",
});
},
srcDirectory: () => {
return p.confirm({
message: "Would you like to use 'src' directory?",
});
},
trpc: () => {
return p.confirm({
message: "Would you like to use tRPC?",
Expand Down Expand Up @@ -350,6 +362,7 @@ export const runCli = async (): Promise<CliResults> => {
flags: {
...cliResults.flags,
appRouter: project.appRouter ?? cliResults.flags.appRouter,
srcDirectory: project.srcDirectory ?? cliResults.flags.srcDirectory,
noGit: !project.git || cliResults.flags.noGit,
noInstall: !project.install || cliResults.flags.noInstall,
importAlias: project.importAlias ?? cliResults.flags.importAlias,
Expand Down
29 changes: 24 additions & 5 deletions cli/src/helpers/createProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getUserPkgManager } from "~/utils/getUserPkgManager.js";
interface CreateProjectOptions {
projectName: string;
packages: PkgInstallerMap;
srcDirectory: boolean;
scopedAppName: string;
noInstall: boolean;
importAlias: string;
Expand All @@ -28,6 +29,7 @@ interface CreateProjectOptions {

export const createProject = async ({
projectName,
srcDirectory,
scopedAppName,
packages,
noInstall,
Expand All @@ -42,6 +44,7 @@ export const createProject = async ({
projectName,
projectDir,
pkgManager,
srcDirectory,
scopedAppName,
noInstall,
appRouter,
Expand All @@ -52,6 +55,7 @@ export const createProject = async ({
installPackages({
projectName,
scopedAppName,
srcDirectory,
projectDir,
pkgManager,
packages,
Expand All @@ -68,11 +72,11 @@ export const createProject = async ({
path.join(projectDir, "next.config.js")
);

selectLayoutFile({ projectDir, packages });
selectPageFile({ projectDir, packages });
selectLayoutFile({ projectDir, packages, srcDirectory });
selectPageFile({ projectDir, packages, srcDirectory });
} else {
selectAppFile({ projectDir, packages });
selectIndexFile({ projectDir, packages });
selectAppFile({ projectDir, packages, srcDirectory });
selectIndexFile({ projectDir, packages, srcDirectory });
}

// If no tailwind, select use css modules
Expand All @@ -83,12 +87,27 @@ export const createProject = async ({
);
const indexModuleCssDest = path.join(
projectDir,
"src",
srcDirectory ? "src" : "",
appRouter ? "app" : "pages",
"index.module.css"
);
fs.copyFileSync(indexModuleCss, indexModuleCssDest);
}

if (!srcDirectory) {
const tsconfigFile = path.join(projectDir, "tsconfig.json");
const nextconfigFile = path.join(projectDir, "next.config.js");
fs.writeFileSync(
tsconfigFile,
fs.readFileSync(tsconfigFile, "utf8").replace("./src/*", "./*")
);
fs.writeFileSync(
nextconfigFile,
fs
.readFileSync(nextconfigFile, "utf8")
.replace("./src/env.js", "./env.js")
);
}

return projectDir;
};
16 changes: 16 additions & 0 deletions cli/src/helpers/scaffoldProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { logger } from "~/utils/logger.js";
export const scaffoldProject = async ({
projectName,
projectDir,
srcDirectory,
pkgManager,
noInstall,
}: InstallerOptions) => {
Expand Down Expand Up @@ -90,6 +91,21 @@ export const scaffoldProject = async ({
path.join(projectDir, ".gitignore")
);

if (!srcDirectory) {
await Promise.all([
fs.rename(
path.join(projectDir, "src", "env.js"),
projectDir + "/" + "env.js"
),
fs.rename(
path.join(projectDir, "src", "styles"),
projectDir + "/" + "styles"
),
]);

await fs.rm(path.join(projectDir, "src"), { recursive: true });
}

const scaffoldedName =
projectName === "." ? "App" : chalk.cyan.bold(projectName);

Expand Down
30 changes: 25 additions & 5 deletions cli/src/helpers/selectBoilerplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { PKG_ROOT } from "~/consts.js";
import { type InstallerOptions } from "~/installers/index.js";

type SelectBoilerplateProps = Required<
Pick<InstallerOptions, "packages" | "projectDir">
Pick<InstallerOptions, "packages" | "projectDir" | "srcDirectory">
>;
// This generates the _app.tsx file that is used to render the app
export const selectAppFile = ({
projectDir,
srcDirectory,
packages,
}: SelectBoilerplateProps) => {
const appFileDir = path.join(PKG_ROOT, "template/extras/src/pages/_app");
Expand All @@ -36,13 +37,18 @@ export const selectAppFile = ({
}

const appSrc = path.join(appFileDir, appFile);
const appDest = path.join(projectDir, "src/pages/_app.tsx");

const appDest = path.join(
projectDir,
srcDirectory ? "src/pages/_app.tsx" : "pages/_app"
);
fs.copySync(appSrc, appDest);
};

// Similar to _app, but for app router
export const selectLayoutFile = ({
projectDir,
srcDirectory,
packages,
}: SelectBoilerplateProps) => {
const layoutFileDir = path.join(PKG_ROOT, "template/extras/src/app/layout");
Expand All @@ -59,13 +65,19 @@ export const selectLayoutFile = ({
}

const appSrc = path.join(layoutFileDir, layoutFile);
const appDest = path.join(projectDir, "src/app/layout.tsx");

const appDest = path.join(
projectDir,
srcDirectory ? "src/app/layout.tsx" : "app/layout.tsx"
);

fs.copySync(appSrc, appDest);
};

// This selects the proper index.tsx to be used that showcases the chosen tech
export const selectIndexFile = ({
projectDir,
srcDirectory,
packages,
}: SelectBoilerplateProps) => {
const indexFileDir = path.join(PKG_ROOT, "template/extras/src/pages/index");
Expand All @@ -88,13 +100,17 @@ export const selectIndexFile = ({
}

const indexSrc = path.join(indexFileDir, indexFile);
const indexDest = path.join(projectDir, "src/pages/index.tsx");
const indexDest = path.join(
projectDir,
srcDirectory ? "src/pages/index.tsx" : "pages/index"
);
fs.copySync(indexSrc, indexDest);
};

// Similar to index, but for app router
export const selectPageFile = ({
projectDir,
srcDirectory,
packages,
}: SelectBoilerplateProps) => {
const indexFileDir = path.join(PKG_ROOT, "template/extras/src/app/page");
Expand All @@ -117,6 +133,10 @@ export const selectPageFile = ({
}

const indexSrc = path.join(indexFileDir, indexFile);
const indexDest = path.join(projectDir, "src/app/page.tsx");

const indexDest = path.join(
projectDir,
srcDirectory ? "src/app/page.tsx" : "app/page.tsx"
);
fs.copySync(indexSrc, indexDest);
};
3 changes: 2 additions & 1 deletion cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const main = async () => {
const {
appName,
packages,
flags: { noGit, noInstall, importAlias, appRouter },
flags: { noGit, noInstall, importAlias, appRouter, srcDirectory },
databaseProvider,
} = await runCli();

Expand All @@ -48,6 +48,7 @@ const main = async () => {
const projectDir = await createProject({
projectName: appDir,
scopedAppName,
srcDirectory,
packages: usePackages,
databaseProvider,
importAlias,
Expand Down
21 changes: 19 additions & 2 deletions cli/src/installers/drizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { type AvailableDependencies } from "./dependencyVersionMap.js";
export const drizzleInstaller: Installer = ({
projectDir,
packages,
srcDirectory,
scopedAppName,
databaseProvider,
}) => {
Expand Down Expand Up @@ -56,7 +57,10 @@ export const drizzleInstaller: Installer = ({
? `with-auth-${databaseProvider}.ts`
: `base-${databaseProvider}.ts`
);
const schemaDest = path.join(projectDir, "src/server/db/schema.ts");
const schemaDest = path.join(
projectDir,
srcDirectory ? "src/server/db/schema.ts" : "server/db/schema.ts"
);

// Replace placeholder table prefix with project name
let schemaContent = fs.readFileSync(schemaSrc, "utf-8");
Expand All @@ -73,7 +77,10 @@ export const drizzleInstaller: Installer = ({
extrasDir,
`src/server/db/index-drizzle/with-${databaseProvider}.ts`
);
const clientDest = path.join(projectDir, "src/server/db/index.ts");
const clientDest = path.join(
projectDir,
srcDirectory ? "src/server/db/index.ts" : "server/db/index.ts"
);

// add db:* scripts to package.json
const packageJsonPath = path.join(projectDir, "package.json");
Expand All @@ -95,4 +102,14 @@ export const drizzleInstaller: Installer = ({
fs.writeJSONSync(packageJsonPath, packageJsonContent, {
spaces: 2,
});

if (!srcDirectory) {
const drizzleConfigFile = path.join(projectDir, "drizzle.config.ts");
fs.writeFileSync(
drizzleConfigFile,
fs
.readFileSync(drizzleConfigFile, "utf8")
.replace("./src/server/db/schema.ts", "./server/db/schema.ts")
);
}
};
6 changes: 5 additions & 1 deletion cli/src/installers/envVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { type DatabaseProvider, type Installer } from "~/installers/index.js";
export const envVariablesInstaller: Installer = ({
projectDir,
packages,
srcDirectory,
databaseProvider,
projectName,
}) => {
Expand Down Expand Up @@ -44,7 +45,10 @@ export const envVariablesInstaller: Installer = ({
"template/extras/src/env",
envFile
);
const envSchemaDest = path.join(projectDir, "src/env.js");
const envSchemaDest = path.join(
projectDir,
srcDirectory ? "src/env.js" : "env.js"
);
fs.copyFileSync(envSchemaSrc, envSchemaDest);
}

Expand Down
1 change: 1 addition & 0 deletions cli/src/installers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface InstallerOptions {
projectDir: string;
pkgManager: PackageManager;
noInstall: boolean;
srcDirectory: boolean;
packages?: PkgInstallerMap;
appRouter?: boolean;
projectName: string;
Expand Down
18 changes: 13 additions & 5 deletions cli/src/installers/nextAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { addPackageDependency } from "~/utils/addPackageDependency.js";
export const nextAuthInstaller: Installer = ({
projectDir,
packages,
srcDirectory,
appRouter,
}) => {
const usingPrisma = packages?.prisma.inUse;
Expand All @@ -26,12 +27,16 @@ export const nextAuthInstaller: Installer = ({

const extrasDir = path.join(PKG_ROOT, "template/extras");

const apiHandlerFile = "src/pages/api/auth/[...nextauth].ts";
const routeHandlerFile = "src/app/api/auth/[...nextauth]/route.ts";
const apiHandlerFile = "pages/api/auth/[...nextauth].ts";
const routeHandlerFile = "app/api/auth/[...nextauth]/route.ts";
const srcToUse = appRouter ? routeHandlerFile : apiHandlerFile;

const apiHandlerSrc = path.join(extrasDir, srcToUse);
const apiHandlerDest = path.join(projectDir, srcToUse);
const apiHandlerSrc = path.join(extrasDir, "src", srcToUse);
const apiHandlerDest = path.join(
projectDir,
srcDirectory ? "src" : "",
srcToUse
);

const authConfigSrc = path.join(
extrasDir,
Expand All @@ -43,7 +48,10 @@ export const nextAuthInstaller: Installer = ({
? "with-drizzle.ts"
: "base.ts"
);
const authConfigDest = path.join(projectDir, "src/server/auth.ts");
const authConfigDest = path.join(
projectDir,
srcDirectory ? "src/server/auth.ts" : "server/auth.ts"
);

fs.copySync(apiHandlerSrc, apiHandlerDest);
fs.copySync(authConfigSrc, authConfigDest);
Expand Down
6 changes: 5 additions & 1 deletion cli/src/installers/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { addPackageDependency } from "~/utils/addPackageDependency.js";

export const prismaInstaller: Installer = ({
projectDir,
srcDirectory,
packages,
databaseProvider,
}) => {
Expand Down Expand Up @@ -63,7 +64,10 @@ export const prismaInstaller: Installer = ({
? "src/server/db/db-prisma-planetscale.ts"
: "src/server/db/db-prisma.ts"
);
const clientDest = path.join(projectDir, "src/server/db.ts");
const clientDest = path.join(
projectDir,
srcDirectory ? "src/server/db.ts" : "server/db.ts"
);

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