Skip to content

Commit 9cd4a14

Browse files
authored
Merge pull request #1 from webreinvent/feature/add-coc-web
Feature -> Master | nuxtstore web intro and contribution guide.md
2 parents 21bfbf6 + 240b31b commit 9cd4a14

File tree

3 files changed

+269
-20
lines changed

3 files changed

+269
-20
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ dist
88

99
# Node dependencies
1010
node_modules
11+
package-lock.json
12+
pnpm-lock.yaml
1113

1214
# Logs
1315
logs

content/3.web/guide.md

+247
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
---
2+
title: Guide
3+
---
4+
5+
## Brief overview & Development guide
6+
7+
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.
8+
9+
This guide will provide you with an overview of the project structure, tools and best practices to help you get started quickly.
10+
11+
## Project Structure
12+
13+
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.
14+
15+
The architecture includes the following apps:
16+
17+
- `server` - NuxtStore server middleware powered by **[Fastify](https://fastify.dev/)**
18+
- `web` - A typescript based web application powered by **[Nuxt](https://nuxtjs.org)** and **[Vue](https://vuejs.org)**
19+
20+
21+
> **Note**: These two standalone modules are capable of being integrated with any backend service with consistent APIs without extra configuration to setup.
22+
23+
#### Web application
24+
25+
Web app follows a typical Nuxt.js directory [structure](https://nuxt.com/docs/guide/directory-structure/nuxt) with a few tweaks:
26+
27+
```shell
28+
29+
apps/
30+
└── root/
31+
├── ...
32+
├── assets/ # Static assets
33+
├── components/
34+
│ ├── atoms/ # Atomic components related to feature (e.g atoms/cart/NsAddToCartButton.vue)
35+
│ ├── molecules/ # Molecular components made using atoms (e.g molecules/form/NsEmailInput.vue)
36+
│ └── organisms/ # Large components made using atoms and molecules (e.g organisms/address/NsAddressCard.vue)
37+
├── composables/ # Custom hooks composing reactive logic
38+
├── constants/
39+
│ ├── api.ts # API-related constants (HTTP methods)
40+
│ └── ui.ts # UI-related constants (colors, breakpoints)
41+
├── data/ # Static data or JSON files
42+
├── layouts/ # Nuxt Layouts
43+
├── middleware/ # Client side route middlewares
44+
│ ├── auth.ts
45+
│ └── ...
46+
├── pages/ # Pages
47+
│ ├── index.vue # App home page component
48+
│ └── ...
49+
├── plugins/ # App plugins run on both client and server
50+
├── public/ # Public assets
51+
├── server/ # In-app backend server
52+
├── shared/
53+
│ ├── types/ # Type definitions
54+
│ └── utils/ # Non-reactive helper functions
55+
├── stores/ # Pinia stores for state management
56+
│ ├── auth.store.ts
57+
│ └── cart.store.ts
58+
├── tests/ # Component and feature unit tests
59+
├── eslint.config.mjs # Linter rules
60+
├── features.json # Feature flags
61+
├── app.vue # Application entry point
62+
├── nuxt.config.ts # Nuxt.js configuration
63+
├── package.json # Package entry point
64+
├── tailwind.config.ts # TailwindCSS configuration
65+
├── tsconfig.json # TypeScript configuration
66+
├── vitest.config.ts # Vitest configuration
67+
└── ...
68+
69+
```
70+
71+
List of essential directories:
72+
73+
- `components` NuxtStore UI components, like `ProductCard` or `Review`
74+
- `stores` Pinia store containing global state, getters and mutators
75+
- `composables` Contains reusable composition functions, e.g. data fetchers and stateful helpers
76+
- `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.
77+
- `tests` Contains mocks for components, composables and store actions
78+
79+
## Project Guide
80+
81+
This project follows a few conventions to help with organizing your code:
82+
83+
- Each function is located in a dedicated module and exported from the `index.ts` file.
84+
- In this nuxt application, avoid importing auto-imported APIs (ref, onMounted etc.) or compiler macros (defineProps, defineEmits etc.) and PrimeVue components (Button, InputText, Dialog, Carousel etc.)
85+
- Names are short, descriptive and must follow our consistent naming convention ([guide](https://docs.vaah.dev/guide/code))
86+
- Follow this [nuxt guide](https://docs.vaah.dev/guide/nuxt) to avoid common mistakes and comply with industry standard practices
87+
- Functions (including composables and utils) are exported using explicit named exports instead of anonymous exports
88+
- 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
89+
- This project follows [gitflow](https://docs.vaah.dev/guide/git) as the branching strategy
90+
- Feature branch name must follow a simple convention such as
91+
`feature/<purpose>-<page_name>-<subject>`. Subject can be component or specific feature
92+
(e.g **ui-cart-summary**, **ui-layout-footer**, **flow-checkout-payment**, **action-cart-quantity**)
93+
- [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
94+
95+
> **Note:** In order to add code comments, we prefix the action with a `@` for contributors reference such as **@todo:** or **@debug:**
96+
97+
### Components
98+
99+
NuxtStore UI follows [atomic design principle](https://atomicdesign.bradfrost.com/chapter-2/) to develop components and it 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.
100+
101+
- Introduction to project components:
102+
103+
- Consists of representational components that are designed to fulfill project requirements
104+
- Components should be placed in dedicated folders inside components directory
105+
- Root of the component folder has `atoms`, `molecules` and `organisms`
106+
- Each component name must be prefixed with **"Ns"** (e.g **NsProductCard**)
107+
- TypeScript types are located inside the SFC for ease of access and coupling
108+
- Tests for components are located in the `/tests/components` folder (e.g **NsUserAddress.spec.ts**)
109+
- Folders inside /atoms, /molecules and /organisms must follow their purpose
110+
111+
Expected directory structure:
112+
113+
```shell
114+
components/
115+
└── atoms/
116+
└── cart/
117+
└── NsAddToCartButton.vue
118+
└── NsCartIcon.vue
119+
└── ui/
120+
└── NsHeader.vue
121+
└── NsFooter.vue
122+
└── ...
123+
└── molecules/
124+
└── form/
125+
└── NsEmailInput.vue
126+
└── NsPasswordInput.vue
127+
└── ...
128+
└── organisms/
129+
└── address/
130+
└── NsAddressCard.vue
131+
└── cart/
132+
└── NsCartOverview.vue
133+
└── forms/
134+
└── NsSignUpForm.vue
135+
└── NsAddressForm.vue
136+
└── ...
137+
```
138+
139+
For more information about available NuxtStore components for Vue (Nuxt), check out [documentation]().
140+
141+
- **Convention:**
142+
143+
- Vue (Nuxt) components should follow `PascalCase` pattern (`CategoryFilters`, `Heading`)
144+
- 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`
145+
- Line of code in a single file component must not exceed 200 (formatted)
146+
147+
- **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.
148+
149+
- Multiword props are defined in `camelCase` and passed in template as `kebab-case`
150+
151+
Example:
152+
```ts
153+
export type CartActionProps = {
154+
icon?: string,
155+
label?: string,
156+
...
157+
};
158+
159+
const props = withDefaults(defineProps<CartActionProps>(), {
160+
icon: "pi pi-cart",
161+
label: "Add to cart"
162+
});
163+
```
164+
165+
### State Management
166+
167+
We are using [Pinia state management](https://pinia.vuejs.org/ssr/nuxt.html) to lock the data responsibility along with the composition functions (composables).
168+
169+
- 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
170+
- Each store file must end with a `.store.ts` extension
171+
- 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**)
172+
- [`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
173+
- Sequentially a store structure should have state >> getters >> actions for readability
174+
175+
### Composables
176+
177+
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.
178+
Project composables are located on top level inside the `composables/` directory.
179+
180+
**Convention:**
181+
- Each composable should be prefixed with `use` keyword (`useCartStatus`)
182+
- Composables should follow `camelCase` pattern (`useProductReviews`)
183+
- Composables follow single responsibility principle. Avoid calling side effects inside composables
184+
- 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.
185+
- [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
186+
- 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)
187+
188+
> **Note:** State connected to the certain composable is read-only and reactive. They are modified by the internal modifiers (setters)
189+
190+
### Utils
191+
192+
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:
193+
194+
- Project utils end with a `.utils.ts` extension (e.g format.utils.ts, wishlist.utils.ts)
195+
- `utils/index.utils.ts` contains all the shared functions across all contexts/features (e.g **isEqual\<T\>(a,b)** compares two entities for equality)
196+
- Named exports are preferred over default export (e.g export function cleanUserInput(){ })
197+
- One utility method can have multiple exports, allowing relevant functions to be grouped together
198+
- Utility functions are synchronous and do not communicate with any reactive source (store/composables)
199+
200+
### Localization
201+
202+
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.
203+
Refer to the [Nuxt-i18n](https://i18n.nuxtjs.org) documentation for translating content with SSR support.
204+
205+
### Testing
206+
207+
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).
208+
209+
Testing configuration files: (runs on nuxt environment and `happy-dom`)
210+
211+
- `vitest.config.ts` - Test runner config file
212+
213+
Testing commands:
214+
215+
- `npm run unittest` - Run all the test scripts in the project (in watch mode by default)
216+
- `npm run unittest <Component>` - Run specific component/feature mocks
217+
218+
### Conventions enforced by automated tooling
219+
220+
To help you code with best practices in mind, this boilerplate comes with some automated tooling.
221+
222+
- All test descriptions follow naming convention `test('should <component>... ')`
223+
- Automatic code linting is managed by [nuxt-eslint-module](https://eslint.nuxt.com/packages/config)
224+
225+
### Cache control
226+
227+
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):
228+
229+
```ts
230+
routeRules: {
231+
'/_ipx/**': { headers: { 'cache-control': `public, max-age=31536000, immutable` } },
232+
'/icons/**': { headers: { 'cache-control': `public, max-age=31536000, immutable` } },
233+
'/favicon.ico': { headers: { 'cache-control': `putypescriptblic, max-age=31536000, immutable` } },
234+
},
235+
```
236+
237+
You can read about more possible rules in the [Nuxt documentation](https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering).
238+
239+
#### More about performance
240+
241+
Additional performance good practices and vue project guide can be found [HERE](https://docs.vaah.dev/guide/vue-and-nuxt-performance-improvement).
242+
243+
### Recommended IDE setup
244+
245+
1. Visual Studio Code (with prettier_extension @`v9.14.0` exact and the latest version of microsoft eslint_extension)
246+
2. webstorm (for developers comfortable in jetbrains IDE like phpstorm with no extra configuration -
247+
[ws_eslint_customisation_guide](https://www.jetbrains.com/help/webstorm/eslint.html#ws_js_eslint_activate))

nuxt.config.ts

+20-20
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)