Skip to content

Commit

Permalink
init nextjs-app from nextjs-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
alan2207 committed Aug 24, 2024
1 parent a1835b7 commit 337b887
Show file tree
Hide file tree
Showing 171 changed files with 20,118 additions and 0 deletions.
4 changes: 4 additions & 0 deletions apps/nextjs-app/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NEXT_PUBLIC_API_URL=http://localhost:8080/api
NEXT_PUBLIC_ENABLE_API_MOCKING=false
NEXT_PUBLIC_MOCK_API_PORT=8080
NEXT_PUBLIC_URL=http://localhost:3000
4 changes: 4 additions & 0 deletions apps/nextjs-app/.env.example-e2e
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NEXT_PUBLIC_API_URL=http://localhost:8080/api
NEXT_PUBLIC_ENABLE_API_MOCKING=false
NEXT_PUBLIC_MOCK_API_PORT=8080
NEXT_PUBLIC_URL=http://localhost:3000
154 changes: 154 additions & 0 deletions apps/nextjs-app/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
module.exports = {
root: true,
env: {
node: true,
es6: true,
},
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
ignorePatterns: [
'node_modules/*',
'public/mockServiceWorker.js',
'generators/*',
],
extends: ['eslint:recommended', 'next/core-web-vitals'],
plugins: ['check-file'],
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
settings: {
react: { version: 'detect' },
'import/resolver': {
typescript: {},
},
},
env: {
browser: true,
node: true,
es6: true,
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:prettier/recommended',
'plugin:testing-library/react',
'plugin:jest-dom/recommended',
'plugin:tailwindcss/recommended',
'plugin:vitest/legacy-recommended',
],
rules: {
'@next/next/no-img-element': 'off',
'import/no-restricted-paths': [
'error',
{
zones: [
// disables cross-feature imports:
// eg. src/features/discussions should not import from src/features/comments, etc.
{
target: './src/features/auth',
from: './src/features',
except: ['./auth'],
},
{
target: './src/features/comments',
from: './src/features',
except: ['./comments'],
},
{
target: './src/features/discussions',
from: './src/features',
except: ['./discussions'],
},
{
target: './src/features/teams',
from: './src/features',
except: ['./teams'],
},
{
target: './src/features/users',
from: './src/features',
except: ['./users'],
},
// enforce unidirectional codebase:

// e.g. src/app can import from src/features but not the other way around
{
target: './src/features',
from: './src/app',
},

// e.g src/features and src/app can import from these shared modules but not the other way around
{
target: [
'./src/components',
'./src/hooks',
'./src/lib',
'./src/types',
'./src/utils',
],
from: ['./src/features', './src/app'],
},
],
},
],
'import/no-cycle': 'error',
'linebreak-style': ['error', 'unix'],
'react/prop-types': 'off',
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
'import/default': 'off',
'import/no-named-as-default-member': 'off',
'import/no-named-as-default': 'off',
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/explicit-function-return-type': ['off'],
'@typescript-eslint/explicit-module-boundary-types': ['off'],
'@typescript-eslint/no-empty-function': ['off'],
'@typescript-eslint/no-explicit-any': ['off'],
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
'check-file/filename-naming-convention': [
'error',
{
'src/!(pages)/*.{ts,tsx}': 'KEBAB_CASE',
},
{
ignoreMiddleExtensions: true,
},
],
},
},
{
plugins: ['check-file'],
files: ['src/**/!(__tests__)/*'],
rules: {
'check-file/folder-naming-convention': [
'error',
{
'**/*': 'KEBAB_CASE',
},
],
},
},
],
};
43 changes: 43 additions & 0 deletions apps/nextjs-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/e2e/.auth/

# storybook
migration-storybook.log
storybook.log
storybook-static


# production
/dist

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*


# local
mocked-db.json

/.next
/.vite
tsconfig.tsbuildinfo
1 change: 1 addition & 0 deletions apps/nextjs-app/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.hbs
7 changes: 7 additions & 0 deletions apps/nextjs-app/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
20 changes: 20 additions & 0 deletions apps/nextjs-app/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],

addons: [
'@storybook/addon-actions',
'@storybook/addon-links',
'@storybook/node-logger',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-docs',
'@storybook/addon-a11y',
],
framework: '@storybook/nextjs',
docs: {
autodocs: 'tag',
},
typescript: {
reactDocgen: 'react-docgen-typescript',
},
};
8 changes: 8 additions & 0 deletions apps/nextjs-app/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import '../src/styles/globals.css';

export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
};

export const decorators = [(Story) => <Story />];
9 changes: 9 additions & 0 deletions apps/nextjs-app/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"dsznajder.es7-react-js-snippets",
"mariusalchimavicius.json-to-ts",
"bradlc.vscode-tailwindcss"
]
}
6 changes: 6 additions & 0 deletions apps/nextjs-app/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
32 changes: 32 additions & 0 deletions apps/nextjs-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Next.js Pages Application

## Get Started

Prerequisites:

- Node 20+
- Yarn 1.22+

To set up the app execute the following commands.

```bash
git clone https://github.com/alan2207/bulletproof-react.git
cd bulletproof-react
cd apps/nextjs-pages
cp .env.example .env
yarn install
```

#### `yarn run-mock-server`

Make sure to start the mock server before running the app.
The mock server runs on [http://localhost:8080/api](http://localhost:8080/api).

##### `yarn dev`

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

## Project Structure

Since the `pages` folder isn't very flexible and doesn't allow file collocation, we are keeping the `app` folder which is our application layer where we compose all the features, and then we just re-export Next.js page specific files (the pages and `getServerSideProps`) from the `pages` folder so Next.js can pick them up and serve as pages.
2 changes: 2 additions & 0 deletions apps/nextjs-app/__mocks__/vitest-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="vite/client" />
/// <reference types="vitest/globals" />
52 changes: 52 additions & 0 deletions apps/nextjs-app/__mocks__/zustand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { act } from '@testing-library/react';
import { afterEach, vi } from 'vitest';
import * as zustand from 'zustand';

const { create: actualCreate, createStore: actualCreateStore } =
await vi.importActual<typeof zustand>('zustand');

// a variable to hold reset functions for all stores declared in the app
export const storeResetFns = new Set<() => void>();

const createUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
const store = actualCreate(stateCreator);
const initialState = store.getInitialState();
storeResetFns.add(() => {
store.setState(initialState, true);
});
return store;
};

// when creating a store, we get its initial state, create a reset function and add it in the set
export const create = (<T>(stateCreator: zustand.StateCreator<T>) => {
// to support curried version of create
return typeof stateCreator === 'function'
? createUncurried(stateCreator)
: createUncurried;
}) as typeof zustand.create;

const createStoreUncurried = <T>(stateCreator: zustand.StateCreator<T>) => {
const store = actualCreateStore(stateCreator);
const initialState = store.getInitialState();
storeResetFns.add(() => {
store.setState(initialState, true);
});
return store;
};

// when creating a store, we get its initial state, create a reset function and add it in the set
export const createStore = (<T>(stateCreator: zustand.StateCreator<T>) => {
// to support curried version of createStore
return typeof stateCreator === 'function'
? createStoreUncurried(stateCreator)
: createStoreUncurried;
}) as typeof zustand.createStore;

// reset all stores after each test run
afterEach(() => {
act(() => {
storeResetFns.forEach((resetFn) => {
resetFn();
});
});
});
5 changes: 5 additions & 0 deletions apps/nextjs-app/e2e/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: 'plugin:playwright/recommended',
};
Loading

0 comments on commit 337b887

Please sign in to comment.