├── .gitattributes ├── .gitignore ├── .release-it.json ├── CHANGELOG.md ├── README.md ├── eslint.config.mjs ├── index.js ├── package.json ├── pnpm-lock.yaml ├── scripts └── build.js ├── src ├── index.ts └── utils │ ├── cli │ ├── postinstall │ │ ├── index.ts │ │ └── pnpm.ts │ └── preinstall │ │ ├── index.ts │ │ └── yarn.ts │ ├── deepMerge.ts │ ├── index.ts │ ├── installDependencies.ts │ ├── nuxt │ ├── renderNuxtTemplate.ts │ ├── types.ts │ ├── utils.ts │ └── versions.ts │ ├── presets.ts │ ├── prompts.ts │ └── renderTemplate.ts ├── template ├── javascript │ ├── base │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src │ │ │ ├── App.vue │ │ │ ├── components │ │ │ │ └── HelloWorld.vue │ │ │ ├── pages │ │ │ │ ├── README.md │ │ │ │ └── index.vue │ │ │ ├── plugins │ │ │ │ └── index.js │ │ │ ├── router │ │ │ │ └── index.js │ │ │ └── styles │ │ │ │ ├── README.md │ │ │ │ └── settings.scss │ │ └── vite.config.mjs │ ├── default │ │ ├── README.md │ │ ├── _browserslistrc │ │ ├── _editorconfig │ │ ├── _gitignore │ │ ├── index.html │ │ ├── jsconfig.json │ │ ├── package.json │ │ ├── public │ │ │ └── favicon.ico │ │ ├── src │ │ │ ├── App.vue │ │ │ ├── assets │ │ │ │ ├── logo.png │ │ │ │ └── logo.svg │ │ │ ├── components │ │ │ │ ├── AppFooter.vue │ │ │ │ ├── HelloWorld.vue │ │ │ │ └── README.md │ │ │ ├── main.js │ │ │ └── plugins │ │ │ │ ├── README.md │ │ │ │ ├── index.js │ │ │ │ └── vuetify.js │ │ └── vite.config.mjs │ └── essentials │ │ ├── _eslintrc-auto-import.json │ │ ├── package.json │ │ ├── src │ │ ├── App.vue │ │ ├── layouts │ │ │ ├── README.md │ │ │ └── default.vue │ │ ├── plugins │ │ │ └── index.js │ │ ├── router │ │ │ └── index.js │ │ └── stores │ │ │ ├── README.md │ │ │ ├── app.js │ │ │ └── index.js │ │ └── vite.config.mjs └── typescript │ ├── base │ ├── env.d.ts │ ├── eslint.config.js │ ├── package.json │ ├── src │ │ ├── App.vue │ │ ├── components │ │ │ └── HelloWorld.vue │ │ ├── pages │ │ │ ├── README.md │ │ │ └── index.vue │ │ ├── plugins │ │ │ └── index.ts │ │ ├── router │ │ │ └── index.ts │ │ └── styles │ │ │ ├── README.md │ │ │ └── settings.scss │ └── vite.config.mts │ ├── default │ ├── README.md │ ├── _browserslistrc │ ├── _editorconfig │ ├── _gitignore │ ├── env.d.ts │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ ├── logo.png │ │ │ └── logo.svg │ │ ├── components │ │ │ ├── HelloWorld.vue │ │ │ └── README.md │ │ ├── main.ts │ │ └── plugins │ │ │ ├── README.md │ │ │ ├── index.ts │ │ │ └── vuetify.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.mts │ ├── essentials │ ├── _eslintrc-auto-import.json │ ├── env.d.ts │ ├── package.json │ ├── src │ │ ├── App.vue │ │ ├── auto-imports.d.ts │ │ ├── components.d.ts │ │ ├── components │ │ │ └── AppFooter.vue │ │ ├── layouts │ │ │ ├── README.md │ │ │ └── default.vue │ │ ├── plugins │ │ │ └── index.ts │ │ ├── router │ │ │ └── index.ts │ │ ├── stores │ │ │ ├── README.md │ │ │ ├── app.ts │ │ │ └── index.ts │ │ └── typed-router.d.ts │ └── vite.config.mts │ └── nuxt │ ├── app-layout.vue │ ├── app.vue │ ├── assets │ ├── logo.png │ └── logo.svg │ ├── components │ ├── AppFooter.vue │ └── HelloWorld.vue │ ├── layouts │ └── default.vue │ ├── modules │ └── vuetify.ts │ ├── pages │ └── index.vue │ ├── plugins │ ├── vuetify-nuxt.ts │ └── vuetify.ts │ └── vuetify.config.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | dist 4 | 5 | vuetify-project 6 | 7 | .idea 8 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore: release v${version}", 4 | "tagName": "v${version}", 5 | "tagAnnotation": "Release v${version}", 6 | "push": true 7 | }, 8 | "github": { 9 | "release": true, 10 | "tokenRef": "GITHUB_TOKEN" 11 | }, 12 | "npm": { 13 | "publish": true 14 | }, 15 | "plugins": { 16 | "@release-it/conventional-changelog": { 17 | "preset": "angular", 18 | "infile": "CHANGELOG.md" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.5.2](https://github.com/vuetifyjs/create-vuetify/compare/v2.5.1...v2.5.2) (2025-05-31) 4 | 5 | * use eslint-config-vuetify v4 ([#74](https://github.com/vuetifyjs/create-vuetify/issues/74)) ([53ac15b](https://github.com/vuetifyjs/create-vuetify/commit/53ac15bb749ef9b9e07e29325a0f9eaba3511d13)) 6 | 7 | ## [2.5.1](https://github.com/vuetifyjs/create-vuetify/compare/v2.5.0...v2.5.1) (2025-04-27) 8 | 9 | # [2.5.0](https://github.com/vuetifyjs/create-vuetify/compare/v2.4.0...v2.5.0) (2025-04-27) 10 | 11 | 12 | ### Bug Fixes 13 | 14 | * add @vue/compiler-sfc as dependency for yarn 2+ ([#73](https://github.com/vuetifyjs/create-vuetify/issues/73)) ([64dc19a](https://github.com/vuetifyjs/create-vuetify/commit/64dc19a10a6a9e3a4736f03dc4bc91bd68b968c7)) 15 | 16 | 17 | ### Features 18 | 19 | * use fontsource for roboto font ([#69](https://github.com/vuetifyjs/create-vuetify/issues/69)) ([a07c735](https://github.com/vuetifyjs/create-vuetify/commit/a07c7357a3b61c19f20aea90696f7e4dbe93e576)) 20 | 21 | # [2.4.0](https://github.com/vuetifyjs/create-vuetify/compare/v2.3.1...v2.4.0) (2025-04-10) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * await deps installation ([799128d](https://github.com/vuetifyjs/create-vuetify/commit/799128d2812ab1ba12d9a25761e238c0b85d5ecb)) 27 | * exit cli if directory is not overwritten ([80574d7](https://github.com/vuetifyjs/create-vuetify/commit/80574d77c79fb7575383288607a3d7aa8f06b8db)) 28 | * **HelloWorld:** modernize & cleanup HelloWorld ([#67](https://github.com/vuetifyjs/create-vuetify/issues/67)) ([0fd9089](https://github.com/vuetifyjs/create-vuetify/commit/0fd9089ec3bc25198f73d67a3d74bac7c7d3a4c3)) 29 | * provide logos in nuxt template ([622cba3](https://github.com/vuetifyjs/create-vuetify/commit/622cba309ad91487ae7b8055e8f4c1f08a580c0a)) 30 | 31 | 32 | ### Features 33 | 34 | * add pinia functions to auto-import config ([d7ff19a](https://github.com/vuetifyjs/create-vuetify/commit/d7ff19aeb0c9eda689d3c0613da68be38c9ab3e2)) 35 | 36 | # [2.3.0](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.6...v2.3.0) (2024-11-16) 37 | 38 | 39 | ### Features 40 | 41 | * add modern sass vite options ([#60](https://github.com/vuetifyjs/create-vuetify/issues/60)) ([c8ef277](https://github.com/vuetifyjs/create-vuetify/commit/c8ef2779cde65ffa5ada7c53c4a1e3c36b8f893f)) 42 | * add Nuxt template ([#61](https://github.com/vuetifyjs/create-vuetify/issues/61)) ([b75e547](https://github.com/vuetifyjs/create-vuetify/commit/b75e547360a3b1835d4f5509da1386b02a181921)) 43 | * update to latest vue install defaults ([#62](https://github.com/vuetifyjs/create-vuetify/issues/62)) ([d44e563](https://github.com/vuetifyjs/create-vuetify/commit/d44e5636e391128309ffd9eec2ec471024fea69f)) 44 | 45 | ## [2.2.6](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.5...v2.2.6) (2024-07-10) 46 | 47 | 48 | ### Bug Fixes 49 | 50 | * **AppFooter:** add missing discord icon ([32f8e7c](https://github.com/vuetifyjs/create-vuetify/commit/32f8e7cf62cb0a2a220d58b8e69e06a7ca74fad8)) 51 | * **router:** add workaround for dynamic import error ([fa4d655](https://github.com/vuetifyjs/create-vuetify/commit/fa4d655debeddc68af816aee22606e7e8fe14b0a)) 52 | * **sass:** avoid deprecation warning by pinning version ([6d5cd73](https://github.com/vuetifyjs/create-vuetify/commit/6d5cd735bb799403386e1c5d4e27623bdf762496)) 53 | 54 | ## [2.2.5](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.4...v2.2.5) (2024-06-30) 55 | 56 | 57 | ### Bug Fixes 58 | 59 | * **tsconfig:** only apply layout types in essentials preset ([96079b0](https://github.com/vuetifyjs/create-vuetify/commit/96079b0e1b0acaa2763426f1eff5d42bd254d82c)) 60 | * **tsconfig:** only apply router types in base preset ([b803f19](https://github.com/vuetifyjs/create-vuetify/commit/b803f19814f9d6455042b067b21e787d9be341eb)) 61 | 62 | ## [2.2.4](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.3...v2.2.4) (2024-06-07) 63 | 64 | ## [2.2.3](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.2...v2.2.3) (2024-04-24) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * **prompts:** make install dependencies default ([5fcdb16](https://github.com/vuetifyjs/create-vuetify/commit/5fcdb1647feb32dea7d91a0d030c4f0915110233)) 70 | * **templates:** update language for HelloWorld in default install ([91e3ff9](https://github.com/vuetifyjs/create-vuetify/commit/91e3ff90054ccafdc41a442cd97cb3db5be50763)) 71 | 72 | ## [2.2.2](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.1...v2.2.2) (2024-04-16) 73 | 74 | 75 | ### Bug Fixes 76 | 77 | * **prompts:** remove unused functionality ([2fa2d24](https://github.com/vuetifyjs/create-vuetify/commit/2fa2d24a6fff585f4ad762f98f4efbd86861f07e)) 78 | 79 | ## [2.2.1](https://github.com/vuetifyjs/create-vuetify/compare/v2.2.0...v2.2.1) (2024-03-20) 80 | 81 | 82 | ### Bug Fixes 83 | 84 | * **javascript:** update target vue/vuetify versions ([d68cd3c](https://github.com/vuetifyjs/create-vuetify/commit/d68cd3c5cee37a5e4d08ea6cdbb0144fbb9ec211)) 85 | * vue-router when using base preset ([#47](https://github.com/vuetifyjs/create-vuetify/issues/47)) ([edf8d10](https://github.com/vuetifyjs/create-vuetify/commit/edf8d108991995bb79a4ce546d86090b6c6f212e)) 86 | 87 | # [2.2.0](https://github.com/vuetifyjs/create-vuetify/compare/v2.1.2...v2.2.0) (2024-03-11) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * **javascript:** swap router files ([3959c62](https://github.com/vuetifyjs/create-vuetify/commit/3959c623029260d644fe503389f365f9d9c97bac)) 93 | 94 | 95 | ### Features 96 | 97 | * **HelloWorld:** update design and add footer ([08a2e1d](https://github.com/vuetifyjs/create-vuetify/commit/08a2e1ddbffa3c7d7d67f81e120a41fa813a5c86)) 98 | 99 | ## [2.1.2](https://github.com/vuetifyjs/create-vuetify/compare/v2.1.1...v2.1.2) (2024-03-10) 100 | 101 | 102 | ### Bug Fixes 103 | 104 | * **js/ts:** upgrade all packages, fix types across all package managers ([7bb5d6d](https://github.com/vuetifyjs/create-vuetify/commit/7bb5d6d3bda670cf16a7d4f5fdcd81bbe396666b)), closes [#42](https://github.com/vuetifyjs/create-vuetify/issues/42) 105 | * **vite.config:** correct autoimport name space ([6dada34](https://github.com/vuetifyjs/create-vuetify/commit/6dada34ad922a3995ce4bab5c3e309c04520ca96)) 106 | 107 | ## [2.1.1](https://github.com/vuetifyjs/create-vuetify/compare/v2.1.0...v2.1.1) (2024-02-29) 108 | 109 | 110 | ### Bug Fixes 111 | 112 | * **default:** don't suppress warning in dev script ([105c901](https://github.com/vuetifyjs/create-vuetify/commit/105c901ae061bdbeae81346269e49dffd9a7d189)) 113 | 114 | # [2.1.0](https://github.com/vuetifyjs/create-vuetify/compare/v2.0.0...v2.1.0) (2024-01-29) 115 | 116 | 117 | ### Bug Fixes 118 | 119 | * **package scripts:** adjust how JSON import warning is suppressed ([2e62807](https://github.com/vuetifyjs/create-vuetify/commit/2e6280702ce7d7cfeb1885f5fda8ce75378844b9)) 120 | 121 | 122 | ### Features 123 | 124 | * **essentials:** add auto import plugin ([0a710a5](https://github.com/vuetifyjs/create-vuetify/commit/0a710a525742dcff39aa265238003ea2e36a32d6)) 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | Vuetify One Logo 5 | 6 |

7 | 8 | [![npm version](https://img.shields.io/npm/v/create-vuetify.svg)](https://www.npmjs.com/package/create-vuetify) 9 | [![npm downloads](https://img.shields.io/npm/dm/create-vuetify?color=blue)](https://npm.chart.dev/create-vuetify) 10 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 11 | 12 | Scaffold your next Vuetify application 13 | 14 | ## 💿 Getting Started 15 | 16 | Using your terminal, navigate to your sites directory and run the following command: 17 | 18 | **Using Yarn:** 19 | ```bash 20 | yarn create vuetify 21 | ``` 22 | 23 | **Using NPM:** 24 | ```bash 25 | npm create vuetify@latest 26 | ``` 27 | 28 | **Using PNPM:** 29 | ```bash 30 | pnpm create vuetify 31 | ``` 32 | 33 | **Using Bun:** 34 | ```bash 35 | bun create vuetify 36 | ``` 37 | 38 | ## 🐉 Vuetify 39 | 40 | Vuetify is an Open Source UI Library that is developed exactly according to Material Design spec. Every component is handcrafted to bring you the best possible UI tools to your next great app. The development doesn't stop at the core components outlined in Google's spec. Through the support of community members and sponsors, additional components will be designed and made available for everyone to enjoy. 41 | 42 | The documentation for **Vuetify** is hosted [here](https://vuetifyjs.com/). 43 | 44 | ### 💖 Support Development 45 | 46 | Vuetify is an open source MIT project that has been made possible due to the generous contributions by our [sponsors and backers](https://vuetifyjs.com/introduction/sponsors-and-backers/). If you are interested in supporting this project, please consider: 47 | 48 | - [Requesting Enterprise Support](https://support.vuetifyjs.com/) 49 | - [Sponsoring John on Github](https://github.com/users/johnleider/sponsorship) 50 | - [Sponsoring Kael on Github](https://github.com/users/kaelwd/sponsorship) 51 | - [Supporting the team on Open Collective](https://opencollective.com/vuetify) 52 | - [Becoming a sponsor on Patreon](https://www.patreon.com/vuetify) 53 | - [Becoming a subscriber on Tidelift](https://tidelift.com/subscription/npm/vuetify) 54 | - [Making a one-time donation with Paypal](https://paypal.me/vuetify) 55 | 56 | ## 🌐 Browser Support 57 | 58 | | Browser | Status | 59 | | - | - | 60 | | Chromium (Chrome, Edge) | ✅ Supported * | 61 | | Firefox | ✅ Supported * | 62 | | Safari 15.4+ | ✅ Supported | 63 | | Safari 13. | ❗ Requires polyfill | 64 | | Edge <79 | ⛔ Not supported | 65 | | Internet Explorer | ⛔ Not supported | 66 | 67 | ## 📃 Resources 68 | 69 | - 📄 [Documentation](https://vuetifyjs.com/) 70 | - 🚨 [Report an Issue](https://issues.vuetifyjs.com/) 71 | - 🏬 [Vuetify Store](https://store.vuetifyjs.com/) 72 | - 🗑️ [Vuetify Bin](https://bin.vuetifyjs.com/) 73 | - 🎮 [Vuetify Playground](https://play.vuetifyjs.com/) 74 | - 💬 [Discord](https://community.vuetifyjs.com/) 75 | 76 | ## ⭐ Licensing 77 | 78 | - Copyright 2023 Vuetify 79 | - Vuetify [License Information](https://github.com/vuetifyjs/vuetify/blob/master/LICENSE.md) 80 | 81 |
82 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import vuetify from 'eslint-config-vuetify' 2 | 3 | export default vuetify({ 4 | vue: true, 5 | perfectionist: { 6 | import: false, 7 | }, 8 | }, { 9 | rules: { 10 | 'unicorn/no-process-exit': 'off', 11 | }, 12 | }) 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import './dist/index.mjs' 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-vuetify", 3 | "version": "2.5.2", 4 | "author": "Elijah Kotyluk ", 5 | "license": "MIT", 6 | "type": "module", 7 | "bin": { 8 | "create-vuetify": "index.js" 9 | }, 10 | "files": [ 11 | "index.js", 12 | "template", 13 | "dist" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/vuetifyjs/create-vuetify.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/vuetifyjs/create-vuetify/issues" 21 | }, 22 | "scripts": { 23 | "build": "node ./scripts/build.js", 24 | "start": "node ./index.js", 25 | "dev": "node --run build && node --run start", 26 | "lint": "eslint --fix .", 27 | "prepublishOnly": "node --run build" 28 | }, 29 | "dependencies": { 30 | "kolorist": "^1.8.0", 31 | "magicast": "^0.3.5", 32 | "minimist": "^1.2.8", 33 | "package-manager-detector": "^1.2.0", 34 | "prompts": "^2.4.2", 35 | "tinyexec": "^1.0.1", 36 | "validate-npm-package-name": "^6.0.0" 37 | }, 38 | "devDependencies": { 39 | "@release-it/conventional-changelog": "^10.0.0", 40 | "@types/minimist": "^1.2.5", 41 | "@types/node": "^22.13.10", 42 | "@types/prompts": "^2.4.9", 43 | "@types/validate-npm-package-name": "^4.0.2", 44 | "esbuild": "^0.25.1", 45 | "eslint": "^9.23.0", 46 | "eslint-config-vuetify": "4.0.0", 47 | "nypm": "^0.6.0", 48 | "release-it": "^18.1.2", 49 | "typescript": "^5.8.2" 50 | }, 51 | "packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228" 52 | } 53 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | import { build } from 'esbuild' 2 | 3 | async function bundleMain () { 4 | await build({ 5 | bundle: true, 6 | entryPoints: ['src/index.ts'], 7 | outfile: 'dist/index.mjs', 8 | format: 'esm', 9 | platform: 'node', 10 | target: 'node18', 11 | external: ['package-browser-detector', 'magicast', 'validate-npm-package-name', 'kolorist', 'minimist', 'prompts'], 12 | }) 13 | } 14 | 15 | async function bundle () { 16 | await bundleMain() 17 | } 18 | 19 | bundle() 20 | .catch(error => { 21 | console.error(error) 22 | process.exit(1) 23 | }) 24 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Node 2 | import { dirname, join, resolve } from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | import { mkdirSync, rmSync, writeFileSync } from 'node:fs' 5 | 6 | // Types 7 | import type { ContextState } from './utils/prompts' 8 | import type { NuxtPresetName } from './utils/presets' 9 | 10 | // Utils 11 | import { initPrompts } from './utils/prompts' 12 | import { red } from 'kolorist' 13 | import minimist from 'minimist' 14 | import { installDependencies, renderTemplate } from './utils' 15 | import { renderNuxtTemplate } from './utils/nuxt/renderNuxtTemplate' 16 | 17 | const validPresets = ['base', 'custom', 'default', 'essentials'] 18 | 19 | async function run () { 20 | const argv = minimist(process.argv.slice(2), { 21 | alias: { 22 | typescript: ['ts'], 23 | }, 24 | }) 25 | 26 | if (argv.preset && !validPresets.includes(argv.preset)) { 27 | throw new Error(`'${argv.preset}' is not a valid preset. Valid presets are: ${validPresets.join(', ')}.`) 28 | } 29 | 30 | const banner = 'Vuetify.js - Material Component Framework for Vue' 31 | 32 | console.log(`\n${banner}\n`) 33 | 34 | const context: ContextState = { 35 | canOverwrite: false, 36 | cwd: process.cwd(), 37 | projectName: 'vuetify-project', 38 | useRouter: false, 39 | useTypeScript: argv.typescript, 40 | usePreset: argv.preset, 41 | useStore: undefined, 42 | usePackageManager: undefined, 43 | } 44 | 45 | const { 46 | canOverwrite, 47 | cwd, 48 | projectName, 49 | useTypeScript, 50 | usePackageManager, 51 | installDependencies: installDeps, 52 | usePreset, 53 | useNuxtV4Compat, 54 | useNuxtModule, 55 | useNuxtSSR, 56 | useNuxtSSRClientHints, 57 | } = await initPrompts(context) 58 | 59 | const projectRoot = join(cwd, projectName) 60 | 61 | if (canOverwrite) { 62 | // Clean dir 63 | rmSync(projectRoot, { recursive: true }) 64 | } 65 | 66 | const preset = context.usePreset ?? usePreset 67 | 68 | if (preset.startsWith('nuxt-')) { 69 | const templateRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../template/typescript') 70 | const templatePath = resolve(dirname(fileURLToPath(import.meta.url)), '../template/typescript/nuxt') 71 | // we are going to run Nuxi CLI that will handle the creation for us 72 | await renderNuxtTemplate({ 73 | cwd, 74 | projectName, 75 | projectRoot, 76 | templateRoot, 77 | templatePath, 78 | nuxtPreset: preset as NuxtPresetName, 79 | useNuxtV4Compat, 80 | useNuxtModule, 81 | useNuxtSSR, 82 | useNuxtSSRClientHints, 83 | }) 84 | } else { 85 | // Create project directory 86 | mkdirSync(projectRoot) 87 | 88 | // Create base package.json 89 | writeFileSync(resolve(projectRoot, 'package.json'), JSON.stringify({ name: projectName }, null, 2)) 90 | 91 | console.log('\n◌ Generating scaffold...') 92 | 93 | const jsOrTs = useTypeScript ? 'typescript' : 'javascript' 94 | const templatePath = resolve(dirname(fileURLToPath(import.meta.url)), '../template', jsOrTs) 95 | 96 | renderTemplate(resolve(templatePath, 'default'), projectRoot) 97 | 98 | if (['base', 'essentials'].includes(usePreset)) { 99 | renderTemplate(resolve(templatePath, 'base'), projectRoot) 100 | } 101 | 102 | if (['essentials', 'recommended'].includes(usePreset)) { 103 | renderTemplate(resolve(templatePath, 'essentials'), projectRoot) 104 | } 105 | 106 | if (usePackageManager && installDeps) { 107 | console.log(`◌ Installing dependencies with ${usePackageManager}...\n`) 108 | await installDependencies(projectRoot, usePackageManager) 109 | } 110 | } 111 | 112 | console.log(`\n${projectName} has been generated at ${projectRoot}\n`) 113 | } 114 | 115 | run() 116 | .then(() => { 117 | console.log('Discord community: https://community.vuetifyjs.com') 118 | console.log('Github: https://github.com/vuetifyjs/vuetify') 119 | console.log('Support Vuetify: https://github.com/sponsors/johnleider') 120 | process.exit(0) 121 | }) 122 | .catch(error => { 123 | console.error(`\n${red('✖')} ${error}\n`) 124 | process.exit(1) 125 | }) 126 | -------------------------------------------------------------------------------- /src/utils/cli/postinstall/index.ts: -------------------------------------------------------------------------------- 1 | export { default as pnpm } from './pnpm' 2 | -------------------------------------------------------------------------------- /src/utils/cli/postinstall/pnpm.ts: -------------------------------------------------------------------------------- 1 | import { x } from 'tinyexec' 2 | 3 | export async function pnpmIgnored (root: string) { 4 | const pnpmVersion = (await x(`pnpm`, ['-v'], { nodeOptions: { cwd: root } })).stdout.trim() 5 | const [major] = pnpmVersion.split('.').map(Number) 6 | if (major && major >= 10) { 7 | const detect = (await x('pnpm', ['ignored-builds'], { nodeOptions: { cwd: root } })).stdout 8 | if (detect.startsWith('Automatically ignored builds during installation:\n None')) { 9 | return 10 | } 11 | return detect 12 | } 13 | } 14 | 15 | export default async function pnpm (root: string) { 16 | const detect = await pnpmIgnored(root) 17 | if (detect) { 18 | console.warn(detect) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/cli/preinstall/index.ts: -------------------------------------------------------------------------------- 1 | export { default as yarn } from './yarn' 2 | -------------------------------------------------------------------------------- /src/utils/cli/preinstall/yarn.ts: -------------------------------------------------------------------------------- 1 | import { x } from 'tinyexec' 2 | import { appendFileSync } from 'node:fs' 3 | import { resolve } from 'node:path' 4 | 5 | const templateToAppend = ` 6 | packageExtensions: 7 | unplugin-vue-router@*: 8 | dependencies: 9 | "@vue/compiler-sfc": "*" 10 | ` 11 | 12 | export async function yarnFile (root: string) { 13 | const pnpmVersion = (await (x('yarn', ['-v'], { nodeOptions: { cwd: root } }))).stdout.trim() 14 | const [major] = pnpmVersion.split('.').map(Number) 15 | if (major && major >= 2) { 16 | appendFileSync(resolve(root, '.yarnrc.yml'), templateToAppend) 17 | } 18 | } 19 | 20 | export default async function yarn (root: string) { 21 | await yarnFile(root) 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/deepMerge.ts: -------------------------------------------------------------------------------- 1 | type GenericObject = { [key: string]: any } 2 | interface DeepMerge { 3 | (...sources: Array): GenericObject 4 | } 5 | 6 | const isObject: { (key: any): boolean } = (v: unknown): boolean => { 7 | return ( 8 | v === Object(v) 9 | && v !== null 10 | && !Array.isArray(v) 11 | ) 12 | } 13 | 14 | const deepMerge: DeepMerge = (...sources: T): GenericObject => 15 | sources.reduce((acc, curr) => { 16 | for (const key of Object.keys(curr)) { 17 | if (Array.isArray(acc[key]) && Array.isArray(curr[key])) { 18 | acc[key] = Array.from(new Set((acc[key]).concat(curr[key]))) 19 | } else if (isObject(acc[key]) && isObject(curr[key])) { 20 | acc[key] = deepMerge(acc[key], curr[key]) 21 | } else { 22 | acc[key] = curr[key] 23 | } 24 | } 25 | 26 | return acc 27 | }, {}) 28 | 29 | export { deepMerge } 30 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { installDependencies } from './installDependencies' 2 | 3 | export { renderTemplate } from './renderTemplate' 4 | -------------------------------------------------------------------------------- /src/utils/installDependencies.ts: -------------------------------------------------------------------------------- 1 | import { installDependencies as installDependencies$1 } from 'nypm' 2 | import { pnpm } from './cli/postinstall' 3 | import { yarn } from './cli/preinstall' 4 | 5 | const userAgent = process.env.npm_config_user_agent ?? '' 6 | 7 | export const packageManager = /bun/.test(userAgent) 8 | ? 'bun' 9 | : 'pnpm' 10 | 11 | export async function installDependencies (root: string = process.cwd(), manager: 'npm' | 'pnpm' | 'yarn' | 'bun' = packageManager) { 12 | if (manager === 'yarn') { 13 | await yarn(root) 14 | } 15 | await installDependencies$1({ 16 | packageManager: manager, 17 | cwd: root, 18 | silent: true, 19 | }) 20 | .catch(() => { 21 | console.error( 22 | `Failed to install dependencies using ${manager}.`, 23 | ) 24 | }) 25 | if (manager === 'pnpm') { 26 | await pnpm(root) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/nuxt/renderNuxtTemplate.ts: -------------------------------------------------------------------------------- 1 | // Node 2 | import fs from 'node:fs' 3 | import path from 'node:path' 4 | import { spawnSync } from 'node:child_process' 5 | 6 | // Types 7 | import type { NuxtContext, PackageJsonEntry } from './types' 8 | 9 | // Utils 10 | import { addPackageObject, detectPkgInfo, editFile, getPaths, runCommand } from './utils' 11 | import { versions } from './versions' 12 | import { detect } from 'package-manager-detector' 13 | import { generateCode, parseModule } from 'magicast' 14 | import { addNuxtModule, getDefaultExportOptions } from 'magicast/helpers' 15 | 16 | export async function renderNuxtTemplate (ctx: NuxtContext) { 17 | const { 18 | cwd, 19 | projectName, 20 | projectRoot, 21 | useNuxtV4Compat, 22 | nuxtPreset, 23 | } = ctx 24 | 25 | const pkgInfo = detectPkgInfo() 26 | const pkgManager = pkgInfo ? pkgInfo.name : 'npm' 27 | const isYarn1 = pkgManager === 'yarn' && pkgInfo?.version.startsWith('1.') 28 | 29 | const customCommand = useNuxtV4Compat 30 | ? `npx nuxi@latest init -t v4-compat ${projectName}` 31 | : `npm exec nuxi init ${projectName}` 32 | 33 | const fullCustomCommand = customCommand 34 | // Only Yarn 1.x doesn't support `@version` in the `create` command 35 | .replace('@latest', () => (isYarn1 ? '' : '@latest')) 36 | .replace(/^npm exec/, () => { 37 | // Prefer `pnpm dlx`, `yarn dlx`, or `bun x` 38 | if (pkgManager === 'pnpm') { 39 | return 'pnpm dlx' 40 | } 41 | 42 | if (pkgManager === 'yarn' && !isYarn1) { 43 | return 'yarn dlx' 44 | } 45 | 46 | if (pkgManager === 'bun') { 47 | return 'bun x' 48 | } 49 | 50 | // Use `npm exec` in all other cases, 51 | // including Yarn 1.x and other custom npm clients. 52 | return 'npm exec' 53 | }) 54 | 55 | const [command, ...args] = fullCustomCommand.split(' ') 56 | 57 | const nuxiCli = spawnSync(command, args, { 58 | cwd, 59 | stdio: ['inherit', 'inherit', 'pipe'], 60 | shell: true, 61 | }) 62 | 63 | if (nuxiCli.error) { 64 | throw nuxiCli.error 65 | } 66 | 67 | // configure package.json 68 | configurePackageJson(ctx) 69 | 70 | const pmDetection = await detect({ cwd: projectRoot }) 71 | 72 | // install dependencies 73 | runCommand(pmDetection, 'install', [], projectRoot) 74 | 75 | // copy/replace resources 76 | prepareProject(ctx) 77 | 78 | // install nuxt eslint: https://eslint.nuxt.com/packages/module#quick-setup 79 | if (nuxtPreset !== 'nuxt-default') { 80 | // we need eslint before executing the prepare command: 81 | // once prepare command run, the eslint.config.mjs file will be created 82 | runCommand(pmDetection, 'execute', ['nuxi', 'module', 'add', 'eslint'], projectRoot) 83 | } 84 | } 85 | 86 | function configurePackageJson ({ 87 | projectName, 88 | projectRoot, 89 | useNuxtModule, 90 | nuxtPreset, 91 | }: NuxtContext) { 92 | const packageJson = path.join(projectRoot, 'package.json') 93 | const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8')) 94 | pkg.name = projectName 95 | 96 | // prepare scripts 97 | const scripts: PackageJsonEntry[] = [ 98 | ['prepare', 'nuxt prepare'], 99 | ['typecheck', 'nuxt typecheck'], 100 | ] 101 | if (nuxtPreset !== 'nuxt-default') { 102 | scripts.push(['lint', 'eslint .']) 103 | scripts.push(['lint:fix', 'eslint . --fix']) 104 | } 105 | 106 | // prepare dependencies 107 | const dependencies: PackageJsonEntry[] = [ 108 | ['vuetify', versions.vuetify], 109 | ] 110 | if (dependencies.length > 0) { 111 | addPackageObject('dependencies', dependencies, pkg) 112 | } 113 | 114 | // prepare devDependencies 115 | const devDependencies: PackageJsonEntry[] = [ 116 | ['@mdi/font', versions['@mdi/font']], 117 | ['@nuxt/fonts', versions['@nuxt/fonts']], 118 | ['sass-embedded', versions['sass-embedded']], 119 | ['typescript', versions.typescript], 120 | ['vue-tsc', versions['vue-tsc']], 121 | ] 122 | if (useNuxtModule) { 123 | devDependencies.push(['vuetify-nuxt-module', versions['vuetify-nuxt-module']]) 124 | } else { 125 | devDependencies.push(['upath', versions['upath']]) 126 | devDependencies.push(['@vuetify/loader-shared', versions['@vuetify/loader-shared']]) 127 | devDependencies.push(['vite-plugin-vuetify', versions['vite-plugin-vuetify']]) 128 | } 129 | if (devDependencies.length > 0) { 130 | addPackageObject('devDependencies', devDependencies, pkg) 131 | } 132 | 133 | // add scripts 134 | addPackageObject('scripts', scripts, pkg, false) 135 | 136 | // save package.json 137 | fs.writeFileSync(packageJson, JSON.stringify(pkg, null, 2), 'utf8') 138 | } 139 | 140 | function configureVuetify (ctx: NuxtContext, nuxtConfig: ReturnType) { 141 | const config = getDefaultExportOptions(nuxtConfig) 142 | config.ssr = ctx.useNuxtSSR 143 | config.features = { 144 | inlineStyles: !ctx.useNuxtModule, 145 | devLogs: !ctx.useNuxtModule, 146 | } 147 | config.build = { transpile: ['vuetify'] } 148 | config.vite = { 149 | ssr: { 150 | noExternal: ['vuetify'], 151 | }, 152 | } 153 | config.css = [] 154 | // vuetify-nuxt-module: 155 | // - will detect @mdi/font adding to the css array 156 | // - will add vuetify/styles to the css array if not disabled 157 | if (!ctx.useNuxtModule) { 158 | config.css.push('@mdi/font/css/materialdesignicons.css') 159 | config.css.push('vuetify/styles') 160 | } 161 | // todo: add only required fonts 162 | addNuxtModule(nuxtConfig, '@nuxt/fonts') 163 | return config 164 | } 165 | 166 | function copyResources ( 167 | ctx: NuxtContext, 168 | rootPath: string, 169 | templateDir: string, 170 | ) { 171 | const { 172 | useNuxtSSR, 173 | useNuxtV4Compat, 174 | nuxtPreset, 175 | useNuxtModule, 176 | templateRoot, 177 | } = ctx 178 | 179 | // assets folder 180 | const assetsDir = path.join(rootPath, 'assets') 181 | let templateAssetsDir = path.join(templateRoot, 'default/src/assets') 182 | fs.mkdirSync(assetsDir) 183 | fs.copyFileSync( 184 | path.join(templateAssetsDir, 'logo.png'), 185 | path.join(assetsDir, 'logo.png'), 186 | ) 187 | fs.copyFileSync( 188 | path.join(templateAssetsDir, 'logo.svg'), 189 | path.join(assetsDir, 'logo.svg'), 190 | ) 191 | if (nuxtPreset !== 'nuxt-default') { 192 | templateAssetsDir = path.join(templateRoot, 'base/src/styles') 193 | 194 | fs.copyFileSync( 195 | path.join(templateAssetsDir, 'settings.scss'), 196 | path.join(assetsDir, 'settings.scss'), 197 | ) 198 | } 199 | 200 | // plugins folder 201 | const pluginsDir = path.join(rootPath, 'plugins') 202 | const templatePluginsDir = path.join(templateDir, 'plugins') 203 | fs.mkdirSync(pluginsDir) 204 | if (useNuxtModule) { 205 | // vuetify configuration file 206 | // v4 compat: modules at root => rootPath is `${rootPath}/app` 207 | // https://nuxt.com/docs/getting-started/upgrade#migrating-to-nuxt-4 208 | fs.copyFileSync( 209 | path.join(templateDir, 'vuetify.config.ts'), 210 | path.join(rootPath, useNuxtV4Compat ? '../vuetify.config.ts' : 'vuetify.config.ts'), 211 | ) 212 | // vuetify plugin 213 | fs.copyFileSync( 214 | path.join(templatePluginsDir, 'vuetify-nuxt.ts'), 215 | path.join(pluginsDir, 'vuetify.ts'), 216 | ) 217 | } else { 218 | // custom vuetify nuxt module 219 | // v4 compat: modules at root => rootPath is `${rootPath}/app` 220 | // https://nuxt.com/docs/getting-started/upgrade#migrating-to-nuxt-4 221 | const modulesDir = path.join(rootPath, useNuxtV4Compat ? '../modules' : 'modules') 222 | const templateModulesDir = path.join(templateDir, 'modules') 223 | fs.mkdirSync(modulesDir) 224 | fs.copyFileSync( 225 | path.resolve(templateModulesDir, 'vuetify.ts'), 226 | path.resolve(modulesDir, 'vuetify.ts'), 227 | ) 228 | // vuetify plugin 229 | editFile( 230 | path.join(templatePluginsDir, 'vuetify.ts'), 231 | content => { 232 | return useNuxtSSR ? content : content.replace('ssr: true,', 'ssr: false,') 233 | }, 234 | path.resolve(pluginsDir, 'vuetify.ts'), 235 | ) 236 | } 237 | // components 238 | fs.copyFileSync( 239 | path.resolve(templateDir, nuxtPreset === 'nuxt-essentials' ? 'app-layout.vue' : 'app.vue'), 240 | path.resolve(rootPath, 'app.vue'), 241 | ) 242 | 243 | // layouts 244 | if (nuxtPreset === 'nuxt-essentials') { 245 | const layoutsDir = path.join(rootPath, 'layouts') 246 | const templateLayoutsDir = path.join(templateDir, 'layouts') 247 | fs.mkdirSync(layoutsDir) 248 | fs.copyFileSync( 249 | path.resolve(templateLayoutsDir, 'default.vue'), 250 | path.resolve(layoutsDir, 'default.vue'), 251 | ) 252 | } 253 | const componentsDir = path.join(rootPath, 'components') 254 | const templateComponentsDir = path.join(templateDir, 'components') 255 | fs.mkdirSync(componentsDir) 256 | fs.copyFileSync( 257 | path.resolve(templateComponentsDir, 'AppFooter.vue'), 258 | path.resolve(componentsDir, 'AppFooter.vue'), 259 | ) 260 | fs.copyFileSync( 261 | path.resolve(templateComponentsDir, 'HelloWorld.vue'), 262 | path.resolve(componentsDir, 'HelloWorld.vue'), 263 | ) 264 | // pages 265 | const pagesDir = path.join(rootPath, 'pages') 266 | const templatePagesDir = path.join(templateDir, 'pages') 267 | fs.mkdirSync(pagesDir) 268 | fs.copyFileSync( 269 | path.resolve(templatePagesDir, 'index.vue'), 270 | path.resolve(pagesDir, 'index.vue'), 271 | ) 272 | } 273 | 274 | function prepareNuxtModule ( 275 | ctx: NuxtContext, 276 | nuxtConfig: ReturnType, 277 | ) { 278 | // prepare nuxt config 279 | const moduleOptions = { 280 | ssrClientHints: { 281 | reloadOnFirstRequest: false, 282 | viewportSize: ctx.useNuxtSSR && ctx.useNuxtSSRClientHints, 283 | prefersColorScheme: false, 284 | prefersColorSchemeOptions: { 285 | useBrowserThemeOnly: false, 286 | }, 287 | }, 288 | styles: ctx.nuxtPreset === 'nuxt-default' 289 | ? true 290 | : { 291 | configFile: 'assets/settings.scss', 292 | }, 293 | } 294 | configureVuetify(ctx, nuxtConfig) 295 | addNuxtModule( 296 | nuxtConfig, 297 | 'vuetify-nuxt-module', 298 | 'vuetify', 299 | { moduleOptions }, 300 | ) 301 | } 302 | 303 | function prepareVuetifyModule ( 304 | ctx: NuxtContext, 305 | nuxtConfig: ReturnType, 306 | ) { 307 | // prepare nuxt config 308 | const config = configureVuetify(ctx, nuxtConfig) 309 | 310 | // enable auto import and include styles 311 | const styles = ctx.nuxtPreset === 'nuxt-essentials' 312 | ? { 313 | configFile: 'assets/settings.scss', 314 | } 315 | : true 316 | config.vuetify = { autoImport: true, styles } 317 | } 318 | 319 | function prepareProject (ctx: NuxtContext) { 320 | const { 321 | projectRoot, 322 | templatePath, 323 | useNuxtV4Compat, 324 | useNuxtModule, 325 | } = ctx 326 | const [rootPath, templateDir] = getPaths(projectRoot, templatePath, useNuxtV4Compat) 327 | 328 | // load nuxt config file 329 | // v4 compat: rootPath is `${rootPath}/app` 330 | // https://nuxt.com/docs/getting-started/upgrade#migrating-to-nuxt-4 331 | const nuxtConfigFile = path.join(rootPath, useNuxtV4Compat ? '../nuxt.config.ts' : 'nuxt.config.ts') 332 | const nuxtConfig = parseModule(fs.readFileSync(nuxtConfigFile, 'utf8')) 333 | 334 | // prepare nuxt config 335 | if (useNuxtModule) { 336 | prepareNuxtModule(ctx, nuxtConfig) 337 | } else { 338 | prepareVuetifyModule(ctx, nuxtConfig) 339 | } 340 | 341 | // prepare nuxt config 342 | let code = generateCode(nuxtConfig, { 343 | trailingComma: true, 344 | quote: 'single', 345 | arrayBracketSpacing: false, 346 | objectCurlySpacing: true, 347 | lineTerminator: '\n', 348 | format: { 349 | trailingComma: true, 350 | quote: 'single', 351 | arrayBracketSpacing: false, 352 | objectCurlySpacing: true, 353 | useSemi: false, 354 | }, 355 | }).code 356 | 357 | // add some hints to the nuxt config 358 | // https://github.com/Pinegrow/pg-nuxt-vuetify-tailwindcss/blob/00fac86769bc43e034b90dacfd03becc92b93b53/nuxt.config.ts#L100-L104 359 | if (useNuxtModule) { 360 | code = code.replace('ssrClientHints:', `// check https://nuxt.vuetifyjs.com/guide/server-side-rendering.html 361 | ssrClientHints:`) 362 | code = code.replace('styles:', `// /* If customizing sass global variables ($utilities, $reset, $color-pack, $body-font-family, etc) */ 363 | // disableVuetifyStyles: true, 364 | styles:`) 365 | } else { 366 | code = code.replace('ssr:', `// when enabling/disabling ssr option, remember to update ssr option in plugins/vuetify.ts 367 | ssr:`) 368 | } 369 | code = code.replace('features:', `// when enabling ssr option you need to disable inlineStyles and maybe devLogs 370 | features:`) 371 | 372 | // save nuxt config file 373 | fs.writeFileSync( 374 | nuxtConfigFile, 375 | code, 376 | 'utf8', 377 | ) 378 | 379 | // prepare resources 380 | copyResources(ctx, rootPath, templateDir) 381 | } 382 | -------------------------------------------------------------------------------- /src/utils/nuxt/types.ts: -------------------------------------------------------------------------------- 1 | export interface NuxtContext { 2 | cwd: string 3 | projectName: string 4 | projectRoot: string 5 | templateRoot: string 6 | templatePath: string 7 | nuxtPreset: 'nuxt-base' | 'nuxt-default' | 'nuxt-essentials' 8 | useNuxtV4Compat: boolean 9 | useNuxtModule: boolean 10 | useNuxtSSR: boolean 11 | useNuxtSSRClientHints?: boolean 12 | } 13 | 14 | export type PackageJsonEntry = [name: string, value: string] 15 | -------------------------------------------------------------------------------- /src/utils/nuxt/utils.ts: -------------------------------------------------------------------------------- 1 | // Node 2 | import process from 'node:process' 3 | import path from 'node:path' 4 | import { spawnSync } from 'node:child_process' 5 | import fs from 'node:fs' 6 | 7 | // Types 8 | import type { PackageJsonEntry } from './types' 9 | import type { AgentCommands, DetectResult } from 'package-manager-detector' 10 | 11 | // Utils 12 | import { resolveCommand } from 'package-manager-detector/commands' 13 | 14 | export function detectPkgInfo () { 15 | const userAgent = process.env.npm_config_user_agent 16 | if (!userAgent) { 17 | return undefined 18 | } 19 | const pkgSpec = userAgent.split(' ')[0] 20 | const pkgSpecArr = pkgSpec.split('/') 21 | return { 22 | name: pkgSpecArr[0], 23 | version: pkgSpecArr[1], 24 | } 25 | } 26 | 27 | export function addPackageObject ( 28 | key: 'scripts' | 'dependencies' | 'devDependencies' | 'overrides' | 'resolutions', 29 | entry: PackageJsonEntry[], 30 | pkg: any, 31 | sort = true, 32 | ) { 33 | pkg[key] ??= {} 34 | if (!sort) { 35 | for (const [name, value] of entry) { 36 | pkg[key][name] = value 37 | } 38 | 39 | return 40 | } 41 | 42 | const entries = Object.entries(pkg[key]) 43 | pkg[key] = {} 44 | for (const [name, value] of entry) { 45 | entries.push([name, value]) 46 | } 47 | for (const [k, v] of entries.sort(([a], [b]) => a.localeCompare(b))) { 48 | pkg[key][k] = v 49 | } 50 | } 51 | 52 | export function runCommand ( 53 | pmDetection: DetectResult | null, 54 | command: keyof AgentCommands, 55 | args: string[], 56 | cwd: string, 57 | ) { 58 | let runCommand = 'npm' 59 | let runArgs: string[] = [command] 60 | 61 | // run install and prepare 62 | if (pmDetection) { 63 | const prepare = resolveCommand(pmDetection.name, command, args)! 64 | runCommand = prepare.command 65 | runArgs = prepare.args 66 | } 67 | 68 | const run = spawnSync( 69 | runCommand, 70 | runArgs.filter(Boolean), { 71 | cwd, 72 | stdio: ['inherit', 'inherit', 'pipe'], 73 | shell: true, 74 | }, 75 | ) 76 | if (run.error) { 77 | throw run.error 78 | } 79 | } 80 | 81 | export function editFile (file: string, callback: (content: string) => string, destination?: string) { 82 | const content = fs.readFileSync(file, 'utf8') 83 | fs.writeFileSync(destination ?? file, callback(content), 'utf8') 84 | } 85 | 86 | export function getPaths ( 87 | rootPath: string, 88 | templateDir: string, 89 | v4: boolean, 90 | ): [rootPath: string, templateDir: string] { 91 | return v4 92 | ? [path.join(rootPath, 'app'), templateDir] 93 | : [rootPath, templateDir] 94 | } 95 | -------------------------------------------------------------------------------- /src/utils/nuxt/versions.ts: -------------------------------------------------------------------------------- 1 | export const versions = { 2 | 'vuetify': '^3.8.1', 3 | 'typescript': '^5.6.3', 4 | 'vue-tsc': '^2.1.6', 5 | 'sass-embedded': '^1.86.3', 6 | '@vuetify/loader-shared': '^2.1.0', 7 | 'vite-plugin-vuetify': '^2.1.1', 8 | 'vuetify-nuxt-module': '^0.18.6', 9 | 'upath': '^2.0.1', 10 | '@mdi/font': '^7.4.47', 11 | '@nuxt/fonts': '^0.11.1', 12 | } as const 13 | -------------------------------------------------------------------------------- /src/utils/presets.ts: -------------------------------------------------------------------------------- 1 | export interface Preset { 2 | useEslint: boolean 3 | useRouter: boolean 4 | useStore: boolean 5 | } 6 | 7 | export type NuxtPresetName = 'nuxt-base' | 'nuxt-default' | 'nuxt-essentials' 8 | export type PresetName = 'base' | 'default' | 'essentials' | NuxtPresetName 9 | 10 | const defaultContext: Preset = { 11 | useEslint: false, 12 | useRouter: false, 13 | useStore: false, 14 | } 15 | 16 | const baseContext: Preset = { 17 | ...defaultContext, 18 | useEslint: true, 19 | useRouter: true, 20 | } 21 | 22 | const essentialsContext: Preset = { 23 | ...baseContext, 24 | useStore: true, 25 | } 26 | 27 | const presets: Record = { 28 | 'base': baseContext, 29 | 'default': defaultContext, 30 | 'essentials': essentialsContext, 31 | 'nuxt-base': baseContext, 32 | 'nuxt-default': defaultContext, 33 | 'nuxt-essentials': essentialsContext, 34 | } 35 | 36 | export { presets } 37 | -------------------------------------------------------------------------------- /src/utils/prompts.ts: -------------------------------------------------------------------------------- 1 | // Node 2 | import { join, resolve } from 'node:path' 3 | import { existsSync, readdirSync } from 'node:fs' 4 | 5 | // Types 6 | import type { Options as PromptOptions } from 'prompts' 7 | 8 | // Utils 9 | import { presets } from './presets' 10 | import { red } from 'kolorist' 11 | import prompts from 'prompts' 12 | import { packageManager as defaultPackageManager } from './installDependencies' 13 | import validate from 'validate-npm-package-name' 14 | 15 | type ContextState = { 16 | cwd: string 17 | projectName?: string 18 | canOverwrite?: boolean 19 | useTypeScript?: boolean 20 | usePackageManager?: 'npm' | 'pnpm' | 'yarn' | 'bun' 21 | installDependencies?: boolean 22 | usePreset?: 'base' | 'default' | 'essentials' | 'nuxt-base' | 'nuxt-default' | 'nuxt-essentials' 23 | useEslint?: boolean 24 | useRouter?: boolean 25 | useStore?: boolean 26 | useNuxtV4Compat?: boolean 27 | useNuxtModule?: boolean 28 | useNuxtSSR?: boolean 29 | useNuxtSSRClientHints?: boolean 30 | } 31 | 32 | const promptOptions: PromptOptions = { 33 | onCancel: () => { 34 | throw new Error(red('✖') + ' Operation cancelled') 35 | }, 36 | } 37 | 38 | type DefinedContextState = { [P in keyof ContextState]-?: ContextState[P] } 39 | 40 | const initPrompts = async (context: ContextState) => { 41 | type Answers = prompts.Answers< 42 | 'projectName' | 'canOverwrite' | 'usePreset' | 'useTypeScript' | 'usePackageManager' | 'installDependencies' | 'useNuxtV4Compat' | 'useNuxtModule' | 'useNuxtSSR' | 'useNuxtSSRClientHints' 43 | > 44 | 45 | if (context.usePreset) { 46 | context = { 47 | ...context, 48 | ...presets[context.usePreset], 49 | } 50 | } 51 | 52 | const answers: Answers = await prompts([ 53 | { 54 | name: 'projectName', 55 | type: 'text', 56 | message: 'Project name:', 57 | initial: 'vuetify-project', 58 | format: (v: string) => v.trim(), 59 | validate: (v: string) => { 60 | const { errors, warnings, validForNewPackages: isValid } = validate(String(v).trim()) 61 | 62 | const error = isValid ? null : (errors ? errors[0] : warnings![0]) 63 | 64 | if (!isValid) { 65 | return `Package ${error}` 66 | } 67 | return true 68 | }, 69 | }, 70 | { 71 | name: 'canOverwrite', 72 | active: 'Yes', 73 | inactive: 'No', 74 | initial: false, 75 | type: (_: any, { projectName }) => { 76 | const projectPath = join(context.cwd, projectName) 77 | 78 | return ( 79 | !existsSync(projectPath) 80 | || readdirSync(projectPath).length === 0 81 | ) 82 | ? null 83 | : 'toggle' 84 | }, 85 | onState (a) { 86 | if (!a.value) { 87 | console.error('\n\n', red('✖') + ' Target directory exists and is not empty.') 88 | process.exit(1) 89 | } 90 | }, 91 | message: (prev: string) => `The project path: ${resolve(context.cwd, prev)} already exists, would you like to overwrite this directory?`, 92 | }, 93 | { 94 | name: 'usePreset', 95 | type: context.usePreset ? null : 'select', 96 | message: 'Which preset would you like to install?', 97 | initial: 1, 98 | choices: [ 99 | { title: 'Barebones (Only Vue & Vuetify)', value: 'default' }, 100 | { title: 'Default (Adds routing, ESLint & SASS variables)', value: 'base' }, 101 | { title: 'Recommended (Everything from Default. Adds auto importing, layouts & pinia)', value: 'essentials' }, 102 | { title: 'Nuxt Barebones (Only Vuetify)', value: 'nuxt-default' }, 103 | { title: 'Nuxt Default (Adds Nuxt ESLint & SASS variables)', value: 'nuxt-base' }, 104 | { title: 'Nuxt Recommended (Everything from Default. Enables auto importing & layouts)', value: 'nuxt-essentials' }, 105 | ], 106 | }, 107 | { 108 | name: 'useTypeScript', 109 | type: usePreset => { 110 | const p = context.usePreset ?? usePreset 111 | return p.startsWith('nuxt-') || context.useTypeScript ? null : 'toggle' 112 | }, 113 | message: 'Use TypeScript?', 114 | active: 'Yes', 115 | inactive: 'No', 116 | initial: false, 117 | }, 118 | { 119 | name: 'usePackageManager', 120 | type: (_, { usePreset }) => { 121 | const p = context.usePreset ?? usePreset 122 | return p.startsWith('nuxt-') ? null : 'select' 123 | }, 124 | message: 'Would you like to install dependencies with yarn, npm, pnpm, or bun?', 125 | initial: defaultPackageManager === 'bun' ? 3 : 0, 126 | choices: [ 127 | { title: 'pnpm', value: 'pnpm' }, 128 | { title: 'npm', value: 'npm' }, 129 | { title: 'yarn', value: 'yarn' }, 130 | { title: 'bun', value: 'bun' }, 131 | { title: 'none', value: null }, 132 | ], 133 | }, 134 | { 135 | name: 'installDependencies', 136 | type: (_, { usePreset }) => { 137 | const p = context.usePreset ?? usePreset 138 | return p.startsWith('nuxt-') || context.installDependencies ? null : 'toggle' 139 | }, 140 | message: 'Install Dependencies?', 141 | active: 'Yes', 142 | inactive: 'No', 143 | initial: 'Yes', 144 | }, 145 | { 146 | name: 'useNuxtV4Compat', 147 | type: (_, { usePreset }) => { 148 | const p = context.usePreset ?? usePreset 149 | return p.startsWith('nuxt-') ? 'toggle' : null 150 | }, 151 | message: 'Use Nuxt v4 compatibility?', 152 | active: 'Yes', 153 | inactive: 'No', 154 | initial: 'Yes', 155 | }, 156 | { 157 | name: 'useNuxtModule', 158 | type: (_, { usePreset }) => { 159 | const p = context.usePreset ?? usePreset 160 | return p.startsWith('nuxt-') ? 'toggle' : null 161 | }, 162 | message: 'Use vuetify-nuxt-module?', 163 | active: 'Yes', 164 | inactive: 'No', 165 | initial: 'Yes', 166 | }, 167 | { 168 | name: 'useNuxtSSR', 169 | type: (_, { usePreset }) => { 170 | const p = context.usePreset ?? usePreset 171 | return p.startsWith('nuxt-') ? 'toggle' : null 172 | }, 173 | message: 'Enable Nuxt SSR?', 174 | active: 'Yes', 175 | inactive: 'No', 176 | initial: 'Yes', 177 | }, 178 | { 179 | name: 'useNuxtSSRClientHints', 180 | type: (useNuxtSSR, { usePreset, useNuxtModule }) => { 181 | const p = context.usePreset ?? usePreset 182 | if (!p.startsWith('nuxt-')) { 183 | return null 184 | } 185 | return useNuxtModule && useNuxtSSR ? 'toggle' : null 186 | }, 187 | message: 'Enable Nuxt SSR Http Client Hints?', 188 | active: 'Yes', 189 | inactive: 'No', 190 | initial: 'Yes', 191 | }, 192 | ], promptOptions) 193 | 194 | return { 195 | ...context, 196 | ...answers, 197 | } as DefinedContextState 198 | } 199 | 200 | export { initPrompts } 201 | export type { ContextState } 202 | -------------------------------------------------------------------------------- /src/utils/renderTemplate.ts: -------------------------------------------------------------------------------- 1 | import { copyFileSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs' 2 | import { basename, dirname, resolve } from 'node:path' 3 | 4 | import { deepMerge } from './deepMerge' 5 | 6 | function mergePkg (source: string, destination: string) { 7 | const target = JSON.parse(readFileSync(destination, 'utf8')) 8 | const src = JSON.parse(readFileSync(source, 'utf8')) 9 | const mergedPkg = deepMerge(target, src) 10 | 11 | const keysToSort = ['devDependencies', 'dependencies'] 12 | for (const k of keysToSort) { 13 | mergedPkg[k] = Object.keys(mergedPkg[k]).sort().reduce((a: { [key: string]: string }, c) => (a[c] = mergedPkg[k][c], a), {}) 14 | } 15 | 16 | writeFileSync(destination, JSON.stringify(mergedPkg, null, 2) + '\n') 17 | } 18 | 19 | function renderDirectory (source: string, destination: string) { 20 | mkdirSync(destination, { recursive: true }) 21 | 22 | for (const path of readdirSync(source)) { 23 | renderTemplate(resolve(source, path), resolve(destination, path)) 24 | } 25 | } 26 | 27 | function renderFile (source: string, destination: string) { 28 | const filename = basename(source) 29 | 30 | if (filename.startsWith('_')) { 31 | destination = resolve(dirname(destination), filename.replace('_', '.')) 32 | } 33 | if (filename === 'package.json') { 34 | mergePkg(source, destination) 35 | } else { 36 | copyFileSync(source, destination) 37 | } 38 | } 39 | 40 | function renderTemplate (source: string, destination: string) { 41 | if (statSync(source).isDirectory()) { 42 | renderDirectory(source, destination) 43 | } else { 44 | renderFile(source, destination) 45 | } 46 | } 47 | 48 | export { renderTemplate } 49 | -------------------------------------------------------------------------------- /template/javascript/base/eslint.config.js: -------------------------------------------------------------------------------- 1 | import vuetify from 'eslint-config-vuetify' 2 | 3 | export default vuetify() 4 | -------------------------------------------------------------------------------- /template/javascript/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "eslint . --fix" 4 | }, 5 | "devDependencies": { 6 | "eslint": "^9.23.0", 7 | "eslint-config-vuetify": "^4.0.0", 8 | "globals": "^16.0.0", 9 | "vue-router": "^4.5.0", 10 | "unplugin-vue-router": "^0.12.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /template/javascript/base/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /template/javascript/base/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 91 | -------------------------------------------------------------------------------- /template/javascript/base/src/pages/README.md: -------------------------------------------------------------------------------- 1 | # Pages 2 | 3 | Vue components created in this folder will automatically be converted to navigatable routes. 4 | 5 | Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository. 6 | -------------------------------------------------------------------------------- /template/javascript/base/src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /template/javascript/base/src/plugins/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.js 3 | * 4 | * Automatically included in `./src/main.js` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from './vuetify' 9 | import router from '@/router' 10 | 11 | export function registerPlugins (app) { 12 | app 13 | .use(vuetify) 14 | .use(router) 15 | } 16 | -------------------------------------------------------------------------------- /template/javascript/base/src/router/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * router/index.ts 3 | * 4 | * Automatic routes for `./src/pages/*.vue` 5 | */ 6 | 7 | // Composables 8 | import { createRouter, createWebHistory } from 'vue-router/auto' 9 | import { routes } from 'vue-router/auto-routes' 10 | 11 | const router = createRouter({ 12 | history: createWebHistory(import.meta.env.BASE_URL), 13 | routes, 14 | }) 15 | 16 | // Workaround for https://github.com/vitejs/vite/issues/11804 17 | router.onError((err, to) => { 18 | if (err?.message?.includes?.('Failed to fetch dynamically imported module')) { 19 | if (localStorage.getItem('vuetify:dynamic-reload')) { 20 | console.error('Dynamic import error, reloading page did not fix it', err) 21 | } else { 22 | console.log('Reloading page to fix dynamic import error') 23 | localStorage.setItem('vuetify:dynamic-reload', 'true') 24 | location.assign(to.fullPath) 25 | } 26 | } else { 27 | console.error(err) 28 | } 29 | }) 30 | 31 | router.isReady().then(() => { 32 | localStorage.removeItem('vuetify:dynamic-reload') 33 | }) 34 | 35 | export default router 36 | -------------------------------------------------------------------------------- /template/javascript/base/src/styles/README.md: -------------------------------------------------------------------------------- 1 | # Styles 2 | 3 | This directory is for configuring the styles of the application. 4 | -------------------------------------------------------------------------------- /template/javascript/base/src/styles/settings.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * src/styles/settings.scss 3 | * 4 | * Configures SASS variables and Vuetify overwrites 5 | */ 6 | 7 | // https://vuetifyjs.com/features/sass-variables/` 8 | // @use 'vuetify/settings' with ( 9 | // $color-pack: false 10 | // ); 11 | -------------------------------------------------------------------------------- /template/javascript/base/vite.config.mjs: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import Components from 'unplugin-vue-components/vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 5 | import Fonts from 'unplugin-fonts/vite' 6 | import VueRouter from 'unplugin-vue-router/vite' 7 | 8 | // Utilities 9 | import { defineConfig } from 'vite' 10 | import { fileURLToPath, URL } from 'node:url' 11 | 12 | // https://vitejs.dev/config/ 13 | export default defineConfig({ 14 | plugins: [ 15 | VueRouter(), 16 | Vue({ 17 | template: { transformAssetUrls }, 18 | }), 19 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 20 | Vuetify({ 21 | autoImport: true, 22 | styles: { 23 | configFile: 'src/styles/settings.scss', 24 | }, 25 | }), 26 | Components(), 27 | Fonts({ 28 | fontsource: { 29 | families: [ 30 | { 31 | name: 'Roboto', 32 | weights: [100, 300, 400, 500, 700, 900], 33 | styles: ['normal', 'italic'], 34 | }, 35 | ], 36 | }, 37 | }), 38 | ], 39 | optimizeDeps: { 40 | exclude: [ 41 | 'vuetify', 42 | 'vue-router', 43 | 'unplugin-vue-router/runtime', 44 | 'unplugin-vue-router/data-loaders', 45 | 'unplugin-vue-router/data-loaders/basic', 46 | ], 47 | }, 48 | define: { 'process.env': {} }, 49 | resolve: { 50 | alias: { 51 | '@': fileURLToPath(new URL('src', import.meta.url)), 52 | }, 53 | extensions: [ 54 | '.js', 55 | '.json', 56 | '.jsx', 57 | '.mjs', 58 | '.ts', 59 | '.tsx', 60 | '.vue', 61 | ], 62 | }, 63 | server: { 64 | port: 3000, 65 | }, 66 | css: { 67 | preprocessorOptions: { 68 | sass: { 69 | api: 'modern-compiler', 70 | }, 71 | scss: { 72 | api: 'modern-compiler', 73 | }, 74 | }, 75 | }, 76 | }) 77 | -------------------------------------------------------------------------------- /template/javascript/default/README.md: -------------------------------------------------------------------------------- 1 | # Vuetify (Default) 2 | 3 | This is the official scaffolding tool for Vuetify, designed to give you a head start in building your new Vuetify application. It sets up a base template with all the necessary configurations and standard directory structure, enabling you to begin development without the hassle of setting up the project from scratch. 4 | 5 | ## ❗️ Important Links 6 | 7 | - 📄 [Docs](https://vuetifyjs.com/) 8 | - 🚨 [Issues](https://issues.vuetifyjs.com/) 9 | - 🏬 [Store](https://store.vuetifyjs.com/) 10 | - 🎮 [Playground](https://play.vuetifyjs.com/) 11 | - 💬 [Discord](https://community.vuetifyjs.com) 12 | 13 | ## 💿 Install 14 | 15 | Set up your project using your preferred package manager. Use the corresponding command to install the dependencies: 16 | 17 | | Package Manager | Command | 18 | |---------------------------------------------------------------|----------------| 19 | | [yarn](https://yarnpkg.com/getting-started) | `yarn install` | 20 | | [npm](https://docs.npmjs.com/cli/v7/commands/npm-install) | `npm install` | 21 | | [pnpm](https://pnpm.io/installation) | `pnpm install` | 22 | | [bun](https://bun.sh/#getting-started) | `bun install` | 23 | 24 | After completing the installation, your environment is ready for Vuetify development. 25 | 26 | ## ✨ Features 27 | 28 | - 🖼️ **Optimized Front-End Stack**: Leverage the latest Vue 3 and Vuetify 3 for a modern, reactive UI development experience. [Vue 3](https://v3.vuejs.org/) | [Vuetify 3](https://vuetifyjs.com/en/) 29 | - 🗃️ **State Management**: Integrated with [Pinia](https://pinia.vuejs.org/), the intuitive, modular state management solution for Vue. 30 | - 🚦 **Routing and Layouts**: Utilizes Vue Router for SPA navigation and vite-plugin-vue-layouts for organizing Vue file layouts. [Vue Router](https://router.vuejs.org/) | [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) 31 | - ⚡ **Next-Gen Tooling**: Powered by Vite, experience fast cold starts and instant HMR (Hot Module Replacement). [Vite](https://vitejs.dev/) 32 | - 🧩 **Automated Component Importing**: Streamline your workflow with unplugin-vue-components, automatically importing components as you use them. [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components) 33 | 34 | These features are curated to provide a seamless development experience from setup to deployment, ensuring that your Vuetify application is both powerful and maintainable. 35 | 36 | ## 💡 Usage 37 | 38 | This section covers how to start the development server and build your project for production. 39 | 40 | ### Starting the Development Server 41 | 42 | To start the development server with hot-reload, run the following command. The server will be accessible at [http://localhost:3000](http://localhost:3000): 43 | 44 | ```bash 45 | yarn dev 46 | ``` 47 | 48 | (Repeat for npm, pnpm, and bun with respective commands.) 49 | 50 | > Add NODE_OPTIONS='--no-warnings' to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script. 51 | 52 | ### Building for Production 53 | 54 | To build your project for production, use: 55 | 56 | ```bash 57 | yarn build 58 | ``` 59 | 60 | (Repeat for npm, pnpm, and bun with respective commands.) 61 | 62 | Once the build process is completed, your application will be ready for deployment in a production environment. 63 | 64 | ## 💪 Support Vuetify Development 65 | 66 | This project is built with [Vuetify](https://vuetifyjs.com/en/), a UI Library with a comprehensive collection of Vue components. Vuetify is an MIT licensed Open Source project that has been made possible due to the generous contributions by our [sponsors and backers](https://vuetifyjs.com/introduction/sponsors-and-backers/). If you are interested in supporting this project, please consider: 67 | 68 | - [Requesting Enterprise Support](https://support.vuetifyjs.com/) 69 | - [Sponsoring John on Github](https://github.com/users/johnleider/sponsorship) 70 | - [Sponsoring Kael on Github](https://github.com/users/kaelwd/sponsorship) 71 | - [Supporting the team on Open Collective](https://opencollective.com/vuetify) 72 | - [Becoming a sponsor on Patreon](https://www.patreon.com/vuetify) 73 | - [Becoming a subscriber on Tidelift](https://tidelift.com/subscription/npm/vuetify) 74 | - [Making a one-time donation with Paypal](https://paypal.me/vuetify) 75 | 76 | ## 📑 License 77 | [MIT](http://opensource.org/licenses/MIT) 78 | 79 | Copyright (c) 2016-present Vuetify, LLC 80 | -------------------------------------------------------------------------------- /template/javascript/default/_browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /template/javascript/default/_editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /template/javascript/default/_gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /template/javascript/default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to Vuetify 3 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /template/javascript/default/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "es5", 5 | "module": "esnext", 6 | "baseUrl": "./", 7 | "moduleResolution": "bundler", 8 | "paths": { 9 | "@/*": [ 10 | "src/*" 11 | ] 12 | }, 13 | "lib": [ 14 | "esnext", 15 | "dom", 16 | "dom.iterable", 17 | "scripthost" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /template/javascript/default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@mdi/font": "7.4.47", 12 | "@fontsource/roboto": "5.2.5", 13 | "vue": "^3.5.13", 14 | "vuetify": "^3.8.1" 15 | }, 16 | "devDependencies": { 17 | "@vitejs/plugin-vue": "^5.2.3", 18 | "globals": "^16.0.0", 19 | "sass-embedded": "^1.86.3", 20 | "unplugin-fonts": "^1.3.1", 21 | "unplugin-vue-components": "^28.4.1", 22 | "vite-plugin-vuetify": "^2.1.1", 23 | "vite": "^6.2.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /template/javascript/default/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuetifyjs/create/3bd143d7463a84de18766d0f6f93d6eddfc2d070/template/javascript/default/public/favicon.ico -------------------------------------------------------------------------------- /template/javascript/default/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /template/javascript/default/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuetifyjs/create/3bd143d7463a84de18766d0f6f93d6eddfc2d070/template/javascript/default/src/assets/logo.png -------------------------------------------------------------------------------- /template/javascript/default/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /template/javascript/default/src/components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 73 | 74 | 83 | -------------------------------------------------------------------------------- /template/javascript/default/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 91 | -------------------------------------------------------------------------------- /template/javascript/default/src/components/README.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | Vue template files in this folder are automatically imported. 4 | 5 | ## 🚀 Usage 6 | 7 | Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it. 8 | 9 | The following example assumes a component located at `src/components/MyComponent.vue`: 10 | 11 | ```vue 12 | 17 | 18 | 21 | ``` 22 | 23 | When your template is rendered, the component's import will automatically be inlined, which renders to this: 24 | 25 | ```vue 26 | 31 | 32 | 35 | ``` 36 | -------------------------------------------------------------------------------- /template/javascript/default/src/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * main.js 3 | * 4 | * Bootstraps Vuetify and other plugins then mounts the App` 5 | */ 6 | 7 | // Plugins 8 | import { registerPlugins } from '@/plugins' 9 | 10 | // Components 11 | import App from './App.vue' 12 | 13 | // Composables 14 | import { createApp } from 'vue' 15 | 16 | // Styles 17 | import 'unfonts.css' 18 | 19 | const app = createApp(App) 20 | 21 | registerPlugins(app) 22 | 23 | app.mount('#app') 24 | -------------------------------------------------------------------------------- /template/javascript/default/src/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally. 4 | -------------------------------------------------------------------------------- /template/javascript/default/src/plugins/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.js 3 | * 4 | * Automatically included in `./src/main.js` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from './vuetify' 9 | 10 | export function registerPlugins (app) { 11 | app.use(vuetify) 12 | } 13 | -------------------------------------------------------------------------------- /template/javascript/default/src/plugins/vuetify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/vuetify.js 3 | * 4 | * Framework documentation: https://vuetifyjs.com` 5 | */ 6 | 7 | // Styles 8 | import '@mdi/font/css/materialdesignicons.css' 9 | import 'vuetify/styles' 10 | 11 | // Composables 12 | import { createVuetify } from 'vuetify' 13 | 14 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 15 | export default createVuetify({ 16 | theme: { 17 | defaultTheme: 'dark', 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /template/javascript/default/vite.config.mjs: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import Components from 'unplugin-vue-components/vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 5 | import Fonts from 'unplugin-fonts/vite' 6 | 7 | // Utilities 8 | import { defineConfig } from 'vite' 9 | import { fileURLToPath, URL } from 'node:url' 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig({ 13 | plugins: [ 14 | Vue({ 15 | template: { transformAssetUrls }, 16 | }), 17 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 18 | Vuetify(), 19 | Components(), 20 | Fonts({ 21 | fontsource: { 22 | families: [ 23 | { 24 | name: 'Roboto', 25 | weights: [100, 300, 400, 500, 700, 900], 26 | styles: ['normal', 'italic'], 27 | }, 28 | ], 29 | }, 30 | }), 31 | ], 32 | optimizeDeps: { 33 | exclude: ['vuetify'], 34 | }, 35 | define: { 'process.env': {} }, 36 | resolve: { 37 | alias: { 38 | '@': fileURLToPath(new URL('src', import.meta.url)), 39 | }, 40 | extensions: [ 41 | '.js', 42 | '.json', 43 | '.jsx', 44 | '.mjs', 45 | '.ts', 46 | '.tsx', 47 | '.vue', 48 | ], 49 | }, 50 | server: { 51 | port: 3000, 52 | }, 53 | css: { 54 | preprocessorOptions: { 55 | sass: { 56 | api: 'modern-compiler', 57 | }, 58 | scss: { 59 | api: 'modern-compiler', 60 | }, 61 | }, 62 | }, 63 | }) 64 | -------------------------------------------------------------------------------- /template/javascript/essentials/_eslintrc-auto-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "Component": true, 4 | "ComponentPublicInstance": true, 5 | "ComputedRef": true, 6 | "DirectiveBinding": true, 7 | "EffectScope": true, 8 | "ExtractDefaultPropTypes": true, 9 | "ExtractPropTypes": true, 10 | "ExtractPublicPropTypes": true, 11 | "InjectionKey": true, 12 | "MaybeRef": true, 13 | "MaybeRefOrGetter": true, 14 | "PropType": true, 15 | "Ref": true, 16 | "Slot": true, 17 | "Slots": true, 18 | "VNode": true, 19 | "WritableComputedRef": true, 20 | "computed": true, 21 | "createApp": true, 22 | "customRef": true, 23 | "defineAsyncComponent": true, 24 | "defineComponent": true, 25 | "defineStore": true, 26 | "effectScope": true, 27 | "getCurrentInstance": true, 28 | "getCurrentScope": true, 29 | "h": true, 30 | "inject": true, 31 | "isProxy": true, 32 | "isReactive": true, 33 | "isReadonly": true, 34 | "isRef": true, 35 | "markRaw": true, 36 | "nextTick": true, 37 | "onActivated": true, 38 | "onBeforeMount": true, 39 | "onBeforeRouteLeave": true, 40 | "onBeforeRouteUpdate": true, 41 | "onBeforeUnmount": true, 42 | "onBeforeUpdate": true, 43 | "onDeactivated": true, 44 | "onErrorCaptured": true, 45 | "onMounted": true, 46 | "onRenderTracked": true, 47 | "onRenderTriggered": true, 48 | "onScopeDispose": true, 49 | "onServerPrefetch": true, 50 | "onUnmounted": true, 51 | "onUpdated": true, 52 | "onWatcherCleanup": true, 53 | "provide": true, 54 | "reactive": true, 55 | "readonly": true, 56 | "ref": true, 57 | "resolveComponent": true, 58 | "shallowReactive": true, 59 | "shallowReadonly": true, 60 | "shallowRef": true, 61 | "storeToRefs": true, 62 | "toRaw": true, 63 | "toRef": true, 64 | "toRefs": true, 65 | "toValue": true, 66 | "triggerRef": true, 67 | "unref": true, 68 | "useAttrs": true, 69 | "useCssModule": true, 70 | "useCssVars": true, 71 | "useId": true, 72 | "useModel": true, 73 | "useRoute": true, 74 | "useRouter": true, 75 | "useSlots": true, 76 | "useTemplateRef": true, 77 | "watch": true, 78 | "watchEffect": true, 79 | "watchPostEffect": true, 80 | "watchSyncEffect": true 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /template/javascript/essentials/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "pinia" : "^3.0.1", 4 | "unplugin-auto-import": "^19.1.1", 5 | "vite-plugin-vue-layouts-next": "^0.0.8" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/layouts/README.md: -------------------------------------------------------------------------------- 1 | # Layouts 2 | 3 | Layouts are reusable components that wrap around pages. They are used to provide a consistent look and feel across multiple pages. 4 | 5 | Full documentation for this feature can be found in the Official [vite-plugin-vue-layouts-next](https://github.com/loicduong/vite-plugin-vue-layouts-next) repository. 6 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/plugins/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.js 3 | * 4 | * Automatically included in `./src/main.js` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from './vuetify' 9 | import pinia from '@/stores' 10 | import router from '@/router' 11 | 12 | export function registerPlugins (app) { 13 | app 14 | .use(vuetify) 15 | .use(router) 16 | .use(pinia) 17 | } 18 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/router/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * router/index.ts 3 | * 4 | * Automatic routes for `./src/pages/*.vue` 5 | */ 6 | 7 | // Composables 8 | import { createRouter, createWebHistory } from 'vue-router/auto' 9 | import { setupLayouts } from 'virtual:generated-layouts' 10 | import { routes } from 'vue-router/auto-routes' 11 | 12 | const router = createRouter({ 13 | history: createWebHistory(import.meta.env.BASE_URL), 14 | routes: setupLayouts(routes), 15 | }) 16 | 17 | // Workaround for https://github.com/vitejs/vite/issues/11804 18 | router.onError((err, to) => { 19 | if (err?.message?.includes?.('Failed to fetch dynamically imported module')) { 20 | if (localStorage.getItem('vuetify:dynamic-reload')) { 21 | console.error('Dynamic import error, reloading page did not fix it', err) 22 | } else { 23 | console.log('Reloading page to fix dynamic import error') 24 | localStorage.setItem('vuetify:dynamic-reload', 'true') 25 | location.assign(to.fullPath) 26 | } 27 | } else { 28 | console.error(err) 29 | } 30 | }) 31 | 32 | router.isReady().then(() => { 33 | localStorage.removeItem('vuetify:dynamic-reload') 34 | }) 35 | 36 | export default router 37 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/stores/README.md: -------------------------------------------------------------------------------- 1 | # Store 2 | 3 | Pinia stores are used to store reactive state and expose actions to mutate it. 4 | 5 | Full documentation for this feature can be found in the Official [Pinia](https://pinia.esm.dev/) repository. 6 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/stores/app.js: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { defineStore } from 'pinia' 3 | 4 | export const useAppStore = defineStore('app', { 5 | state: () => ({ 6 | // 7 | }), 8 | }) 9 | -------------------------------------------------------------------------------- /template/javascript/essentials/src/stores/index.js: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { createPinia } from 'pinia' 3 | 4 | export default createPinia() 5 | -------------------------------------------------------------------------------- /template/javascript/essentials/vite.config.mjs: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import AutoImport from 'unplugin-auto-import/vite' 3 | import Components from 'unplugin-vue-components/vite' 4 | import Fonts from 'unplugin-fonts/vite' 5 | import Layouts from 'vite-plugin-vue-layouts-next' 6 | import Vue from '@vitejs/plugin-vue' 7 | import VueRouter from 'unplugin-vue-router/vite' 8 | import { VueRouterAutoImports } from 'unplugin-vue-router' 9 | import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 10 | 11 | // Utilities 12 | import { defineConfig } from 'vite' 13 | import { fileURLToPath, URL } from 'node:url' 14 | 15 | // https://vitejs.dev/config/ 16 | export default defineConfig({ 17 | plugins: [ 18 | VueRouter(), 19 | Layouts(), 20 | Vue({ 21 | template: { transformAssetUrls }, 22 | }), 23 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 24 | Vuetify({ 25 | autoImport: true, 26 | styles: { 27 | configFile: 'src/styles/settings.scss', 28 | }, 29 | }), 30 | Components(), 31 | Fonts({ 32 | google: { 33 | families: [{ 34 | name: 'Roboto', 35 | styles: 'wght@100;300;400;500;700;900', 36 | }], 37 | }, 38 | }), 39 | AutoImport({ 40 | imports: [ 41 | 'vue', 42 | VueRouterAutoImports, 43 | { 44 | pinia: ['defineStore', 'storeToRefs'], 45 | }, 46 | ], 47 | eslintrc: { 48 | enabled: true, 49 | }, 50 | vueTemplate: true, 51 | }), 52 | ], 53 | optimizeDeps: { 54 | exclude: [ 55 | 'vuetify', 56 | 'vue-router', 57 | 'unplugin-vue-router/runtime', 58 | 'unplugin-vue-router/data-loaders', 59 | 'unplugin-vue-router/data-loaders/basic', 60 | ], 61 | }, 62 | define: { 'process.env': {} }, 63 | resolve: { 64 | alias: { 65 | '@': fileURLToPath(new URL('src', import.meta.url)), 66 | }, 67 | extensions: [ 68 | '.js', 69 | '.json', 70 | '.jsx', 71 | '.mjs', 72 | '.ts', 73 | '.tsx', 74 | '.vue', 75 | ], 76 | }, 77 | server: { 78 | port: 3000, 79 | }, 80 | css: { 81 | preprocessorOptions: { 82 | sass: { 83 | api: 'modern-compiler', 84 | }, 85 | scss: { 86 | api: 'modern-compiler', 87 | }, 88 | }, 89 | }, 90 | }) 91 | -------------------------------------------------------------------------------- /template/typescript/base/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /template/typescript/base/eslint.config.js: -------------------------------------------------------------------------------- 1 | import vuetify from 'eslint-config-vuetify' 2 | 3 | export default vuetify() 4 | -------------------------------------------------------------------------------- /template/typescript/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "eslint . --fix" 4 | }, 5 | "devDependencies": { 6 | "eslint": "^9.23.0", 7 | "eslint-config-vuetify": "^4.0.0", 8 | "vue-router": "^4.5.0", 9 | "unplugin-vue-router": "^0.12.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /template/typescript/base/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /template/typescript/base/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 91 | -------------------------------------------------------------------------------- /template/typescript/base/src/pages/README.md: -------------------------------------------------------------------------------- 1 | # Pages 2 | 3 | Vue components created in this folder will automatically be converted to navigatable routes. 4 | 5 | Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository. 6 | -------------------------------------------------------------------------------- /template/typescript/base/src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /template/typescript/base/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.ts 3 | * 4 | * Automatically included in `./src/main.ts` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from './vuetify' 9 | import router from '../router' 10 | 11 | // Types 12 | import type { App } from 'vue' 13 | 14 | export function registerPlugins (app: App) { 15 | app 16 | .use(vuetify) 17 | .use(router) 18 | } 19 | -------------------------------------------------------------------------------- /template/typescript/base/src/router/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * router/index.ts 3 | * 4 | * Automatic routes for `./src/pages/*.vue` 5 | */ 6 | 7 | // Composables 8 | import { createRouter, createWebHistory } from 'vue-router/auto' 9 | import { routes } from 'vue-router/auto-routes' 10 | 11 | const router = createRouter({ 12 | history: createWebHistory(import.meta.env.BASE_URL), 13 | routes, 14 | }) 15 | 16 | // Workaround for https://github.com/vitejs/vite/issues/11804 17 | router.onError((err, to) => { 18 | if (err?.message?.includes?.('Failed to fetch dynamically imported module')) { 19 | if (localStorage.getItem('vuetify:dynamic-reload')) { 20 | console.error('Dynamic import error, reloading page did not fix it', err) 21 | } else { 22 | console.log('Reloading page to fix dynamic import error') 23 | localStorage.setItem('vuetify:dynamic-reload', 'true') 24 | location.assign(to.fullPath) 25 | } 26 | } else { 27 | console.error(err) 28 | } 29 | }) 30 | 31 | router.isReady().then(() => { 32 | localStorage.removeItem('vuetify:dynamic-reload') 33 | }) 34 | 35 | export default router 36 | -------------------------------------------------------------------------------- /template/typescript/base/src/styles/README.md: -------------------------------------------------------------------------------- 1 | # Styles 2 | 3 | This directory is for configuring the styles of the application. 4 | -------------------------------------------------------------------------------- /template/typescript/base/src/styles/settings.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * src/styles/settings.scss 3 | * 4 | * Configures SASS variables and Vuetify overwrites 5 | */ 6 | 7 | // https://vuetifyjs.com/features/sass-variables/` 8 | // @use 'vuetify/settings' with ( 9 | // $color-pack: false 10 | // ); 11 | -------------------------------------------------------------------------------- /template/typescript/base/vite.config.mts: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import Components from 'unplugin-vue-components/vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 5 | import Fonts from 'unplugin-fonts/vite' 6 | import VueRouter from 'unplugin-vue-router/vite' 7 | 8 | // Utilities 9 | import { defineConfig } from 'vite' 10 | import { fileURLToPath, URL } from 'node:url' 11 | 12 | // https://vitejs.dev/config/ 13 | export default defineConfig({ 14 | plugins: [ 15 | VueRouter({ 16 | dts: 'src/typed-router.d.ts', 17 | }), 18 | Vue({ 19 | template: { transformAssetUrls }, 20 | }), 21 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 22 | Vuetify({ 23 | autoImport: true, 24 | styles: { 25 | configFile: 'src/styles/settings.scss', 26 | }, 27 | }), 28 | Components({ 29 | dts: 'src/components.d.ts', 30 | }), 31 | Fonts({ 32 | fontsource: { 33 | families: [ 34 | { 35 | name: 'Roboto', 36 | weights: [100, 300, 400, 500, 700, 900], 37 | styles: ['normal', 'italic'], 38 | }, 39 | ], 40 | }, 41 | }), 42 | ], 43 | optimizeDeps: { 44 | exclude: [ 45 | 'vuetify', 46 | 'vue-router', 47 | 'unplugin-vue-router/runtime', 48 | 'unplugin-vue-router/data-loaders', 49 | 'unplugin-vue-router/data-loaders/basic', 50 | ], 51 | }, 52 | define: { 'process.env': {} }, 53 | resolve: { 54 | alias: { 55 | '@': fileURLToPath(new URL('src', import.meta.url)), 56 | }, 57 | extensions: [ 58 | '.js', 59 | '.json', 60 | '.jsx', 61 | '.mjs', 62 | '.ts', 63 | '.tsx', 64 | '.vue', 65 | ], 66 | }, 67 | server: { 68 | port: 3000, 69 | }, 70 | css: { 71 | preprocessorOptions: { 72 | sass: { 73 | api: 'modern-compiler', 74 | }, 75 | scss: { 76 | api: 'modern-compiler', 77 | }, 78 | }, 79 | }, 80 | }) 81 | -------------------------------------------------------------------------------- /template/typescript/default/README.md: -------------------------------------------------------------------------------- 1 | # Vuetify (Default) 2 | 3 | This is the official scaffolding tool for Vuetify, designed to give you a head start in building your new Vuetify application. It sets up a base template with all the necessary configurations and standard directory structure, enabling you to begin development without the hassle of setting up the project from scratch. 4 | 5 | ## ❗️ Important Links 6 | 7 | - 📄 [Docs](https://vuetifyjs.com/) 8 | - 🚨 [Issues](https://issues.vuetifyjs.com/) 9 | - 🏬 [Store](https://store.vuetifyjs.com/) 10 | - 🎮 [Playground](https://play.vuetifyjs.com/) 11 | - 💬 [Discord](https://community.vuetifyjs.com) 12 | 13 | ## 💿 Install 14 | 15 | Set up your project using your preferred package manager. Use the corresponding command to install the dependencies: 16 | 17 | | Package Manager | Command | 18 | |---------------------------------------------------------------|----------------| 19 | | [yarn](https://yarnpkg.com/getting-started) | `yarn install` | 20 | | [npm](https://docs.npmjs.com/cli/v7/commands/npm-install) | `npm install` | 21 | | [pnpm](https://pnpm.io/installation) | `pnpm install` | 22 | | [bun](https://bun.sh/#getting-started) | `bun install` | 23 | 24 | After completing the installation, your environment is ready for Vuetify development. 25 | 26 | ## ✨ Features 27 | 28 | - 🖼️ **Optimized Front-End Stack**: Leverage the latest Vue 3 and Vuetify 3 for a modern, reactive UI development experience. [Vue 3](https://v3.vuejs.org/) | [Vuetify 3](https://vuetifyjs.com/en/) 29 | - 🗃️ **State Management**: Integrated with [Pinia](https://pinia.vuejs.org/), the intuitive, modular state management solution for Vue. 30 | - 🚦 **Routing and Layouts**: Utilizes Vue Router for SPA navigation and vite-plugin-vue-layouts-next for organizing Vue file layouts. [Vue Router](https://router.vuejs.org/) | [vite-plugin-vue-layouts-next](https://github.com/loicduong/vite-plugin-vue-layouts-next) 31 | - 💻 **Enhanced Development Experience**: Benefit from TypeScript's static type checking and the ESLint plugin suite for Vue, ensuring code quality and consistency. [TypeScript](https://www.typescriptlang.org/) | [ESLint Plugin Vue](https://eslint.vuejs.org/) 32 | - ⚡ **Next-Gen Tooling**: Powered by Vite, experience fast cold starts and instant HMR (Hot Module Replacement). [Vite](https://vitejs.dev/) 33 | - 🧩 **Automated Component Importing**: Streamline your workflow with unplugin-vue-components, automatically importing components as you use them. [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components) 34 | - 🛠️ **Strongly-Typed Vue**: Use vue-tsc for type-checking your Vue components, and enjoy a robust development experience. [vue-tsc](https://github.com/johnsoncodehk/volar/tree/master/packages/vue-tsc) 35 | 36 | These features are curated to provide a seamless development experience from setup to deployment, ensuring that your Vuetify application is both powerful and maintainable. 37 | 38 | ## 💡 Usage 39 | 40 | This section covers how to start the development server and build your project for production. 41 | 42 | ### Starting the Development Server 43 | 44 | To start the development server with hot-reload, run the following command. The server will be accessible at [http://localhost:3000](http://localhost:3000): 45 | 46 | ```bash 47 | yarn dev 48 | ``` 49 | 50 | (Repeat for npm, pnpm, and bun with respective commands.) 51 | 52 | > Add NODE_OPTIONS='--no-warnings' to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script. 53 | 54 | ### Building for Production 55 | 56 | To build your project for production, use: 57 | 58 | ```bash 59 | yarn build 60 | ``` 61 | 62 | (Repeat for npm, pnpm, and bun with respective commands.) 63 | 64 | Once the build process is completed, your application will be ready for deployment in a production environment. 65 | 66 | ## 💪 Support Vuetify Development 67 | 68 | This project is built with [Vuetify](https://vuetifyjs.com/en/), a UI Library with a comprehensive collection of Vue components. Vuetify is an MIT licensed Open Source project that has been made possible due to the generous contributions by our [sponsors and backers](https://vuetifyjs.com/introduction/sponsors-and-backers/). If you are interested in supporting this project, please consider: 69 | 70 | - [Requesting Enterprise Support](https://support.vuetifyjs.com/) 71 | - [Sponsoring John on Github](https://github.com/users/johnleider/sponsorship) 72 | - [Sponsoring Kael on Github](https://github.com/users/kaelwd/sponsorship) 73 | - [Supporting the team on Open Collective](https://opencollective.com/vuetify) 74 | - [Becoming a sponsor on Patreon](https://www.patreon.com/vuetify) 75 | - [Becoming a subscriber on Tidelift](https://tidelift.com/subscription/npm/vuetify) 76 | - [Making a one-time donation with Paypal](https://paypal.me/vuetify) 77 | 78 | ## 📑 License 79 | [MIT](http://opensource.org/licenses/MIT) 80 | 81 | Copyright (c) 2016-present Vuetify, LLC 82 | -------------------------------------------------------------------------------- /template/typescript/default/_browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /template/typescript/default/_editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue}] 2 | charset = utf-8 3 | indent_size = 2 4 | indent_style = space 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | -------------------------------------------------------------------------------- /template/typescript/default/_gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /template/typescript/default/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /template/typescript/default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Welcome to Vuetify 3 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /template/typescript/default/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "run-p type-check \"build-only {@}\" --", 8 | "preview": "vite preview", 9 | "build-only": "vite build", 10 | "type-check": "vue-tsc --build --force" 11 | }, 12 | "dependencies": { 13 | "@mdi/font": "7.4.47", 14 | "@fontsource/roboto": "5.2.5", 15 | "vue": "^3.5.13", 16 | "vuetify": "^3.8.1" 17 | }, 18 | "devDependencies": { 19 | "@tsconfig/node22": "^22.0.0", 20 | "@types/node": "^22.9.0", 21 | "@vitejs/plugin-vue": "^5.2.3", 22 | "@vue/tsconfig": "^0.7.0", 23 | "npm-run-all2": "^7.0.2", 24 | "sass-embedded": "^1.86.3", 25 | "typescript": "~5.8.2", 26 | "unplugin-fonts": "^1.3.1", 27 | "unplugin-vue-components": "^28.4.1", 28 | "vite-plugin-vuetify": "^2.1.1", 29 | "vite": "^6.2.2", 30 | "vue-tsc": "^2.2.8" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /template/typescript/default/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuetifyjs/create/3bd143d7463a84de18766d0f6f93d6eddfc2d070/template/typescript/default/public/favicon.ico -------------------------------------------------------------------------------- /template/typescript/default/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /template/typescript/default/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuetifyjs/create/3bd143d7463a84de18766d0f6f93d6eddfc2d070/template/typescript/default/src/assets/logo.png -------------------------------------------------------------------------------- /template/typescript/default/src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /template/typescript/default/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 91 | -------------------------------------------------------------------------------- /template/typescript/default/src/components/README.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | Vue template files in this folder are automatically imported. 4 | 5 | ## 🚀 Usage 6 | 7 | Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it. 8 | 9 | The following example assumes a component located at `src/components/MyComponent.vue`: 10 | 11 | ```vue 12 | 17 | 18 | 21 | ``` 22 | 23 | When your template is rendered, the component's import will automatically be inlined, which renders to this: 24 | 25 | ```vue 26 | 31 | 32 | 35 | ``` 36 | -------------------------------------------------------------------------------- /template/typescript/default/src/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * main.ts 3 | * 4 | * Bootstraps Vuetify and other plugins then mounts the App` 5 | */ 6 | 7 | // Plugins 8 | import { registerPlugins } from '@/plugins' 9 | 10 | // Components 11 | import App from './App.vue' 12 | 13 | // Composables 14 | import { createApp } from 'vue' 15 | 16 | // Styles 17 | import 'unfonts.css' 18 | 19 | const app = createApp(App) 20 | 21 | registerPlugins(app) 22 | 23 | app.mount('#app') 24 | -------------------------------------------------------------------------------- /template/typescript/default/src/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally. 4 | -------------------------------------------------------------------------------- /template/typescript/default/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.ts 3 | * 4 | * Automatically included in `./src/main.ts` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from './vuetify' 9 | 10 | // Types 11 | import type { App } from 'vue' 12 | 13 | export function registerPlugins (app: App) { 14 | app.use(vuetify) 15 | } 16 | -------------------------------------------------------------------------------- /template/typescript/default/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/vuetify.ts 3 | * 4 | * Framework documentation: https://vuetifyjs.com` 5 | */ 6 | 7 | // Styles 8 | import '@mdi/font/css/materialdesignicons.css' 9 | import 'vuetify/styles' 10 | 11 | // Composables 12 | import { createVuetify } from 'vuetify' 13 | 14 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 15 | export default createVuetify({ 16 | theme: { 17 | defaultTheme: 'dark', 18 | }, 19 | }) 20 | -------------------------------------------------------------------------------- /template/typescript/default/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 8 | 9 | "baseUrl": ".", 10 | "paths": { 11 | "@/*": ["./src/*"] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /template/typescript/default/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { 5 | "path": "./tsconfig.node.json" 6 | }, 7 | { 8 | "path": "./tsconfig.app.json" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /template/typescript/default/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node22/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "noEmit": true, 13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 14 | 15 | "module": "ESNext", 16 | "moduleResolution": "Bundler", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /template/typescript/default/vite.config.mts: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import Components from 'unplugin-vue-components/vite' 3 | import Vue from '@vitejs/plugin-vue' 4 | import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 5 | import Fonts from 'unplugin-fonts/vite' 6 | 7 | // Utilities 8 | import { defineConfig } from 'vite' 9 | import { fileURLToPath, URL } from 'node:url' 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig({ 13 | plugins: [ 14 | Vue({ 15 | template: { transformAssetUrls }, 16 | }), 17 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 18 | Vuetify(), 19 | Components(), 20 | Fonts({ 21 | fontsource: { 22 | families: [ 23 | { 24 | name: 'Roboto', 25 | weights: [100, 300, 400, 500, 700, 900], 26 | styles: ['normal', 'italic'], 27 | }, 28 | ], 29 | }, 30 | }), 31 | ], 32 | optimizeDeps: { 33 | exclude: ['vuetify'], 34 | }, 35 | define: { 'process.env': {} }, 36 | resolve: { 37 | alias: { 38 | '@': fileURLToPath(new URL('src', import.meta.url)), 39 | }, 40 | extensions: [ 41 | '.js', 42 | '.json', 43 | '.jsx', 44 | '.mjs', 45 | '.ts', 46 | '.tsx', 47 | '.vue', 48 | ], 49 | }, 50 | server: { 51 | port: 3000, 52 | }, 53 | css: { 54 | preprocessorOptions: { 55 | sass: { 56 | api: 'modern-compiler', 57 | }, 58 | scss: { 59 | api: 'modern-compiler', 60 | }, 61 | }, 62 | }, 63 | }) 64 | -------------------------------------------------------------------------------- /template/typescript/essentials/_eslintrc-auto-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "Component": true, 4 | "ComponentPublicInstance": true, 5 | "ComputedRef": true, 6 | "DirectiveBinding": true, 7 | "EffectScope": true, 8 | "ExtractDefaultPropTypes": true, 9 | "ExtractPropTypes": true, 10 | "ExtractPublicPropTypes": true, 11 | "InjectionKey": true, 12 | "MaybeRef": true, 13 | "MaybeRefOrGetter": true, 14 | "PropType": true, 15 | "Ref": true, 16 | "Slot": true, 17 | "Slots": true, 18 | "VNode": true, 19 | "WritableComputedRef": true, 20 | "computed": true, 21 | "createApp": true, 22 | "customRef": true, 23 | "defineAsyncComponent": true, 24 | "defineComponent": true, 25 | "defineStore": true, 26 | "effectScope": true, 27 | "getCurrentInstance": true, 28 | "getCurrentScope": true, 29 | "h": true, 30 | "inject": true, 31 | "isProxy": true, 32 | "isReactive": true, 33 | "isReadonly": true, 34 | "isRef": true, 35 | "markRaw": true, 36 | "nextTick": true, 37 | "onActivated": true, 38 | "onBeforeMount": true, 39 | "onBeforeRouteLeave": true, 40 | "onBeforeRouteUpdate": true, 41 | "onBeforeUnmount": true, 42 | "onBeforeUpdate": true, 43 | "onDeactivated": true, 44 | "onErrorCaptured": true, 45 | "onMounted": true, 46 | "onRenderTracked": true, 47 | "onRenderTriggered": true, 48 | "onScopeDispose": true, 49 | "onServerPrefetch": true, 50 | "onUnmounted": true, 51 | "onUpdated": true, 52 | "onWatcherCleanup": true, 53 | "provide": true, 54 | "reactive": true, 55 | "readonly": true, 56 | "ref": true, 57 | "resolveComponent": true, 58 | "shallowReactive": true, 59 | "shallowReadonly": true, 60 | "shallowRef": true, 61 | "storeToRefs": true, 62 | "toRaw": true, 63 | "toRef": true, 64 | "toRefs": true, 65 | "toValue": true, 66 | "triggerRef": true, 67 | "unref": true, 68 | "useAttrs": true, 69 | "useCssModule": true, 70 | "useCssVars": true, 71 | "useId": true, 72 | "useModel": true, 73 | "useRoute": true, 74 | "useRouter": true, 75 | "useSlots": true, 76 | "useTemplateRef": true, 77 | "watch": true, 78 | "watchEffect": true, 79 | "watchPostEffect": true, 80 | "watchSyncEffect": true 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /template/typescript/essentials/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /template/typescript/essentials/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "pinia" : "^3.0.1", 4 | "unplugin-auto-import": "^19.1.1", 5 | "vite-plugin-vue-layouts-next": "^0.0.8" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | // biome-ignore lint: disable 7 | export {} 8 | declare global { 9 | const EffectScope: typeof import('vue')['EffectScope'] 10 | const computed: typeof import('vue')['computed'] 11 | const createApp: typeof import('vue')['createApp'] 12 | const customRef: typeof import('vue')['customRef'] 13 | const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 14 | const defineComponent: typeof import('vue')['defineComponent'] 15 | const defineStore: typeof import('pinia')['defineStore'] 16 | const effectScope: typeof import('vue')['effectScope'] 17 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 18 | const getCurrentScope: typeof import('vue')['getCurrentScope'] 19 | const h: typeof import('vue')['h'] 20 | const inject: typeof import('vue')['inject'] 21 | const isProxy: typeof import('vue')['isProxy'] 22 | const isReactive: typeof import('vue')['isReactive'] 23 | const isReadonly: typeof import('vue')['isReadonly'] 24 | const isRef: typeof import('vue')['isRef'] 25 | const markRaw: typeof import('vue')['markRaw'] 26 | const nextTick: typeof import('vue')['nextTick'] 27 | const onActivated: typeof import('vue')['onActivated'] 28 | const onBeforeMount: typeof import('vue')['onBeforeMount'] 29 | const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] 30 | const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] 31 | const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 32 | const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 33 | const onDeactivated: typeof import('vue')['onDeactivated'] 34 | const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 35 | const onMounted: typeof import('vue')['onMounted'] 36 | const onRenderTracked: typeof import('vue')['onRenderTracked'] 37 | const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 38 | const onScopeDispose: typeof import('vue')['onScopeDispose'] 39 | const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 40 | const onUnmounted: typeof import('vue')['onUnmounted'] 41 | const onUpdated: typeof import('vue')['onUpdated'] 42 | const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] 43 | const provide: typeof import('vue')['provide'] 44 | const reactive: typeof import('vue')['reactive'] 45 | const readonly: typeof import('vue')['readonly'] 46 | const ref: typeof import('vue')['ref'] 47 | const resolveComponent: typeof import('vue')['resolveComponent'] 48 | const shallowReactive: typeof import('vue')['shallowReactive'] 49 | const shallowReadonly: typeof import('vue')['shallowReadonly'] 50 | const shallowRef: typeof import('vue')['shallowRef'] 51 | const storeToRefs: typeof import('pinia')['storeToRefs'] 52 | const toRaw: typeof import('vue')['toRaw'] 53 | const toRef: typeof import('vue')['toRef'] 54 | const toRefs: typeof import('vue')['toRefs'] 55 | const toValue: typeof import('vue')['toValue'] 56 | const triggerRef: typeof import('vue')['triggerRef'] 57 | const unref: typeof import('vue')['unref'] 58 | const useAttrs: typeof import('vue')['useAttrs'] 59 | const useCssModule: typeof import('vue')['useCssModule'] 60 | const useCssVars: typeof import('vue')['useCssVars'] 61 | const useId: typeof import('vue')['useId'] 62 | const useModel: typeof import('vue')['useModel'] 63 | const useRoute: typeof import('vue-router')['useRoute'] 64 | const useRouter: typeof import('vue-router')['useRouter'] 65 | const useSlots: typeof import('vue')['useSlots'] 66 | const useTemplateRef: typeof import('vue')['useTemplateRef'] 67 | const watch: typeof import('vue')['watch'] 68 | const watchEffect: typeof import('vue')['watchEffect'] 69 | const watchPostEffect: typeof import('vue')['watchPostEffect'] 70 | const watchSyncEffect: typeof import('vue')['watchSyncEffect'] 71 | } 72 | // for type re-export 73 | declare global { 74 | // @ts-ignore 75 | export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' 76 | import('vue') 77 | } 78 | 79 | // for vue template auto import 80 | import { UnwrapRef } from 'vue' 81 | declare module 'vue' { 82 | interface GlobalComponents {} 83 | interface ComponentCustomProperties { 84 | readonly EffectScope: UnwrapRef 85 | readonly computed: UnwrapRef 86 | readonly createApp: UnwrapRef 87 | readonly customRef: UnwrapRef 88 | readonly defineAsyncComponent: UnwrapRef 89 | readonly defineComponent: UnwrapRef 90 | readonly defineStore: UnwrapRef 91 | readonly effectScope: UnwrapRef 92 | readonly getCurrentInstance: UnwrapRef 93 | readonly getCurrentScope: UnwrapRef 94 | readonly h: UnwrapRef 95 | readonly inject: UnwrapRef 96 | readonly isProxy: UnwrapRef 97 | readonly isReactive: UnwrapRef 98 | readonly isReadonly: UnwrapRef 99 | readonly isRef: UnwrapRef 100 | readonly markRaw: UnwrapRef 101 | readonly nextTick: UnwrapRef 102 | readonly onActivated: UnwrapRef 103 | readonly onBeforeMount: UnwrapRef 104 | readonly onBeforeRouteLeave: UnwrapRef 105 | readonly onBeforeRouteUpdate: UnwrapRef 106 | readonly onBeforeUnmount: UnwrapRef 107 | readonly onBeforeUpdate: UnwrapRef 108 | readonly onDeactivated: UnwrapRef 109 | readonly onErrorCaptured: UnwrapRef 110 | readonly onMounted: UnwrapRef 111 | readonly onRenderTracked: UnwrapRef 112 | readonly onRenderTriggered: UnwrapRef 113 | readonly onScopeDispose: UnwrapRef 114 | readonly onServerPrefetch: UnwrapRef 115 | readonly onUnmounted: UnwrapRef 116 | readonly onUpdated: UnwrapRef 117 | readonly onWatcherCleanup: UnwrapRef 118 | readonly provide: UnwrapRef 119 | readonly reactive: UnwrapRef 120 | readonly readonly: UnwrapRef 121 | readonly ref: UnwrapRef 122 | readonly resolveComponent: UnwrapRef 123 | readonly shallowReactive: UnwrapRef 124 | readonly shallowReadonly: UnwrapRef 125 | readonly shallowRef: UnwrapRef 126 | readonly storeToRefs: UnwrapRef 127 | readonly toRaw: UnwrapRef 128 | readonly toRef: UnwrapRef 129 | readonly toRefs: UnwrapRef 130 | readonly toValue: UnwrapRef 131 | readonly triggerRef: UnwrapRef 132 | readonly unref: UnwrapRef 133 | readonly useAttrs: UnwrapRef 134 | readonly useCssModule: UnwrapRef 135 | readonly useCssVars: UnwrapRef 136 | readonly useId: UnwrapRef 137 | readonly useModel: UnwrapRef 138 | readonly useRoute: UnwrapRef 139 | readonly useRouter: UnwrapRef 140 | readonly useSlots: UnwrapRef 141 | readonly useTemplateRef: UnwrapRef 142 | readonly watch: UnwrapRef 143 | readonly watchEffect: UnwrapRef 144 | readonly watchPostEffect: UnwrapRef 145 | readonly watchSyncEffect: UnwrapRef 146 | } 147 | } -------------------------------------------------------------------------------- /template/typescript/essentials/src/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | export {} 7 | 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | HelloWorld: typeof import('./src/components/HelloWorld.vue')['default'] 11 | RouterLink: typeof import('vue-router')['RouterLink'] 12 | RouterView: typeof import('vue-router')['RouterView'] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 73 | 74 | 83 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/layouts/README.md: -------------------------------------------------------------------------------- 1 | # Layouts 2 | 3 | Layouts are reusable components that wrap around pages. They are used to provide a consistent look and feel across multiple pages. 4 | 5 | Full documentation for this feature can be found in the Official [vite-plugin-vue-layouts-next](https://github.com/loicduong/vite-plugin-vue-layouts-next) repository. 6 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/index.ts 3 | * 4 | * Automatically included in `./src/main.ts` 5 | */ 6 | 7 | // Plugins 8 | import vuetify from './vuetify' 9 | import pinia from '../stores' 10 | import router from '../router' 11 | 12 | // Types 13 | import type { App } from 'vue' 14 | 15 | export function registerPlugins (app: App) { 16 | app 17 | .use(vuetify) 18 | .use(router) 19 | .use(pinia) 20 | } 21 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/router/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * router/index.ts 3 | * 4 | * Automatic routes for `./src/pages/*.vue` 5 | */ 6 | 7 | // Composables 8 | import { createRouter, createWebHistory } from 'vue-router/auto' 9 | import { setupLayouts } from 'virtual:generated-layouts' 10 | import { routes } from 'vue-router/auto-routes' 11 | 12 | const router = createRouter({ 13 | history: createWebHistory(import.meta.env.BASE_URL), 14 | routes: setupLayouts(routes), 15 | }) 16 | 17 | // Workaround for https://github.com/vitejs/vite/issues/11804 18 | router.onError((err, to) => { 19 | if (err?.message?.includes?.('Failed to fetch dynamically imported module')) { 20 | if (localStorage.getItem('vuetify:dynamic-reload')) { 21 | console.error('Dynamic import error, reloading page did not fix it', err) 22 | } else { 23 | console.log('Reloading page to fix dynamic import error') 24 | localStorage.setItem('vuetify:dynamic-reload', 'true') 25 | location.assign(to.fullPath) 26 | } 27 | } else { 28 | console.error(err) 29 | } 30 | }) 31 | 32 | router.isReady().then(() => { 33 | localStorage.removeItem('vuetify:dynamic-reload') 34 | }) 35 | 36 | export default router 37 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/stores/README.md: -------------------------------------------------------------------------------- 1 | # Store 2 | 3 | Pinia stores are used to store reactive state and expose actions to mutate it. 4 | 5 | Full documentation for this feature can be found in the Official [Pinia](https://pinia.esm.dev/) repository. 6 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/stores/app.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { defineStore } from 'pinia' 3 | 4 | export const useAppStore = defineStore('app', { 5 | state: () => ({ 6 | // 7 | }), 8 | }) 9 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/stores/index.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { createPinia } from 'pinia' 3 | 4 | export default createPinia() 5 | -------------------------------------------------------------------------------- /template/typescript/essentials/src/typed-router.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️ 5 | // It's recommended to commit this file. 6 | // Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry. 7 | 8 | declare module 'vue-router/auto-routes' { 9 | import type { 10 | RouteRecordInfo, 11 | ParamValue, 12 | ParamValueOneOrMore, 13 | ParamValueZeroOrMore, 14 | ParamValueZeroOrOne, 15 | } from 'unplugin-vue-router/types' 16 | 17 | /** 18 | * Route name map generated by unplugin-vue-router 19 | */ 20 | export interface RouteNamedMap { 21 | '/': RouteRecordInfo<'/', '/', Record, Record>, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /template/typescript/essentials/vite.config.mts: -------------------------------------------------------------------------------- 1 | // Plugins 2 | import AutoImport from 'unplugin-auto-import/vite' 3 | import Components from 'unplugin-vue-components/vite' 4 | import Fonts from 'unplugin-fonts/vite' 5 | import Layouts from 'vite-plugin-vue-layouts-next' 6 | import Vue from '@vitejs/plugin-vue' 7 | import VueRouter from 'unplugin-vue-router/vite' 8 | import { VueRouterAutoImports } from 'unplugin-vue-router' 9 | import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 10 | 11 | // Utilities 12 | import { defineConfig } from 'vite' 13 | import { fileURLToPath, URL } from 'node:url' 14 | 15 | // https://vitejs.dev/config/ 16 | export default defineConfig({ 17 | plugins: [ 18 | VueRouter({ 19 | dts: 'src/typed-router.d.ts', 20 | }), 21 | Layouts(), 22 | AutoImport({ 23 | imports: [ 24 | 'vue', 25 | VueRouterAutoImports, 26 | { 27 | pinia: ['defineStore', 'storeToRefs'], 28 | }, 29 | ], 30 | dts: 'src/auto-imports.d.ts', 31 | eslintrc: { 32 | enabled: true, 33 | }, 34 | vueTemplate: true, 35 | }), 36 | Components({ 37 | dts: 'src/components.d.ts', 38 | }), 39 | Vue({ 40 | template: { transformAssetUrls }, 41 | }), 42 | // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme 43 | Vuetify({ 44 | autoImport: true, 45 | styles: { 46 | configFile: 'src/styles/settings.scss', 47 | }, 48 | }), 49 | Fonts({ 50 | fontsource: { 51 | families: [ 52 | { 53 | name: 'Roboto', 54 | weights: [100, 300, 400, 500, 700, 900], 55 | styles: ['normal', 'italic'], 56 | }, 57 | ], 58 | }, 59 | }), 60 | ], 61 | optimizeDeps: { 62 | exclude: [ 63 | 'vuetify', 64 | 'vue-router', 65 | 'unplugin-vue-router/runtime', 66 | 'unplugin-vue-router/data-loaders', 67 | 'unplugin-vue-router/data-loaders/basic', 68 | ], 69 | }, 70 | define: { 'process.env': {} }, 71 | resolve: { 72 | alias: { 73 | '@': fileURLToPath(new URL('src', import.meta.url)), 74 | }, 75 | extensions: [ 76 | '.js', 77 | '.json', 78 | '.jsx', 79 | '.mjs', 80 | '.ts', 81 | '.tsx', 82 | '.vue', 83 | ], 84 | }, 85 | server: { 86 | port: 3000, 87 | }, 88 | css: { 89 | preprocessorOptions: { 90 | sass: { 91 | api: 'modern-compiler', 92 | }, 93 | scss: { 94 | api: 'modern-compiler', 95 | }, 96 | }, 97 | }, 98 | }) 99 | -------------------------------------------------------------------------------- /template/typescript/nuxt/app-layout.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /template/typescript/nuxt/app.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /template/typescript/nuxt/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuetifyjs/create/3bd143d7463a84de18766d0f6f93d6eddfc2d070/template/typescript/nuxt/assets/logo.png -------------------------------------------------------------------------------- /template/typescript/nuxt/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /template/typescript/nuxt/components/AppFooter.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 73 | 74 | 83 | -------------------------------------------------------------------------------- /template/typescript/nuxt/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 91 | -------------------------------------------------------------------------------- /template/typescript/nuxt/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /template/typescript/nuxt/modules/vuetify.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtModule } from '@nuxt/kit' 2 | import type { Options as ModuleOptions } from '@vuetify/loader-shared' 3 | import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' 4 | import path from 'upath' 5 | import { isObject, resolveVuetifyBase } from '@vuetify/loader-shared' 6 | import { pathToFileURL } from 'node:url' 7 | import fs from 'node:fs' 8 | import fsp from 'node:fs/promises' 9 | 10 | // WARNING: Remove the file from modules directory if you install vuetify-nuxt-module 11 | export default defineNuxtModule({ 12 | meta: { 13 | name: 'vuetify-module', 14 | configKey: 'vuetify', 15 | }, 16 | defaults: () => ({ styles: true }), 17 | setup (options, nuxt) { 18 | let configFile: string | undefined 19 | const vuetifyBase = resolveVuetifyBase() 20 | const noneFiles = new Set() 21 | let isNone = false 22 | let sassVariables = false 23 | const PREFIX = 'vuetify-styles/' 24 | const SSR_PREFIX = `/@${PREFIX}` 25 | const resolveCss = resolveCssFactory() 26 | 27 | nuxt.hook('vite:extendConfig', viteInlineConfig => { 28 | // add vuetify transformAssetUrls 29 | viteInlineConfig.vue ??= {} 30 | viteInlineConfig.vue.template ??= {} 31 | viteInlineConfig.vue.template.transformAssetUrls = transformAssetUrls 32 | 33 | viteInlineConfig.plugins = viteInlineConfig.plugins ?? [] 34 | viteInlineConfig.plugins.push(vuetify({ 35 | autoImport: options.autoImport, 36 | styles: true, 37 | })) 38 | 39 | viteInlineConfig.css ??= {} 40 | viteInlineConfig.css.preprocessorOptions ??= {} 41 | viteInlineConfig.css.preprocessorOptions.sass ??= {} 42 | viteInlineConfig.css.preprocessorOptions.sass.api = 'modern-compiler' 43 | viteInlineConfig.css.preprocessorOptions.scss ??= {} 44 | viteInlineConfig.css.preprocessorOptions.scss.api = 'modern-compiler' 45 | 46 | viteInlineConfig.plugins.push({ 47 | name: 'vuetify:nuxt:styles', 48 | enforce: 'pre', 49 | async configResolved (config) { 50 | if (isObject(options.styles)) { 51 | sassVariables = true 52 | configFile = path.isAbsolute(options.styles.configFile) ? path.resolve(options.styles.configFile) : path.resolve(path.join(config.root || process.cwd(), options.styles.configFile)) 53 | configFile = pathToFileURL(configFile).href 54 | } else { 55 | isNone = options.styles === 'none' 56 | } 57 | }, 58 | async resolveId (source, importer, { custom, ssr }) { 59 | if (source.startsWith(PREFIX) || source.startsWith(SSR_PREFIX)) { 60 | if (/\.s[ca]ss$/.test(source)) { 61 | return source 62 | } 63 | 64 | const idx = source.indexOf('?') 65 | return idx === -1 ? source : source.slice(0, idx) 66 | } 67 | if ( 68 | source === 'vuetify/styles' || ( 69 | importer 70 | && source.endsWith('.css') 71 | && isSubdir(vuetifyBase, path.isAbsolute(source) ? source : importer) 72 | ) 73 | ) { 74 | if (options.styles === 'sass') { 75 | return this.resolve(await resolveCss(source), importer, { skipSelf: true, custom }) 76 | } 77 | 78 | const resolution = await this.resolve(source, importer, { skipSelf: true, custom }) 79 | 80 | if (!resolution) { 81 | return undefined 82 | } 83 | 84 | const target = await resolveCss(resolution.id) 85 | if (isNone) { 86 | noneFiles.add(target) 87 | return target 88 | } 89 | 90 | return `${ssr ? SSR_PREFIX : PREFIX}${path.relative(vuetifyBase, target)}` 91 | } 92 | 93 | return undefined 94 | }, 95 | load (id) { 96 | if (sassVariables) { 97 | const target = id.startsWith(PREFIX) 98 | ? path.resolve(vuetifyBase, id.slice(PREFIX.length)) 99 | : (id.startsWith(SSR_PREFIX) 100 | ? path.resolve(vuetifyBase, id.slice(SSR_PREFIX.length)) 101 | : undefined) 102 | 103 | if (target) { 104 | const suffix = /\.scss/.test(target) ? ';\n' : '\n' 105 | return { 106 | code: `@use "${configFile}"${suffix}@use "${pathToFileURL(target).href}"${suffix}`, 107 | map: { 108 | mappings: '', 109 | }, 110 | } 111 | } 112 | } 113 | 114 | return isNone && noneFiles.has(id) ? '' : undefined 115 | }, 116 | }) 117 | }) 118 | }, 119 | }) 120 | 121 | function resolveCssFactory () { 122 | const mappings = new Map() 123 | return async (source: string) => { 124 | let mapping = mappings.get(source) 125 | if (!mapping) { 126 | try { 127 | mapping = source.replace(/\.css$/, '.sass') 128 | await fsp.access(mapping, fs.constants.R_OK) 129 | } catch (error) { 130 | if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) { 131 | throw error 132 | } 133 | mapping = source.replace(/\.css$/, '.scss') 134 | } 135 | mappings.set(source, mapping) 136 | } 137 | return mapping 138 | } 139 | } 140 | 141 | function isSubdir (root: string, test: string) { 142 | const relative = path.relative(root, test) 143 | return relative && !relative.startsWith('..') && !path.isAbsolute(relative) 144 | } 145 | 146 | export { type Options as ModuleOptions } from '@vuetify/loader-shared' 147 | -------------------------------------------------------------------------------- /template/typescript/nuxt/pages/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /template/typescript/nuxt/plugins/vuetify-nuxt.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtPlugin(nuxtApp => { 2 | // check https://vuetify-nuxt-module.netlify.app/guide/nuxt-runtime-hooks.html 3 | nuxtApp.hook('vuetify:before-create', options => { 4 | if (import.meta.client) { 5 | console.log('vuetify:before-create', options) 6 | } 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /template/typescript/nuxt/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import { createVuetify } from 'vuetify' 2 | 3 | export default defineNuxtPlugin(nuxtApp => { 4 | const vuetify = createVuetify({ 5 | // WARNING: when switching ssr option in nuxt.config.ts file you need to manually change it here 6 | ssr: true, 7 | // your Vuetify options here 8 | theme: { 9 | defaultTheme: 'dark', 10 | }, 11 | }) 12 | 13 | nuxtApp.vueApp.use(vuetify) 14 | }) 15 | -------------------------------------------------------------------------------- /template/typescript/nuxt/vuetify.config.ts: -------------------------------------------------------------------------------- 1 | import { defineVuetifyConfiguration } from 'vuetify-nuxt-module/custom-configuration' 2 | 3 | export default defineVuetifyConfiguration({ 4 | // your Vuetify options here 5 | theme: { 6 | defaultTheme: 'dark', 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "moduleResolution": "bundler", 5 | "resolveJsonModule": true, 6 | "useDefineForClassFields": true, 7 | "jsx": "preserve", 8 | "noImplicitThis": true, 9 | "strict": true, 10 | "isolatedModules": true, 11 | "target": "esnext", 12 | "esModuleInterop": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "types": [ 15 | "node" 16 | ] 17 | }, 18 | "include": ["./src"] 19 | } 20 | --------------------------------------------------------------------------------