├── src
├── samples
│ ├── globalscss.ts
│ ├── page.ts
│ └── layout.ts
├── index.ts
├── functions.ts
└── handles.ts
├── tsconfig.json
├── tsup.config.ts
├── .gitignore
├── package.json
└── README.md
/src/samples/globalscss.ts:
--------------------------------------------------------------------------------
1 | export const globalscssFileContent = `@tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;`;
4 |
--------------------------------------------------------------------------------
/src/samples/page.ts:
--------------------------------------------------------------------------------
1 | export const pageFileContent = `import React from "react";
2 |
3 | const page = () => {
4 | return
hello world
;
5 | };
6 |
7 | export default page;`;
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2016",
4 | "module": "commonjs",
5 | "rootDir": "./src",
6 | "outDir": "./dist",
7 | "esModuleInterop": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "strict": true,
10 | "skipLibCheck": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["cjs", "esm"], // Build for commonJS and ESmodules
6 | dts: true, // Generate declaration file (.d.ts)
7 | splitting: false,
8 | sourcemap: true,
9 | clean: true,
10 | });
11 |
--------------------------------------------------------------------------------
/src/samples/layout.ts:
--------------------------------------------------------------------------------
1 | export const layoutFileContent = `import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const inter = Inter({ subsets: ["latin"] });
6 |
7 | export const metadata: Metadata = {
8 | title: "",
9 | description: "",
10 | };
11 |
12 | export default function RootLayout({
13 | children,
14 | }: Readonly<{
15 | children: React.ReactNode;
16 | }>) {
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 | /dist
9 |
10 | # testing
11 | /coverage
12 |
13 | # database
14 | /prisma/db.sqlite
15 | /prisma/db.sqlite-journal
16 |
17 | # next.js
18 | /.next/
19 | /out/
20 | next-env.d.ts
21 |
22 | # production
23 | /build
24 |
25 | # misc
26 | .DS_Store
27 | *.pem
28 |
29 | # debug
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 | .pnpm-debug.log*
34 |
35 | # local env files
36 | .env
37 | .env*.local
38 |
39 | # vercel
40 | .vercel
41 |
42 | # local env files
43 | .env*.local
44 | .env
45 |
46 | # typescript
47 | *.tsbuildinfo
48 | next-env.d.ts
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-clean",
3 | "version": "1.0.3",
4 | "main": "./dist/index.js",
5 | "module": "./dist/index.mjs",
6 | "types": "./dist/index.d.ts",
7 | "files": [
8 | "dist"
9 | ],
10 | "bin": {
11 | "next-clean": "dist/index.js"
12 | },
13 | "scripts": {
14 | "build": "tsup",
15 | "test": "echo \"Error: no test specified\" && exit 1",
16 | "prepublishOnly": "npm run build"
17 | },
18 | "keywords": [],
19 | "author": "",
20 | "license": "MIT",
21 | "description": "",
22 | "devDependencies": {
23 | "@types/node": "^20.12.13",
24 | "tsup": "^8.0.2",
25 | "typescript": "^5.4.5"
26 | },
27 | "dependencies": {
28 | "commander": "^12.1.0",
29 | "ts-node": "^10.9.2"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | import path from "path";
4 | import { fileExists } from "./functions";
5 | import {
6 | handlePublicFolder,
7 | handleReadmeFile,
8 | handleFavicon,
9 | handleLayoutFile,
10 | handlePageFile,
11 | handleGlobalsFile,
12 | } from "./handles";
13 |
14 | const currentDir: string = process.cwd();
15 |
16 | async function main() {
17 | const publicPath: string = path.join(currentDir, "public");
18 | const readmePath: string = path.join(currentDir, "README.md");
19 |
20 | await handlePublicFolder(publicPath);
21 | await handleReadmeFile(readmePath);
22 |
23 | const srcPath: string = path.join(currentDir, "src");
24 | if (await fileExists(srcPath)) {
25 | const appPath: string = path.join(srcPath, "app");
26 | if (await fileExists(appPath)) {
27 | console.log("You are using app directory, smart choice!");
28 | await handleFavicon(appPath);
29 | await handleLayoutFile(appPath);
30 | await handlePageFile(appPath);
31 | await handleGlobalsFile(appPath);
32 | }
33 | } else if (await fileExists(currentDir)) {
34 | const appPath: string = path.join(currentDir, "app");
35 | if (await fileExists(appPath)) {
36 | console.log("You are using app directory, smart choice!");
37 | await handleFavicon(appPath);
38 | await handleLayoutFile(appPath);
39 | await handlePageFile(appPath);
40 | } else {
41 | console.log("app folder does not exist , please check again");
42 | }
43 | } else {
44 | console.log(
45 | "no src folder found. Please run the command in the root of your next.js project."
46 | );
47 | }
48 | }
49 |
50 | main();
51 |
--------------------------------------------------------------------------------
/src/functions.ts:
--------------------------------------------------------------------------------
1 | //functions.ts
2 | import fs from "fs";
3 | import path from "path";
4 |
5 | /**
6 | * Checks if a file exists at the given file path.
7 | * @param filePath - The path to the file.
8 | * @returns A Promise that resolves to a boolean indicating whether the file exists or not.
9 | */
10 | export async function fileExists(filePath: string): Promise {
11 | return new Promise((resolve) => {
12 | fs.access(filePath, fs.constants.F_OK, (err) => {
13 | resolve(!err);
14 | });
15 | });
16 | }
17 |
18 | /**
19 | * Deletes all files within the given folder path.
20 | * @param folderPath - The path to the folder.
21 | * @returns A Promise that resolves when all files are deleted, or rejects with an error.
22 | */
23 | export async function deleteAllFilesInFolder(
24 | folderPath: string
25 | ): Promise {
26 | return new Promise((resolve, reject) => {
27 | fs.readdir(folderPath, (err, files) => {
28 | if (err) {
29 | reject(err);
30 | } else {
31 | for (const file of files) {
32 | fs.unlink(path.join(folderPath, file), (err) => {
33 | if (err) {
34 | reject(err);
35 | }
36 | });
37 | }
38 | resolve();
39 | }
40 | });
41 | });
42 | }
43 |
44 | /**
45 | * Updates the content of a file at the given file path.
46 | * @param filePath - The path to the file.
47 | * @param content - The new content to be written to the file.
48 | * @returns A Promise that resolves when the file is updated, or rejects with an error.
49 | */
50 | export async function fileUpdation(
51 | filePath: string,
52 | content: string
53 | ): Promise {
54 | return new Promise((resolve, reject) => {
55 | fs.writeFile(filePath, content, (err) => {
56 | if (err) {
57 | reject(err);
58 | } else {
59 | resolve();
60 | }
61 | });
62 | });
63 | }
64 |
65 | /**
66 | * Deletes a single file at the given file path.
67 | * @param filePath - The path to the file.
68 | * @returns A Promise that resolves when the file is deleted, or rejects with an error.
69 | */
70 | export async function singleFileDeletion(filePath: string): Promise {
71 | return new Promise((resolve, reject) => {
72 | fs.unlink(filePath, (err) => {
73 | if (err) {
74 | reject(err);
75 | } else {
76 | resolve();
77 | }
78 | });
79 | });
80 | }
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # next-clean
2 |
3 | next-clean is a command-line tool designed to clean and reset the boilerplate code in a Next.js project. It aims to provide a fresh starting point for developers who want to work on a clean Next.js codebase without any unnecessary files or configurations.
4 |
5 | ## Installation
6 |
7 | To use next-clean, you first need to install it globally using npm or yarn:
8 |
9 | ```bash
10 | npm install -g next-clean
11 | ```
12 |
13 | or
14 |
15 | ```bash
16 | yarn global add next-clean
17 | ```
18 |
19 | ## Usage
20 |
21 | Navigate to your Next.js project directory and run the following command:
22 |
23 | ```bash
24 | next-clean
25 | ```
26 |
27 | This command will perform the following actions:
28 |
29 | 1. **Clean the `public` folder**: If the `public` folder exists, all files within it will be deleted.
30 | 2. **Empty the `README.md` file**: If the `README.md` file exists, its content will be cleared.
31 | 3. **Handle the `app` folder**:
32 | - If the `src/app` folder exists, the following actions will be performed:
33 | - Delete the `favicon.ico` file (if it exists).
34 | - Update the content of the `layout.tsx` file with a default layout.
35 | - Update the content of the `page.tsx` file with a default page.
36 | - Update the content of the `globals.css` file with default global styles.
37 | - If the `src/app` folder does not exist, but the `app` folder exists in the root directory, the same actions will be performed in the root `app` folder.
38 |
39 | After running the `next-clean` command, your Next.js project will be cleaned, and the boilerplate code will be reset to a fresh state.
40 |
41 | ## Your files will look like this after running next-clean
42 |
43 | 1. **public folder**: All files will be deleted.
44 | 2. **README.md**: The content will be cleared.
45 | 3. **layout.tsx**:
46 |
47 | ```tsx
48 | import type { Metadata } from "next";
49 | import { Inter } from "next/font/google";
50 | import "./globals.css";
51 |
52 | const inter = Inter({ subsets: ["latin"] });
53 |
54 | export const metadata: Metadata = {
55 | title: "",
56 | description: "",
57 | };
58 |
59 | export default function RootLayout({
60 | children,
61 | }: Readonly<{
62 | children: React.ReactNode;
63 | }>) {
64 | return (
65 |
66 | {children}
67 |
68 | );
69 | }
70 | ```
71 |
72 | 4. **page.tsx**:
73 |
74 | ```tsx
75 | import React from "react";
76 |
77 | const page = () => {
78 | return hello world
;
79 | };
80 |
81 | export default page;
82 | ```
83 |
84 | 5. **globals.css**:
85 |
86 | ```css
87 | @tailwind base;
88 | @tailwind components;
89 | @tailwind utilities;
90 | ```
91 |
92 | 6. **favicon.ico**: Deleted (if it exists).
93 |
94 | ## Contributing
95 |
96 | If you find any issues or would like to contribute to the development of next-clean, feel free to open an issue or submit a pull request on the [GitHub repository](https://github.com/amaan8429/next-clean).
97 |
98 | ## License
99 |
100 | next-clean is released under the [MIT License](LICENSE).
101 |
--------------------------------------------------------------------------------
/src/handles.ts:
--------------------------------------------------------------------------------
1 | //handles.ts
2 | import path from "path";
3 | import { pageFileContent } from "./samples/page";
4 | import { layoutFileContent } from "./samples/layout";
5 | import { globalscssFileContent } from "./samples/globalscss";
6 | import {
7 | fileExists,
8 | deleteAllFilesInFolder,
9 | fileUpdation,
10 | singleFileDeletion,
11 | } from "./functions";
12 |
13 | /**
14 | * Handles the public folder by deleting all files within it if it exists.
15 | * @param publicPath - The path to the public folder.
16 | */
17 | export async function handlePublicFolder(publicPath: string) {
18 | if (await fileExists(publicPath)) {
19 | try {
20 | await deleteAllFilesInFolder(publicPath);
21 | console.log("public folder cleaned successfully");
22 | } catch (err) {
23 | console.error(`Error cleaning public folder: ${err}`);
24 | }
25 | } else {
26 | console.log("public folder does not exist , please check again");
27 | }
28 | }
29 |
30 | /**
31 | * Handles the README.md file by emptying its content if it exists.
32 | * @param readmePath - The path to the README.md file.
33 | */
34 | export async function handleReadmeFile(readmePath: string) {
35 | if (await fileExists(readmePath)) {
36 | try {
37 | await fileUpdation(readmePath, "");
38 | console.log("README.md updated successfully");
39 | } catch (err) {
40 | console.error(`Error updating README.md: ${err}`);
41 | }
42 | } else {
43 | console.log("README.md does not exist, please check again");
44 | }
45 | }
46 |
47 | /**
48 | * Handles the favicon.ico file by deleting it if it exists.
49 | * @param appPath - The path to the app folder.
50 | */
51 | export async function handleFavicon(appPath: string) {
52 | const faviconPath: string = path.join(appPath, "favicon.ico");
53 | const faviconExists: boolean = await fileExists(faviconPath);
54 | if (faviconExists) {
55 | try {
56 | await singleFileDeletion(faviconPath);
57 | console.log("favicon.ico deleted successfully");
58 | } catch (err) {
59 | console.error(`Error deleting favicon.ico: ${err}`);
60 | }
61 | } else {
62 | console.log("favicon.ico does not exist");
63 | }
64 | }
65 |
66 | /**
67 | * Handles the layout.tsx file by updating its content with the provided content.
68 | * @param appPath - The path to the app folder.
69 | */
70 | export async function handleLayoutFile(appPath: string) {
71 | const layoutPath: string = path.join(appPath, "layout.tsx");
72 | if (await fileExists(layoutPath)) {
73 | try {
74 | await fileUpdation(layoutPath, layoutFileContent);
75 | console.log("layout.tsx file updated successfully");
76 | } catch (err) {
77 | console.error(`Error updating layout file: ${err}`);
78 | }
79 | } else {
80 | console.log("layout.tsx file does not exist");
81 | }
82 | }
83 |
84 | /**
85 | * Handles the page.tsx file by updating its content with the provided content.
86 | * @param appPath - The path to the app folder.
87 | */
88 | export async function handlePageFile(appPath: string) {
89 | const pagePath: string = path.join(appPath, "page.tsx");
90 | if (await fileExists(pagePath)) {
91 | try {
92 | await fileUpdation(pagePath, pageFileContent);
93 | console.log("page.tsx file updated successfully");
94 | } catch (err) {
95 | console.error(`Error updating page.tsx file: ${err}`);
96 | }
97 | } else {
98 | console.log("page.tsx does not exist in app folder");
99 | }
100 | }
101 |
102 | /**
103 | * Handles the globals.css file by updating its content with the provided content.
104 | * @param appPath - The path to the app folder.
105 | */
106 | export async function handleGlobalsFile(appPath: string) {
107 | const globalsPath: string = path.join(appPath, "globals.css");
108 | if (await fileExists(globalsPath)) {
109 | try {
110 | await fileUpdation(globalsPath, globalscssFileContent);
111 | console.log("globals.css file updated successfully");
112 | } catch (err) {
113 | console.error(`Error updating globals.css file: ${err}`);
114 | }
115 | } else {
116 | console.log("globals.css does not exist");
117 | }
118 | }
119 |
--------------------------------------------------------------------------------