Skip to content

Commit 01ae136

Browse files
Added: web guide and fixed config error due to keys
1 parent 21bfbf6 commit 01ae136

File tree

2 files changed

+249
-20
lines changed

2 files changed

+249
-20
lines changed

content/3.web/guide.md

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# Brief overview & Development guide
2+
3+
NuxtStore is a platform-agnostic ecommerce frontend solution that allows seamless integration with various ecommerce backends! It provides tools for building customizable, high-performance online storefronts.
4+
5+
This guide will provide you with an overview of the project structure, tools and best practices to help you get started quickly.
6+
7+
## Project Structure
8+
9+
This project follows a modular and scalable architecture, by decoupling the composable frontend from the server middleware allowing for easy collaboration among multiple teams and ensuring consistent development practices across the codebase.
10+
11+
The architecture includes the following apps:
12+
13+
- `server` - NuxtStore server middleware powered by **[Fastify](https://fastify.dev/)**
14+
- `web` - A typescript based web application powered by **[Nuxt](https://nuxtjs.org)** and **[Vue](https://vuejs.org)**
15+
16+
17+
> **Note**: These two standalone modules are capable of being integrated with any backend service with consistent APIs without extra configuration to setup.
18+
19+
#### Web application
20+
21+
Web app follows a Nuxt.js [Structure](https://nuxt.com/docs/guide/directory-structure/nuxt) file structure:
22+
23+
```shell
24+
25+
apps/
26+
└── root/
27+
├── ...
28+
├── assets/ # Static assets
29+
├── components/
30+
│ ├── ecommerce/ # Ecommerce-specific components (ProductCard)
31+
│ ├── ...
32+
│ └── ui/ # UI components (Header, Footer)
33+
├── composables/ # Custom hooks composing reactive logic
34+
├── constants/
35+
│ ├── api.ts # API-related constants (HTTP methods)
36+
│ └── ui.ts # UI-related constants (colors, breakpoints)
37+
├── data/ # Static data or JSON files
38+
├── layouts/ # Layouts
39+
├── middleware/ # Client route middleware (e.g auth)
40+
│ ├── auth.ts
41+
│ └── ...
42+
├── pages/ # Pages
43+
│ ├── index.vue # App home page component
44+
│ └── ...
45+
├── plugins/ # App plugins run on both client and server
46+
├── public/ # Public assets
47+
├── server/ # In-app backend server
48+
├── shared/
49+
│ ├── types/ # Type definitions
50+
│ └── utils/ # Non-reactive helper functions
51+
├── stores/ # Pinia stores for state management
52+
│ ├── auth.store.ts
53+
│ └── cart.store.ts
54+
├── tests/ # Component and feature unit tests
55+
├── eslint.config.mjs # Linter rules
56+
├── features.json # Feature flags
57+
├── app.vue # Application entry point
58+
├── nuxt.config.ts # Nuxt.js configuration
59+
├── package.json # Package entry point
60+
├── tailwind.config.js # TailwindCSS configuration
61+
├── tsconfig.json # TypeScript configuration
62+
├── vitest.config.ts # Vitest configuration
63+
└── ...
64+
65+
```
66+
67+
List of essential directories:
68+
69+
- `components/ecommerce` NuxtStore UI components, like `ProductCard` or `Review`
70+
- `stores` Pinia store containing global state, getters and mutators
71+
- `composables` Contains reusable composition functions, e.g. data fetchers and stateful helpers
72+
- `shared` Contains types and utilities [shared](https://github.com/nuxt/nuxt/releases/tag/v3.14.0) across client and server e.g `product.type.ts`. Auto-import support will follow in next major release.
73+
- `tests` Contains mocks for components, composables and store actions
74+
75+
## Guide
76+
77+
This project follows a few conventions to help with organizing your code:
78+
79+
- Each function is located in a dedicated module and exported from the `index.ts` file.
80+
- In this nuxt application, avoid importing auto-imported APIs (ref, onMounted etc.) and PrimeVue components (Dialog, Carousel etc.)
81+
- Names are short, descriptive and must follow our consistent naming convention ([guide](https://docs.vaah.dev/guide/code))
82+
- Follow this [nuxt guide](https://docs.vaah.dev/guide/nuxt) to avoid common mistakes and comply with industry standard practices
83+
- Functions (including composables and utils) are exported using explicit named exports instead of anonymous exports
84+
- Actions or functions are defined following the trivial function definitions (`func() { }`) **instead of arrow functions** (`const func = () => { }`) to create visible distinction between constants and methods
85+
- This project follows [gitflow](https://docs.vaah.dev/guide/git) as the branching strategy
86+
- Feature branch name must follow a simple convention such as
87+
`feature/<purpose>-<page_name>-<subject>`. Subject can be component or specific feature
88+
(e.g **ui-cart-summary**, **ui-layout-footer**, **flow-checkout-payment**, **action-cart-quantity**)
89+
- [JS doc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) are added at necessary places to define the structure of a complex entity or explain the construct of a method
90+
91+
> **Note:** In order to add code comments, we prefix the action with a `@` for contributors reference such as **@todo:** or **@debug:**
92+
93+
### Components
94+
95+
NuxtStore UI leverages [PrimeVue](https://primevue.org/) as the building blocks of storefront components. All components are auto-imported by their name (no path-prefix) and are located inside subfolders in the `components` directory.
96+
97+
- Introduction to project components:
98+
99+
- Representational components that are designed to fulfill project requirements
100+
- Each component name must be prefixed with **"Ns"** (e.g **NsProductCard**)
101+
- TypeScript types are located inside the SFC for ease of access and coupling
102+
- Tests for components are located in the `/tests/components` folder (e.g **NsUserAddress.spec.ts**)
103+
- Folders inside /components must follow their purpose
104+
105+
Expected file/folder structure:
106+
107+
```shell
108+
components/
109+
└── ui/
110+
└── NsHeader.vue
111+
└── ...
112+
└── ecommerce/
113+
└── NsCartItem.vue
114+
└── ...
115+
└── forms/
116+
└── NsSignUpForm.vue
117+
└── ...
118+
└── ...
119+
```
120+
121+
For more information about available NuxtStore components for Vue (Nuxt), check out [documentation]().
122+
123+
- **Convention:**
124+
125+
- Vue (Nuxt) components should follow `Pascal case` pattern (`CategoryFilters`, `Heading`)
126+
- The types for component's props should be named `{Component}Props` and exist in the same SFC as the component. For example, `GalleryProps` or `HeadingProps`
127+
- Line of code in a single file component must not exceed 200 (formatted)
128+
129+
- **Prop Declaration:** In vue, there are multiple ways to define component props especially with typescript. [[checkout official docs](https://vuejs.org/guide/typescript/composition-api.html#typing-component-props)] To ensure consistency and still allow customisability, we have adopted the following pattern.
130+
131+
- Multiword props are defined in `camelCase` and passed in template as `kebab-case`
132+
133+
Example:
134+
```ts
135+
export type CartActionProps = {
136+
icon?: string,
137+
label?: string,
138+
...
139+
};
140+
141+
const props = withDefaults(defineProps<CartActionProps>(), {
142+
icon: "pi pi-cart",
143+
label: "Add to cart"
144+
});
145+
```
146+
147+
### State Management
148+
149+
We are using [Pinia state management](https://pinia.vuejs.org/ssr/nuxt.html) to lock the data responsibility along with the composition functions (composables).
150+
151+
- Pinia stores are defined in the style of [Setup stores](https://pinia.vuejs.org/core-concepts/#Setup-Stores), keeping performance and modern standards in view
152+
- Each store file must end with a `.store.ts` extension
153+
- Store methods are as modular and reusable as possible and do not perform side effects. If required, new action is created in order to handle `post{Action}` (e.g **addToCart** , **postAddToCart**)
154+
- [`useState`](https://nuxt.com/docs/getting-started/state-management) is used in very narrow context where creating global store for shared state doesn't make sense
155+
- Sequentially a store structure should have state >> getters >> actions for readability
156+
157+
### Composables
158+
159+
Composables are useful when stateful logic has to be reused across components - e.g. controlling component state or leverage lifecycle hooks or accessing DOM api.
160+
Project composables are located on top level inside the `composables/` directory.
161+
162+
**Convention:**
163+
- Each composable should be prefixed with `use` keyword (`useCartStatus`)
164+
- Composables should follow `camelCase` pattern (`useProductReviews`)
165+
- Composables follow single responsibility principle. Avoid calling side effects inside composables
166+
- This project inherits most of the composables from [vueuse](https://vueuse.org/guide/best-practice.html) library. Always search for an already available `vueuse` composable before writing on own for common tasks or DOM manipulation.
167+
- [Composables](https://vuejs.org/guide/reusability/composables.html#conventions-and-best-practices) should only be called in synchronous environment and on top level. Remember to clean attached event handlers or timeouts if defined inside a composable using vue's unmount hooks
168+
- Built-in Nuxt composables must be called in the right context to avoid critical errors such as [Nuxt instance unavailable](https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables)
169+
170+
> **Note:** State connected to the certain composable is read-only and reactive. They are modified by the internal modifiers (setters)
171+
172+
### Utils
173+
174+
Main purpose of utils is to encapsulate non-reactive helper method logic and draw a semantic boundary between reactive composables and other auto-imported utility functions. Utils are contained inside the Shared/ folder. This project follows some simple conventions for utils:
175+
176+
- Project utils end with a `.utils.ts` extension (e.g format.utils.ts, wishlist.utils.ts)
177+
- `utils/index.utils.ts` contains all the shared functions across all contexts/features (e.g **isEqual\<T\>(a,b)** compares two entities for equality)
178+
- Named exports are preferred over default export (e.g export function cleanUserInput(){ })
179+
- One utility method can have multiple exports, allowing relevant functions to be grouped together
180+
- Utility functions are synchronous and do not communicate with any reactive source (store/composables)
181+
182+
### Localization
183+
184+
NuxtStore ships with a basic setup for i18n localization powered by the [Nuxt-i18n](https://i18n.nuxtjs.org) library. Project locale translations are stored in `locale/[namespace].json` files. Translations are grouped by _features_, and imported only where required to minimize bundle size.
185+
Refer to the [Nuxt-i18n](https://i18n.nuxtjs.org) documentation for translating content with SSR support.
186+
187+
### Testing
188+
189+
The project provides a basic setup for testing TypeScript code with [Vitest](https://vitest.dev/) and [Vue Test Utils](https://test-utils.vuejs.org/guide/) for testing Vue (Nuxt) components and [pinia stores](https://pinia.vuejs.org/cookbook/testing.html#Mocking-the-returned-value-of-an-action).
190+
191+
Testing configuration files: (runs on nuxt environment and `happy-dom`)
192+
193+
- `vitest.config.ts` - Test runner config file
194+
195+
Testing commands:
196+
197+
- `npm run unittest` - Run all the test scripts in the project (in watch mode by default)
198+
- `npm run unittest <Component>` - Run specific component/feature mocks
199+
200+
### Conventions enforced by automated tooling
201+
202+
To help you code with best practices in mind, this boilerplate comes with some automated tooling.
203+
204+
- All test descriptions follow naming convention `test('should <component>... ')`
205+
- Automatic code linting is managed by [nuxt-eslint-module](https://eslint.nuxt.com/packages/config)
206+
207+
### Cache control
208+
209+
As the initial Nuxt setup is not caching images generated by `NuxtImg`, the `nuxt.config.ts` in this repository has been extended to cache those images as well (by setting proper headers):
210+
211+
```ts
212+
routeRules: {
213+
'/_ipx/**': { headers: { 'cache-control': `public, max-age=31536000, immutable` } },
214+
'/icons/**': { headers: { 'cache-control': `public, max-age=31536000, immutable` } },
215+
'/favicon.ico': { headers: { 'cache-control': `putypescriptblic, max-age=31536000, immutable` } },
216+
},
217+
```
218+
219+
You can read about more possible rules in the [Nuxt documentation](https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering).
220+
221+
#### More about performance
222+
223+
Additional performance good practices and vue project guide can be found [HERE](https://docs.vaah.dev/guide/vue-and-nuxt-performance-improvement).
224+
225+
### Recommended IDE setup
226+
227+
1. Visual Studio Code (with prettier_extension @`v9.14.0` exact and the latest version of microsoft eslint_extension)
228+
2. webstorm (for developers comfortable in jetbrains IDE like phpstorm with no extra configuration -
229+
[ws_eslint_customisation_guide](https://www.jetbrains.com/help/webstorm/eslint.html#ws_js_eslint_activate))

nuxt.config.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,24 @@ export default defineNuxtConfig({
1212
'nuxt-og-image'
1313
],
1414

15-
hooks: {
16-
// Define `@nuxt/ui` components as global to use them in `.md` (feel free to add those you need)
17-
'components:extend': (components) => {
18-
const globals = components.filter(c => ['UButton', 'UIcon'].includes(c.pascalName))
19-
20-
globals.forEach(c => c.global = true)
21-
}
15+
devtools: {
16+
enabled: true
2217
},
2318

2419
colorMode: {
2520
disableTransition: true
2621
},
2722

23+
routeRules: {
24+
'/api/search.json': { prerender: true }
25+
},
26+
27+
future: {
28+
compatibilityVersion: 4
29+
},
30+
31+
compatibilityDate: '2024-07-11',
32+
2833
nitro: {
2934
prerender: {
3035
routes: [
@@ -34,20 +39,17 @@ export default defineNuxtConfig({
3439
}
3540
},
3641

37-
routeRules: {
38-
'/api/search.json': { prerender: true }
39-
},
40-
41-
devtools: {
42-
enabled: true
43-
},
44-
4542
typescript: {
4643
strict: false
4744
},
4845

49-
future: {
50-
compatibilityVersion: 4
46+
hooks: {
47+
// Define `@nuxt/ui` components as global to use them in `.md` (feel free to add those you need)
48+
'components:extend': (components) => {
49+
const globals = components.filter(c => ['UButton', 'UIcon'].includes(c.pascalName))
50+
51+
globals.forEach(c => c.global = true)
52+
}
5153
},
5254

5355
eslint: {
@@ -57,7 +59,5 @@ export default defineNuxtConfig({
5759
braceStyle: '1tbs'
5860
}
5961
}
60-
},
61-
62-
compatibilityDate: '2024-07-11'
62+
}
6363
})

0 commit comments

Comments
 (0)