Coder Social home page Coder Social logo

vitestrict's Introduction

ViteStrict

Vue 3 Composition API & Reactivity Transform / File-based routing / Layouts / APIs auto-importing
Vite / SSG / PWA / I18n / UnoCSS / Iconify / Pinia / Vitest / Cypress / Netlify / pnpm

Mocking up web app with the opinionated variation of the 🏕️ Opinionated Vite Starter Template.

☁️ Online Demo ☁️

💡 About this project

The project is based on 🏕️Vitesse by @antfu.
The template has been refactored to enforce an opinionated way of managing file structure for predictability and easy scalability with a large application in mind.
You will find a guide for the directory tree below.

The template aims to provide a clean basis with as little subjective preferences as possible (except for the pre-packed deps and the file architecture, of course).
Most of the configuration settings remain default. Recommended Eslint presets are included, but mostly not tweaked in any unnecessary way.

You may want to configure the starter to match your own personal preferences before use.

🧰 Features

Most of the precious Vitesse stuff is still here:

⚡️🗂🍍📑📲🎨😃🌍🔥🤙🏻🖨🦔🦾⚙️☁️

There are only a few notable differences:

🦧 Why

Vitesse is a great starting point for any Vue app. It has basically everything you could want to have in your package.json.
With a little of playing with it, however, I found the template to be a bit too far from where I could just start coding my things.
In my projects, I prefer to have clear, pre-imposed rules and practises for extending app's source code. So adding a component, composable, or any other feature comes without a single doubt, such as "where do I put these?". The development process should be intuitive, without ambiguities, and basically mindless.

Smaller repositories usually aren't too difficult to maintain. Flat component architecture will do just fine.
The larger projects are problematic though. The flat structure won't work, because components' parental prefixes will stack indefinitely. At the same time, nesting files has to be thoughtful and preferably pre-planned, or it can end up as a rabbit hole of frequent, time-consuming application refactoring.

I made this variation of Vitesse mainly for my own purposes, so I could start coding immediately after cloning the template without tweaking configs or remaking the directory tree.
It aims to scale predictably and enforce consistent development at the cost of freedom and a few amenities.

🚚 Clone template

npx degit brofrain/vitestrict my-new-app

🛠️ Project setup

pnpm i

🔥 Compile and hot-reload for development

pnpm dev

🩺 Lint

pnpm lint

🔍 Type check

pnpm tsc

🧹 Format with Prettier

pnpm format

🧪 Run tests with Vitest

pnpm test

pnpm test:run

pnpm test:coverage

🚀 Compile and minify for production

pnpm build

🏁 Checklist

  • Change the project name in package.json
  • Change the author name in LICENSE or simply remove the file
  • Change the favicon in public/
  • Track down all STARTER_DOCS comments across the template, make sure you're familiar with the functionality of the related code, and then remove the comment

🌳 Project structure

📁app
├─ 📁cypress                      # Cypress tests
├─ 📁dist                         # output directory
├─ 📁locales                      # translation files
├─ 📁node_modules
├─ 📁public                       # static assets (https://vitejs.dev/guide/assets.html#the-public-directory)
├─ 📁src                          # source code
├─ 📁test                         # Vitest tests
├─ ⚙️.env                         # environment variables
├─ ⚙️.eslintrc                    # Eslint config
├─ ⚙️.gitignore
├─ ⚙️.npmrc                       # pnpm config
├─ ⚙️.prettierrc                  # Prettier config
├─ 🚩index.html                   # Vite entry point
├─ 🏷️LICENSE
├─ 📦package.json                 # package config
├─ 🔒pnpm-lock.yaml
├─ 📜README.md
├─ ⚙️tsconfig.json                # TypeScript config
├─ ⚙️unocss.config.json           # UnoCSS config
└─ ⚙️vite.config.ts               # Vite config

src/

📁src
├─ 📁api                          # functions connecting the application with external services
├─ 📁assets                       # assets used by the application (images, videos, locally stored fonts etc.)
├─ 📁components                   # components
├─ 📁composables                  # composables
├─ 📁helpers                      # helper functions
├─ 📁layouts                      # layouts used by vite-plugin-vue-layouts
├─ 📁modules                      # plugin modules
├─ 📁pages                        # views used by vite-plugin-pages & Vue Router
├─ 📁stores                       # Pinia stores
├─ 📁styles                       # styles
├─ 📁types                        # types
├─ 🔻App.vue                      # root component of the application
├─ 🔹main.ts                      # initialization code of the application
├─ 🔹env.d.ts                     # type declarations for environment variables
├─ 🔹shims.d.ts                   # module declaration for .vue files
├─ 🔹__vite_auto-imports.d.ts     # type declarations for unplugin-auto-import plugin (auto-generated - don't edit)
└─ 🔹__vite_components.d.ts       # type declarations for unplugin-vue-components plugin (auto-generated - don't edit)

More detailed subfolders descriptions:

src/api/

Files of directories inside api/ should contain functions that return ready-to-use data fetched from external services.

📁api
├─ 📁gql   # GraphQL
├─ 📁rest  # REST
└─ 📁ws    # WebSocket

It's best to group the functions by their functionality scope:

📁api
└─ 📁rest
   ├─ 🔹blog.ts
   └─ 🔹user.ts

or API used:

📁api
└─ 📁rest
   ├─ 🔹github.ts
   └─ 🔹gitlab.ts

It's very likely that you will utilize only one of these techniques in your project. Feel free to simplify the architecture then:

📁api
├─ 🔹blog.ts
└─ 🔹user.ts

src/assets/

Assets referenced in the source code should be stored here.
👉 Vite Asset Handling Guide

📁assets
├─ 🖼️example.png
└─ 🖼️example.svg
📁assets
├─ 📁img
│  ├─ 🖼️example.png
│  └─ 🖼️example.svg
├─ 📁font
│  └─ 🆎example.ttf
└─ 📁media
   └─ 🎞️example.mp4

src/components/

📁components
├─ 📁_base
├─ 📁&app
├─ 📁&shared
└─ 📁[page-name]
Components folder structure:
  • _base/ should contain base components (e.g. buttons, inputs, tables) widely reused in the application.
    These components are globally auto-imported (thanks to unplugin-vue-components) and receive "base" prefix (the prefix can be changed via vite.config.ts).
    It's recommended to use kebab-case in source code for them to clearly distinguish globally-available components from the manually imported ones.
    The "_" directory prefix is meant to mark the folder as special, separated from anything else. Its contents should not be directly imported anywhere in the code.

  • &app/ is an optional folder meant to contain components imported directly in App.vue.
    The "&" prefix prevents the directory from being confused with folders named after router's page components.

  • &layouts/ is an optional folder that holds subfolders for specific layouts. These subfolders are intended to contain the child components of the related layout components.

  • &shared/ should contain reusable components that are too compound to be treated as base components (e.g. search bar, ad banner, confirmation modal).
    Folders like this should also be created inside page-specific directories or even deeper in the structure whenever reusable non-base components appear. Their parent folders limit the scopes of their usability (e.g. contents of src/components/&shared/ can be imported anywhere in the code, while components inside src/components/blog/&shared/ should not be used outside of the src/components/blog/ directory. Likewise contents of src/components/blog/slider/&shared/ are meant to be referenced only inside the src/components/blog/slider/ directory).
    Same as with &app/ and &layouts/, the "&" prefix distinguishes the folder from directories associated with specific pages or with adjacent components.

  • xyz/ is the folder associated with the "xyz" page. Each page should have its own component directory, named after its filename in src/pages/ (e.g. src/pages/index.vue should have its components stored inside src/components/index/, while src/pages/blog.vue (or src/pages/blog/index.vue) should store its unique children inside src/components/blog/).
    These folders should not be nested. Instead parent page name has to be added as a prefix to a nested page folder name (e.g. in your application you want to preview a specific user at "/app/user/[id]" and display all users at the "/app/user/all" url. To do so, you can create pages src/pages/user/[id].vue and src/pages/user/all.vue. Then you will store components of each of these pages inside directories src/components/user-[id]/ and src/components/user-all/ respectively).

Why "&"?

The prefix doesn't really matter - it's only meant to clearly identify a folder as not related to a page or, if nested, as not related to an adjacent component.
Why exactly "&"? The inspiration comes from SASS and its approach to class nesting:

.a {
  .b {
    color: blue;
  }

  &.c {
    color: cyan;
  }
}
<div class="a">
  <div class="b">blue</div>
</div>

<div class="a c">cyan</div>

This is pretty much what the prefixed folders contain - resources referenced in the same scope as the stuff adjacent to their parents. They could be stored outside of the "&" wrappers, but they have to be grouped, so they don't blend together with other components.

Style guide

For simplicity, it's best to treat the official style guide as the most basic set of rules.
However, due to being a bit outdated and presenting a different solution to the file distribution problem, a few deviations are necessary.

All component names should be PascalCase, except for globally-available base or built-in components.
Example:

<template>
  <div class="some-component">
    <!-- locally imported component -->
    <SomeSubcomponent />

    <!-- base component -->
    <base-button> Click me! </base-button>

    <!-- Vue component -->
    <transition> <div></div> </transition>

    <!-- Vue Router component -->
    <router-link> Click me! </router-link>
  </div>
</template>

<script lang="ts" setup>
  import SomeSubcomponent from "./some-component/SomeSubcomponent.vue";
</script>

Following the above rule, forcing component names to be multi-word is not necessary (A - use multi-word component names) - the code will not produce any conflicts with HTML elements.

Child components or private composables should be stored in a directory named as their parent in kebab-case and adjacent to the parent (e.g. src/components/blog/SomeSlider.vue has its own unique buttons to change the active slide. This child button should be placed in src/components/blog/some-slider/Button.vue).

Since the file structure inside src/components/ is not flat but nested, components should not include their parent in their name - the location of each component in the directory tree always describes where it is being referenced.
A component name should be always as short as possible, but at the same time must fully describe what the component is and utilize full words over abbreviations (B - full-word component names).
It is also important not to forget about including "The" prefix in components that have only one active instance at a time (B - single-instance component names).

Example application structure:
📁components
├─ 📁_base
│  ├─ 🔻Button.vue
│  ├─ 🔻Checkbox.vue
│  └─ 🔻Input.vue
│
├─ 📁&app
│  ├─ 📁navbar
│  │  ├─ 🔻Link.vue
│  │  └─ 🔻TheBrand.vue
│  ├─ 🔻TheFooter.vue
│  └─ 🔻TheNavbar.vue
│
├─ 📁&layouts
│  └─ 📁docs
│     └─ 🔻TheContentNavigation.vue
│
├─ 📁&shared
│  └─ 🔻SomeSharedComponent.vue
│  └─ 🔻TheLoneSharedComponent.vue
│
├─ 📁index
│  ├─ 📁&shared
│  │  └─ 🔻SomeSubcomponentSharedAcrossIndexComponents.vue
│  ├─ 📁some-component
│  │  ├─ 🔻SomeSubcomponentOfSomeComponent.vue
│  │  └─ 🔹useSomeFeatureOfSomeComponent.vue
│  ├─ 🔻SomeComponent.vue
│  ├─ 🔻SomeOtherComponent.vue
│  └─ 🔻TheLoneComponent.vue
│
└─ 📁some-page
   └─ 🔻SomeComponent.vue

src/composables/

Composables are stored here.
Adjust the file structure to your needs.

src/helpers/

📁helpers
├─ 🔹_global.ts
├─ 🔹rest.ts
└─ 🔹utility.ts

helpers/ folder should contain stateless helper functions grouped by their scope of functionality.
helpers/_gloabal.ts is a special file whose contents are globally available without the need to import them (thanks to unplugin-auto-import). For obvious reasons, only very simple and self explanatory functions should be in here (e.g. typechecking helpers, array/object manipulators).

src/layouts/

Layout components used by vite-plugin-vue-layouts live here.
Quote from the GitHub page of the plugin:

This works best along with the vite-plugin-pages.

Layouts are stored in the /src/layouts folder by default and are standard Vue components with a <router-view></router-view> in the template.

Pages without a layout specified use default.vue for their layout.

You can use route blocks to allow each page to determine its layout. The block below in a page will look for /src/layouts/users.vue for its layout.

See the Vitesse starter template for a working example.

<route lang="yaml">
  meta:
    layout: users
</route>

src/pages/

This folder utilizes the file based routing of vite-plugin-pages.
Example:

📁pages
│
├─ 🔻index.vue  # /
│
├─ 🔻some-page.vue  # /some-page
│
├─ 📁some-other-page
│  │
│  ├─ 🔻index.vue  # /some-other-page
│  │
│  └─ 📁[someArg]
│     │
│     └─ 🔻[someSubArg].vue  # /some-other-page/[someArg]/[someSubArg]
│
└─ 🔻[...all].vue  # route not found

src/stores/

Pinia stores live here.
👉 Official Guide

📁stores
├─ 🔹example1.ts
└─ 🔹example2.ts

src/types/

Types used throughout the application are stored here.
Adjust the folder structure to your needs.

🏷️ License

MIT License

Copyright (c) 2022-PRESENT Kajetan Welc

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

vitestrict's People

Contributors

brofrain avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.