├── .gitattributes ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── config.ts ├── index.ts └── templates.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/* linguist-generated=true 2 | 3 | *.ts linguist-language=TypeScript -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | DS_Store 2 | node_modules 3 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | .git/ 3 | node_modules/ 4 | .gitignore 5 | tsconfig.json 6 | *.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Minyeong Jeong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # next-file-gen 2 | 3 | Simple and lightweight, Next.js App Router sub-file auto-generator. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | # Using npm 9 | npm install --save-dev @next-file-gen/core 10 | 11 | # Using yarn 12 | yarn add --dev @next-file-gen/core 13 | ``` 14 | 15 | ## Usage 16 | 17 | ### Running the tool 18 | 19 | From your project root directory, run: 20 | 21 | ```bash 22 | npx @next-file-gen/core 23 | ``` 24 | 25 | You can also add it as a script in your package.json: 26 | 27 | ```json 28 | { 29 | "scripts": { 30 | "dev:watch": "next-file-gen" 31 | } 32 | } 33 | ``` 34 | 35 | Then run it with `npm run dev:watch`. 36 | 37 | ### Folder Naming Patterns 38 | 39 | Create folders with the following naming patterns to automatically generate template files: 40 | 41 | 1. **[name].default**: Creates all default files (`page.tsx`, `layout.tsx`, `loading.tsx`, `error.tsx`) 42 | 43 | ``` 44 | # Example: Creating a folder named settings.default 45 | # Result: Creates all default files in the settings folder 46 | ``` 47 | 48 | 2. **[name].page**: For page components (generates `page.tsx` file) 49 | 50 | ``` 51 | # Example: Creating a folder named blog.page 52 | # Result: Creates blog/page.tsx file 53 | ``` 54 | 55 | 3. **[name].layout**: For layout components (generates `layout.tsx` file) 56 | 57 | ``` 58 | # Example: Creating a folder named dashboard.layout 59 | # Result: Creates dashboard/layout.tsx file 60 | ``` 61 | 62 | 4. **[name].error**: For error components (generates `error.tsx` file) 63 | 64 | ``` 65 | # Example: Creating a folder named profile.error 66 | # Result: Creates profile/error.tsx file 67 | ``` 68 | 69 | 5. **[name].loading**: For loading components (generates `loading.tsx` file) 70 | 71 | ``` 72 | # Example: Creating a folder named products.loading 73 | # Result: Creates products/loading.tsx file 74 | ``` 75 | 76 | Note: You can also use `:` instead of `.` as a separator (e.g., `blog:page`). 77 | 78 | ## Configuration 79 | 80 | A `next-file-gen.config.json` file is automatically created in your project root. You can modify this file to change the following settings: 81 | 82 | ```json 83 | { 84 | "watchDir": "/app", 85 | "ignorePatterns": ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"] 86 | } 87 | ``` 88 | 89 | - **watchDir**: The directory to watch for changes 90 | - **ignorePatterns**: Patterns to exclude from watching 91 | 92 | ## Notes 93 | 94 | - Folders without specific patterns will be kept as is without any files being generated 95 | - The tool watches the `app` directory by default. Edit the config file to watch a different directory 96 | - Existing files will not be overwritten 97 | - Files are generated with clean, minimal boilerplate code 98 | 99 | ## Technical Stack 100 | 101 | - TypeScript 102 | - Node.js 103 | - chokidar (file system watcher) 104 | 105 | ## License 106 | 107 | MIT 108 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-file-gen", 3 | "version": "1.0.3", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "next-file-gen", 9 | "version": "1.0.3", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/chokidar": "^2.1.7", 13 | "@types/node": "^22.13.9", 14 | "ts-node": "^10.9.2", 15 | "ts-node-dev": "^2.0.0", 16 | "typescript": "^5.8.2" 17 | } 18 | }, 19 | "node_modules/@cspotcode/source-map-support": { 20 | "version": "0.8.1", 21 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 22 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 23 | "dev": true, 24 | "dependencies": { 25 | "@jridgewell/trace-mapping": "0.3.9" 26 | }, 27 | "engines": { 28 | "node": ">=12" 29 | } 30 | }, 31 | "node_modules/@jridgewell/resolve-uri": { 32 | "version": "3.1.2", 33 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 34 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 35 | "dev": true, 36 | "engines": { 37 | "node": ">=6.0.0" 38 | } 39 | }, 40 | "node_modules/@jridgewell/sourcemap-codec": { 41 | "version": "1.5.0", 42 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 43 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 44 | "dev": true 45 | }, 46 | "node_modules/@jridgewell/trace-mapping": { 47 | "version": "0.3.9", 48 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 49 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 50 | "dev": true, 51 | "dependencies": { 52 | "@jridgewell/resolve-uri": "^3.0.3", 53 | "@jridgewell/sourcemap-codec": "^1.4.10" 54 | } 55 | }, 56 | "node_modules/@tsconfig/node10": { 57 | "version": "1.0.11", 58 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", 59 | "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", 60 | "dev": true 61 | }, 62 | "node_modules/@tsconfig/node12": { 63 | "version": "1.0.11", 64 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 65 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 66 | "dev": true 67 | }, 68 | "node_modules/@tsconfig/node14": { 69 | "version": "1.0.3", 70 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 71 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 72 | "dev": true 73 | }, 74 | "node_modules/@tsconfig/node16": { 75 | "version": "1.0.4", 76 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 77 | "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 78 | "dev": true 79 | }, 80 | "node_modules/@types/chokidar": { 81 | "version": "2.1.7", 82 | "resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-2.1.7.tgz", 83 | "integrity": "sha512-A7/MFHf6KF7peCzjEC1BBTF8jpmZTokb3vr/A0NxRGfwRLK3Ws+Hq6ugVn6cJIMfM6wkCak/aplWrxbTcu8oig==", 84 | "deprecated": "This is a stub types definition. chokidar provides its own type definitions, so you do not need this installed.", 85 | "dev": true, 86 | "dependencies": { 87 | "chokidar": "*" 88 | } 89 | }, 90 | "node_modules/@types/node": { 91 | "version": "22.13.9", 92 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz", 93 | "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", 94 | "dev": true, 95 | "dependencies": { 96 | "undici-types": "~6.20.0" 97 | } 98 | }, 99 | "node_modules/@types/strip-bom": { 100 | "version": "3.0.0", 101 | "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", 102 | "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", 103 | "dev": true 104 | }, 105 | "node_modules/@types/strip-json-comments": { 106 | "version": "0.0.30", 107 | "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", 108 | "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", 109 | "dev": true 110 | }, 111 | "node_modules/acorn": { 112 | "version": "8.14.1", 113 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", 114 | "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", 115 | "dev": true, 116 | "bin": { 117 | "acorn": "bin/acorn" 118 | }, 119 | "engines": { 120 | "node": ">=0.4.0" 121 | } 122 | }, 123 | "node_modules/acorn-walk": { 124 | "version": "8.3.4", 125 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", 126 | "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", 127 | "dev": true, 128 | "dependencies": { 129 | "acorn": "^8.11.0" 130 | }, 131 | "engines": { 132 | "node": ">=0.4.0" 133 | } 134 | }, 135 | "node_modules/anymatch": { 136 | "version": "3.1.3", 137 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 138 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 139 | "dev": true, 140 | "dependencies": { 141 | "normalize-path": "^3.0.0", 142 | "picomatch": "^2.0.4" 143 | }, 144 | "engines": { 145 | "node": ">= 8" 146 | } 147 | }, 148 | "node_modules/arg": { 149 | "version": "4.1.3", 150 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 151 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 152 | "dev": true 153 | }, 154 | "node_modules/balanced-match": { 155 | "version": "1.0.2", 156 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 157 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 158 | "dev": true 159 | }, 160 | "node_modules/binary-extensions": { 161 | "version": "2.3.0", 162 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", 163 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", 164 | "dev": true, 165 | "engines": { 166 | "node": ">=8" 167 | }, 168 | "funding": { 169 | "url": "https://github.com/sponsors/sindresorhus" 170 | } 171 | }, 172 | "node_modules/brace-expansion": { 173 | "version": "1.1.11", 174 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 175 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 176 | "dev": true, 177 | "dependencies": { 178 | "balanced-match": "^1.0.0", 179 | "concat-map": "0.0.1" 180 | } 181 | }, 182 | "node_modules/braces": { 183 | "version": "3.0.3", 184 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 185 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 186 | "dev": true, 187 | "dependencies": { 188 | "fill-range": "^7.1.1" 189 | }, 190 | "engines": { 191 | "node": ">=8" 192 | } 193 | }, 194 | "node_modules/buffer-from": { 195 | "version": "1.1.2", 196 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 197 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 198 | "dev": true 199 | }, 200 | "node_modules/chokidar": { 201 | "version": "4.0.3", 202 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", 203 | "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", 204 | "dev": true, 205 | "dependencies": { 206 | "readdirp": "^4.0.1" 207 | }, 208 | "engines": { 209 | "node": ">= 14.16.0" 210 | }, 211 | "funding": { 212 | "url": "https://paulmillr.com/funding/" 213 | } 214 | }, 215 | "node_modules/concat-map": { 216 | "version": "0.0.1", 217 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 218 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 219 | "dev": true 220 | }, 221 | "node_modules/create-require": { 222 | "version": "1.1.1", 223 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 224 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 225 | "dev": true 226 | }, 227 | "node_modules/diff": { 228 | "version": "4.0.2", 229 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 230 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 231 | "dev": true, 232 | "engines": { 233 | "node": ">=0.3.1" 234 | } 235 | }, 236 | "node_modules/dynamic-dedupe": { 237 | "version": "0.3.0", 238 | "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", 239 | "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", 240 | "dev": true, 241 | "dependencies": { 242 | "xtend": "^4.0.0" 243 | } 244 | }, 245 | "node_modules/fill-range": { 246 | "version": "7.1.1", 247 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 248 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 249 | "dev": true, 250 | "dependencies": { 251 | "to-regex-range": "^5.0.1" 252 | }, 253 | "engines": { 254 | "node": ">=8" 255 | } 256 | }, 257 | "node_modules/fs.realpath": { 258 | "version": "1.0.0", 259 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 260 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 261 | "dev": true 262 | }, 263 | "node_modules/fsevents": { 264 | "version": "2.3.3", 265 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 266 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 267 | "dev": true, 268 | "hasInstallScript": true, 269 | "optional": true, 270 | "os": [ 271 | "darwin" 272 | ], 273 | "engines": { 274 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 275 | } 276 | }, 277 | "node_modules/function-bind": { 278 | "version": "1.1.2", 279 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 280 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 281 | "dev": true, 282 | "funding": { 283 | "url": "https://github.com/sponsors/ljharb" 284 | } 285 | }, 286 | "node_modules/glob": { 287 | "version": "7.2.3", 288 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 289 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 290 | "deprecated": "Glob versions prior to v9 are no longer supported", 291 | "dev": true, 292 | "dependencies": { 293 | "fs.realpath": "^1.0.0", 294 | "inflight": "^1.0.4", 295 | "inherits": "2", 296 | "minimatch": "^3.1.1", 297 | "once": "^1.3.0", 298 | "path-is-absolute": "^1.0.0" 299 | }, 300 | "engines": { 301 | "node": "*" 302 | }, 303 | "funding": { 304 | "url": "https://github.com/sponsors/isaacs" 305 | } 306 | }, 307 | "node_modules/glob-parent": { 308 | "version": "5.1.2", 309 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 310 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 311 | "dev": true, 312 | "dependencies": { 313 | "is-glob": "^4.0.1" 314 | }, 315 | "engines": { 316 | "node": ">= 6" 317 | } 318 | }, 319 | "node_modules/hasown": { 320 | "version": "2.0.2", 321 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 322 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 323 | "dev": true, 324 | "dependencies": { 325 | "function-bind": "^1.1.2" 326 | }, 327 | "engines": { 328 | "node": ">= 0.4" 329 | } 330 | }, 331 | "node_modules/inflight": { 332 | "version": "1.0.6", 333 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 334 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 335 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 336 | "dev": true, 337 | "dependencies": { 338 | "once": "^1.3.0", 339 | "wrappy": "1" 340 | } 341 | }, 342 | "node_modules/inherits": { 343 | "version": "2.0.4", 344 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 345 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 346 | "dev": true 347 | }, 348 | "node_modules/is-binary-path": { 349 | "version": "2.1.0", 350 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 351 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 352 | "dev": true, 353 | "dependencies": { 354 | "binary-extensions": "^2.0.0" 355 | }, 356 | "engines": { 357 | "node": ">=8" 358 | } 359 | }, 360 | "node_modules/is-core-module": { 361 | "version": "2.16.1", 362 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", 363 | "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", 364 | "dev": true, 365 | "dependencies": { 366 | "hasown": "^2.0.2" 367 | }, 368 | "engines": { 369 | "node": ">= 0.4" 370 | }, 371 | "funding": { 372 | "url": "https://github.com/sponsors/ljharb" 373 | } 374 | }, 375 | "node_modules/is-extglob": { 376 | "version": "2.1.1", 377 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 378 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 379 | "dev": true, 380 | "engines": { 381 | "node": ">=0.10.0" 382 | } 383 | }, 384 | "node_modules/is-glob": { 385 | "version": "4.0.3", 386 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 387 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 388 | "dev": true, 389 | "dependencies": { 390 | "is-extglob": "^2.1.1" 391 | }, 392 | "engines": { 393 | "node": ">=0.10.0" 394 | } 395 | }, 396 | "node_modules/is-number": { 397 | "version": "7.0.0", 398 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 399 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 400 | "dev": true, 401 | "engines": { 402 | "node": ">=0.12.0" 403 | } 404 | }, 405 | "node_modules/make-error": { 406 | "version": "1.3.6", 407 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 408 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 409 | "dev": true 410 | }, 411 | "node_modules/minimatch": { 412 | "version": "3.1.2", 413 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 414 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 415 | "dev": true, 416 | "dependencies": { 417 | "brace-expansion": "^1.1.7" 418 | }, 419 | "engines": { 420 | "node": "*" 421 | } 422 | }, 423 | "node_modules/minimist": { 424 | "version": "1.2.8", 425 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 426 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 427 | "dev": true, 428 | "funding": { 429 | "url": "https://github.com/sponsors/ljharb" 430 | } 431 | }, 432 | "node_modules/mkdirp": { 433 | "version": "1.0.4", 434 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 435 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 436 | "dev": true, 437 | "bin": { 438 | "mkdirp": "bin/cmd.js" 439 | }, 440 | "engines": { 441 | "node": ">=10" 442 | } 443 | }, 444 | "node_modules/normalize-path": { 445 | "version": "3.0.0", 446 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 447 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 448 | "dev": true, 449 | "engines": { 450 | "node": ">=0.10.0" 451 | } 452 | }, 453 | "node_modules/once": { 454 | "version": "1.4.0", 455 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 456 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 457 | "dev": true, 458 | "dependencies": { 459 | "wrappy": "1" 460 | } 461 | }, 462 | "node_modules/path-is-absolute": { 463 | "version": "1.0.1", 464 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 465 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 466 | "dev": true, 467 | "engines": { 468 | "node": ">=0.10.0" 469 | } 470 | }, 471 | "node_modules/path-parse": { 472 | "version": "1.0.7", 473 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 474 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 475 | "dev": true 476 | }, 477 | "node_modules/picomatch": { 478 | "version": "2.3.1", 479 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 480 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 481 | "dev": true, 482 | "engines": { 483 | "node": ">=8.6" 484 | }, 485 | "funding": { 486 | "url": "https://github.com/sponsors/jonschlinkert" 487 | } 488 | }, 489 | "node_modules/readdirp": { 490 | "version": "4.1.2", 491 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", 492 | "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", 493 | "dev": true, 494 | "engines": { 495 | "node": ">= 14.18.0" 496 | }, 497 | "funding": { 498 | "type": "individual", 499 | "url": "https://paulmillr.com/funding/" 500 | } 501 | }, 502 | "node_modules/resolve": { 503 | "version": "1.22.10", 504 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", 505 | "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", 506 | "dev": true, 507 | "dependencies": { 508 | "is-core-module": "^2.16.0", 509 | "path-parse": "^1.0.7", 510 | "supports-preserve-symlinks-flag": "^1.0.0" 511 | }, 512 | "bin": { 513 | "resolve": "bin/resolve" 514 | }, 515 | "engines": { 516 | "node": ">= 0.4" 517 | }, 518 | "funding": { 519 | "url": "https://github.com/sponsors/ljharb" 520 | } 521 | }, 522 | "node_modules/rimraf": { 523 | "version": "2.7.1", 524 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 525 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 526 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 527 | "dev": true, 528 | "dependencies": { 529 | "glob": "^7.1.3" 530 | }, 531 | "bin": { 532 | "rimraf": "bin.js" 533 | } 534 | }, 535 | "node_modules/source-map": { 536 | "version": "0.6.1", 537 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 538 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 539 | "dev": true, 540 | "engines": { 541 | "node": ">=0.10.0" 542 | } 543 | }, 544 | "node_modules/source-map-support": { 545 | "version": "0.5.21", 546 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 547 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 548 | "dev": true, 549 | "dependencies": { 550 | "buffer-from": "^1.0.0", 551 | "source-map": "^0.6.0" 552 | } 553 | }, 554 | "node_modules/strip-bom": { 555 | "version": "3.0.0", 556 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 557 | "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", 558 | "dev": true, 559 | "engines": { 560 | "node": ">=4" 561 | } 562 | }, 563 | "node_modules/strip-json-comments": { 564 | "version": "2.0.1", 565 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 566 | "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 567 | "dev": true, 568 | "engines": { 569 | "node": ">=0.10.0" 570 | } 571 | }, 572 | "node_modules/supports-preserve-symlinks-flag": { 573 | "version": "1.0.0", 574 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 575 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 576 | "dev": true, 577 | "engines": { 578 | "node": ">= 0.4" 579 | }, 580 | "funding": { 581 | "url": "https://github.com/sponsors/ljharb" 582 | } 583 | }, 584 | "node_modules/to-regex-range": { 585 | "version": "5.0.1", 586 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 587 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 588 | "dev": true, 589 | "dependencies": { 590 | "is-number": "^7.0.0" 591 | }, 592 | "engines": { 593 | "node": ">=8.0" 594 | } 595 | }, 596 | "node_modules/tree-kill": { 597 | "version": "1.2.2", 598 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", 599 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", 600 | "dev": true, 601 | "bin": { 602 | "tree-kill": "cli.js" 603 | } 604 | }, 605 | "node_modules/ts-node": { 606 | "version": "10.9.2", 607 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 608 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 609 | "dev": true, 610 | "dependencies": { 611 | "@cspotcode/source-map-support": "^0.8.0", 612 | "@tsconfig/node10": "^1.0.7", 613 | "@tsconfig/node12": "^1.0.7", 614 | "@tsconfig/node14": "^1.0.0", 615 | "@tsconfig/node16": "^1.0.2", 616 | "acorn": "^8.4.1", 617 | "acorn-walk": "^8.1.1", 618 | "arg": "^4.1.0", 619 | "create-require": "^1.1.0", 620 | "diff": "^4.0.1", 621 | "make-error": "^1.1.1", 622 | "v8-compile-cache-lib": "^3.0.1", 623 | "yn": "3.1.1" 624 | }, 625 | "bin": { 626 | "ts-node": "dist/bin.js", 627 | "ts-node-cwd": "dist/bin-cwd.js", 628 | "ts-node-esm": "dist/bin-esm.js", 629 | "ts-node-script": "dist/bin-script.js", 630 | "ts-node-transpile-only": "dist/bin-transpile.js", 631 | "ts-script": "dist/bin-script-deprecated.js" 632 | }, 633 | "peerDependencies": { 634 | "@swc/core": ">=1.2.50", 635 | "@swc/wasm": ">=1.2.50", 636 | "@types/node": "*", 637 | "typescript": ">=2.7" 638 | }, 639 | "peerDependenciesMeta": { 640 | "@swc/core": { 641 | "optional": true 642 | }, 643 | "@swc/wasm": { 644 | "optional": true 645 | } 646 | } 647 | }, 648 | "node_modules/ts-node-dev": { 649 | "version": "2.0.0", 650 | "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz", 651 | "integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==", 652 | "dev": true, 653 | "dependencies": { 654 | "chokidar": "^3.5.1", 655 | "dynamic-dedupe": "^0.3.0", 656 | "minimist": "^1.2.6", 657 | "mkdirp": "^1.0.4", 658 | "resolve": "^1.0.0", 659 | "rimraf": "^2.6.1", 660 | "source-map-support": "^0.5.12", 661 | "tree-kill": "^1.2.2", 662 | "ts-node": "^10.4.0", 663 | "tsconfig": "^7.0.0" 664 | }, 665 | "bin": { 666 | "ts-node-dev": "lib/bin.js", 667 | "tsnd": "lib/bin.js" 668 | }, 669 | "engines": { 670 | "node": ">=0.8.0" 671 | }, 672 | "peerDependencies": { 673 | "node-notifier": "*", 674 | "typescript": "*" 675 | }, 676 | "peerDependenciesMeta": { 677 | "node-notifier": { 678 | "optional": true 679 | } 680 | } 681 | }, 682 | "node_modules/ts-node-dev/node_modules/chokidar": { 683 | "version": "3.6.0", 684 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 685 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 686 | "dev": true, 687 | "dependencies": { 688 | "anymatch": "~3.1.2", 689 | "braces": "~3.0.2", 690 | "glob-parent": "~5.1.2", 691 | "is-binary-path": "~2.1.0", 692 | "is-glob": "~4.0.1", 693 | "normalize-path": "~3.0.0", 694 | "readdirp": "~3.6.0" 695 | }, 696 | "engines": { 697 | "node": ">= 8.10.0" 698 | }, 699 | "funding": { 700 | "url": "https://paulmillr.com/funding/" 701 | }, 702 | "optionalDependencies": { 703 | "fsevents": "~2.3.2" 704 | } 705 | }, 706 | "node_modules/ts-node-dev/node_modules/readdirp": { 707 | "version": "3.6.0", 708 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 709 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 710 | "dev": true, 711 | "dependencies": { 712 | "picomatch": "^2.2.1" 713 | }, 714 | "engines": { 715 | "node": ">=8.10.0" 716 | } 717 | }, 718 | "node_modules/tsconfig": { 719 | "version": "7.0.0", 720 | "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", 721 | "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", 722 | "dev": true, 723 | "dependencies": { 724 | "@types/strip-bom": "^3.0.0", 725 | "@types/strip-json-comments": "0.0.30", 726 | "strip-bom": "^3.0.0", 727 | "strip-json-comments": "^2.0.0" 728 | } 729 | }, 730 | "node_modules/typescript": { 731 | "version": "5.8.2", 732 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 733 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 734 | "dev": true, 735 | "bin": { 736 | "tsc": "bin/tsc", 737 | "tsserver": "bin/tsserver" 738 | }, 739 | "engines": { 740 | "node": ">=14.17" 741 | } 742 | }, 743 | "node_modules/undici-types": { 744 | "version": "6.20.0", 745 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 746 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", 747 | "dev": true 748 | }, 749 | "node_modules/v8-compile-cache-lib": { 750 | "version": "3.0.1", 751 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 752 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 753 | "dev": true 754 | }, 755 | "node_modules/wrappy": { 756 | "version": "1.0.2", 757 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 758 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 759 | "dev": true 760 | }, 761 | "node_modules/xtend": { 762 | "version": "4.0.2", 763 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 764 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 765 | "dev": true, 766 | "engines": { 767 | "node": ">=0.4" 768 | } 769 | }, 770 | "node_modules/yn": { 771 | "version": "3.1.1", 772 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 773 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 774 | "dev": true, 775 | "engines": { 776 | "node": ">=6" 777 | } 778 | } 779 | } 780 | } 781 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@next-file-gen/core", 3 | "version": "1.0.3", 4 | "description": "Next.js auto file generator", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "start": "ts-node src/index.ts", 8 | "build": "tsc", 9 | "dev": "ts-node-dev --respawn src/index.ts", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "nextjs", 14 | "auto", 15 | "file", 16 | "auto-file", 17 | "next-file-gen", 18 | "next-file-gen-generator" 19 | ], 20 | "author": "myjeong19", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "@types/chokidar": "^2.1.7", 24 | "@types/node": "^22.13.9", 25 | "ts-node": "^10.9.2", 26 | "ts-node-dev": "^2.0.0", 27 | "typescript": "^5.8.2" 28 | }, 29 | "bin": { 30 | "next-file-gen": "./dist/index.js" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | 4 | /** 5 | * Application configuration interface 6 | */ 7 | export interface Config { 8 | /** Directory to watch for changes */ 9 | watchDir: string; 10 | /** Patterns to ignore when watching */ 11 | ignorePatterns: string[]; 12 | } 13 | 14 | /** Default configuration values */ 15 | const defaultConfig: Config = { 16 | watchDir: 'app', 17 | ignorePatterns: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**'], 18 | }; 19 | 20 | /** Path to the configuration file */ 21 | const CONFIG_FILE_PATH = path.join(process.cwd(), 'next-file-gen.config.json'); 22 | 23 | /** 24 | * Gets absolute path from a relative path 25 | * @param relativePath Relative path to convert 26 | * @returns Absolute path 27 | */ 28 | export function getAbsolutePath(relativePath: string): string { 29 | if (path.isAbsolute(relativePath)) { 30 | return relativePath; 31 | } 32 | return path.join(process.cwd(), relativePath); 33 | } 34 | 35 | /** 36 | * Loads configuration from file or returns default config 37 | * @returns The loaded configuration 38 | */ 39 | export function loadConfig(): Config { 40 | try { 41 | if (fs.existsSync(CONFIG_FILE_PATH)) { 42 | const configFile = fs.readFileSync(CONFIG_FILE_PATH, 'utf-8'); 43 | const userConfig = JSON.parse(configFile); 44 | 45 | // Override with environment variables if provided 46 | const envWatchDir = process.env.WATCH_DIR; 47 | if (envWatchDir) { 48 | userConfig.watchDir = envWatchDir; 49 | } 50 | 51 | return { ...defaultConfig, ...userConfig }; 52 | } 53 | } catch (error) { 54 | console.error('Failed to load config file:', error); 55 | } 56 | 57 | return defaultConfig; 58 | } 59 | 60 | /** 61 | * Saves configuration to file 62 | * @param config Configuration to save 63 | */ 64 | export function saveConfig(config: Config): void { 65 | try { 66 | // Ensure watchDir is saved as a relative path 67 | const configToSave = { ...config }; 68 | 69 | // If watchDir is an absolute path, try to make it relative 70 | if (path.isAbsolute(configToSave.watchDir)) { 71 | try { 72 | // Try to make the path relative to current working directory 73 | const relativePath = path.relative(process.cwd(), configToSave.watchDir); 74 | if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) { 75 | configToSave.watchDir = relativePath; 76 | } 77 | } catch (err) { 78 | // If conversion fails, keep the original path 79 | console.warn('Could not convert watchDir to relative path'); 80 | } 81 | } 82 | 83 | fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(configToSave, null, 2), 'utf-8'); 84 | console.log(`Config saved: ${CONFIG_FILE_PATH}`); 85 | } catch (error) { 86 | console.error('Failed to save config:', error); 87 | } 88 | } 89 | 90 | /** 91 | * Creates default configuration file if it doesn't exist 92 | */ 93 | export function createDefaultConfigIfNotExists(): void { 94 | if (!fs.existsSync(CONFIG_FILE_PATH)) { 95 | saveConfig(defaultConfig); 96 | console.log(`Default config file created: ${CONFIG_FILE_PATH}`); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as chokidar from 'chokidar'; 4 | import * as fs from 'fs'; 5 | import * as path from 'path'; 6 | import { loadConfig, createDefaultConfigIfNotExists, getAbsolutePath } from './config'; 7 | import { defaultTemplates, processTemplate, toPascalCase } from './templates'; 8 | 9 | // Initialize configuration 10 | createDefaultConfigIfNotExists(); 11 | const config = loadConfig(); 12 | 13 | // Track processed directories to avoid duplicate processing 14 | const processedDirectories = new Set(); 15 | 16 | // File type extensions for folder name patterns 17 | const FILE_TYPE_EXTENSIONS: Record = { 18 | page: ['page.tsx'], 19 | layout: ['layout.tsx'], 20 | error: ['error.tsx'], 21 | loading: ['loading.tsx'], 22 | default: ['page.tsx', 'layout.tsx', 'loading.tsx', 'error.tsx'], 23 | }; 24 | 25 | // Return type for template processing 26 | interface TemplateResult { 27 | templates: Record; 28 | newDirectoryPath?: string; 29 | } 30 | 31 | /** 32 | * Get appropriate templates based on directory name and patterns 33 | */ 34 | function getTemplatesForDirectory(directoryPath: string): Record { 35 | const dirName = path.basename(directoryPath); 36 | 37 | // Check if the directory name follows pattern format (name.type) 38 | if (dirName.includes('.') || dirName.includes(':')) { 39 | const result = getTemplatesFromPattern(dirName, directoryPath); 40 | return result.templates; 41 | } 42 | 43 | // No templates for non-pattern directories 44 | return {}; 45 | } 46 | 47 | /** 48 | * Process a directory name with a pattern (e.g., "profile.page" or "profile:layout") 49 | */ 50 | function getTemplatesFromPattern(patternDirName: string, directoryPath: string): TemplateResult { 51 | let baseName: string; 52 | let fileType: string; 53 | 54 | // Support both '.' and ':' as separators 55 | if (patternDirName.includes('.')) { 56 | [baseName, fileType] = patternDirName.split('.'); 57 | } else if (patternDirName.includes(':')) { 58 | [baseName, fileType] = patternDirName.split(':'); 59 | } else { 60 | return { templates: {} }; 61 | } 62 | 63 | // Unsupported pattern detection 64 | if ( 65 | baseName === 'page' || 66 | baseName === 'layout' || 67 | baseName === 'loading' || 68 | baseName === 'error' 69 | ) { 70 | console.log( 71 | `Unsupported pattern: ${patternDirName}. Pattern should be fileName.type or fileName:type` 72 | ); 73 | return { templates: {} }; 74 | } 75 | 76 | let templates: Record = {}; 77 | 78 | // Filter files based on pattern 79 | if (fileType && FILE_TYPE_EXTENSIONS[fileType]) { 80 | const fileTypes = FILE_TYPE_EXTENSIONS[fileType]; 81 | 82 | // Special handling for "default" pattern - create all standard files 83 | if (fileType === 'default') { 84 | console.log(`Default pattern detected, creating all standard files`); 85 | // Add all default templates 86 | Object.entries(defaultTemplates).forEach(([fileName, content]) => { 87 | templates[fileName] = content; 88 | }); 89 | } else { 90 | // Filter by file type 91 | fileTypes.forEach((fileName: string) => { 92 | // Use default template if available 93 | if (defaultTemplates[fileName]) { 94 | templates[fileName] = defaultTemplates[fileName]; 95 | } 96 | }); 97 | } 98 | } 99 | 100 | // Rename directory (remove pattern suffix) 101 | const parentDir = path.dirname(directoryPath); 102 | const newDirPath = path.join(parentDir, baseName); 103 | 104 | if (directoryPath !== newDirPath && !fileExistsSafe(newDirPath)) { 105 | try { 106 | fs.renameSync(directoryPath, newDirPath); 107 | console.log(`Renamed directory: ${directoryPath} -> ${newDirPath}`); 108 | return { templates, newDirectoryPath: newDirPath }; 109 | } catch (error) { 110 | console.error(`Failed to rename directory: ${directoryPath}`, error); 111 | } 112 | } 113 | 114 | return { templates }; 115 | } 116 | 117 | /** 118 | * Create template files for a newly detected directory 119 | */ 120 | function createFilesForDirectory(directoryPath: string): void { 121 | console.log(`New directory detected: ${directoryPath}`); 122 | 123 | // Skip if already processed or invalid 124 | if (processedDirectories.has(directoryPath) || !isValidDirectory(directoryPath)) { 125 | console.log(`Skipping already processed or invalid directory: ${directoryPath}`); 126 | return; 127 | } 128 | 129 | // Mark as processed immediately to avoid duplicate processing 130 | processedDirectories.add(directoryPath); 131 | 132 | const dirName = path.basename(directoryPath); 133 | 134 | // Check if this is a pattern directory 135 | if (dirName.includes('.') || dirName.includes(':')) { 136 | // Handle pattern directories 137 | handlePatternDirectory(directoryPath, dirName); 138 | } else { 139 | // Handle regular directories 140 | handleRegularDirectory(directoryPath, dirName); 141 | } 142 | } 143 | 144 | /** 145 | * Handle pattern directories with special naming (e.g., profile.page) 146 | */ 147 | function handlePatternDirectory(directoryPath: string, dirName: string): void { 148 | const result = getTemplatesFromPattern(dirName, directoryPath); 149 | 150 | console.log(`Pattern detected: ${dirName}`); 151 | console.log(`Templates keys: ${Object.keys(result.templates)}`); 152 | console.log(`Templates count: ${Object.keys(result.templates).length}`); 153 | 154 | let targetPath = directoryPath; 155 | 156 | // If directory was renamed, use the new path 157 | if (result.newDirectoryPath) { 158 | targetPath = result.newDirectoryPath; 159 | console.log(`Using new path for file creation: ${targetPath}`); 160 | 161 | // Also mark the new path as processed to avoid duplicate processing 162 | processedDirectories.add(targetPath); 163 | } 164 | 165 | // Create only the pattern-specific files 166 | Object.entries(result.templates).forEach(([fileName, content]) => { 167 | const filePath = path.join(targetPath, fileName); 168 | console.log(`Creating file: ${filePath}`); 169 | 170 | if (!fileExistsSafe(filePath)) { 171 | createFile(filePath, content, path.basename(targetPath), targetPath); 172 | } else { 173 | console.log(`File already exists: ${filePath}`); 174 | } 175 | }); 176 | } 177 | 178 | /** 179 | * Handle regular directories without special patterns 180 | */ 181 | function handleRegularDirectory(directoryPath: string, dirName: string): void { 182 | console.log(`Regular directory detected: ${directoryPath}`); 183 | console.log(`Keeping the directory structure as is`); 184 | 185 | // Keep directory as is, don't modify or create any files 186 | // This allows for manual creation of folders without auto-generation 187 | } 188 | 189 | /** 190 | * Check if a path is a valid directory 191 | */ 192 | function isValidDirectory(directoryPath: string): boolean { 193 | return fileExistsSafe(directoryPath) && fs.statSync(directoryPath).isDirectory(); 194 | } 195 | 196 | /** 197 | * Check if a file exists safely 198 | */ 199 | function fileExistsSafe(filePath: string): boolean { 200 | try { 201 | // Use fs.stat to bypass cache 202 | fs.statSync(filePath); 203 | return true; 204 | } catch (err) { 205 | return false; 206 | } 207 | } 208 | 209 | /** 210 | * Create a file with processed template content 211 | */ 212 | function createFile( 213 | filePath: string, 214 | content: string, 215 | dirName: string, 216 | directoryPath: string 217 | ): void { 218 | try { 219 | const processedContent = processTemplate(content, { 220 | name: dirName, 221 | Name: toPascalCase(dirName), 222 | path: normalizePathForTemplate(directoryPath), 223 | }); 224 | 225 | // Ensure directory exists 226 | const dir = path.dirname(filePath); 227 | if (!fileExistsSafe(dir)) { 228 | fs.mkdirSync(dir, { recursive: true }); 229 | console.log(`Created directory: ${dir}`); 230 | } 231 | 232 | // Write file 233 | fs.writeFileSync(filePath, processedContent); 234 | console.log(`File created: ${filePath}`); 235 | } catch (error) { 236 | console.error(`Failed to create file: ${filePath}`, error); 237 | } 238 | } 239 | 240 | /** 241 | * Normalize a path for use in templates 242 | */ 243 | function normalizePathForTemplate(directoryPath: string): string { 244 | const absoluteWatchDir = getAbsolutePath(config.watchDir); 245 | return directoryPath.replace(absoluteWatchDir, '').replace(/\\/g, '/'); 246 | } 247 | 248 | /** 249 | * Initialize the directory watcher 250 | */ 251 | function initWatcher(): void { 252 | // Get absolute path for watch directory 253 | const absoluteWatchDir = getAbsolutePath(config.watchDir); 254 | 255 | // Check if watch directory exists instead of creating it 256 | if (!fileExistsSafe(absoluteWatchDir)) { 257 | console.error(`Watch directory does not exist: ${absoluteWatchDir}`); 258 | console.log(`Please create the directory manually before running the app.`); 259 | process.exit(1); 260 | } 261 | 262 | const watcher = createWatcher(absoluteWatchDir); 263 | 264 | console.log(`Started watching directory: ${absoluteWatchDir}`); 265 | 266 | setupWatcherEvents(watcher); 267 | setupProcessEvents(watcher); 268 | } 269 | 270 | /** 271 | * Ensure the watch directory exists (function kept for reference but not used) 272 | */ 273 | function ensureWatchDirectory(): void { 274 | // Keep this function for reference but don't use it 275 | // The check is now done in initWatcher 276 | } 277 | 278 | /** 279 | * Create a file system watcher 280 | */ 281 | function createWatcher(watchPath: string): chokidar.FSWatcher { 282 | return chokidar.watch(watchPath, { 283 | persistent: true, 284 | ignoreInitial: false, 285 | ignored: config.ignorePatterns, 286 | awaitWriteFinish: { 287 | stabilityThreshold: 2000, 288 | pollInterval: 100, 289 | }, 290 | }); 291 | } 292 | 293 | /** 294 | * Set up watcher event handlers 295 | */ 296 | function setupWatcherEvents(watcher: chokidar.FSWatcher): void { 297 | watcher.on('addDir', (directoryPath: string) => { 298 | if (directoryPath !== config.watchDir) { 299 | createFilesForDirectory(directoryPath); 300 | } 301 | }); 302 | 303 | watcher.on('error', (error: unknown) => { 304 | console.error('Error occurred during watching:', error); 305 | }); 306 | } 307 | 308 | /** 309 | * Set up process event handlers 310 | */ 311 | function setupProcessEvents(watcher: chokidar.FSWatcher): void { 312 | process.on('SIGINT', () => { 313 | watcher.close().then(() => { 314 | console.log('Watching ended'); 315 | process.exit(0); 316 | }); 317 | }); 318 | } 319 | 320 | // Start the watcher 321 | initWatcher(); 322 | -------------------------------------------------------------------------------- /src/templates.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Default file templates for Next.js components 3 | */ 4 | export const defaultTemplates: Record = { 5 | 'page.tsx': ` 6 | 7 | export default function Page() { 8 | return ( 9 | <> 10 | ); 11 | } 12 | `, 13 | 'layout.tsx': ` 14 | 15 | 16 | interface LayoutProps { 17 | children: React.ReactNode; 18 | } 19 | 20 | export default function Layout({ children }: LayoutProps) { 21 | return ( 22 | <> 23 | {children} 24 | ); 25 | } 26 | `, 27 | 'loading.tsx': ` 28 | 29 | export default function Loading() { 30 | return <>; 31 | } 32 | `, 33 | 'error.tsx': ` 34 | 35 | interface ErrorProps { 36 | error: Error; 37 | reset: () => void; 38 | } 39 | 40 | export default function Error({ error, reset }: ErrorProps) { 41 | return ( 42 | <> 43 | ); 44 | } 45 | `, 46 | }; 47 | 48 | /** 49 | * Processes a template string by replacing variables 50 | * @param template The template string 51 | * @param variables Variables to replace in the template 52 | * @returns Processed template 53 | */ 54 | export function processTemplate(template: string, variables: Record): string { 55 | let result = template; 56 | 57 | Object.entries(variables).forEach(([key, value]) => { 58 | const regex = new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'); 59 | result = result.replace(regex, value); 60 | }); 61 | 62 | return result; 63 | } 64 | 65 | /** 66 | * Converts a string to PascalCase 67 | * @param str The input string 68 | * @returns The PascalCase string 69 | */ 70 | export function toPascalCase(str: string): string { 71 | return str.charAt(0).toUpperCase() + str.slice(1); 72 | } 73 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Language and Environment */ 6 | "target": "ES2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 7 | "lib": ["ES2017", "DOM"], 8 | 9 | /* Modules */ 10 | "module": "commonjs" /* Specify what module code is generated. */, 11 | 12 | /* Emit */ 13 | "outDir": "dist", 14 | "removeComments": true, 15 | 16 | /* Interop Constraints */ 17 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 18 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 19 | 20 | /* Type Checking */ 21 | "strict": true /* Enable all strict type-checking options. */, 22 | 23 | /* Completeness */ 24 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 25 | } 26 | } 27 | --------------------------------------------------------------------------------