├── .gitignore ├── LICENSE.txt ├── README.md ├── index.mjs ├── package.json ├── pnpm-lock.yaml ├── src ├── components.mjs ├── create-diff.mjs ├── git.mjs └── parse-file-path.mjs └── tests ├── fixtures ├── non-src-dir │ ├── .env.example │ ├── app │ │ └── page.tsx │ ├── components.json │ ├── components │ │ ├── comp.tsx │ │ └── ui │ │ │ ├── new-ui-comp.tsx │ │ │ └── select.tsx │ ├── hooks │ │ └── hook.ts │ ├── lib │ │ └── lib.ts │ ├── non-src-sub │ │ └── test.ts │ └── package.json └── src-dir │ ├── .env.example │ ├── components.json │ ├── non-src-sub │ └── test.ts │ ├── package.json │ └── src │ ├── app │ └── page.tsx │ ├── components │ ├── comp.tsx │ └── ui │ │ ├── new-ui-comp.tsx │ │ └── select.tsx │ ├── hooks │ └── hook.ts │ └── lib │ └── lib.ts └── tests ├── __snapshots__ └── test-fixtures.spec.mjs.snap ├── parse-file-path.spec.mjs └── test-fixtures.spec.mjs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2024 Jack Herrington 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShadCN Project Differ 2 | 3 | This CLI tool figures out the difference between the initial commit of a ShadCN project and the current state of the project and creates a new ShadCN JSON output file with the changes. This ShadCN JSON file can then be used with the ShadCN CLI to generate a new project or add to an existing project. 4 | 5 | # Steps 6 | 7 | 1. Create a new NextJS app 8 | 2. Add ShadCN to the app 9 | 3. Create a new initial commit; `rm -fr .git && git init && git add . && git commit -m "Initial commit"` 10 | 4. Make your updates to the project 11 | 5. Run the CLI tool; `npx shadcn-differ` 12 | 13 | The reason we are recreating the initial commit is so that the resulting JSON output is only the changes to the project after ShadCN was added, and not the entire project history. 14 | 15 | You can then take the resulting JSON ouput and host it on a URL, then use that with the ShadCN CLI to generate a new project or add to an existing project. 16 | 17 | ```bash 18 | npx shadcn@latest init http://your-json-output-url 19 | ``` 20 | 21 | You can use the `--src-dir` flag if you want to use the `src` directory in your project. 22 | 23 | Or you can add the JSON output to an existing project: 24 | 25 | ```bash 26 | npx shadcn@latest add http://your-json-output-url 27 | ``` 28 | 29 | # Why is this useful? 30 | 31 | This allows library maintainers or SaaS services to create a one step installer for their library or service into an existing project, or to bootstrap a new project with the library or service. 32 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import fs from "node:fs"; 4 | import path from "node:path"; 5 | import { program } from "commander"; 6 | 7 | import { scanForAlteredFiles, scanForFiles, hasSrcDir } from "./src/git.mjs"; 8 | import { readComponentsManifest } from "./src/components.mjs"; 9 | import { createDiff } from "./src/create-diff.mjs"; 10 | 11 | program.option("-n, --name "); 12 | program.parse(); 13 | 14 | const options = program.opts(); 15 | 16 | const main = () => { 17 | const name = options.name || path.basename(process.cwd()); 18 | 19 | const { alteredFiles, specificFiles } = scanForAlteredFiles([ 20 | "./package.json", 21 | ]); 22 | const currentFiles = scanForFiles(process.cwd()); 23 | 24 | const currentPackageJson = fs.readFileSync("./package.json", "utf-8"); 25 | 26 | const config = readComponentsManifest(process.cwd()); 27 | config.isSrcDir = hasSrcDir(process.cwd()); 28 | 29 | const output = createDiff({ 30 | name, 31 | config, 32 | alteredFiles, 33 | currentFiles, 34 | specificFiles, 35 | currentPackageJson, 36 | }); 37 | 38 | console.log(JSON.stringify(output, null, 2)); 39 | }; 40 | 41 | main(); 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shadcn-differ", 3 | "version": "1.0.3", 4 | "description": "", 5 | "main": "index.mjs", 6 | "bin": "index.mjs", 7 | "scripts": { 8 | "test": "npx vitest" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "dependencies": { 14 | "commander": "^12.1.0", 15 | "ignore": "^6.0.2" 16 | }, 17 | "devDependencies": { 18 | "vitest": "^2.1.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | commander: 12 | specifier: ^12.1.0 13 | version: 12.1.0 14 | ignore: 15 | specifier: ^6.0.2 16 | version: 6.0.2 17 | devDependencies: 18 | vitest: 19 | specifier: ^2.1.1 20 | version: 2.1.1 21 | 22 | packages: 23 | 24 | '@esbuild/aix-ppc64@0.21.5': 25 | resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} 26 | engines: {node: '>=12'} 27 | cpu: [ppc64] 28 | os: [aix] 29 | 30 | '@esbuild/android-arm64@0.21.5': 31 | resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} 32 | engines: {node: '>=12'} 33 | cpu: [arm64] 34 | os: [android] 35 | 36 | '@esbuild/android-arm@0.21.5': 37 | resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} 38 | engines: {node: '>=12'} 39 | cpu: [arm] 40 | os: [android] 41 | 42 | '@esbuild/android-x64@0.21.5': 43 | resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} 44 | engines: {node: '>=12'} 45 | cpu: [x64] 46 | os: [android] 47 | 48 | '@esbuild/darwin-arm64@0.21.5': 49 | resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} 50 | engines: {node: '>=12'} 51 | cpu: [arm64] 52 | os: [darwin] 53 | 54 | '@esbuild/darwin-x64@0.21.5': 55 | resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} 56 | engines: {node: '>=12'} 57 | cpu: [x64] 58 | os: [darwin] 59 | 60 | '@esbuild/freebsd-arm64@0.21.5': 61 | resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} 62 | engines: {node: '>=12'} 63 | cpu: [arm64] 64 | os: [freebsd] 65 | 66 | '@esbuild/freebsd-x64@0.21.5': 67 | resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} 68 | engines: {node: '>=12'} 69 | cpu: [x64] 70 | os: [freebsd] 71 | 72 | '@esbuild/linux-arm64@0.21.5': 73 | resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} 74 | engines: {node: '>=12'} 75 | cpu: [arm64] 76 | os: [linux] 77 | 78 | '@esbuild/linux-arm@0.21.5': 79 | resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} 80 | engines: {node: '>=12'} 81 | cpu: [arm] 82 | os: [linux] 83 | 84 | '@esbuild/linux-ia32@0.21.5': 85 | resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} 86 | engines: {node: '>=12'} 87 | cpu: [ia32] 88 | os: [linux] 89 | 90 | '@esbuild/linux-loong64@0.21.5': 91 | resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} 92 | engines: {node: '>=12'} 93 | cpu: [loong64] 94 | os: [linux] 95 | 96 | '@esbuild/linux-mips64el@0.21.5': 97 | resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} 98 | engines: {node: '>=12'} 99 | cpu: [mips64el] 100 | os: [linux] 101 | 102 | '@esbuild/linux-ppc64@0.21.5': 103 | resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} 104 | engines: {node: '>=12'} 105 | cpu: [ppc64] 106 | os: [linux] 107 | 108 | '@esbuild/linux-riscv64@0.21.5': 109 | resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} 110 | engines: {node: '>=12'} 111 | cpu: [riscv64] 112 | os: [linux] 113 | 114 | '@esbuild/linux-s390x@0.21.5': 115 | resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} 116 | engines: {node: '>=12'} 117 | cpu: [s390x] 118 | os: [linux] 119 | 120 | '@esbuild/linux-x64@0.21.5': 121 | resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} 122 | engines: {node: '>=12'} 123 | cpu: [x64] 124 | os: [linux] 125 | 126 | '@esbuild/netbsd-x64@0.21.5': 127 | resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} 128 | engines: {node: '>=12'} 129 | cpu: [x64] 130 | os: [netbsd] 131 | 132 | '@esbuild/openbsd-x64@0.21.5': 133 | resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} 134 | engines: {node: '>=12'} 135 | cpu: [x64] 136 | os: [openbsd] 137 | 138 | '@esbuild/sunos-x64@0.21.5': 139 | resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} 140 | engines: {node: '>=12'} 141 | cpu: [x64] 142 | os: [sunos] 143 | 144 | '@esbuild/win32-arm64@0.21.5': 145 | resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} 146 | engines: {node: '>=12'} 147 | cpu: [arm64] 148 | os: [win32] 149 | 150 | '@esbuild/win32-ia32@0.21.5': 151 | resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} 152 | engines: {node: '>=12'} 153 | cpu: [ia32] 154 | os: [win32] 155 | 156 | '@esbuild/win32-x64@0.21.5': 157 | resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} 158 | engines: {node: '>=12'} 159 | cpu: [x64] 160 | os: [win32] 161 | 162 | '@jridgewell/sourcemap-codec@1.5.0': 163 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 164 | 165 | '@rollup/rollup-android-arm-eabi@4.21.3': 166 | resolution: {integrity: sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==} 167 | cpu: [arm] 168 | os: [android] 169 | 170 | '@rollup/rollup-android-arm64@4.21.3': 171 | resolution: {integrity: sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==} 172 | cpu: [arm64] 173 | os: [android] 174 | 175 | '@rollup/rollup-darwin-arm64@4.21.3': 176 | resolution: {integrity: sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==} 177 | cpu: [arm64] 178 | os: [darwin] 179 | 180 | '@rollup/rollup-darwin-x64@4.21.3': 181 | resolution: {integrity: sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==} 182 | cpu: [x64] 183 | os: [darwin] 184 | 185 | '@rollup/rollup-linux-arm-gnueabihf@4.21.3': 186 | resolution: {integrity: sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==} 187 | cpu: [arm] 188 | os: [linux] 189 | 190 | '@rollup/rollup-linux-arm-musleabihf@4.21.3': 191 | resolution: {integrity: sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==} 192 | cpu: [arm] 193 | os: [linux] 194 | 195 | '@rollup/rollup-linux-arm64-gnu@4.21.3': 196 | resolution: {integrity: sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==} 197 | cpu: [arm64] 198 | os: [linux] 199 | 200 | '@rollup/rollup-linux-arm64-musl@4.21.3': 201 | resolution: {integrity: sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==} 202 | cpu: [arm64] 203 | os: [linux] 204 | 205 | '@rollup/rollup-linux-powerpc64le-gnu@4.21.3': 206 | resolution: {integrity: sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==} 207 | cpu: [ppc64] 208 | os: [linux] 209 | 210 | '@rollup/rollup-linux-riscv64-gnu@4.21.3': 211 | resolution: {integrity: sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==} 212 | cpu: [riscv64] 213 | os: [linux] 214 | 215 | '@rollup/rollup-linux-s390x-gnu@4.21.3': 216 | resolution: {integrity: sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==} 217 | cpu: [s390x] 218 | os: [linux] 219 | 220 | '@rollup/rollup-linux-x64-gnu@4.21.3': 221 | resolution: {integrity: sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==} 222 | cpu: [x64] 223 | os: [linux] 224 | 225 | '@rollup/rollup-linux-x64-musl@4.21.3': 226 | resolution: {integrity: sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==} 227 | cpu: [x64] 228 | os: [linux] 229 | 230 | '@rollup/rollup-win32-arm64-msvc@4.21.3': 231 | resolution: {integrity: sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==} 232 | cpu: [arm64] 233 | os: [win32] 234 | 235 | '@rollup/rollup-win32-ia32-msvc@4.21.3': 236 | resolution: {integrity: sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==} 237 | cpu: [ia32] 238 | os: [win32] 239 | 240 | '@rollup/rollup-win32-x64-msvc@4.21.3': 241 | resolution: {integrity: sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==} 242 | cpu: [x64] 243 | os: [win32] 244 | 245 | '@types/estree@1.0.5': 246 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 247 | 248 | '@vitest/expect@2.1.1': 249 | resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} 250 | 251 | '@vitest/mocker@2.1.1': 252 | resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} 253 | peerDependencies: 254 | '@vitest/spy': 2.1.1 255 | msw: ^2.3.5 256 | vite: ^5.0.0 257 | peerDependenciesMeta: 258 | msw: 259 | optional: true 260 | vite: 261 | optional: true 262 | 263 | '@vitest/pretty-format@2.1.1': 264 | resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} 265 | 266 | '@vitest/runner@2.1.1': 267 | resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} 268 | 269 | '@vitest/snapshot@2.1.1': 270 | resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} 271 | 272 | '@vitest/spy@2.1.1': 273 | resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} 274 | 275 | '@vitest/utils@2.1.1': 276 | resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} 277 | 278 | assertion-error@2.0.1: 279 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 280 | engines: {node: '>=12'} 281 | 282 | cac@6.7.14: 283 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 284 | engines: {node: '>=8'} 285 | 286 | chai@5.1.1: 287 | resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} 288 | engines: {node: '>=12'} 289 | 290 | check-error@2.1.1: 291 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 292 | engines: {node: '>= 16'} 293 | 294 | commander@12.1.0: 295 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} 296 | engines: {node: '>=18'} 297 | 298 | debug@4.3.7: 299 | resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} 300 | engines: {node: '>=6.0'} 301 | peerDependencies: 302 | supports-color: '*' 303 | peerDependenciesMeta: 304 | supports-color: 305 | optional: true 306 | 307 | deep-eql@5.0.2: 308 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 309 | engines: {node: '>=6'} 310 | 311 | esbuild@0.21.5: 312 | resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} 313 | engines: {node: '>=12'} 314 | hasBin: true 315 | 316 | estree-walker@3.0.3: 317 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 318 | 319 | fsevents@2.3.3: 320 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 321 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 322 | os: [darwin] 323 | 324 | get-func-name@2.0.2: 325 | resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} 326 | 327 | ignore@6.0.2: 328 | resolution: {integrity: sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==} 329 | engines: {node: '>= 4'} 330 | 331 | loupe@3.1.1: 332 | resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} 333 | 334 | magic-string@0.30.11: 335 | resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} 336 | 337 | ms@2.1.3: 338 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 339 | 340 | nanoid@3.3.7: 341 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 342 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 343 | hasBin: true 344 | 345 | pathe@1.1.2: 346 | resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} 347 | 348 | pathval@2.0.0: 349 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 350 | engines: {node: '>= 14.16'} 351 | 352 | picocolors@1.1.0: 353 | resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} 354 | 355 | postcss@8.4.45: 356 | resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} 357 | engines: {node: ^10 || ^12 || >=14} 358 | 359 | rollup@4.21.3: 360 | resolution: {integrity: sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==} 361 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 362 | hasBin: true 363 | 364 | siginfo@2.0.0: 365 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 366 | 367 | source-map-js@1.2.1: 368 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 369 | engines: {node: '>=0.10.0'} 370 | 371 | stackback@0.0.2: 372 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 373 | 374 | std-env@3.7.0: 375 | resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} 376 | 377 | tinybench@2.9.0: 378 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 379 | 380 | tinyexec@0.3.0: 381 | resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} 382 | 383 | tinypool@1.0.1: 384 | resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} 385 | engines: {node: ^18.0.0 || >=20.0.0} 386 | 387 | tinyrainbow@1.2.0: 388 | resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} 389 | engines: {node: '>=14.0.0'} 390 | 391 | tinyspy@3.0.2: 392 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 393 | engines: {node: '>=14.0.0'} 394 | 395 | vite-node@2.1.1: 396 | resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} 397 | engines: {node: ^18.0.0 || >=20.0.0} 398 | hasBin: true 399 | 400 | vite@5.4.5: 401 | resolution: {integrity: sha512-pXqR0qtb2bTwLkev4SE3r4abCNioP3GkjvIDLlzziPpXtHgiJIjuKl+1GN6ESOT3wMjG3JTeARopj2SwYaHTOA==} 402 | engines: {node: ^18.0.0 || >=20.0.0} 403 | hasBin: true 404 | peerDependencies: 405 | '@types/node': ^18.0.0 || >=20.0.0 406 | less: '*' 407 | lightningcss: ^1.21.0 408 | sass: '*' 409 | sass-embedded: '*' 410 | stylus: '*' 411 | sugarss: '*' 412 | terser: ^5.4.0 413 | peerDependenciesMeta: 414 | '@types/node': 415 | optional: true 416 | less: 417 | optional: true 418 | lightningcss: 419 | optional: true 420 | sass: 421 | optional: true 422 | sass-embedded: 423 | optional: true 424 | stylus: 425 | optional: true 426 | sugarss: 427 | optional: true 428 | terser: 429 | optional: true 430 | 431 | vitest@2.1.1: 432 | resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} 433 | engines: {node: ^18.0.0 || >=20.0.0} 434 | hasBin: true 435 | peerDependencies: 436 | '@edge-runtime/vm': '*' 437 | '@types/node': ^18.0.0 || >=20.0.0 438 | '@vitest/browser': 2.1.1 439 | '@vitest/ui': 2.1.1 440 | happy-dom: '*' 441 | jsdom: '*' 442 | peerDependenciesMeta: 443 | '@edge-runtime/vm': 444 | optional: true 445 | '@types/node': 446 | optional: true 447 | '@vitest/browser': 448 | optional: true 449 | '@vitest/ui': 450 | optional: true 451 | happy-dom: 452 | optional: true 453 | jsdom: 454 | optional: true 455 | 456 | why-is-node-running@2.3.0: 457 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 458 | engines: {node: '>=8'} 459 | hasBin: true 460 | 461 | snapshots: 462 | 463 | '@esbuild/aix-ppc64@0.21.5': 464 | optional: true 465 | 466 | '@esbuild/android-arm64@0.21.5': 467 | optional: true 468 | 469 | '@esbuild/android-arm@0.21.5': 470 | optional: true 471 | 472 | '@esbuild/android-x64@0.21.5': 473 | optional: true 474 | 475 | '@esbuild/darwin-arm64@0.21.5': 476 | optional: true 477 | 478 | '@esbuild/darwin-x64@0.21.5': 479 | optional: true 480 | 481 | '@esbuild/freebsd-arm64@0.21.5': 482 | optional: true 483 | 484 | '@esbuild/freebsd-x64@0.21.5': 485 | optional: true 486 | 487 | '@esbuild/linux-arm64@0.21.5': 488 | optional: true 489 | 490 | '@esbuild/linux-arm@0.21.5': 491 | optional: true 492 | 493 | '@esbuild/linux-ia32@0.21.5': 494 | optional: true 495 | 496 | '@esbuild/linux-loong64@0.21.5': 497 | optional: true 498 | 499 | '@esbuild/linux-mips64el@0.21.5': 500 | optional: true 501 | 502 | '@esbuild/linux-ppc64@0.21.5': 503 | optional: true 504 | 505 | '@esbuild/linux-riscv64@0.21.5': 506 | optional: true 507 | 508 | '@esbuild/linux-s390x@0.21.5': 509 | optional: true 510 | 511 | '@esbuild/linux-x64@0.21.5': 512 | optional: true 513 | 514 | '@esbuild/netbsd-x64@0.21.5': 515 | optional: true 516 | 517 | '@esbuild/openbsd-x64@0.21.5': 518 | optional: true 519 | 520 | '@esbuild/sunos-x64@0.21.5': 521 | optional: true 522 | 523 | '@esbuild/win32-arm64@0.21.5': 524 | optional: true 525 | 526 | '@esbuild/win32-ia32@0.21.5': 527 | optional: true 528 | 529 | '@esbuild/win32-x64@0.21.5': 530 | optional: true 531 | 532 | '@jridgewell/sourcemap-codec@1.5.0': {} 533 | 534 | '@rollup/rollup-android-arm-eabi@4.21.3': 535 | optional: true 536 | 537 | '@rollup/rollup-android-arm64@4.21.3': 538 | optional: true 539 | 540 | '@rollup/rollup-darwin-arm64@4.21.3': 541 | optional: true 542 | 543 | '@rollup/rollup-darwin-x64@4.21.3': 544 | optional: true 545 | 546 | '@rollup/rollup-linux-arm-gnueabihf@4.21.3': 547 | optional: true 548 | 549 | '@rollup/rollup-linux-arm-musleabihf@4.21.3': 550 | optional: true 551 | 552 | '@rollup/rollup-linux-arm64-gnu@4.21.3': 553 | optional: true 554 | 555 | '@rollup/rollup-linux-arm64-musl@4.21.3': 556 | optional: true 557 | 558 | '@rollup/rollup-linux-powerpc64le-gnu@4.21.3': 559 | optional: true 560 | 561 | '@rollup/rollup-linux-riscv64-gnu@4.21.3': 562 | optional: true 563 | 564 | '@rollup/rollup-linux-s390x-gnu@4.21.3': 565 | optional: true 566 | 567 | '@rollup/rollup-linux-x64-gnu@4.21.3': 568 | optional: true 569 | 570 | '@rollup/rollup-linux-x64-musl@4.21.3': 571 | optional: true 572 | 573 | '@rollup/rollup-win32-arm64-msvc@4.21.3': 574 | optional: true 575 | 576 | '@rollup/rollup-win32-ia32-msvc@4.21.3': 577 | optional: true 578 | 579 | '@rollup/rollup-win32-x64-msvc@4.21.3': 580 | optional: true 581 | 582 | '@types/estree@1.0.5': {} 583 | 584 | '@vitest/expect@2.1.1': 585 | dependencies: 586 | '@vitest/spy': 2.1.1 587 | '@vitest/utils': 2.1.1 588 | chai: 5.1.1 589 | tinyrainbow: 1.2.0 590 | 591 | '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.5)': 592 | dependencies: 593 | '@vitest/spy': 2.1.1 594 | estree-walker: 3.0.3 595 | magic-string: 0.30.11 596 | optionalDependencies: 597 | vite: 5.4.5 598 | 599 | '@vitest/pretty-format@2.1.1': 600 | dependencies: 601 | tinyrainbow: 1.2.0 602 | 603 | '@vitest/runner@2.1.1': 604 | dependencies: 605 | '@vitest/utils': 2.1.1 606 | pathe: 1.1.2 607 | 608 | '@vitest/snapshot@2.1.1': 609 | dependencies: 610 | '@vitest/pretty-format': 2.1.1 611 | magic-string: 0.30.11 612 | pathe: 1.1.2 613 | 614 | '@vitest/spy@2.1.1': 615 | dependencies: 616 | tinyspy: 3.0.2 617 | 618 | '@vitest/utils@2.1.1': 619 | dependencies: 620 | '@vitest/pretty-format': 2.1.1 621 | loupe: 3.1.1 622 | tinyrainbow: 1.2.0 623 | 624 | assertion-error@2.0.1: {} 625 | 626 | cac@6.7.14: {} 627 | 628 | chai@5.1.1: 629 | dependencies: 630 | assertion-error: 2.0.1 631 | check-error: 2.1.1 632 | deep-eql: 5.0.2 633 | loupe: 3.1.1 634 | pathval: 2.0.0 635 | 636 | check-error@2.1.1: {} 637 | 638 | commander@12.1.0: {} 639 | 640 | debug@4.3.7: 641 | dependencies: 642 | ms: 2.1.3 643 | 644 | deep-eql@5.0.2: {} 645 | 646 | esbuild@0.21.5: 647 | optionalDependencies: 648 | '@esbuild/aix-ppc64': 0.21.5 649 | '@esbuild/android-arm': 0.21.5 650 | '@esbuild/android-arm64': 0.21.5 651 | '@esbuild/android-x64': 0.21.5 652 | '@esbuild/darwin-arm64': 0.21.5 653 | '@esbuild/darwin-x64': 0.21.5 654 | '@esbuild/freebsd-arm64': 0.21.5 655 | '@esbuild/freebsd-x64': 0.21.5 656 | '@esbuild/linux-arm': 0.21.5 657 | '@esbuild/linux-arm64': 0.21.5 658 | '@esbuild/linux-ia32': 0.21.5 659 | '@esbuild/linux-loong64': 0.21.5 660 | '@esbuild/linux-mips64el': 0.21.5 661 | '@esbuild/linux-ppc64': 0.21.5 662 | '@esbuild/linux-riscv64': 0.21.5 663 | '@esbuild/linux-s390x': 0.21.5 664 | '@esbuild/linux-x64': 0.21.5 665 | '@esbuild/netbsd-x64': 0.21.5 666 | '@esbuild/openbsd-x64': 0.21.5 667 | '@esbuild/sunos-x64': 0.21.5 668 | '@esbuild/win32-arm64': 0.21.5 669 | '@esbuild/win32-ia32': 0.21.5 670 | '@esbuild/win32-x64': 0.21.5 671 | 672 | estree-walker@3.0.3: 673 | dependencies: 674 | '@types/estree': 1.0.5 675 | 676 | fsevents@2.3.3: 677 | optional: true 678 | 679 | get-func-name@2.0.2: {} 680 | 681 | ignore@6.0.2: {} 682 | 683 | loupe@3.1.1: 684 | dependencies: 685 | get-func-name: 2.0.2 686 | 687 | magic-string@0.30.11: 688 | dependencies: 689 | '@jridgewell/sourcemap-codec': 1.5.0 690 | 691 | ms@2.1.3: {} 692 | 693 | nanoid@3.3.7: {} 694 | 695 | pathe@1.1.2: {} 696 | 697 | pathval@2.0.0: {} 698 | 699 | picocolors@1.1.0: {} 700 | 701 | postcss@8.4.45: 702 | dependencies: 703 | nanoid: 3.3.7 704 | picocolors: 1.1.0 705 | source-map-js: 1.2.1 706 | 707 | rollup@4.21.3: 708 | dependencies: 709 | '@types/estree': 1.0.5 710 | optionalDependencies: 711 | '@rollup/rollup-android-arm-eabi': 4.21.3 712 | '@rollup/rollup-android-arm64': 4.21.3 713 | '@rollup/rollup-darwin-arm64': 4.21.3 714 | '@rollup/rollup-darwin-x64': 4.21.3 715 | '@rollup/rollup-linux-arm-gnueabihf': 4.21.3 716 | '@rollup/rollup-linux-arm-musleabihf': 4.21.3 717 | '@rollup/rollup-linux-arm64-gnu': 4.21.3 718 | '@rollup/rollup-linux-arm64-musl': 4.21.3 719 | '@rollup/rollup-linux-powerpc64le-gnu': 4.21.3 720 | '@rollup/rollup-linux-riscv64-gnu': 4.21.3 721 | '@rollup/rollup-linux-s390x-gnu': 4.21.3 722 | '@rollup/rollup-linux-x64-gnu': 4.21.3 723 | '@rollup/rollup-linux-x64-musl': 4.21.3 724 | '@rollup/rollup-win32-arm64-msvc': 4.21.3 725 | '@rollup/rollup-win32-ia32-msvc': 4.21.3 726 | '@rollup/rollup-win32-x64-msvc': 4.21.3 727 | fsevents: 2.3.3 728 | 729 | siginfo@2.0.0: {} 730 | 731 | source-map-js@1.2.1: {} 732 | 733 | stackback@0.0.2: {} 734 | 735 | std-env@3.7.0: {} 736 | 737 | tinybench@2.9.0: {} 738 | 739 | tinyexec@0.3.0: {} 740 | 741 | tinypool@1.0.1: {} 742 | 743 | tinyrainbow@1.2.0: {} 744 | 745 | tinyspy@3.0.2: {} 746 | 747 | vite-node@2.1.1: 748 | dependencies: 749 | cac: 6.7.14 750 | debug: 4.3.7 751 | pathe: 1.1.2 752 | vite: 5.4.5 753 | transitivePeerDependencies: 754 | - '@types/node' 755 | - less 756 | - lightningcss 757 | - sass 758 | - sass-embedded 759 | - stylus 760 | - sugarss 761 | - supports-color 762 | - terser 763 | 764 | vite@5.4.5: 765 | dependencies: 766 | esbuild: 0.21.5 767 | postcss: 8.4.45 768 | rollup: 4.21.3 769 | optionalDependencies: 770 | fsevents: 2.3.3 771 | 772 | vitest@2.1.1: 773 | dependencies: 774 | '@vitest/expect': 2.1.1 775 | '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.5) 776 | '@vitest/pretty-format': 2.1.1 777 | '@vitest/runner': 2.1.1 778 | '@vitest/snapshot': 2.1.1 779 | '@vitest/spy': 2.1.1 780 | '@vitest/utils': 2.1.1 781 | chai: 5.1.1 782 | debug: 4.3.7 783 | magic-string: 0.30.11 784 | pathe: 1.1.2 785 | std-env: 3.7.0 786 | tinybench: 2.9.0 787 | tinyexec: 0.3.0 788 | tinypool: 1.0.1 789 | tinyrainbow: 1.2.0 790 | vite: 5.4.5 791 | vite-node: 2.1.1 792 | why-is-node-running: 2.3.0 793 | transitivePeerDependencies: 794 | - less 795 | - lightningcss 796 | - msw 797 | - sass 798 | - sass-embedded 799 | - stylus 800 | - sugarss 801 | - supports-color 802 | - terser 803 | 804 | why-is-node-running@2.3.0: 805 | dependencies: 806 | siginfo: 2.0.0 807 | stackback: 0.0.2 808 | -------------------------------------------------------------------------------- /src/components.mjs: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import fs from "node:fs"; 3 | 4 | const WHITELISTED_COMPONENTS = [ 5 | "accordion", 6 | "alert", 7 | "alert-dialog", 8 | "aspect-ratio", 9 | "avatar", 10 | "badge", 11 | "breadcrumb", 12 | "button", 13 | "calendar", 14 | "card", 15 | "carousel", 16 | "chart", 17 | "checkbox", 18 | "collapsible", 19 | "command", 20 | "context-menu", 21 | "table", 22 | "dialog", 23 | "drawer", 24 | "dropdown-menu", 25 | "form", 26 | "hover-card", 27 | "input", 28 | "input-otp", 29 | "label", 30 | "menubar", 31 | "navigation-menu", 32 | "pagination", 33 | "popover", 34 | "progress", 35 | "radio-group", 36 | "resizable", 37 | "scroll-area", 38 | "select", 39 | "separator", 40 | "sheet", 41 | "skeleton", 42 | "slider", 43 | "sonner", 44 | "switch", 45 | "tabs", 46 | "textarea", 47 | "toast", 48 | "toggle", 49 | "toggle-group", 50 | "tooltip", 51 | ]; 52 | 53 | export function findComponentFiles(config, originalFiles) { 54 | const registryDependencies = []; 55 | const compDir = config.ui.replace("@/", config.isSrcDir ? "src/" : ""); 56 | for (const { path: filePath } of originalFiles) { 57 | if (filePath.startsWith(compDir)) { 58 | const fileExtension = path.extname(filePath); 59 | const fileName = path.basename(filePath, fileExtension); 60 | if ( 61 | (fileExtension === ".tsx" || fileExtension === ".jsx") && 62 | WHITELISTED_COMPONENTS.includes(fileName) 63 | ) { 64 | registryDependencies.push(path.basename(filePath, fileExtension)); 65 | } 66 | } 67 | } 68 | return registryDependencies; 69 | } 70 | 71 | export function readComponentsManifest(dir) { 72 | const manifestPath = path.join(dir, "./components.json"); 73 | if (fs.existsSync(manifestPath)) { 74 | const config = JSON.parse(fs.readFileSync(manifestPath, "utf-8")).aliases; 75 | return config; 76 | } else { 77 | console.error("Components manifest not found"); 78 | process.exit(1); 79 | } 80 | } 81 | 82 | export function getAliasedPaths(config) { 83 | return [ 84 | config.components.replace("@/", ""), 85 | config.utils.replace("@/", ""), 86 | config.ui.replace("@/", ""), 87 | config.lib.replace("@/", ""), 88 | config.hooks.replace("@/", ""), 89 | ]; 90 | } 91 | 92 | export function isBuiltinComponent(config, filePath) { 93 | if (filePath.startsWith(config.ui.replace("@/", ""))) { 94 | const component = path.basename(filePath, path.extname(filePath)); 95 | return WHITELISTED_COMPONENTS.includes(component); 96 | } 97 | return false; 98 | } 99 | -------------------------------------------------------------------------------- /src/create-diff.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | findComponentFiles, 3 | getAliasedPaths, 4 | isBuiltinComponent, 5 | } from "./components.mjs"; 6 | import { parseFilePath } from "./parse-file-path.mjs"; 7 | 8 | function addFile(output, config, inSrcDir, relativeFilePath, content) { 9 | if (!isBuiltinComponent(config, relativeFilePath)) { 10 | output.files.push( 11 | parseFilePath(inSrcDir, config, `./${relativeFilePath}`, content) 12 | ); 13 | } 14 | } 15 | 16 | function addDependencies( 17 | output, 18 | initialPackageContents, 19 | currentPackageContents 20 | ) { 21 | const initialPackageJson = JSON.parse(initialPackageContents); 22 | const currentPackageJson = JSON.parse(currentPackageContents); 23 | 24 | output.dependencies = Object.keys( 25 | currentPackageJson.dependencies || {} 26 | ).filter((dep) => !initialPackageJson.dependencies.hasOwnProperty(dep)); 27 | output.devDependencies = Object.keys( 28 | currentPackageJson.devDependencies || {} 29 | ).filter((dep) => !initialPackageJson.devDependencies.hasOwnProperty(dep)); 30 | } 31 | 32 | function scanWithSrcDir(output, config, alteredFiles) { 33 | for (const { path, content } of alteredFiles) { 34 | if (path.startsWith("src/")) { 35 | addFile(output, config, true, path.replace("src/", ""), content); 36 | } else { 37 | addFile(output, config, false, path, content); 38 | } 39 | } 40 | } 41 | 42 | function isInAppDir(path) { 43 | return path.startsWith("app/"); 44 | } 45 | 46 | function scanWithoutSrcDir(output, config, alteredFiles) { 47 | const aliasedPaths = getAliasedPaths(config); 48 | 49 | for (const { path, content } of alteredFiles) { 50 | addFile( 51 | output, 52 | config, 53 | aliasedPaths.includes(path) || isInAppDir(path), 54 | path, 55 | content 56 | ); 57 | } 58 | } 59 | 60 | export function createDiff({ 61 | name, 62 | config, 63 | alteredFiles, 64 | specificFiles, 65 | currentFiles, 66 | currentPackageJson, 67 | }) { 68 | const output = { 69 | name, 70 | type: "registry:block", 71 | dependencies: [], 72 | devDependencies: [], 73 | registryDependencies: [], 74 | files: [], 75 | tailwind: {}, 76 | cssVars: {}, 77 | meta: {}, 78 | }; 79 | 80 | if (config.isSrcDir) { 81 | scanWithSrcDir(output, config, alteredFiles); 82 | } else { 83 | scanWithoutSrcDir(output, config, alteredFiles); 84 | } 85 | 86 | output.registryDependencies = findComponentFiles(config, currentFiles); 87 | 88 | addDependencies(output, specificFiles["./package.json"], currentPackageJson); 89 | 90 | return output; 91 | } 92 | -------------------------------------------------------------------------------- /src/git.mjs: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import { execSync } from "node:child_process"; 4 | import ignore from "ignore"; 5 | import { start } from "node:repl"; 6 | 7 | const INITIAL_DIR = "_initial"; 8 | 9 | const EXCLUDE_DIRS = [ 10 | "node_modules", 11 | "dist", 12 | "fonts", 13 | "build", 14 | "public", 15 | "static", 16 | ".next", 17 | ".git", 18 | INITIAL_DIR, 19 | ]; 20 | 21 | const EXCLUDE_FILES = [ 22 | ".DS_Store", 23 | "next-env.d.ts", 24 | "package-lock.json", 25 | "yarn.lock", 26 | "pnpm-lock.yaml", 27 | "bun.lockb", 28 | "package.json", 29 | "tailwind.config.ts", 30 | "tailwind.config.js", 31 | "components.json", 32 | "favicon.ico", 33 | ]; 34 | 35 | function cloneInitialCommit() { 36 | deleteInitialDir(); 37 | 38 | try { 39 | // Get the initial commit hash 40 | const initialCommit = execSync("git rev-list --max-parents=0 HEAD") 41 | .toString() 42 | .trim(); 43 | 44 | // Clone the initial commit quietly 45 | execSync(`git worktree add -f ${INITIAL_DIR} ${initialCommit}`, { 46 | stdio: "ignore", 47 | }); 48 | } catch (error) { 49 | console.error("Error cloning initial commit:", error.message); 50 | process.exit(1); 51 | } 52 | } 53 | 54 | function deleteInitialDir() { 55 | if (fs.existsSync(INITIAL_DIR)) { 56 | fs.rmSync(INITIAL_DIR, { recursive: true }); 57 | 58 | try { 59 | execSync("git worktree prune", { stdio: "ignore" }); 60 | } catch (error) { 61 | console.error("Error pruning git worktree:", error.message); 62 | } 63 | } 64 | } 65 | 66 | function checkIfFileIsChanged(relativeFilePath) { 67 | const initialFilePath = path.join(INITIAL_DIR, relativeFilePath); 68 | const fullPath = path.join(process.cwd(), relativeFilePath); 69 | if (!fs.existsSync(initialFilePath)) { 70 | return true; // New file 71 | } 72 | const currentContent = fs.readFileSync(fullPath, "utf-8"); 73 | const initialContent = fs.readFileSync(initialFilePath, "utf-8"); 74 | return currentContent !== initialContent; 75 | } 76 | 77 | export function scanForFiles(startDir, checkFile = false) { 78 | const foundFiles = []; 79 | 80 | let ignorer = () => false; 81 | if (fs.existsSync(path.join(startDir, ".gitignore"))) { 82 | const gitIgnore = ignore().add( 83 | fs.readFileSync(path.join(startDir, ".gitignore")).toString() 84 | ); 85 | ignorer = (relativeFilePath) => { 86 | return gitIgnore.ignores(relativeFilePath); 87 | }; 88 | } 89 | 90 | function scanDirectory(dir, relativePath = "") { 91 | const entries = fs.readdirSync(dir, { withFileTypes: true }); 92 | 93 | for (const entry of entries) { 94 | const fullPath = path.join(dir, entry.name); 95 | const relativeFilePath = path.join(relativePath, entry.name); 96 | 97 | if (entry.isDirectory()) { 98 | if (!EXCLUDE_DIRS.includes(entry.name)) { 99 | scanDirectory(path.join(dir, entry.name), relativeFilePath); 100 | } 101 | } else if ( 102 | !checkFile || 103 | (checkFile && checkIfFileIsChanged(relativeFilePath)) 104 | ) { 105 | if (!EXCLUDE_FILES.includes(entry.name) && !ignorer(relativeFilePath)) { 106 | foundFiles.push({ 107 | path: relativeFilePath, 108 | content: fs.readFileSync(fullPath, "utf-8"), 109 | }); 110 | } 111 | } 112 | } 113 | } 114 | 115 | scanDirectory(startDir); 116 | 117 | return foundFiles; 118 | } 119 | 120 | export function scanForAlteredFiles(specificFilesToReturn = []) { 121 | cloneInitialCommit(); 122 | 123 | const alteredFiles = scanForFiles(process.cwd(), true); 124 | 125 | const specificFiles = specificFilesToReturn.reduce((out, file) => { 126 | const fullPath = path.join(process.cwd(), INITIAL_DIR, file); 127 | out[file] = fs.readFileSync(fullPath, "utf-8"); 128 | return out; 129 | }, {}); 130 | 131 | deleteInitialDir(); 132 | 133 | return { 134 | alteredFiles, 135 | specificFiles, 136 | }; 137 | } 138 | 139 | export function hasSrcDir(dir) { 140 | return fs.existsSync(path.join(dir, "src")); 141 | } 142 | -------------------------------------------------------------------------------- /src/parse-file-path.mjs: -------------------------------------------------------------------------------- 1 | function fixAlias(alias) { 2 | return alias.replace("@", "."); 3 | } 4 | 5 | export function parseFilePath(wasInSrcDir, config, filePath, content) { 6 | const out = { 7 | path: filePath, 8 | content, 9 | type: "registry:example", 10 | target: wasInSrcDir ? filePath : `~/${filePath.replace("./", "")}`, 11 | }; 12 | 13 | if (filePath.startsWith(fixAlias(config.ui))) { 14 | out.type = "registry:ui"; 15 | out.target = undefined; 16 | } else if (filePath.startsWith(fixAlias(config.components))) { 17 | out.type = "registry:block"; 18 | out.target = undefined; 19 | } else if (filePath.startsWith(fixAlias(config.hooks))) { 20 | out.type = "registry:hook"; 21 | out.target = undefined; 22 | } else if (filePath.startsWith(fixAlias(config.lib))) { 23 | out.type = "registry:lib"; 24 | out.target = undefined; 25 | } 26 | 27 | if (out.type === "registry:example") { 28 | out.path = filePath; 29 | } 30 | 31 | return out; 32 | } 33 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/.env.example: -------------------------------------------------------------------------------- 1 | example env; -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/app/page.tsx: -------------------------------------------------------------------------------- 1 | page; 2 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/components/comp.tsx: -------------------------------------------------------------------------------- 1 | comp; 2 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/components/ui/new-ui-comp.tsx: -------------------------------------------------------------------------------- 1 | new-ui-comp; -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | select; 2 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/hooks/hook.ts: -------------------------------------------------------------------------------- 1 | hook; 2 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/lib/lib.ts: -------------------------------------------------------------------------------- 1 | lib; 2 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/non-src-sub/test.ts: -------------------------------------------------------------------------------- 1 | non - src - sub; 2 | -------------------------------------------------------------------------------- /tests/fixtures/non-src-dir/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-avatar": "^1.1.0", 13 | "@radix-ui/react-collapsible": "^1.1.0", 14 | "@radix-ui/react-dialog": "^1.1.1", 15 | "@radix-ui/react-dropdown-menu": "^2.1.1", 16 | "@radix-ui/react-icons": "^1.3.0", 17 | "@radix-ui/react-popover": "^1.1.1", 18 | "@radix-ui/react-progress": "^1.1.0", 19 | "@radix-ui/react-separator": "^1.1.0", 20 | "@radix-ui/react-slot": "^1.1.0", 21 | "@workos-inc/authkit-nextjs": "^0.10.1", 22 | "class-variance-authority": "^0.7.0", 23 | "clsx": "^2.1.1", 24 | "lucide-react": "^0.441.0", 25 | "next": "14.2.11", 26 | "react": "^18", 27 | "react-dom": "^18", 28 | "tailwind-merge": "^2.5.2", 29 | "tailwindcss-animate": "^1.0.7", 30 | "vaul": "^0.9.3" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^20", 34 | "@types/react": "^18", 35 | "@types/react-dom": "^18", 36 | "eslint": "^8", 37 | "eslint-config-next": "14.2.11", 38 | "postcss": "^8", 39 | "tailwindcss": "^3.4.1", 40 | "typescript": "^5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/.env.example: -------------------------------------------------------------------------------- 1 | example env; -------------------------------------------------------------------------------- /tests/fixtures/src-dir/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/non-src-sub/test.ts: -------------------------------------------------------------------------------- 1 | non - src - sub; 2 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-avatar": "^1.1.0", 13 | "@radix-ui/react-collapsible": "^1.1.0", 14 | "@radix-ui/react-dialog": "^1.1.1", 15 | "@radix-ui/react-dropdown-menu": "^2.1.1", 16 | "@radix-ui/react-icons": "^1.3.0", 17 | "@radix-ui/react-popover": "^1.1.1", 18 | "@radix-ui/react-progress": "^1.1.0", 19 | "@radix-ui/react-separator": "^1.1.0", 20 | "@radix-ui/react-slot": "^1.1.0", 21 | "@workos-inc/authkit-nextjs": "^0.10.1", 22 | "class-variance-authority": "^0.7.0", 23 | "clsx": "^2.1.1", 24 | "lucide-react": "^0.441.0", 25 | "next": "14.2.11", 26 | "react": "^18", 27 | "react-dom": "^18", 28 | "tailwind-merge": "^2.5.2", 29 | "tailwindcss-animate": "^1.0.7", 30 | "vaul": "^0.9.3" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^20", 34 | "@types/react": "^18", 35 | "@types/react-dom": "^18", 36 | "eslint": "^8", 37 | "eslint-config-next": "14.2.11", 38 | "postcss": "^8", 39 | "tailwindcss": "^3.4.1", 40 | "typescript": "^5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | page; 2 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/src/components/comp.tsx: -------------------------------------------------------------------------------- 1 | comp; 2 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/src/components/ui/new-ui-comp.tsx: -------------------------------------------------------------------------------- 1 | new-ui-comp; -------------------------------------------------------------------------------- /tests/fixtures/src-dir/src/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | select; 2 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/src/hooks/hook.ts: -------------------------------------------------------------------------------- 1 | hook; 2 | -------------------------------------------------------------------------------- /tests/fixtures/src-dir/src/lib/lib.ts: -------------------------------------------------------------------------------- 1 | lib; 2 | -------------------------------------------------------------------------------- /tests/tests/__snapshots__/test-fixtures.spec.mjs.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`fixture tests > should diff a non-src directory project 1`] = ` 4 | { 5 | "cssVars": {}, 6 | "dependencies": [], 7 | "devDependencies": [], 8 | "files": [ 9 | { 10 | "content": "example env;", 11 | "path": "./.env.example", 12 | "target": "~/.env.example", 13 | "type": "registry:example", 14 | }, 15 | { 16 | "content": "page; 17 | ", 18 | "path": "./app/page.tsx", 19 | "target": "./app/page.tsx", 20 | "type": "registry:example", 21 | }, 22 | { 23 | "content": "comp; 24 | ", 25 | "path": "./components/comp.tsx", 26 | "target": undefined, 27 | "type": "registry:block", 28 | }, 29 | { 30 | "content": "new-ui-comp;", 31 | "path": "./components/ui/new-ui-comp.tsx", 32 | "target": undefined, 33 | "type": "registry:ui", 34 | }, 35 | { 36 | "content": "hook; 37 | ", 38 | "path": "./hooks/hook.ts", 39 | "target": undefined, 40 | "type": "registry:hook", 41 | }, 42 | { 43 | "content": "lib; 44 | ", 45 | "path": "./lib/lib.ts", 46 | "target": undefined, 47 | "type": "registry:lib", 48 | }, 49 | { 50 | "content": "non - src - sub; 51 | ", 52 | "path": "./non-src-sub/test.ts", 53 | "target": "~/non-src-sub/test.ts", 54 | "type": "registry:example", 55 | }, 56 | ], 57 | "meta": {}, 58 | "name": "example", 59 | "registryDependencies": [ 60 | "select", 61 | ], 62 | "tailwind": {}, 63 | "type": "registry:block", 64 | } 65 | `; 66 | 67 | exports[`fixture tests > should diff a src directory project 1`] = ` 68 | { 69 | "cssVars": {}, 70 | "dependencies": [], 71 | "devDependencies": [], 72 | "files": [ 73 | { 74 | "content": "example env;", 75 | "path": "./.env.example", 76 | "target": "~/.env.example", 77 | "type": "registry:example", 78 | }, 79 | { 80 | "content": "non - src - sub; 81 | ", 82 | "path": "./non-src-sub/test.ts", 83 | "target": "~/non-src-sub/test.ts", 84 | "type": "registry:example", 85 | }, 86 | { 87 | "content": "page; 88 | ", 89 | "path": "./app/page.tsx", 90 | "target": "./app/page.tsx", 91 | "type": "registry:example", 92 | }, 93 | { 94 | "content": "comp; 95 | ", 96 | "path": "./components/comp.tsx", 97 | "target": undefined, 98 | "type": "registry:block", 99 | }, 100 | { 101 | "content": "new-ui-comp;", 102 | "path": "./components/ui/new-ui-comp.tsx", 103 | "target": undefined, 104 | "type": "registry:ui", 105 | }, 106 | { 107 | "content": "hook; 108 | ", 109 | "path": "./hooks/hook.ts", 110 | "target": undefined, 111 | "type": "registry:hook", 112 | }, 113 | { 114 | "content": "lib; 115 | ", 116 | "path": "./lib/lib.ts", 117 | "target": undefined, 118 | "type": "registry:lib", 119 | }, 120 | ], 121 | "meta": {}, 122 | "name": "example", 123 | "registryDependencies": [ 124 | "select", 125 | ], 126 | "tailwind": {}, 127 | "type": "registry:block", 128 | } 129 | `; 130 | -------------------------------------------------------------------------------- /tests/tests/parse-file-path.spec.mjs: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import { parseFilePath } from "../../src/parse-file-path.mjs"; 3 | 4 | const CONFIG_IN_SRC = { 5 | components: "@/components", 6 | utils: "@/lib/utils", 7 | ui: "@/components/ui", 8 | lib: "@/lib", 9 | hooks: "@/hooks", 10 | isSrcDir: true, 11 | }; 12 | const CONFIG = { 13 | ...CONFIG_IN_SRC, 14 | isSrcDir: false, 15 | }; 16 | 17 | const trim = ({ path, type, target }) => ({ 18 | path, 19 | type, 20 | target, 21 | }); 22 | 23 | describe("parseFilePath", () => { 24 | test("should handle a new component in a src directory", () => { 25 | expect( 26 | trim( 27 | parseFilePath( 28 | true, 29 | CONFIG_IN_SRC, 30 | "./components/spinning-credit-card.tsx" 31 | ) 32 | ) 33 | ).toEqual({ 34 | type: "registry:block", 35 | path: "./components/spinning-credit-card.tsx", 36 | }); 37 | }); 38 | 39 | test("should handle a new component", () => { 40 | expect( 41 | trim( 42 | parseFilePath(false, CONFIG, "./components/spinning-credit-card.tsx") 43 | ) 44 | ).toEqual({ 45 | type: "registry:block", 46 | path: "./components/spinning-credit-card.tsx", 47 | }); 48 | }); 49 | 50 | test("should handle a new page in a src directory", () => { 51 | expect(trim(parseFilePath(true, CONFIG_IN_SRC, "./app/page.tsx"))).toEqual({ 52 | type: "registry:example", 53 | target: "./app/page.tsx", 54 | path: "./app/page.tsx", 55 | }); 56 | }); 57 | 58 | test("should handle a new page in an app directory", () => { 59 | expect(trim(parseFilePath(true, CONFIG, "./app/page.tsx"))).toEqual({ 60 | type: "registry:example", 61 | target: "./app/page.tsx", 62 | path: "./app/page.tsx", 63 | }); 64 | }); 65 | 66 | test("should handle an environment file with a src directory", () => { 67 | expect(trim(parseFilePath(false, CONFIG_IN_SRC, "./.env.example"))).toEqual( 68 | { 69 | type: "registry:example", 70 | target: "~/.env.example", 71 | path: "./.env.example", 72 | } 73 | ); 74 | }); 75 | 76 | test("should handle an environment file", () => { 77 | expect(trim(parseFilePath(false, CONFIG, "./.env.example"))).toEqual({ 78 | type: "registry:example", 79 | target: "~/.env.example", 80 | path: "./.env.example", 81 | }); 82 | }); 83 | 84 | test("should handle a new hook", () => { 85 | expect( 86 | trim( 87 | parseFilePath( 88 | false, 89 | CONFIG_IN_SRC, 90 | "./hooks/use-spinning-credit-card.ts" 91 | ) 92 | ) 93 | ).toEqual({ 94 | type: "registry:hook", 95 | path: "./hooks/use-spinning-credit-card.ts", 96 | }); 97 | }); 98 | 99 | test("should handle a new library file", () => { 100 | expect(trim(parseFilePath(true, CONFIG_IN_SRC, "./lib/utils.ts"))).toEqual({ 101 | type: "registry:lib", 102 | path: "./lib/utils.ts", 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /tests/tests/test-fixtures.spec.mjs: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from "vitest"; 2 | import fs from "node:fs"; 3 | import path from "node:path"; 4 | 5 | import { scanForFiles, hasSrcDir } from "../../src/git.mjs"; 6 | import { readComponentsManifest } from "../../src/components.mjs"; 7 | import { createDiff } from "../../src/create-diff.mjs"; 8 | 9 | const EXPECTED_FILES = [ 10 | { 11 | path: "./.env.example", 12 | content: "example env;", 13 | type: "registry:example", 14 | target: "~/.env.example", 15 | }, 16 | { 17 | path: "./non-src-sub/test.ts", 18 | content: "non - src - sub;\n", 19 | type: "registry:example", 20 | target: "~/non-src-sub/test.ts", 21 | }, 22 | { 23 | path: "./app/page.tsx", 24 | content: "page;\n", 25 | type: "registry:example", 26 | target: "./app/page.tsx", 27 | }, 28 | { 29 | path: "./components/comp.tsx", 30 | content: "comp;\n", 31 | type: "registry:block", 32 | target: undefined, 33 | }, 34 | { 35 | path: "./components/ui/new-ui-comp.tsx", 36 | content: "new-ui-comp;", 37 | type: "registry:ui", 38 | target: undefined, 39 | }, 40 | { 41 | path: "./hooks/hook.ts", 42 | content: "hook;\n", 43 | type: "registry:hook", 44 | target: undefined, 45 | }, 46 | { 47 | path: "./lib/lib.ts", 48 | content: "lib;\n", 49 | type: "registry:lib", 50 | target: undefined, 51 | }, 52 | ]; 53 | 54 | function createDiffRequest(dir) { 55 | const currentFiles = scanForFiles(dir); 56 | 57 | const config = readComponentsManifest(dir); 58 | config.isSrcDir = hasSrcDir(dir); 59 | 60 | const specificFiles = { 61 | "./package.json": fs.readFileSync( 62 | path.join(dir, "./package.json"), 63 | "utf-8" 64 | ), 65 | }; 66 | 67 | return { 68 | name: "example", 69 | config, 70 | alteredFiles: currentFiles, 71 | specificFiles, 72 | currentFiles, 73 | currentPackageJson: specificFiles["./package.json"], 74 | }; 75 | } 76 | 77 | describe("fixture tests", () => { 78 | test("should diff a src directory project", () => { 79 | const diff = createDiff(createDiffRequest("./tests/fixtures/src-dir")); 80 | expect(diff).toMatchSnapshot(); 81 | }); 82 | test("should diff a non-src directory project", () => { 83 | const diff = createDiff(createDiffRequest("./tests/fixtures/non-src-dir")); 84 | expect(diff).toMatchSnapshot(); 85 | }); 86 | }); 87 | --------------------------------------------------------------------------------