├── .eslintignore
├── .eslintrc
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
└── settings.json
├── CHANGELOG.md
├── LICENCE
├── README.md
├── generators
├── action
│ └── index.js
├── app
│ ├── index.js
│ └── templates
│ │ ├── _layout.tsx
│ │ ├── _package.json
│ │ └── src
│ │ ├── .babelrc
│ │ ├── .eslintignore
│ │ ├── .eslintrc
│ │ ├── .gitattributes
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── LICENCE
│ │ ├── README.md
│ │ ├── api
│ │ └── postsApi.js
│ │ ├── app-constants
│ │ └── index.ts
│ │ ├── components
│ │ ├── global
│ │ │ ├── commentItem
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.scss
│ │ │ ├── commentList
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.scss
│ │ │ ├── customErrorBoundary
│ │ │ │ ├── fallbackComponent.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.scss
│ │ │ ├── customnprogress
│ │ │ │ └── index.tsx
│ │ │ ├── layout
│ │ │ │ └── head.tsx
│ │ │ ├── postItem
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.scss
│ │ │ ├── postList
│ │ │ │ ├── index.tsx
│ │ │ │ └── styles.scss
│ │ │ └── snazzyButton
│ │ │ │ ├── index.tsx
│ │ │ │ ├── snazzyButton.stories.tsx
│ │ │ │ └── styles.scss
│ │ └── index.ts
│ │ ├── config
│ │ ├── custom-environment-variables.js
│ │ ├── default.js
│ │ ├── development.js
│ │ ├── production.js
│ │ └── testing.js
│ │ ├── contexts
│ │ └── index.ts
│ │ ├── enums
│ │ └── index.ts
│ │ ├── hocs
│ │ └── index.ts
│ │ ├── hooks
│ │ └── index.ts
│ │ ├── i18n.js
│ │ ├── lib
│ │ ├── config.shim.js
│ │ └── withI18next.js
│ │ ├── models
│ │ ├── comment.d.ts
│ │ ├── dispatchable.d.ts
│ │ ├── index.d.ts
│ │ ├── loadable.d.ts
│ │ └── post.d.ts
│ │ ├── next.config.js
│ │ ├── pages
│ │ ├── _app.js
│ │ ├── _document.js
│ │ ├── about
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── index
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── post
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ └── posts
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── redux-store
│ │ ├── createStore.ts
│ │ ├── posts
│ │ │ ├── actions.ts
│ │ │ ├── constants.ts
│ │ │ ├── reducer.ts
│ │ │ ├── sagas.ts
│ │ │ ├── selectors.ts
│ │ │ └── state.ts
│ │ ├── rootReducer.ts
│ │ ├── rootSaga.ts
│ │ └── storeState.ts
│ │ ├── server.js
│ │ ├── static
│ │ ├── favicon.ico
│ │ ├── images
│ │ │ ├── analyticsfire-logo.svg
│ │ │ └── the-team.JPG
│ │ └── locales
│ │ │ └── en
│ │ │ ├── about.json
│ │ │ ├── common.json
│ │ │ └── home.json
│ │ ├── styles
│ │ ├── _mixins.scss
│ │ ├── _variables.scss
│ │ ├── antd-custom.less
│ │ └── main.scss
│ │ ├── tests
│ │ └── units
│ │ │ ├── components
│ │ │ ├── __snapshots__
│ │ │ │ └── activeLink.test.js.snap
│ │ │ └── activeLink.test.js
│ │ │ ├── jest.config.js
│ │ │ ├── pages
│ │ │ ├── __snapshots__
│ │ │ │ ├── about.test.js.snap
│ │ │ │ └── home.test.js.snap
│ │ │ ├── about.test.js
│ │ │ └── home.test.js
│ │ │ └── setup
│ │ │ ├── assetsTransformer.js
│ │ │ └── index.js
│ │ ├── tsconfig.json
│ │ ├── tslint.json
│ │ ├── typings
│ │ ├── react-redux.d.ts
│ │ └── storybook.react.d.ts
│ │ └── utils
│ │ └── index.ts
├── component
│ ├── index.js
│ └── templates
│ │ ├── _component.js
│ │ ├── _i18n.json
│ │ ├── _styles.scss
│ │ └── _test.js
├── context
│ ├── index.js
│ └── templates
│ │ └── _context.ts
├── enum
│ ├── index.js
│ └── templates
│ │ └── _enum.ts
├── hoc
│ ├── index.js
│ └── templates
│ │ └── _hoc.ts
├── hook
│ ├── index.js
│ └── templates
│ │ └── _hook.ts
├── model
│ ├── index.js
│ └── templates
│ │ └── _model.ts
├── page
│ ├── index.js
│ └── templates
│ │ ├── _i18n.json
│ │ ├── _index.js
│ │ ├── _page.js
│ │ ├── _styles.scss
│ │ └── _test.js
└── store
│ ├── index.js
│ └── templates
│ ├── _actions.ts
│ ├── _constants.ts
│ ├── _reducer.ts
│ ├── _sagas.ts
│ ├── _selectors.ts
│ ├── _state.ts
│ └── _test.js
├── package-lock.json
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 |
2 | /node_modules/**
3 | /templates/**
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "airbnb",
4 | ],
5 | "plugins": ["react", "prettier", "standard", "react-hooks"],
6 | "parserOptions": {
7 | "sourceType": "module",
8 | "ecmaFeatures": {
9 | "jsx": false,
10 | }
11 | },
12 | "env": {
13 | "es6": true,
14 | "node": true
15 | },
16 | "rules": {
17 | "prettier/prettier": ["error", {
18 | "singleQuote": true,
19 | }],
20 | "react-hooks/rules-of-hooks": "error",
21 | "react-hooks/exhaustive-deps": "warn",
22 | "no-unused-vars": "error",
23 | "no-unused-expressions": "error",
24 | "no-undef": "error",
25 | "no-undefined": "error",
26 | "comma-dangle": [2, "always-multiline"],
27 | "max-classes-per-file": ["warn", 1],
28 | "arrow-parens": "off"
29 | }
30 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .vscode
3 | .idea
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | _*.js
2 | _*.scss
3 | _*.json
4 | generators/app/templates/src/LICENCE
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "tabWidth": 2,
4 | "trailingComma": "all",
5 | "printWidth": 120,
6 | "bracketSpacing": true
7 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.colorCustomizations": {
3 | "activityBar.background": "#0F3429",
4 | "titleBar.activeBackground": "#154939",
5 | "titleBar.activeForeground": "#F3FCF9"
6 | }
7 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | #### 2.1.10 (2019-09-17)
2 |
3 | ##### Other Changes
4 |
5 | * store ([0f40378e](https://github.com/ElectronHacked/nextjs-typescript-antd/commit/0f40378e764da535903fadf628039cfaf2ca6a2a))
6 |
7 | #### 2.1.9 (2019-09-17)
8 |
9 | #### 2.1.8 (2019-08-20)
10 |
11 | #### 2.1.7 (2019-08-20)
12 |
13 | #### 2.1.6 (2019-08-20)
14 |
15 | #### 2.1.5 (2019-08-20)
16 |
17 | #### 2.1.4 (2019-08-19)
18 |
19 | #### 2.1.3 (2019-08-19)
20 |
21 | #### 2.1.2 (2019-08-19)
22 |
23 | #### 2.1.1 (2019-08-19)
24 |
25 | ##### Other Changes
26 |
27 | * hoc subgenerator ([657f24ba](https://github.com/ElectronHacked/nextjs-typescript-antd/commit/657f24ba25d2f422e2055d9c332bc01e437eef47))
28 | * context subgenerator ([4a277008](https://github.com/ElectronHacked/nextjs-typescript-antd/commit/4a277008a45b814a8b45576cfd4246520357ee4a))
29 |
30 | ### 2.1.0 (2019-08-17)
31 |
32 | ##### Other Changes
33 |
34 | * hook subgenerator ([0312662e](https://github.com/ElectronHacked/nextjs-typescript-antd/commit/0312662e71c4649577d869550addb168631a06af))
35 |
36 | #### 2.0.2 (2019-08-17)
37 |
38 | ##### Other Changes
39 |
40 | * 120 ([b6ce3067](https://github.com/ElectronHacked/nextjs-typescript-antd/commit/b6ce3067eb612164b602b99d238a54bddb961ca6))
41 |
42 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Boxfusion Pty (Ltd)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # A scaffolder for ReactJS using NextJS, TypeScript & Ant Design
4 |
5 | This yeoman generator will build different React components, creating a skeleton for the different files.
6 |
7 | # Requirements
8 |
9 | This is a Yeoman generator. You need to install Yeoman, NodeJS and npm to install the generator and its dependencies. Make sure you have all installed globally.
10 |
11 | First, download and install NodeJS and npm. More information about NodeJS / npm: https://nodejs.org/
12 |
13 | Second, install Yeoman. More information about Yeoman: http://yeoman.io/
14 |
15 | # Installation
16 |
17 | ```
18 | # install yo
19 | $ npm install -g yo
20 |
21 | # install a generator
22 | $ npm install -g generator-nextjs-typescript-antd
23 | ```
24 |
25 | # Usage
26 |
27 | ```
28 | $ yo nextjs-typescript-antd
29 | ```
30 |
31 | # Table of Contents
32 |
33 | * [Questions? Feedback?](#questions-feedback)
34 | * [Folder Structure](#folder-structure)
35 | * [Available Scripts](#available-scripts)
36 | * [npm run dev](#npm-run-dev)
37 | * [npm run build](#npm-run-build)
38 | * [npm run build](#npm-run-build)
39 | * [npm run export](#npm-run-export)
40 | * [Available Generators](#available-generators)
41 | * [yo nextjs-typescript-antd](#yo-nextjs-typescript-antd)
42 | * [yo nextjs-typescript-antd:page](#yo-nextjs-typescript-antdpage)
43 | * [yo nextjs-typescript-antd:component](#yo-nextjs-typescript-antdcomponent)
44 | * [yo nextjs-typescript-antd:store](#yo-nextjs-typescript-antdstore)
45 | * [yo nextjs-typescript-antd:action](#yo-nextjs-typescript-antdaction)
46 | * [yo nextjs-typescript-antd:enum](#yo-nextjs-typescript-antdenum)
47 | * [yo nextjs-typescript-antd:hoc](#yo-nextjs-typescript-antdhoc)
48 | * [yo nextjs-typescript-antd:hook](#yo-nextjs-typescript-antdhook)
49 | * [yo nextjs-typescript-antd:context](#yo-nextjs-typescript-antdcontext)
50 | * [yo nextjs-typescript-antd:model](#yo-nextjs-typescript-antdmodel)
51 | * [Changelog](#changelog-generator)
52 | * [Release and Publish](#release-and-publish)
53 |
54 | # Questions? Feedback?
55 |
56 | Check out [Next.js FAQ & docs](https://github.com/zeit/next.js#faq) or [let us know](https://github.com/ElectronHacked/nextjs-typescript-antd/issues) your feedback.
57 |
58 | # Folder Structure
59 |
60 | After creating an app, it should look something like:
61 |
62 | ```
63 | my-app/
64 | api/
65 | postsApi.js
66 | components/
67 | global/
68 | commentItem/
69 | index.tsx
70 | styles.scss
71 | commentList/
72 | index.tsx
73 | styles.scss
74 | customNProgress/
75 | index.tsx
76 | styles.scss
77 | layout/
78 | index.tsx
79 | styles.scss
80 | postItem/
81 | index.tsx
82 | styles.scss
83 | postList/
84 | index.tsx
85 | styles.scss
86 | config/
87 | custom-environment-variables.js
88 | default.js
89 | development.js
90 | production.js
91 | testing.js
92 | constants/
93 | index.ts
94 | lib/
95 | withI18next.js
96 | config.shim.js
97 | models/
98 | comment.d.ts
99 | post.d.ts
100 | index.d.ts
101 | loadable.d.ts
102 | dispatchable.d.ts
103 | pages/
104 | about/
105 | index.tsx
106 | styles.scss
107 | index/
108 | index.tsx
109 | styles.scss
110 | post/
111 | index.tsx
112 | styles.scss
113 | posts/
114 | index.tsx
115 | styles.scss
116 | _app.js
117 | _document.js
118 | redux/
119 | posts/
120 | actions.ts
121 | constants.ts
122 | payloads.ts
123 | sagas.ts
124 | selectors.ts
125 | state.ts
126 | createStore.ts
127 | rootReducer.ts
128 | rootSaga.ts
129 | storeState.ts
130 | static/
131 | favicon.ico
132 | locales/
133 | en/
134 | images/
135 | styles/
136 | vendors/
137 | _mixins.scss
138 | _variables.scss
139 | antd-custom.less
140 | main.scss
141 | tests/
142 | units/
143 | components/
144 | jest.config.js
145 | pages/
146 | setup/
147 | index.js
148 | assetsTransformer.js
149 | typings/
150 | react-redux.d.ts
151 | .babelrc
152 | .eslintignore
153 | .eslintrc
154 | .gitignore
155 | .prettierrc
156 | README.md
157 | i18n.js
158 | .babelrc
159 | next.config.js
160 | package.json
161 | ```
162 |
163 | # Available Scripts
164 |
165 | ### `npm run dev`
166 |
167 | Runs the app in the development mode.
168 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
169 |
170 | The page will reload if you make edits.
171 | You will also see any errors in the console.
172 |
173 | ### `npm run build`
174 |
175 | Builds the app for production to the `.next` folder.
176 | It correctly bundles React in production mode and optimizes the build for the best performance.
177 |
178 | ### `npm run start`
179 |
180 | Starts the application in production mode.
181 | The application should be compiled with `next build` first.
182 |
183 | See the section in Next docs about [deployment](https://github.com/zeit/next.js/wiki/Deployment) for more information.
184 |
185 | ### `npm run export`
186 |
187 | Then you have a static version of your app in the out directory.
188 |
189 | You can also customize the output directory. For that `run next export -h` for the help.
190 |
191 | Now you can deploy the out directory to any static hosting service.
192 |
193 | # Available Generators
194 |
195 | ### `yo nextjs-typescript-antd`
196 |
197 | It will prompt you for the details of your new project `nextjs-typescript-antd` project
198 |
199 | ```
200 | $ yo nextjs-typescript-antd
201 | Initializing...
202 | ? Your project name rect-next-project
203 | ? Your project display name React Next Project
204 | ? What's your full name User Name
205 | ? What's your email address username@email.com
206 | create package.json
207 | create .babelrc
208 | create .eslintignore
209 | create .eslintrc
210 | create .gitattributes
211 | create .gitignore
212 | create .prettierrc
213 | ...
214 | ```
215 |
216 | ### `yo nextjs-typescript-antd:page`
217 |
218 | It will prompt you for the name and the title of your new page.
219 |
220 | ```
221 | $ yo nextjs-typescript-antd:page --force
222 | ? Page name User
223 | ? Page title User Details
224 | ? Would you like to create a store for this page? Yes
225 | ? Store name User
226 | create pages\user\index.tsx
227 | create pages\user\styles.scss
228 | create static\locales\en\user.json
229 | create tests\units\pages\user.test.js
230 | force components\global\layout\index.tsx
231 | force server.js
232 | create redux\user\actions.ts
233 | create redux\user\constants.ts
234 | create redux\user\payloads.ts
235 | create redux\user\reducer.ts
236 | create redux\user\sagas.ts
237 | create redux\user\selectors.ts
238 | create redux\user\state.ts
239 | force redux\rootReducer.ts
240 | force redux\rootSaga.ts
241 | force redux\storeState.ts
242 | ```
243 | Sometimes you might want to have your page nested within another page. To achieve this, all you need to do is:
244 | ```
245 | $ yo nextjs-typescript-antd:page --force
246 | ? Page name forgot password
247 | ? Page title Forgot password
248 | ? Is this a nested page? Yes
249 | ? Select the parent page
250 | about
251 | > account
252 | index
253 | people
254 | post
255 | posts
256 | (Move up and down to reveal more choices)
257 | ```
258 | then after selecting `account` as the parent page, your`forgot password` will be generated within the `account` folder. (Please note that you would have created the `account` page earlier). Below is the final output:
259 |
260 |
261 |
262 | ```
263 | $ yo nextjs-typescript-antd:page --force
264 | ? Page name forgot password
265 | ? Page title Forgot password
266 | ? Is this a nested page? Yes
267 | ? Select the parent page account
268 | ? Would you like to create a store for this page? No
269 | create pages\account\forgot-password\index.tsx
270 | create pages\account\forgot-password\styles.scss
271 | create static\locales\en\pages\account\forgot-password.json
272 | create tests\units\pages\account\forgot-password.test.js
273 | force components\global\layout\index.tsx
274 | force server.js
275 | ```
276 |
277 | Note how the page has been named `forgot-password` even though you would have entered `forgot password`. The same would have been the case even if you had entered something like `Forgot Password`, `forgotPassword` or `ForgotPassword`
278 |
279 | You can choose that a the redux `store` be created when you create a page. To achieve this, just answer `y` to the question `Would you like to create a store for this page?`. And the output would be something like below:
280 | ```
281 | $ yo nextjs-typescript-antd:page --force
282 | ? Page name Forgot Password
283 | ? Page title Forgot password
284 | ? Is this a nested page? Yes
285 | ? Select the parent page account
286 | ? Would you like to create a store for this page? Yes
287 | ? Store name (ForgotPassword)
288 | ```
289 | Notice how the default Store name is `ForgotPassword`. The same would have been the case even if you had entered something like `forgot Password`, `forgotPassword` or `forgot-password` for the name of the page . And the final output will be something like
290 |
291 | ```
292 | $ yo nextjs-typescript-antd:page --force
293 | ? Page name Forgot Password
294 | ? Page title Forgot password
295 | ? Is this a nested page? Yes
296 | ? Select the parent page account
297 | ? Would you like to create a store for this page? Yes
298 | ? Store name ForgotPassword
299 | force pages\account\forgot-password\index.tsx
300 | force pages\account\forgot-password\styles.scss
301 | force static\locales\en\pages\account\forgot-password.json
302 | force tests\units\pages\account\forgot-password.test.js
303 | force components\global\layout\index.tsx
304 | force server.js
305 | create redux-store\forgotPassword\actions.ts
306 | create redux-store\forgotPassword\constants.ts
307 | create redux-store\forgotPassword\reducer.ts
308 | create redux-store\forgotPassword\sagas.ts
309 | create redux-store\forgotPassword\selectors.ts
310 | create redux-store\forgotPassword\state.ts
311 | force redux-store\rootReducer.ts
312 | force redux-store\rootSaga.ts
313 | force redux-store\storeState.ts
314 | ```
315 |
316 | ### `yo nextjs-typescript-antd:component`
317 |
318 | It will prompt you the name for your new component.
319 |
320 | ```
321 | $ yo nextjs-typescript-antd:component --force
322 | ? Component name UserDetails
323 | ? Is this a page-specific component? Yes
324 | ? Page name User
325 | create components\global\pages\user\userDetails\index.tsx
326 | create components\global\userDetails\styles.scss
327 | create static\locales\en\userDetails.json
328 | create tests\units\components\userDetails.test.js
329 | ```
330 | Just as you can nest pages, you can also choose that your components be specific to pages which are nested. Below is the code sample:
331 |
332 | ```
333 | $ yo nextjs-typescript-antd:component --force
334 | ? Component name user avatar
335 | ? Is this a page-specific component? Yes
336 | ? Page name
337 | about
338 | account
339 | > account/forgot-password
340 | index
341 | people
342 | (Move up and down to reveal more choices)
343 | ```
344 |
345 | And the final output:
346 | ```
347 | $ yo nextjs-typescript-antd:component --force
348 | ? Component name user avatar
349 | ? Is this a page-specific component? Yes
350 | ? Page name account/forgot-password
351 | create components\pages\account\forgot-password\userAvatar\index.tsx
352 | create components\pages\account\forgot-password\userAvatar\styles.scss
353 | force static\locales\en\userAvatar.json
354 | force tests\units\components\userAvatar.test.js
355 | force components\index.ts
356 | ```
357 |
358 | Note that for the component name we entered `user avatar` and we got `userAvatar`. This is because all the component files should be `camelCase`d and the component names should be `PascalCase`d. Below is the generated component:
359 |
360 | ```jsx
361 | import React, {FC} from 'react';
362 | import './styles.scss';
363 |
364 | interface IProps {};
365 |
366 | export const UserAvatar: FC = () => (
367 |
368 | UserAvatar component
369 |
370 | );
371 |
372 | export default UserAvatar;
373 | ```
374 |
375 |
376 | And the `components\index.ts` file looks something like
377 |
378 | ```jsx
379 | export { default as CommentItem } from './global/commentItem';
380 | export { default as CommentList } from './global/commentList';
381 | export { default as CustomNProgress } from './global/customNProgress';
382 | export { default as Layout } from './global/layout';
383 | export { default as PostItem } from './global/postItem';
384 | export { default as PostList } from './global/postList';
385 | export { default as CustomErrorBoundary } from './global/customErrorBoundary';
386 | export { default as UserAvatar } from './pages/account/forgot-password/userAvatar';
387 | /* new-component-import-goes-here */
388 | ```
389 | This will allow you to import the `UserAvatar` component from anywhere in the project like below
390 |
391 | ```jsx
392 | ...
393 | import { UserAvatar } from 'components';
394 | ...
395 | ```
396 |
397 | This is possible because of the configuration in the `.babelrc` file.
398 |
399 | ```json
400 | ...
401 | "module-resolver",
402 | {
403 | "root": ["./"],
404 | "alias": {
405 | "api": "./api",
406 | "components": "./components",
407 | "constants": "./constants",
408 | "redux-store": "./redux-store"
409 | }
410 | }
411 | ...
412 | ```
413 | ### `yo nextjs-typescript-antd:store`
414 |
415 | It will prompt you the name for your new store.
416 |
417 | ```
418 | $ $ yo nextjs-typescript-antd:store --force
419 | ? Store name People
420 | create redux-store\people\actions.ts
421 | create redux-store\people\constants.ts
422 | create redux-store\people\reducer.ts
423 | create redux-store\people\sagas.ts
424 | create redux-store\people\selectors.ts
425 | create redux-store\people\state.ts
426 | force redux-store\rootReducer.ts
427 | force redux-store\rootSaga.ts
428 | force redux-store\storeState.ts
429 | ```
430 | As you can see, there were 6 new files which were created. Let's have a look at each and explain what's happening in those.
431 |
432 | #### `state.ts`
433 | ```jsx
434 | export type PeopleErrable =
435 | | '__errable__' // Remove this. It's just a placeholder
436 | /* new-errable-goes-here */;
437 |
438 | export type PeopleBooleanable =
439 | | '__booleanable__' // Remove this. It's just a placeholder
440 | /* new-booleanable-goes-here */;
441 |
442 | export type PeopleSuccessible =
443 | | '__successible__' // Remove this. It's just a placeholder
444 | /* new-successible-goes-here */;
445 |
446 | export interface IPeopleState {
447 | //#region Doables
448 | readonly errable?: { [key in PeopleErrable]?: string };
449 | readonly booleanable?: { [key in PeopleBooleanable]?: boolean };
450 | readonly successible?: { [key in PeopleSuccessible]?: string };
451 | //#endregion
452 | }
453 | ```
454 | As you can see, we have 3 `type`s, namely `PeopleErrable`, `PeopleBooleanable` & `PeopleSuccessible` and they are initialized with default values which the comments state that they should be removed as soon as you start creating your own.
455 |
456 | The reason I introduced these 3 `type` in a newly-created store is to allow the user to define the state for all the actions that are `fullfillable` and by this, I simply mean the situation where an action can be dispatched and we wait for it finish (by either succeeding or failing). If we use fetching users as an example, we would normally have three properties in the state: `isFetchUsersInProgress`: `boolean`, `fetchUsersError`: `string` and (sometimes `fetchUserSuccess`: `string` - which, for the purpose of this example, would be a string like `"Successfully fetched the user!"`.
457 |
458 | This approach seem to work properly, except for when you have to create a selector to read them. You would have something like `selectisFetchUsersInProgress`, `fetchUserErrorMessage` and, finally, `fetchUserSuccess`. This creates a problem because for every `fullfilable` action, that would mean you would need a new selector.
459 |
460 | Another problem is when you need an action to set each of those. The old approach means you would need 3 actions, namely: `setisFetchUsersInProgress`, `setFetchUserErrorMessage` and `fetchUserSuccess`. That's not cool! So, to avoid having to do this, I just thought I could get away with these `doable` `type`s and define, in them, what can be doable. This allows me to create just one selector that allows me to select anything that can be `booleanable`, such as `isFetchUsersInProgress`, `isUpdateUserInProgress`, `isSigningOutInProgress`, `showDeleteModal`, etc. with just one selector like the one below
461 |
462 | ```jsx
463 | export const selectPeopleBooleanableState = (key: PeopleBooleanable | PeopleBooleanable[]) =>
464 |
465 | createSelector(
466 | peopleState(),
467 | ({ booleanable }) => (Array.isArray(key) ? !!key.filter(k => booleanable[k]).length : booleanable[key])
468 | );
469 | ```
470 | As you can see, I can simply do something like
471 |
472 | ```jsx
473 | const mapStateToProps = createStructuredSelector({
474 | isFetchUsersInProgress= selectPeopleBooleanableState('isFetchUsersInProgress')
475 | isUpdateUserInProgress= selectPeopleBooleanableState('isUpdateUserInProgress')
476 | isSigningOutInProgress= selectPeopleBooleanableState('isSigningOutInProgress')
477 | showDeleteModal= selectPeopleBooleanableState('showDeleteModal')
478 | });
479 |
480 | // Or the one that returns true if any of the above is true, like
481 | const mapStateToProps = createStructuredSelector({
482 | showloader= selectPeopleBooleanableState(['isFetchUsersInProgress', 'isUpdateUserInProgress', 'isSigningOutInProgress'])
483 | })
484 | ```
485 |
486 | Please be sure to remove the default `doable`s like `__errable__`, `__booleanable__` & `__successible__` once you have added your own.
487 |
488 | Another example of creating an action that can set either of them, is in the `redux-store\people\actions.ts`
489 |
490 | ```jsx
491 | export const togglePeopleErrableState = createAction(
492 | TOGGLE_PEOPLE_ERRABLE_STATE,
493 | key => ({
494 | errable: key,
495 | })
496 | );
497 | ```
498 |
499 | This allows you to what is shown below. (This assumes you have `errable` states such as `fetcUserErrorMsg`, `updateUserErrorMsg`, `signInErrorMsg`, etc.
500 |
501 | ```jsx
502 | dispatch(togglePeopleErrableState({ fetcUserErrorMsg: '' ));
503 | dispatch(togglePeopleErrableState({ updateUserErrorMsg: 'Could not update the user!' ));
504 | dispatch(togglePeopleErrableState({ signInErrorMsg: '' ));
505 |
506 | // Or even
507 | dispatch(togglePeopleErrableState({
508 | fetcUserErrorMsg: '',
509 | updateUserErrorMsg: 'Could not update the user!',
510 | signInErrorMsg: ''
511 | }));
512 |
513 | ```
514 |
515 | All with just one action.
516 |
517 | ### `constants.ts`
518 | ```jsx
519 | export const DEFAULT_ACTION = 'DEFAULT_ACTION';
520 |
521 |
522 |
523 | //#region Reset doable for this state
524 | export const RESET_PEOPLE_DOABLES = 'RESET_PEOPLE_DOABLES';
525 | export const TOGGLE_PEOPLE_BOOLEANABLE_STATE = 'TOGGLE_PEOPLE_BOOLEANABLE_STATE';
526 | export const TOGGLE_PEOPLE_ERRABLE_STATE = 'TOGGLE_PEOPLE_ERRABLE_STATE';
527 | export const TOGGLE_PEOPLE_SUCCESSIBLE_STATE = 'TOGGLE_PEOPLE_SUCCESSIBLE_STATE';
528 | //#endregion
529 |
530 | /* new-constant-export-goes-here */
531 | ```
532 | This contains the constants to set the `doable`s and reset them. Please do not remove the line that says `/* new-constant-export-goes-here */` as that is where the generated code will seat.
533 |
534 | #### `reducer.ts`
535 |
536 | ```jsx
537 | import {
538 | DEFAULT_ACTION,
539 | RESET_PEOPLE_DOABLES,
540 | /* new-constant-import-goes-here */
541 | } from './constants';
542 |
543 | import { IPeopleState } from './state';
544 | import { reducerPayloadDoableHelper } from 'redux-store/rootReducer';
545 |
546 | const initialState: IPeopleState = {
547 | errable: {},
548 | booleanable: {},
549 | successible: {},
550 | };
551 |
552 | export default (
553 | state: IPeopleState = initialState,
554 | { type, payload: incomingPayload }: ReduxActions.Action
555 | ) => {
556 | const payload =
557 | type === RESET_PEOPLE_DOABLES
558 | ? incomingPayload
559 | : (reducerPayloadDoableHelper(state, incomingPayload) as IPeopleState);
560 |
561 | switch (type) {
562 | /* new-constant-cases-go-here */
563 | case DEFAULT_ACTION:
564 | return {
565 | ...state,
566 | ...payload,
567 | }
568 | default:
569 | return state;
570 | }
571 | };
572 | ```
573 | Inside the reducer, the first line ensures that if the action being performed is `RESET_PEOPLE_DOABLES`, we make make use of `./rootReducer/reducerPayloadDoableHelper` which ensures that the current `errable`, `successible` and `boolean` are not overridden by the incoming values. A good example of when that can be the case is if our current state has something like this:
574 |
575 | ```jsx
576 | {
577 | booleanable: {
578 | isFetchUsersInProgress: true,
579 | isUpdateUserInProgress: false,
580 | }
581 | }
582 | ```
583 |
584 | if we were to `togglePeopleBooleanableState` like below
585 | ```jsx
586 | dispatch(togglePeopleBooleanableState({
587 | isSigningOutInProgress: false, showDeleteModal: false
588 | }));
589 | ```
590 |
591 | this would have resulted in a new state being
592 | ```jsx
593 | {
594 | booleanable: {
595 | isSigningOutInProgress: false,
596 | showDeleteModal: false,
597 | }
598 | }
599 | ```
600 | but what we are looking for is
601 | ```jsx
602 | {
603 | booleanable: {
604 | isFetchUsersInProgress: true,
605 | isUpdateUserInProgress: false,
606 | isSigningOutInProgress: false,
607 | showDeleteModal: false,
608 | }
609 | }
610 | ```
611 |
612 | but the `reducerPayloadDoableHelper` function solves that problem.
613 |
614 | I will skip`sagas.ts` file.
615 |
616 | #### `selectors.ts`
617 | This is initialized with 3 selectors that are used to select the `errable`, `successible` or `booleanable` states of a given sub-state. and they are, in the case of `People` state, `selectPeopleBooleanableState`, `selectPeopleErrableState` and `selectPeopleSuccessibleState`. All these will be generated for you when you use the `:store` subgenerator.
618 |
619 | ### `yo nextjs-typescript-antd:action`
620 |
621 | It will prompt you the name for your new action.
622 |
623 | Below is an example of creating a `action`. The first question you are asked is t select the name of the state under which the action will be created. In our case, that will be a `People` state.
624 | ```
625 | $ $ yo nextjs-typescript-antd:action --force
626 | ? Action name jump
627 | ? Select the store (Use arrow keys)
628 | > People
629 | Posts
630 | ```
631 |
632 | In case you are wondering, the list of states is contained in the `.yo-rc.json` file and it's updated each time you use a generator to generate a state. Currently, it looks like below
633 | ```jsx
634 | {
635 | "generator-nextjs-typescript-antd": {
636 | "pages": [
637 | ...
638 | ],
639 | "stores": [
640 | "Posts",
641 | "People"
642 | ]
643 | }
644 | }
645 | ```
646 |
647 | As you can see, there are two stores in our project.
648 |
649 | After that, you are asked if your action will can be fullfillable or not. An example of such can be one that triggers an API call, such as `fetchUsers` because an API can return either success or error in which case you might want to react to that by dispatching a success (`fetchUsersSuccess`) an error (`fetchUsersError`) action.
650 | ```
651 | $ yo nextjs-typescript-antd:action --force
652 | ? Action name fetchUsers
653 | ? Select the store People
654 | ? Is it fullfillable? (Does it have SUCCESS & ERROR?) Yes
655 | force redux-store\people\constants.ts
656 | force redux-store\people\reducer.ts
657 | force redux-store\people\actions.ts
658 | force redux-store\people\state.ts
659 | force redux-store\people\sagas.ts
660 | ```
661 |
662 | As you can see 5 files were updated. Let's see how they were all affected.
663 |
664 | ```jsx
665 | // redux-store\people\constants.ts
666 |
667 | ...
668 | //#region FETCH_USERS-related constants
669 | export const FETCH_USERS = 'FETCH_USERS';
670 | export const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
671 | export const FETCH_USERS_ERROR = 'FETCH_USERS_ERROR';
672 | //#endregion
673 | ...
674 | ```
675 |
676 | ```jsx
677 | // redux-store\people\actions.ts
678 |
679 | ...
680 | //#region fetchUsers-related constants
681 | export const fetchUsers = createAction(FETCH_USERS, () => ({
682 | booleanable: { isFetchUsersInProgress: true },
683 | errable: { fetchUsersErrorMsg: null },
684 | successible: { fetchUsersSuccessMsg: null },
685 | }));
686 |
687 | export const fetchUsersSuccess = createAction(FETCH_USERS_SUCCESS, state => ({
688 | booleanable: { isFetchUsersInProgress: false },
689 | successible: { fetchUsersSuccessMsg: 'FETCH_USERS action fullfilled!' },
690 | state,
691 | }));
692 |
693 | export const fetchUsersError = createAction(FETCH_USERS_ERROR, fetchUsersErrorMsg => ({
694 | booleanable: { isFetchUsersInProgress: false },
695 | errable: { fetchUsersErrorMsg },
696 | }));
697 | //#endregion
698 | ...
699 | ```
700 |
701 | ```jsx
702 | // redux-store\people\reducer.ts
703 |
704 | import {
705 | ...
706 | FETCH_USERS,
707 | FETCH_USERS_SUCCESS,
708 | FETCH_USERS_ERROR,
709 | /* new-constant-import-goes-here */
710 | } from './constants';
711 |
712 | ...
713 |
714 | export default (
715 | state: IPeopleState = initialState,
716 | { type, payload: incomingPayload }: ReduxActions.Action
717 | ) => {
718 | const payload = ...
719 |
720 | switch (type) {
721 | ...
722 | case FETCH_USERS:
723 | case FETCH_USERS_SUCCESS:
724 | case FETCH_USERS_ERROR:
725 | /* new-constant-cases-go-here */
726 | case DEFAULT_ACTION:
727 | return {
728 | ...state,
729 | ...payload,
730 | };
731 | default:
732 | return state;
733 | }
734 | };
735 | ```
736 |
737 | ```jsx
738 | // redux-store\people\sagas.ts
739 |
740 | ...
741 | import {
742 | ...
743 | FETCH_USERS,
744 | /* new-constant-import-goes-here */
745 | } from './constants';
746 | import {
747 | ...
748 | fetchUsersSuccess,
749 | fetchUsersError,
750 | /* new-action-import-goes-here */
751 | } from './actions';
752 | ...
753 | function* fetchUsersSaga() {
754 | const BOOL_VALUE = Math.random() >= 0.5;
755 |
756 | yield delay(500); // Just sleep for half a sec just to look real. A saga requires a yield because it's a generator
757 |
758 | try {
759 | if (BOOL_VALUE) {
760 | yield put(fetchUsersSuccess({}));
761 | } else {
762 | yield put(fetchUsersError('Sorry, An error occured! Please try again later!'));
763 | }
764 | } catch (error) {
765 | yield put(fetchUsersError('Sorry, An error occured! Please try again later!'));
766 | }
767 | }
768 |
769 | export default function* peopleSaga() {
770 | yield all([
771 | ...
772 | takeLatest(FETCH_USERS, fetchUsersSaga),
773 | /* new-saga-registration-goes-here */
774 | ]);
775 | }
776 |
777 | ```
778 |
779 | ```jsx
780 | // redux-store\people\state.ts
781 | ...
782 | export type PeopleErrable = 'fetchUsersErrorMsg' /* new-errable-goes-here */;
783 | export type PeopleBooleanable = 'isFetchUsersInProgress' /* new-booleanable-goes-here */;
784 | export type PeopleSuccessible = 'fetchUsersSuccessMsg' /* new-successible-goes-here */;
785 | ...
786 | ```
787 |
788 | As you can see, it's too much code... but it's too much code that you don't get to write.
789 |
790 | In a case where you do not want to have your action that has `_ERROR` and `_SUCCESS`, you would have only three files updated such as below
791 |
792 | ```jsx
793 | $ yo nextjs-typescript-antd:action --force
794 | ? Action name jump
795 | ? Select the store People
796 | ? Is it fullfillable? (Does it have SUCCESS & ERROR?) No
797 | force redux-store\people\constants.ts
798 | force redux-store\people\reducer.ts
799 | force redux-store\people\actions.ts
800 | ```
801 |
802 | And below is how each would have been affected
803 | ```jsx
804 | // redux-store\people\constants.ts
805 | ...
806 | //#region JUMP-related constants
807 | export const JUMP = 'JUMP';
808 | //#endregion
809 | ...
810 |
811 | // redux-store\people\reducer.ts
812 | ...
813 | switch (type) {
814 | ...
815 | case JUMP:
816 | /* new-constant-cases-go-here */
817 | case DEFAULT_ACTION:
818 | return {
819 | ...state,
820 | ...payload,
821 | };
822 | default:
823 | return state;
824 | }
825 |
826 | ...
827 | // redux-store\people\actions.ts
828 | ...
829 | //#region jump-related constants
830 | export const jump = createAction(JUMP, state => state); // Make sure you pass a proper payload!!!
831 | //#endregion
832 | ...
833 | ```
834 |
835 | As you can see, there was nothing updated with regards to `errable`, `successible` and `booleanable` states because the action is not `async` and can't fail.
836 |
837 | ### `yo nextjs-typescript-antd:enum`
838 |
839 | It will prompt you the name for your new enum.
840 |
841 | ```
842 | $ yo nextjs-typescript-antd:enum --force
843 | ? Enum name UserRoles
844 | create enums\userRoles.ts
845 | force enums\index.ts
846 | ```
847 |
848 | Below is how the two files will be affected
849 |
850 | ```jsx
851 | // enums\userRoles.ts
852 | export enum UserRoles {
853 | DefaultValue = 1,
854 | }
855 | ```
856 | and
857 | ```jsx
858 | // enums\index.ts
859 | ...
860 | export { UserRoles } from './userRoles';
861 | ...
862 | ```
863 | ### `yo nextjs-typescript-antd:antdhoc`
864 |
865 | It will prompt you the name for your new higher-order component.
866 |
867 | ```
868 | $ yo nextjs-typescript-antd:hoc --force
869 | ? HOC name withUserRoles
870 | create hocs\withUserRoles.tsx
871 | force hocs\index.ts
872 | ```
873 | Below is how the two files will be affected
874 |
875 | ```jsx
876 | // hocs\withUserRoles.tsx
877 |
878 | import React, { FC, ComponentType } from 'react';
879 | /**
880 | *
881 | * @param Component - the component that is passed into the HOC can be either a function component or class component.
882 | *
883 | * @see https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb
884 | */
885 | const withUserRoles =
(Component: ComponentType
): FC
=> (props: P) => {
886 | // Your logic comes up in here
887 | return ;
888 | };
889 |
890 | export default withUserRoles;
891 | ```
892 | and
893 | ```jsx
894 | // hocs\index.ts
895 | ...
896 | export { default as withUserRoles } from './withUserRoles';
897 | ...
898 | ```
899 |
900 | ### `yo nextjs-typescript-antd:antdhook`
901 |
902 | It will prompt you the name for your new hook.
903 |
904 | ```
905 | $ yo nextjs-typescript-antd:hook --force
906 | ? Hook name (Should start with use) useUserRoles
907 | create hooks\useUserRoles.ts
908 | force hooks\index.ts
909 | ```
910 | Below is how the two files will be affected
911 |
912 | ```jsx
913 | // hooks\useUserRoles.ts
914 |
915 | export const useUserRoles = () => {
916 | // Put your logic here
917 | return null;
918 | };
919 | ```
920 | and
921 | ```jsx
922 | // hooks\index.ts
923 | ...
924 | export { useUserRoles } from './useUserRoles';
925 | ...
926 | ```
927 |
928 | ### `yo nextjs-typescript-antd:antdcontext`
929 |
930 | It will prompt you the name for your new context.
931 |
932 | ```
933 | $ yo nextjs-typescript-antd:context --force
934 | ? Context name UserContext
935 | create contexts\userContext.ts
936 | force contexts\index.ts
937 | ```
938 | Below is how the two files will be affected
939 |
940 | ```jsx
941 | // contexts\userContext.ts
942 |
943 | import { createContext } from 'react';
944 |
945 | interface IUserContext {}
946 |
947 | export const defaultUserContext: IUserContext = {};
948 |
949 | export const UserContext = createContext(defaultUserContext);
950 | ```
951 | and
952 | ```jsx
953 | // contexts\index.ts
954 | ...
955 | export { UserContext } from './userContext';
956 | ...
957 | ```
958 |
959 | ### `yo nextjs-typescript-antd:model`
960 |
961 | It will prompt you the name for your new interface.
962 |
963 | ```
964 | $ yo nextjs-typescript-antd:model --force
965 | ? Model name User
966 | create models\user.d.ts
967 | force models\index.d.ts
968 | ```
969 |
970 | Below is how the two files will be affected
971 |
972 | ```jsx
973 | // models\user.d.ts
974 |
975 | export interface IUser {
976 | readonly id: string;
977 | }
978 | ```
979 | and
980 | ```jsx
981 | // models\index.d.ts
982 | ...
983 | export { IUser } from './user';
984 | ...
985 | ```
986 |
987 | All that the subgenerators `:hoc`, `:hook`, `:model`, `:hook` and `:context` are giving you is a boilerplate and they also help enforcing the naming convention and eliminate the issues that can arise by someone creating something, like an `hoc` and they forget to export it in the `hocs/index.ts`. That will only mean that they can only import it as `import { someHoc } from 'hocs/someHoc';`. But they should only be importing it as `import { someHoc } from 'hocs';` and that is only possible if they don't forget to export it in the `hocs/index.ts` file. And, also, because the code is generated, you can not create a file in `PascalCase` , as in `SomeHoc` or even `hocs/some-hoc`. All file names will be in `camelCase`.
988 |
989 | # Changelog Generator
990 | Generate a changelog from git commits using https://github.com/lob/generate-changelog. This is meant to be used so that for every patch, minor, or major version, you update the changelog prior to running npm version so that the git tag contains the commit that updated both the changelog and version.
991 |
992 | # Release and Publish
993 | In order to release and publish we have created a npm script `npm run release-and-publish` that will create a new tag in github and will publish your pkg into npm.
994 |
995 | # What does this generator do?
996 |
997 | This yeoman generator will build different React components, creating a skeleton for the different files.
998 |
999 | ### TODO List
1000 |
1001 | - [ ] Allow The User To Choose A CSS Preprocessor (LESS OR LESS)
1002 | - [ ] Allow The User To Specify CRUD Requirements When Generating The Page
1003 | - [ ] Allow The User To Choose A [Layout](https://ant.design/components/layout/) When Generating The Boilerplate
1004 |
1005 | # Credits
1006 |
1007 | Electron Hacked
1008 | Github: https://github.com/ElectronHacked/nextjs-typescript-antd
1009 |
1010 | Inspired by https://github.com/AnalyticsFire/generator-create-next-app-reloaded by Damian Aruj
1011 |
1012 | # Licence
1013 |
1014 | MIT
1015 |
--------------------------------------------------------------------------------
/generators/action/index.js:
--------------------------------------------------------------------------------
1 | const Generator = require('yeoman-generator');
2 | const camelCase = require('camelcase');
3 | const decamelize = require('decamelize');
4 |
5 | module.exports = class extends Generator {
6 | prompting() {
7 | const stores = this.config.get('stores');
8 | const storeOptions = stores.sort();
9 |
10 | return this.prompt([
11 | {
12 | type: 'input',
13 | name: 'name',
14 | message: 'Action name',
15 | validate: str => {
16 | if (str.trim().length > 0) {
17 | return true;
18 | }
19 | return 'Please add a name for your new action';
20 | },
21 | },
22 | {
23 | type: 'list',
24 | name: 'reducerName',
25 | message: 'Select the store',
26 | choices: storeOptions,
27 | },
28 | {
29 | type: 'confirm',
30 | name: 'isFulfillable',
31 | message: 'Is it fullfillable? (Does it have SUCCESS & ERROR?)',
32 | default: false,
33 | },
34 | ]).then(({ name, reducerName, isFulfillable }) => {
35 | this.answers = {
36 | name: camelCase(name),
37 | reducerName,
38 | isFulfillable,
39 | };
40 | });
41 | }
42 |
43 | writing() {
44 | const { name, reducerName, isFulfillable } = this.answers;
45 | const actionNameToCamelCase = camelCase(name);
46 | const actionNameToPascalCase = camelCase(name, {
47 | pascalCase: true,
48 | });
49 |
50 | const regionnify = (content, region) => `//#region ${region}\n${content}\n//#endregion\n`;
51 |
52 | const successToUpper = ACTION_NAME => `${ACTION_NAME}_SUCCESS`;
53 | const errorToUpper = ACTION_NAME => `${ACTION_NAME}_ERROR`;
54 |
55 | const successToCamelCase = ACTION_NAME => `${ACTION_NAME}Success`;
56 | const errorToCamelCase = ACTION_NAME => `${ACTION_NAME}Error`;
57 |
58 | const REDUX_STORE_BASE_PATH = `./redux-store/${reducerName.toLowerCase()}`;
59 |
60 | // #region Export from the `./actions` file
61 |
62 | const CONSTANT_NAME = decamelize(name, '_').toUpperCase();
63 |
64 | let CONSTANT_EXPORT_STATEMENT = `export const ${CONSTANT_NAME} = '${CONSTANT_NAME}';`;
65 |
66 | let CONSTANTS_IMPORT_STATEMENTS = `${CONSTANT_NAME},`;
67 |
68 | const importConstantsHelper = path => {
69 | this.fs.copy(path, path, {
70 | process(content) {
71 | const regEx = new RegExp(/\/\* new-constant-import-goes-here \*\//, 'g');
72 | const newContent = content
73 | .toString()
74 | .replace(regEx, `${CONSTANTS_IMPORT_STATEMENTS}\n\t/* new-constant-import-goes-here */`);
75 | return newContent;
76 | },
77 | });
78 | };
79 |
80 | let CONSTANTS_CASE_STATEMENTS = `case ${CONSTANT_NAME}:`;
81 |
82 | if (isFulfillable) {
83 | CONSTANT_EXPORT_STATEMENT += `\nexport const ${successToUpper(CONSTANT_NAME)} = '${successToUpper(
84 | CONSTANT_NAME,
85 | )}';\nexport const ${errorToUpper(CONSTANT_NAME)} = '${errorToUpper(CONSTANT_NAME)}';`;
86 |
87 | CONSTANTS_IMPORT_STATEMENTS += `\n\t${successToUpper(CONSTANT_NAME)},\n\t${errorToUpper(CONSTANT_NAME)},`;
88 |
89 | CONSTANTS_CASE_STATEMENTS += `\n\t\tcase ${successToUpper(CONSTANT_NAME)}:\n\t\tcase ${errorToUpper(
90 | CONSTANT_NAME,
91 | )}:`;
92 | }
93 |
94 | // Export the constants
95 | const CONSTANTS_PATH = `${REDUX_STORE_BASE_PATH}/constants.ts`;
96 |
97 | // update constsntstss to export the newly-created actions
98 | this.fs.copy(CONSTANTS_PATH, CONSTANTS_PATH, {
99 | process(content) {
100 | const regEx = new RegExp(/\/\* new-constant-export-goes-here \*\//, 'g');
101 | const newContent = content
102 | .toString()
103 | .replace(
104 | regEx,
105 | `${regionnify(
106 | CONSTANT_EXPORT_STATEMENT,
107 | `${CONSTANT_NAME}-related constants`,
108 | )}\n\n/* new-constant-export-goes-here */`,
109 | );
110 | return newContent;
111 | },
112 | });
113 | // #endregion
114 |
115 | // #region Import actions in the reducer and put it in the case statements
116 | const REDUCER_PATH = `${REDUX_STORE_BASE_PATH}/reducer.ts`;
117 |
118 | // Import the constants
119 | importConstantsHelper(REDUCER_PATH);
120 |
121 | // Add constants in the case statemnts
122 | this.fs.copy(REDUCER_PATH, REDUCER_PATH, {
123 | process(content) {
124 | const regEx = new RegExp(/\/\* new-constant-cases-go-here \*\//, 'g');
125 | const newContent = content
126 | .toString()
127 | .replace(regEx, `${CONSTANTS_CASE_STATEMENTS}\n\t\t/* new-constant-cases-go-here */`);
128 | return newContent;
129 | },
130 | });
131 | // #endregion
132 |
133 | // #region Actions file
134 | // Import the constants
135 | const ACTIONS_PATH = `${REDUX_STORE_BASE_PATH}/actions.ts`;
136 |
137 | // Import the constants
138 | importConstantsHelper(ACTIONS_PATH);
139 |
140 | let ACTIONS = '';
141 | const booleanable = `is${actionNameToPascalCase}InProgress`;
142 | const errable = `${actionNameToCamelCase}ErrorMsg`;
143 | const successible = `${actionNameToCamelCase}SuccessMsg`;
144 |
145 | if (isFulfillable) {
146 | ACTIONS = `
147 | export const ${actionNameToCamelCase} = createAction(${CONSTANT_NAME}, () => ({
148 | booleanable: { ${booleanable}: true },
149 | errable: { ${errable}: null },
150 | successible: { ${successible}: null },
151 | }));
152 |
153 | export const ${successToCamelCase(
154 | actionNameToCamelCase,
155 | )} = createAction(${successToUpper(CONSTANT_NAME)}, state => ({
156 | booleanable: { ${booleanable}: false },
157 | successible: { ${successible}: '${CONSTANT_NAME} action fullfilled!' },
158 | state,
159 | }));
160 |
161 | export const ${errorToCamelCase(
162 | actionNameToCamelCase,
163 | )} = createAction(${errorToUpper(CONSTANT_NAME)}, ${errable} => ({
164 | booleanable: { ${booleanable}: false },
165 | errable: { ${errable} },
166 | }));`;
167 | } else {
168 | ACTIONS = `export const ${actionNameToCamelCase} = createAction(${CONSTANT_NAME}, state => state); // Make sure you pass a proper payload!!!`;
169 | }
170 |
171 | // update constsntstss to export the newly-created actions
172 | this.fs.copy(ACTIONS_PATH, ACTIONS_PATH, {
173 | process(content) {
174 | const regEx = new RegExp(/\/\* new-actions-go-here \*\//, 'g');
175 | const newContent = content
176 | .toString()
177 | .replace(
178 | regEx,
179 | `${regionnify(ACTIONS, `${actionNameToCamelCase}-related constants`)}\n\n/* new-actions-go-here */`,
180 | );
181 | return newContent;
182 | },
183 | });
184 | // #endregion
185 |
186 | // #region Do the things in the state.ts
187 | if (isFulfillable) {
188 | const STATE_PATH = `${REDUX_STORE_BASE_PATH}/state.ts`;
189 |
190 | // Booleanable
191 | this.fs.copy(STATE_PATH, STATE_PATH, {
192 | process(content) {
193 | const regEx = new RegExp(/\/\* new-booleanable-goes-here \*\//, 'g');
194 | const newContent = content.toString().replace(regEx, `| '${booleanable}' /* new-booleanable-goes-here */`);
195 | return newContent;
196 | },
197 | });
198 |
199 | // Errable
200 | this.fs.copy(STATE_PATH, STATE_PATH, {
201 | process(content) {
202 | const regEx = new RegExp(/\/\* new-errable-goes-here \*\//, 'g');
203 | const newContent = content.toString().replace(regEx, `| '${errable}' /* new-errable-goes-here */`);
204 | return newContent;
205 | },
206 | });
207 |
208 | // Successible
209 | this.fs.copy(STATE_PATH, STATE_PATH, {
210 | process(content) {
211 | const regEx = new RegExp(/\/\* new-successible-goes-here \*\//, 'g');
212 | const newContent = content.toString().replace(regEx, `| '${successible}' /* new-successible-goes-here */`);
213 | return newContent;
214 | },
215 | });
216 | }
217 | // #endregion
218 |
219 | // #region Sagas
220 | if (isFulfillable) {
221 | const SAGAS_PATH = `${REDUX_STORE_BASE_PATH}/sagas.ts`;
222 |
223 | // Import the constant
224 | this.fs.copy(SAGAS_PATH, SAGAS_PATH, {
225 | process(content) {
226 | const regEx = new RegExp(/\/\* new-constant-import-goes-here \*\//, 'g');
227 | const newContent = content
228 | .toString()
229 | .replace(regEx, `${CONSTANT_NAME},\n\t/* new-constant-import-goes-here */`);
230 | return newContent;
231 | },
232 | });
233 |
234 | // Import the actions
235 | const ACTIONS_IMPORT_STATEMENTS = `${successToCamelCase(actionNameToCamelCase)},\n\t${errorToCamelCase(
236 | actionNameToCamelCase,
237 | )},`;
238 |
239 | this.fs.copy(SAGAS_PATH, SAGAS_PATH, {
240 | process(content) {
241 | const regEx = new RegExp(/\/\* new-action-import-goes-here \*\//, 'g');
242 | const newContent = content
243 | .toString()
244 | .replace(regEx, `${ACTIONS_IMPORT_STATEMENTS}\n\t/* new-action-import-goes-here */`);
245 | return newContent;
246 | },
247 | });
248 |
249 | // Create a saga
250 | const ERROR_MSG = '"Sorry, An error occured! Please try again later!"';
251 | const SAGA_NAME = `${actionNameToCamelCase}Saga`;
252 |
253 | const SAGA = `
254 | function* ${SAGA_NAME}() {
255 | const BOOL_VALUE = Math.random() >= 0.5;
256 |
257 | yield delay(500); // Just sleep for half a sec just to look real. A saga requires a yield because it's a generator
258 |
259 | try {
260 | if (BOOL_VALUE) {
261 | yield put(${successToCamelCase(actionNameToCamelCase)}({}));
262 | } else {
263 | yield put(${errorToCamelCase(actionNameToCamelCase)}(${ERROR_MSG}));
264 | }
265 | } catch (error) {
266 | yield put(${errorToCamelCase(actionNameToCamelCase)}(${ERROR_MSG}));
267 | }
268 | }
269 | `;
270 |
271 | // Add a saga
272 | this.fs.copy(SAGAS_PATH, SAGAS_PATH, {
273 | process(content) {
274 | const regEx = new RegExp(/\/\* new-saga-goes-here \*\//, 'g');
275 | const newContent = content.toString().replace(regEx, `${SAGA}\n\n/* new-saga-goes-here */`);
276 | return newContent;
277 | },
278 | });
279 |
280 | // Register the saga
281 | const SAGA_REGISTRATION = `takeLatest(${CONSTANT_NAME}, ${SAGA_NAME}),`;
282 |
283 | this.fs.copy(SAGAS_PATH, SAGAS_PATH, {
284 | process(content) {
285 | const regEx = new RegExp(/\/\* new-saga-registration-goes-here \*\//, 'g');
286 | const newContent = content
287 | .toString()
288 | .replace(regEx, `${SAGA_REGISTRATION}\n\t\t/* new-saga-registration-goes-here */`);
289 | return newContent;
290 | },
291 | });
292 | // #endregion
293 | }
294 | // #endregion
295 | }
296 | };
297 |
--------------------------------------------------------------------------------
/generators/app/index.js:
--------------------------------------------------------------------------------
1 | const Generator = require('yeoman-generator');
2 | const mkdirp = require('mkdirp');
3 | const camelCase = require('camelcase');
4 | const decamelize = require('decamelize');
5 |
6 | module.exports = class extends Generator {
7 | // note: arguments and options should be defined in the constructor.
8 | constructor(args, opts) {
9 | super(args, opts);
10 | this.log('Initializing...');
11 |
12 | // This makes `appname` a required argument.
13 | this.argument('appname', {
14 | type: String,
15 | required: false,
16 | });
17 | }
18 |
19 | prompting() {
20 | return this.prompt([
21 | {
22 | type: 'input',
23 | name: 'name',
24 | message: 'Your project name',
25 | default: this.options.appname || 'nextjs-ts-ant-app',
26 | },
27 | {
28 | type: 'input',
29 | name: 'displayName',
30 | message: 'Your project display name',
31 | store: true,
32 | },
33 | {
34 | type: 'input',
35 | name: 'fullname',
36 | message: "What's your full name",
37 | store: true,
38 | },
39 | {
40 | type: 'input',
41 | name: 'email',
42 | message: "What's your email address",
43 | store: true,
44 | },
45 | ]).then(({ name, displayName, fullName, email }) => {
46 | this.answers = {
47 | name: decamelize(camelCase(name), '-'),
48 | displayName,
49 | fullName,
50 | email,
51 | };
52 | });
53 | }
54 |
55 | writing() {
56 | const { name, fullName, email, displayName } = this.answers;
57 |
58 | // create folder project
59 | mkdirp(name);
60 |
61 | // change project root to the new folder
62 | this.destinationRoot(this.destinationPath(name));
63 |
64 | // copy package.json and update some values
65 | this.fs.copyTpl(this.templatePath('_package.json'), this.destinationPath('package.json'), {
66 | name,
67 | fullName,
68 | email,
69 | });
70 |
71 | // copy all files starting with .{whaetever} (like .eslintrc)
72 | this.fs.copy(this.templatePath('src/.*'), this.destinationPath('./'));
73 |
74 | // copy all folders and their contents
75 | this.fs.copy(this.templatePath('src'), this.destinationPath('./'));
76 |
77 | // Update the footer to have the name of the Application, the year and the user who created it
78 | try {
79 | this.fs.copyTpl(this.templatePath('_layout.tsx'), this.destinationPath('./components/global/layout/index.tsx'), {
80 | displayName,
81 | fullName,
82 | });
83 | } catch (error) {
84 | if (error && error.error) {
85 | this.log(error.message);
86 | }
87 | }
88 |
89 | // Create the page
90 | this.config.set('pages', [
91 | {
92 | name: 'about',
93 | path: 'about',
94 | },
95 | {
96 | name: 'index',
97 | path: 'index',
98 | },
99 | {
100 | name: 'post',
101 | path: 'post',
102 | },
103 | {
104 | name: 'posts',
105 | path: 'posts',
106 | },
107 | ]);
108 |
109 | // Create the page
110 | this.config.set('stores', ['Posts']);
111 |
112 | // save config file!
113 | this.config.save();
114 | }
115 |
116 | install() {
117 | // install all dependencies
118 | this.npmInstall().then(
119 | () => {
120 | this.log('Dependencies Installed.');
121 | },
122 | () => this.log('We could not finish to install the node dependencies, please try again manually'),
123 | );
124 | }
125 |
126 | end() {
127 | this.log(`Open your new project: cd ${this.answers.name}`);
128 | this.log('To run in dev mode use: yarn dev');
129 | }
130 | };
131 |
--------------------------------------------------------------------------------
/generators/app/templates/_layout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import uuid from 'uuid/v4';
3 | import { Layout, Menu, Breadcrumb } from 'antd';
4 | import Link from 'next/link';
5 | import Head from './head';
6 | // import {CustomNProgress} from 'components';
7 | import { compose } from 'recompose';
8 | import { withRouter, RouterProps } from 'next/router';
9 | import '../../../styles/main.scss';
10 |
11 | const { Header, Content, Footer } = Layout;
12 | const MenuItem = Menu.Item;
13 |
14 | interface Props extends React.HTMLAttributes {
15 | readonly children?: React.ReactNode;
16 | readonly description?: string;
17 | readonly ogImage?: string;
18 | readonly url?: string;
19 | readonly router?: RouterProps;
20 | }
21 |
22 | const activeClass = 'ant-menu-item-selected';
23 |
24 | const MainLayout: React.SFC = ({
25 | title,
26 | description,
27 | ogImage,
28 | url,
29 | router,
30 | children,
31 | }) => {
32 | const { asPath } = router;
33 |
34 | return (
35 | <>
36 | {/* */}
37 | | ${title}`} description={description} ogImage={ogImage} url={url} />
38 |
39 |
40 |
41 |
76 |
77 |
78 |
79 | Home
80 | List
81 | App
82 |
83 |
84 | {children}
85 |
86 |
87 |
90 |
91 | >
92 | );
93 | }
94 |
95 | export default compose(withRouter)(MainLayout);
96 |
--------------------------------------------------------------------------------
/generators/app/templates/_package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= name %>",
3 | "author": "<%= fullName %> <<%= email %>>",
4 | "version": "0.0.0",
5 | "license": "MIT",
6 | "scripts": {
7 | "dev": "node server.js",
8 | "export": "next export",
9 | "build": "next build",
10 | "start": "NODE_ENV=production node server.js",
11 | "test": "npm run test:units",
12 | "test:units": "NODE_ENV=testing jest --config tests/units/jest.config.js",
13 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check",
14 | "precommit": "lint-staged",
15 | "changelog:major": "changelog -M && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version major && git push origin",
16 | "changelog:minor": "changelog -m && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version minor && git push origin",
17 | "changelog:patch": "changelog -p && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version patch && git push origin",
18 | "release": "git push origin --tags",
19 | "release-and-publish": "npm run release && npm publish"
20 | },
21 | "lint-staged": {
22 | "*.{js,jsx,json,css}": ["prettier --single-quote --write", "git add"]
23 | },
24 | "dependencies": {
25 | "@types/cheerio": "^0.22.11",
26 | "@types/react-input-mask": "^2.0.1",
27 | "@zeit/next-bundle-analyzer": "^0.1.2",
28 | "@zeit/next-css": "^1.0.1",
29 | "@zeit/next-less": "^1.0.1",
30 | "@zeit/next-sass": "^1.0.1",
31 | "@zeit/next-source-maps": "0.0.3",
32 | "@zeit/next-typescript": "^1.1.1",
33 | "acorn": "^6.1.1",
34 | "antd": "^3.19.6",
35 | "axios": "^0.18.0",
36 | "babel-cli": "^6.26.0",
37 | "babel-core": "^6.26.3",
38 | "babel-eslint": "^10.0.1",
39 | "babel-jest": "^24.1.0",
40 | "babel-plugin-import": "^1.11.0",
41 | "babel-plugin-module-resolver": "^3.1.3",
42 | "babel-preset-env": "^1.7.0",
43 | "babel-preset-next": "^1.2.0",
44 | "babel-preset-react": "^6.24.1",
45 | "camel-case": "^3.0.0",
46 | "cheerio": "^1.0.0-rc.3",
47 | "color-convert": "^2.0.0",
48 | "compression": "^1.7.3",
49 | "config": "^3.0.1",
50 | "core-js": "^3.1.3",
51 | "diff-dates": "^1.0.12",
52 | "dotenv": "^6.2.0",
53 | "duration": "^0.2.2",
54 | "enzyme": "^3.8.0",
55 | "enzyme-adapter-react-16": "^1.9.1",
56 | "enzyme-to-json": "^3.3.5",
57 | "express": "^4.16.4",
58 | "faker": "^4.1.0",
59 | "filter-chunk-webpack-plugin": "^2.1.0",
60 | "generate-changelog": "^1.7.1",
61 | "glob": "^7.1.4",
62 | "htmlescape": "^1.1.1",
63 | "i18next": "^15.0.0",
64 | "i18next-browser-languagedetector": "^3.0.0",
65 | "i18next-express-middleware": "^1.7.1",
66 | "i18next-node-fs-backend": "^2.1.1",
67 | "i18next-xhr-backend": "^2.0.0",
68 | "interactjs": "^1.4.0",
69 | "jest": "^24.1.0",
70 | "jest-plugin-context": "^2.9.0",
71 | "less": "^3.9.0",
72 | "less-vars-to-js": "^1.3.0",
73 | "lodash": "^4.17.11",
74 | "next": "^8.1.0",
75 | "next-compose-plugins": "^2.1.1",
76 | "next-images": "^1.1.1",
77 | "next-redux-saga": "^3.0.0",
78 | "next-redux-wrapper": "^3.0.0-alpha.0",
79 | "next-runtime-dotenv": "^1.0.1",
80 | "npm": "^6.9.0",
81 | "nprogress": "^0.2.0",
82 | "optimize-css-assets-webpack-plugin": "^5.0.1",
83 | "path": "^0.12.7",
84 | "prettier": "^1.16.4",
85 | "prettier-eslint-cli": "^4.7.1",
86 | "qs": "^6.7.0",
87 | "randomcolor": "^0.5.4",
88 | "react": "^16.8.6",
89 | "react-dom": "^16.8.1",
90 | "react-error-boundary": "^1.2.5",
91 | "react-i18next": "^10.0.4",
92 | "react-input-mask": "^2.0.4",
93 | "react-lines-ellipsis": "^0.14.0",
94 | "react-loading": "^2.0.3",
95 | "react-redux": "^6.0.0",
96 | "react-resize-detector": "^4.1.3",
97 | "react-spinners": "^0.5.4",
98 | "react-table": "^6.10.0",
99 | "react-test-renderer": "^16.8.1",
100 | "react-use": "^7.3.1",
101 | "react-use-form-state": "^0.8.0",
102 | "react-web-notification": "^0.5.0",
103 | "recompose": "^0.30.0",
104 | "redux": "^4.0.1",
105 | "redux-devtools-extension": "^2.13.8",
106 | "redux-logger": "^3.0.6",
107 | "redux-persist": "^5.10.0",
108 | "redux-react-hook": "^3.3.1",
109 | "redux-saga": "^1.0.3",
110 | "utility-types": "^3.5.0",
111 | "uuid": "^3.3.2"
112 | },
113 | "devDependencies": {
114 | "@babel/cli": "^7.4.4",
115 | "@babel/core": "^7.2.2",
116 | "@types/color-convert": "^1.9.0",
117 | "@types/config": "0.0.34",
118 | "@types/faker": "^4.1.5",
119 | "@types/lodash": "^4.14.136",
120 | "@types/next": "^8.0.5",
121 | "@types/next-redux-wrapper": "^2.0.2",
122 | "@types/node": "^12.0.10",
123 | "@types/qs": "^6.5.3",
124 | "@types/react": "^16.8.2",
125 | "@types/react-dom": "^16.8.0",
126 | "@types/react-redux": "^7.0.1",
127 | "@types/react-table": "6.7.10",
128 | "@types/recompose": "^0.30.3",
129 | "@types/redux-actions": "^2.3.1",
130 | "@types/uuid": "^3.4.4",
131 | "@types/webpack-env": "^1.13.9",
132 | "babel-loader": "^8.0.6",
133 | "babel-preset-react-app": "^9.0.0",
134 | "css-loader": "^1.0.1",
135 | "eslint": "^5.13.0",
136 | "eslint-config-airbnb": "^17.1.0",
137 | "eslint-config-prettier": "^4.0.0",
138 | "eslint-config-standard": "^12.0.0",
139 | "eslint-import-resolver-babel-module": "^5.0.1",
140 | "eslint-plugin-import": "^2.17.3",
141 | "eslint-plugin-jsx-a11y": "^6.2.1",
142 | "eslint-plugin-node": "^8.0.1",
143 | "eslint-plugin-prettier": "^3.0.1",
144 | "eslint-plugin-promise": "^4.0.1",
145 | "eslint-plugin-react": "^7.12.4",
146 | "eslint-plugin-react-hooks": "^1.4.0",
147 | "eslint-plugin-standard": "^4.0.0",
148 | "fork-ts-checker-webpack-plugin": "^0.5.2",
149 | "husky": "^1.3.1",
150 | "i": "^0.3.6",
151 | "less-loader": "^5.0.0",
152 | "lint-staged": "^8.1.3",
153 | "mini-css-extract-plugin": "^0.5.0",
154 | "node-sass": "^4.12.0",
155 | "postcss-loader": "^3.0.0",
156 | "redux-actions": "^2.6.5",
157 | "reselect": "^4.0.0",
158 | "sass-loader": "^7.1.0",
159 | "style-loader": "^0.23.1",
160 | "tslint": "^5.12.1",
161 | "tslint-config-prettier": "^1.18.0",
162 | "tslint-config-standard": "^8.0.1",
163 | "tslint-loader": "^3.5.4",
164 | "tslint-react": "^3.6.0",
165 | "typescript": "^3.4.5",
166 | "typescriptnpm": "^1.0.1"
167 | }
168 | }
--------------------------------------------------------------------------------
/generators/app/templates/src/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["next/babel", "@zeit/next-typescript/babel"],
3 | "plugins": [
4 | [
5 | "module-resolver",
6 | {
7 | "root": ["./"],
8 | "alias": {
9 | "api": "./api",
10 | "app-constants": "./app-constants",
11 | "enums": "./enums",
12 | "hocs": "./hocs",
13 | "hooks": "./hooks",
14 | "components": "./components",
15 | "redux-store": "./redux-store"
16 | }
17 | }
18 | ],
19 | [
20 | "import",
21 | {
22 | "libraryName": "antd",
23 | "style": true
24 | }
25 | ]
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/generators/app/templates/src/.eslintignore:
--------------------------------------------------------------------------------
1 | /.next/**
2 | /.out/**
3 | /node_modules/**
4 |
--------------------------------------------------------------------------------
/generators/app/templates/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "standard",
4 | "plugin:react/recommended",
5 | "prettier",
6 | "prettier/react",
7 | "prettier/standard"
8 | ],
9 | "plugins": [
10 | "react",
11 | "prettier",
12 | "standard"
13 | ],
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaFeatures": {
17 | "jsx": true
18 | }
19 | },
20 | "env": {
21 | "es6": true,
22 | "node": true
23 | },
24 | "rules": {
25 | "prettier/prettier": ["error", { "singleQuote": true }]
26 | }
27 | }
--------------------------------------------------------------------------------
/generators/app/templates/src/.gitattributes:
--------------------------------------------------------------------------------
1 | # From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
2 |
3 | # Handle line endings automatically for files detected as text
4 | # and leave all files detected as binary untouched.
5 | * text=auto
6 |
7 | #
8 | # The above will handle all files NOT found below
9 | #
10 |
11 | #
12 | ## These files are text and should be normalized (Convert crlf => lf)
13 | #
14 |
15 | # source code
16 | *.php text
17 | *.css text
18 | *.sass text
19 | *.scss text
20 | *.less text
21 | *.styl text
22 | *.js text eol=lf
23 | *.coffee text
24 | *.json text
25 | *.htm text
26 | *.html text
27 | *.xml text
28 | *.svg text
29 | *.txt text
30 | *.ini text
31 | *.inc text
32 | *.pl text
33 | *.rb text
34 | *.py text
35 | *.scm text
36 | *.sql text
37 | *.sh text
38 | *.bat text
39 |
40 | # templates
41 | *.ejs text
42 | *.hbt text
43 | *.jade text
44 | *.haml text
45 | *.hbs text
46 | *.dot text
47 | *.tmpl text
48 | *.phtml text
49 |
50 | # server config
51 | .htaccess text
52 | .nginx.conf text
53 |
54 | # git config
55 | .gitattributes text
56 | .gitignore text
57 | .gitconfig text
58 |
59 | # code analysis config
60 | .jshintrc text
61 | .jscsrc text
62 | .jshintignore text
63 | .csslintrc text
64 |
65 | # misc config
66 | *.yaml text
67 | *.yml text
68 | .editorconfig text
69 |
70 | # build config
71 | *.npmignore text
72 | *.bowerrc text
73 |
74 | # Heroku
75 | Procfile text
76 | .slugignore text
77 |
78 | # Documentation
79 | *.md text
80 | LICENSE text
81 | AUTHORS text
82 |
83 |
84 | #
85 | ## These files are binary and should be left untouched
86 | #
87 |
88 | # (binary is a macro for -text -diff)
89 | *.png binary
90 | *.jpg binary
91 | *.jpeg binary
92 | *.gif binary
93 | *.ico binary
94 | *.mov binary
95 | *.mp4 binary
96 | *.mp3 binary
97 | *.flv binary
98 | *.fla binary
99 | *.swf binary
100 | *.gz binary
101 | *.zip binary
102 | *.7z binary
103 | *.ttf binary
104 | *.eot binary
105 | *.woff binary
106 | *.pyc binary
107 | *.pdf binary
108 |
--------------------------------------------------------------------------------
/generators/app/templates/src/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 | /dist
12 | /.next
13 | /.out
14 |
15 | # misc
16 | .DS_Store
17 | .env
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
22 | # Don't add any ide-related generated files
23 | .vscode
24 | .sonarlint
--------------------------------------------------------------------------------
/generators/app/templates/src/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "tabWidth": 2,
4 | "trailingComma": "es5",
5 | "printWidth": 120,
6 | "bracketSpacing": true
7 | }
8 |
--------------------------------------------------------------------------------
/generators/app/templates/src/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Analytics Fire
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/generators/app/templates/src/README.md:
--------------------------------------------------------------------------------
1 | # A great component builder for ReactJs using NextJs
2 |
3 | This yeoman generator will build different React components, creating a skeleton for the different files.
4 |
5 | # Requirements
6 |
7 | This is a Yeoman generator. You need to install Yeoman, NodeJS and npm to install the generator and its dependencies. Make sure you have all installed globally.
8 |
9 | First, download and install NodeJS and npm. More information about NodeJS / npm: https://nodejs.org/
10 |
11 | Second, install Yeoman. More information about Yeoman: http://yeoman.io/
12 |
13 | # Installation
14 |
15 | ```
16 | $ npm install -g generator-create-next-app-reloaded
17 | ```
18 |
19 | # Usage
20 |
21 | ```
22 | $ yo create-next-app-reloaded
23 | $ cd create-next-app-reloaded
24 | $ yarn dev
25 | ```
26 |
27 | # Table of Contents
28 |
29 | * [Questions? Feedback?](#questions-feedback)
30 | * [Folder Structure](#folder-structure)
31 | * [Available Scripts](#available-scripts)
32 | * [yarn dev](#yarn-dev)
33 | * [yarn build](#yarn-build)
34 | * [yarn start](#yarn-start)
35 | * [Available Generators](#available-generators)
36 | * [yo create-next-app-reloaded:page](#yo-create-next-app-reloadedpage)
37 | * [yo create-next-app-reloaded:component](#yo-create-next-app-reloadedcomponent)
38 | * [Changelog](#changelog-generator)
39 | * [Release and Publish](#release-and-publish)
40 |
41 | # Questions? Feedback?
42 |
43 | Check out [Next.js FAQ & docs](https://github.com/zeit/next.js#faq) or [let us know](https://github.com/segmentio/create-next-app/issues) your feedback.
44 |
45 | # Folder Structure
46 |
47 | After creating an app, it should look something like:
48 |
49 | ```
50 | my-app/
51 | README.md
52 | .gitignore
53 | .prettierrc
54 | i18n.js
55 | .babelrc
56 | .eslintrc
57 | static/
58 | favicon.ico
59 | locales/
60 | en/
61 | common.js
62 | images/
63 | tests/
64 | units/
65 | components/
66 | jest.config.js
67 | pages/
68 | setup/
69 | index.js
70 | assetsTransformer.js
71 | server.js
72 | components/
73 | activeLink/
74 | footer/
75 | header/
76 | layout/
77 | navLink/
78 | characterInfo/
79 | nav/
80 | config/
81 | custom-environment-variables.js
82 | default.js
83 | development.js
84 | production.js
85 | lib/
86 | withI18next.js
87 | config.shim.js
88 | next.config.js
89 | pages/
90 | about/
91 | index.js
92 | _app.js
93 | _document.js
94 | home/
95 | redux-example/
96 | styles/
97 | _mixins.scss
98 | _variables.scss/
99 | main.scss
100 | vendors/
101 | bootstrap-4.0.0-beta.2/
102 | redux/
103 | actions.js
104 | epics.js
105 | index.js
106 | reducer.js
107 | package.json
108 | ```
109 |
110 | # Available Scripts
111 |
112 | ### `yarn dev`
113 |
114 | Runs the app in the development mode.
115 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
116 |
117 | The page will reload if you make edits.
118 | You will also see any errors in the console.
119 |
120 | ### `yarn build`
121 |
122 | Builds the app for production to the `.next` folder.
123 | It correctly bundles React in production mode and optimizes the build for the best performance.
124 |
125 | ### `yarn start`
126 |
127 | Starts the application in production mode.
128 | The application should be compiled with \`next build\` first.
129 |
130 | See the section in Next docs about [deployment](https://github.com/zeit/next.js/wiki/Deployment) for more information.
131 |
132 | # Available Generators
133 |
134 | ### `yo create-next-app-reloaded:page`
135 |
136 | It will prompt you the name and the title for your new page.
137 |
138 | ```
139 | $ yo create-next-app-reloaded:page
140 | ? Page name: contactUs
141 | ? Page title: Contact Us Page
142 | create pages/contactUs/contactUs.js
143 | create pages/contactUs/index.js
144 | create pages/contactUs/contactUs.scss
145 | create static/locales/en/contactUs.json
146 | create tests/units/pages/contactUs.test.js
147 | conflict server.js // This is because we are adding the new i18n namespace into the namespaces array.
148 | ? Overwrite server.js? overwrite
149 | force server.js
150 | ```
151 |
152 | ### `yo create-next-app-reloaded:component`
153 |
154 | It will prompt you the name for your new component.
155 |
156 | ```
157 | $ yo create-next-app-reloaded:component
158 | ? Component name: myNav
159 | create components/myNav/myNav.js
160 | create components/myNav/index.js
161 | create components/myNav/casa.scss
162 | create static/locales/en/myNav.json
163 | create tests/units/components/myNav.test.js
164 | ```
165 |
166 | # Changelog Generator
167 | Generate a changelog from git commits using https://github.com/lob/generate-changelog. This is meant to be used so that for every patch, minor, or major version, you update the changelog prior to running npm version so that the git tag contains the commit that updated both the changelog and version.
168 |
169 | # Release and Publish
170 | In order to release and publish we have created a npm script `npm run release-and-publish` that will create a new tag in github and will publish your pkg into npm.
171 |
172 | # What does this generator do?
173 |
174 | This yeoman generator will build different React components, creating a skeleton for the different files.
175 |
176 | # Credits
177 |
178 | Damian Aruj
179 | Github: https://github.com/analyticsfire/generator-create-next-app-reloaded
180 |
181 | # Licence
182 |
183 | MIT
184 |
--------------------------------------------------------------------------------
/generators/app/templates/src/api/postsApi.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const BASE_URL = 'https://jsonplaceholder.typicode.com';
4 |
5 | export const fetchAllPostsApi = () =>
6 | axios
7 | .get(`${BASE_URL}/posts`)
8 | .then(response => response)
9 | .catch(error => error.response);
10 |
11 | export const fetchPostCommentsApi = postId =>
12 | axios
13 | .get(`${BASE_URL}/posts/${postId}/comments`)
14 | .then(response => response)
15 | .catch(error => error.response);
16 |
--------------------------------------------------------------------------------
/generators/app/templates/src/app-constants/index.ts:
--------------------------------------------------------------------------------
1 | /* new-constant-export-goes-here */
2 |
--------------------------------------------------------------------------------
/generators/app/templates/src/components/global/commentItem/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card } from 'antd';
3 | import { IComment } from 'models';
4 | import './styles.scss';
5 |
6 | interface IProps {
7 | readonly comment: IComment;
8 | }
9 |
10 | const CommentItem: React.SFC = ({ comment }) => {
11 | const { name, body } = comment;
12 |
13 | return (
14 |
15 |