generator-reackt 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.prettierrc +5 -5
  2. package/.stylelintrc +5 -188
  3. package/LICENSE +21 -7
  4. package/README.md +20 -18
  5. package/generators/app/index.js +1 -392
  6. package/generators/component/index.js +1 -51
  7. package/generators/module/index.js +1 -127
  8. package/generators/util/fs.js +1 -34
  9. package/package.json +83 -63
  10. package/templates/app/.browserslistrc +3 -3
  11. package/templates/app/.editorconfig +11 -11
  12. package/templates/app/.esdoc.json +23 -23
  13. package/templates/app/.lintstagedrc +4 -4
  14. package/templates/app/.prettierrc +5 -5
  15. package/templates/app/.storybook/main.ts +19 -0
  16. package/templates/app/.storybook/preview.ts +36 -0
  17. package/templates/app/.stylelintrc +5 -188
  18. package/templates/app/.yarnrc.yml +3 -0
  19. package/templates/app/_eslint.config.mjs +51 -0
  20. package/templates/app/{package.json → _package.json} +18 -18
  21. package/templates/app/_stories/SuspenseFallback.stories.tsx +17 -0
  22. package/templates/app/gitignore +3 -2
  23. package/templates/app/jest.config.js +15 -15
  24. package/templates/app/src/components/ErrorBoundary.tsx +45 -0
  25. package/templates/app/src/components/Layout.tsx +142 -0
  26. package/templates/app/src/components/SuspenseFallback.tsx +96 -0
  27. package/templates/app/src/index.html +10 -10
  28. package/templates/app/src/{index.scss → index.nscss} +17 -16
  29. package/templates/app/src/index.tsx +60 -0
  30. package/templates/app/src/pages/index.tsx +3 -0
  31. package/templates/app/src/reducers/application.ts +36 -0
  32. package/templates/app/src/reducers/{index.js → index.ts} +8 -7
  33. package/templates/app/src/sagas/application.ts +9 -0
  34. package/templates/app/src/sagas/{index.js → index.ts} +8 -7
  35. package/templates/app/src/selectors/application.test.ts +18 -0
  36. package/templates/app/src/selectors/application.ts +6 -0
  37. package/templates/app/src/types/globals.d.ts +1 -0
  38. package/templates/app/src/types/index.ts +11 -0
  39. package/templates/app/src/utils/{index.js → index.ts} +36 -36
  40. package/templates/app/tsconfig.json +18 -0
  41. package/templates/app/webpack.config.nts +121 -0
  42. package/templates/component/index.stories.tsx +14 -0
  43. package/templates/component/{index.test.js → index.test.tsx} +14 -14
  44. package/templates/{app/src/components/App.js → component/index.tsx} +16 -14
  45. package/templates/module/reducer/{index.js → index.ts} +20 -20
  46. package/templates/module/saga/{index.js → index.ts} +9 -9
  47. package/templates/app/.babelrc +0 -4
  48. package/templates/app/.eslintrc.js +0 -46
  49. package/templates/app/.storybook/addons.js +0 -1
  50. package/templates/app/.storybook/config.js +0 -14
  51. package/templates/app/.storybook/webpack.config.babel.js +0 -7
  52. package/templates/app/jsconfig.json +0 -7
  53. package/templates/app/src/index.js +0 -47
  54. package/templates/app/src/reducers/application.js +0 -21
  55. package/templates/app/src/sagas/application.js +0 -9
  56. package/templates/app/src/selectors/application.js +0 -1
  57. package/templates/app/src/selectors/application.test.js +0 -13
  58. package/templates/app/webpack.config.babel.js +0 -103
  59. package/templates/component/index.js +0 -19
  60. /package/templates/module/reducer/{index.test.js → index.test.ts} +0 -0
  61. /package/templates/module/saga/{index.test.js → index.test.ts} +0 -0
  62. /package/templates/module/selector/{index.js → index.test.ts} +0 -0
  63. /package/templates/module/selector/{index.test.js → index.ts} +0 -0
@@ -0,0 +1,60 @@
1
+ import { Suspense } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { RouterProvider, createHashRouter } from 'react-router-dom';
4
+
5
+ <% if (flags.addRedux) { %>
6
+ import { Provider } from 'react-redux';
7
+ import createSagaMiddleware from 'redux-saga';
8
+ import { applyMiddleware, createStore, compose } from 'redux';
9
+ <% } %>
10
+
11
+ import './index.scss';
12
+ <% if (styleFramework === 'materialize') { %>
13
+ import 'materialize-css';
14
+ <% } %>
15
+ import Layout from './components/Layout';
16
+ import SuspenseFallback from './components/SuspenseFallback';
17
+ import ErrorBoundary from './components/ErrorBoundary';
18
+ <% if (flags.addRedux) { %>
19
+ import rootSaga from './sagas';
20
+ import rootReducer from './reducers';
21
+
22
+ const composer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
23
+ const sagaMiddleware = createSagaMiddleware();
24
+ const enhancers = composer(applyMiddleware(sagaMiddleware));
25
+
26
+ export const store = createStore(rootReducer, {}, enhancers);
27
+
28
+ sagaMiddleware.run(rootSaga);
29
+ <% } %>
30
+
31
+ const rootElem = document.getElementById('root');
32
+
33
+ if (rootElem) {
34
+ const root = createRoot(rootElem);
35
+ const router = createHashRouter([
36
+ {
37
+ path: '/',
38
+ element: <Layout />,
39
+ errorElement: <ErrorBoundary />,
40
+ children: [
41
+ {
42
+ index: true,
43
+ lazy: () => import(`./pages/index`)
44
+ }
45
+ ]
46
+ }
47
+ ]);
48
+
49
+ root.render(
50
+ <% if (flags.addRedux) { %>
51
+ <Provider store={store}>
52
+ <% } %>
53
+ <Suspense fallback={<SuspenseFallback />}>
54
+ <RouterProvider router={router} />
55
+ </Suspense>
56
+ <% if (flags.addRedux) { %>
57
+ </Provider>
58
+ <% } %>
59
+ )
60
+ }
@@ -0,0 +1,3 @@
1
+ export const Component = () => {
2
+ return <h1>Hello World</h1>;
3
+ };
@@ -0,0 +1,36 @@
1
+ import { ActionObject, AppState } from '../types';
2
+ import { buildActions } from '../utils';
3
+
4
+ export const types = buildActions('application', ['INIT_APP']);
5
+
6
+ const initApp = () => ({
7
+ type: types.INIT_APP
8
+ });
9
+
10
+ export const actions = {
11
+ initApp
12
+ };
13
+
14
+ export const initialState: AppState = {
15
+ application: {
16
+ inited: false
17
+ }
18
+ };
19
+
20
+ export const reducer = (
21
+ state: AppState = initialState,
22
+ action: ActionObject = {}
23
+ ) => {
24
+ switch (action.type) {
25
+ case types.INIT_APP:
26
+ return {
27
+ ...state,
28
+ application: {
29
+ ...state.application,
30
+ inited: true
31
+ }
32
+ };
33
+ default:
34
+ return state;
35
+ }
36
+ };
@@ -1,7 +1,8 @@
1
- import { combineReducers } from 'redux';
2
-
3
- import { reducer as application } from './application';
4
-
5
- export default combineReducers({
6
- application
7
- });
1
+ import { combineReducers } from 'redux';
2
+
3
+ import { reducer as application } from './application';
4
+
5
+ // Remember to update this when you add a new module
6
+ export default combineReducers({
7
+ application
8
+ });
@@ -0,0 +1,9 @@
1
+ import { all } from 'redux-saga/effects';
2
+
3
+ export const workers: Record<string, CallableFunction> = {};
4
+
5
+ export const watchers: Record<string, CallableFunction> = {};
6
+
7
+ export default function* saga() {
8
+ yield all(Object.values(watchers).map((watcher) => watcher()));
9
+ }
@@ -1,7 +1,8 @@
1
- import { fork } from 'redux-saga/effects';
2
-
3
- import application from './application';
4
-
5
- export default function* saga() {
6
- yield fork(application);
7
- }
1
+ import { fork } from 'redux-saga/effects';
2
+
3
+ import application from './application';
4
+
5
+ // Remember to update this when you add a new module
6
+ export default function* saga() {
7
+ yield fork(application);
8
+ }
@@ -0,0 +1,18 @@
1
+ import { AppState } from '../types';
2
+ import { getApplication, getApplicationInited } from './application';
3
+
4
+ describe('application selectors', () => {
5
+ const state: AppState = {
6
+ application: {
7
+ inited: false
8
+ }
9
+ };
10
+
11
+ it('can getApplication', () => {
12
+ expect(getApplication(state)).toBe(state.application);
13
+ });
14
+
15
+ it('can getApplicationInited', () => {
16
+ expect(getApplicationInited(state).toBe(state.application.inited));
17
+ });
18
+ });
@@ -0,0 +1,6 @@
1
+ import { AppState } from '../types';
2
+
3
+ export const getApplication = (state: AppState) => state.application;
4
+
5
+ export const getApplicationInited = (state: AppState) =>
6
+ getApplication(state).inited;
@@ -0,0 +1 @@
1
+ declare module '*.scss';
@@ -0,0 +1,11 @@
1
+ <% if (flags.addRedux) { %>
2
+ export type AppState = {
3
+ application: {
4
+ inited: boolean;
5
+ };
6
+ };
7
+
8
+ export type ActionObject = {
9
+ type?: string;
10
+ };
11
+ <% } %>
@@ -1,36 +1,36 @@
1
- <% if (flags.addJest) { %>
2
- import { createElement } from 'react';
3
- <% } %>
4
-
5
- <% if (flags.addRedux) { %>
6
- /* eslint-disable react/display-name, react/prop-types */
7
-
8
- /**
9
- * Creates an object containing action constants namespaced under the specified reducer.
10
- *
11
- * @param {string} reducer The name of the reducer or module
12
- * @param {string[]} actions A list of action names
13
- * @return {object} Object with action name keys and full action string values
14
- */
15
- export function buildActions(reducer, actions) {
16
- const result = {};
17
-
18
- for (const action of actions) {
19
- result[action] = `${reducer}/${action}`;
20
- }
21
-
22
- return result;
23
- }
24
- <% } %>
25
-
26
- <% if (flags.addJest) { %>
27
- /**
28
- * Creates a mock component which will expose its props for snapshot testing purposes.
29
- *
30
- * @param {string} name The component/element name (e.g. "MyComponent")
31
- * @param {object} props An object containing props to setup
32
- * @return {object} Mock component with specified name and props
33
- */
34
- export const mockComponent = (name, props = {}) => () =>
35
- createElement(name, props, props.children);
36
- <% } %>
1
+ <% if (flags.addJest) { %>
2
+ import { createElement, ComponentProps } from 'react';
3
+ <% } %>
4
+
5
+ <% if (flags.addRedux) { %>
6
+ /* eslint-disable react/display-name, react/prop-types */
7
+
8
+ /**
9
+ * Creates an object containing action constants namespaced under the specified reducer.
10
+ *
11
+ * @param {string} reducer The name of the reducer or module
12
+ * @param {string[]} actions A list of action names
13
+ * @return {object} Object with action name keys and full action string values
14
+ */
15
+ export function buildActions(reducer, actions) {
16
+ const result = {};
17
+
18
+ for (const action of actions) {
19
+ result[action] = `${reducer}/${action}`;
20
+ }
21
+
22
+ return result;
23
+ }
24
+ <% } %>
25
+
26
+ <% if (flags.addJest) { %>
27
+ /**
28
+ * Creates a mock component which will expose its props for snapshot testing purposes.
29
+ *
30
+ * @param {string} name The component/element name (e.g. "MyComponent")
31
+ * @param {ComponentProps} props An object containing props to set
32
+ * @return {object} Mock component with specified name and props
33
+ */
34
+ export const mockComponent = (name: string, props: ComponentProps = {}) => () =>
35
+ createElement(name, props, props.children);
36
+ <% } %>
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "lib": ["esnext", "dom", "dom.iterable"],
5
+ "module": "esnext",
6
+ "moduleResolution": "bundler",
7
+ "rootDir": ".",
8
+ <% if (flags.addJest) { %>
9
+ "types": ["jest"],
10
+ <% } %>
11
+ <% if (styleFramework === 'uikit') { %>
12
+ // uikit-react ships a broken index.d.ts that would otherwise fail the build
13
+ "skipLibCheck": true,
14
+ <% } %>
15
+ "jsx": "react-jsx"
16
+ },
17
+ "include": ["src/", ".storybook/"]
18
+ }
@@ -0,0 +1,121 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { dirname, resolve } from 'path';
3
+ import autoprefixer from 'autoprefixer';
4
+ import HtmlPlugin from 'html-webpack-plugin';
5
+ import ESLintPlugin from 'eslint-webpack-plugin';
6
+ import TerserPlugin from 'terser-webpack-plugin';
7
+ <% if (flags.addCnamePlugin) { %>
8
+ import CnameWebpackPlugin from 'cname-webpack-plugin';
9
+ <% } %>
10
+ import StylelintPlugin from 'stylelint-webpack-plugin';
11
+ import postcssFlexbugsFixes from 'postcss-flexbugs-fixes';
12
+ import MiniCssExtractPlugin from 'mini-css-extract-plugin';
13
+ import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
14
+ import { type Configuration, type WebpackPluginInstance } from 'webpack';
15
+ import { CleanWebpackPlugin as CleanPlugin } from 'clean-webpack-plugin';
16
+
17
+ import 'webpack-dev-server';
18
+
19
+ const dev = process.env.NODE_ENV === 'development';
20
+
21
+ const plugins: WebpackPluginInstance[] = [
22
+ new CleanPlugin(),
23
+ new HtmlPlugin({
24
+ template: './src/index.html'
25
+ }),
26
+ new MiniCssExtractPlugin(),
27
+ <% if (flags.addCnamePlugin) { %>
28
+ new CnameWebpackPlugin({
29
+ domain: '<%= deploy.domain %>'
30
+ })
31
+ <% } %>
32
+ ];
33
+
34
+ if (dev) {
35
+ plugins.push(
36
+ new StylelintPlugin({
37
+ configFile: '.stylelintrc',
38
+ context: 'src',
39
+ files: '**/*.scss',
40
+ failOnError: true,
41
+ quiet: false,
42
+ customSyntax: 'postcss-scss'
43
+ }),
44
+ new ESLintPlugin()
45
+ );
46
+ }
47
+
48
+ const config: Configuration = {
49
+ mode: dev ? 'development' : 'production',
50
+ devtool: dev ? 'eval-cheap-module-source-map' : false,
51
+ entry: './src/index.tsx',
52
+ devServer: {
53
+ open: true,
54
+ historyApiFallback: true,
55
+ client: {
56
+ overlay: {
57
+ warnings: false
58
+ }
59
+ }
60
+ },
61
+ module: {
62
+ rules: [
63
+ {
64
+ test: /\.tsx?$/,
65
+ use: ['ts-loader'],
66
+ exclude: [/\.stories\.tsx$/, /node_modules/]
67
+ },
68
+ {
69
+ test: /\.(sa|sc|c)ss$/,
70
+ use: [
71
+ MiniCssExtractPlugin.loader,
72
+ 'css-loader',
73
+ {
74
+ loader: 'postcss-loader',
75
+ options: {
76
+ postcssOptions: {
77
+ ident: 'postcss',
78
+ plugins: [autoprefixer, postcssFlexbugsFixes]
79
+ },
80
+ sourceMap: dev
81
+ }
82
+ },
83
+ {
84
+ loader: 'sass-loader',
85
+ options: {
86
+ sassOptions: {
87
+ quietDeps: true
88
+ }
89
+ }
90
+ }
91
+ ]
92
+ },
93
+ {
94
+ test: /\.html$/,
95
+ use: [
96
+ {
97
+ loader: 'html-loader',
98
+ options: { minimize: true }
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ },
104
+ output: {
105
+ path: resolve(dirname(fileURLToPath(import.meta.url)), 'dist'),
106
+ filename: '[name].js',
107
+ chunkFilename: '[name].js',
108
+ publicPath: '/'
109
+ },
110
+ plugins,
111
+ resolve: {
112
+ extensions: ['.js', '.ts', '.tsx', '.json'],
113
+ modules: ['node_modules', 'src']
114
+ },
115
+ optimization: {
116
+ minimizer: [new TerserPlugin(), new CssMinimizerPlugin()]
117
+ },
118
+ ignoreWarnings: [/import rules are/]
119
+ };
120
+
121
+ export default config;
@@ -0,0 +1,14 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import <%= component.name %> from './<%= component.name %>';
4
+
5
+ const meta = {
6
+ title: 'Components/<%= component.name %>',
7
+ component: <%= component.name %>
8
+ } satisfies Meta<typeof <%= component.name %>>;
9
+
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default: Story = {};
@@ -1,14 +1,14 @@
1
- import renderer from 'react-test-renderer';
2
-
3
- import <%=component.name %> from './<%=component.name %>';
4
-
5
- describe('<<%= component.name %> />', () => {
6
- it('renders correctly', () => {
7
- const component = renderer.create(
8
- <<%=component.name %> />
9
- );
10
- const tree = component.toJSON();
11
-
12
- expect(tree).toMatchSnapshot();
13
- });
14
- });
1
+ import renderer from 'react-test-renderer';
2
+
3
+ import <%=component.name %> from './<%=component.name %>';
4
+
5
+ describe('<<%= component.name %> />', () => {
6
+ it('renders correctly', () => {
7
+ const component = renderer.create(
8
+ <<%=component.name %> />
9
+ );
10
+ const tree = component.toJSON();
11
+
12
+ expect(tree).toMatchSnapshot();
13
+ });
14
+ });
@@ -1,14 +1,16 @@
1
- <% if (flags.addRedux) { %>
2
- import { connect } from 'react-redux';
3
- <% } %>
4
-
5
- export <% if (!flags.addRedux) { %> default <% } %> function App() {
6
- return <h1>Hello World</h1>;
7
- }
8
-
9
- <% if (flags.addRedux) { %>
10
- export default connect(
11
- null,
12
- null
13
- )(App);
14
- <% } %>
1
+ <% if (flags.addRedux) { %>
2
+ import { connect } from 'react-redux';
3
+ <% } %>
4
+
5
+ export default function <%= component.name %>() {
6
+ return <h1>Hello World</h1>;
7
+ }
8
+
9
+ <% if (flags.addRedux) { %>
10
+ export default connect(
11
+ null,
12
+ null
13
+ )(<%= component.name %>);
14
+ <% } else { %>
15
+ export default <%= component.name %>;
16
+ <% } %>
@@ -1,20 +1,20 @@
1
- import { buildActions } from 'utils';
2
-
3
- export const types = buildActions('<%= module.name %>', ['INIT_APP']);
4
-
5
- const initApp = () => ({
6
- type: types.INIT_APP
7
- });
8
-
9
- export const actions = {
10
- initApp
11
- };
12
-
13
- export const initialState = {};
14
-
15
- export const reducer = (state = initialState, action = {}) => {
16
- switch (action.type) {
17
- default:
18
- return state;
19
- }
20
- };
1
+ import { buildActions } from 'utils';
2
+
3
+ export const types = buildActions('<%= module.name %>', ['INIT_APP']);
4
+
5
+ const initApp = () => ({
6
+ type: types.INIT_APP
7
+ });
8
+
9
+ export const actions = {
10
+ initApp
11
+ };
12
+
13
+ export const initialState = {};
14
+
15
+ export const reducer = (state = initialState, action = {}) => {
16
+ switch (action.type) {
17
+ default:
18
+ return state;
19
+ }
20
+ };
@@ -1,9 +1,9 @@
1
- import { all } from 'redux-saga/effects';
2
-
3
- export const workers = {};
4
-
5
- export const watchers = {};
6
-
7
- export default function* saga() {
8
- yield all(Object.values(watchers));
9
- }
1
+ import { all } from 'redux-saga/effects';
2
+
3
+ export const workers = {};
4
+
5
+ export const watchers = {};
6
+
7
+ export default function* saga() {
8
+ yield all(Object.values(watchers));
9
+ }
@@ -1,4 +0,0 @@
1
- {
2
- "presets": ["@babel/preset-env", ["@babel/preset-react", { "runtime": "automatic" }]],
3
- "plugins": ["@babel/plugin-transform-runtime"]
4
- }
@@ -1,46 +0,0 @@
1
- const path = require('path');
2
-
3
- module.exports = {
4
- "env": {
5
- "browser": true,
6
- "es2020": true,
7
- <% if (flags.addJest) { %>
8
- "jest": true,
9
- <% } %>
10
- "node": true
11
- },
12
- "extends": [
13
- "eslint:recommended",
14
- "plugin:react/recommended",
15
- "plugin:prettier/recommended",
16
- "plugin:jsx-a11y/recommended",
17
- "plugin:import/recommended",
18
- "plugin:react-hooks/recommended",
19
- <% if (flags.addJest) { %>
20
- "plugin:jest/recommended"
21
- <% } %>
22
- ],
23
- "parser": "@babel/eslint-parser",
24
- "parserOptions": {
25
- "requireConfigFile": false,
26
- "ecmaFeatures": {
27
- "jsx": true
28
- },
29
- "ecmaVersion": 12
30
- },
31
- "rules": {
32
- "jsx-a11y/label-has-for": 0,
33
- "react/jsx-uses-react": 0,
34
- "react/react-in-jsx-scope": 0
35
- },
36
- "settings": {
37
- "react": {
38
- "version": "detect"
39
- },
40
- "import/resolver": {
41
- "webpack": {
42
- "config": path.join(__dirname, 'webpack.config.babel.js')
43
- }
44
- }
45
- }
46
- };
@@ -1 +0,0 @@
1
- import '@storybook/addon-knobs/register';
@@ -1,14 +0,0 @@
1
- import { withKnobs } from '@storybook/addon-knobs';
2
- import { configure, addDecorator } from '@storybook/react';
3
-
4
- import '../src/index.scss';
5
-
6
- function loadStories() {
7
- const req = require.context('../src/components', true, /\.stories\.js$/);
8
-
9
- req.keys().forEach(filename => req(filename));
10
- }
11
-
12
- addDecorator(withKnobs);
13
-
14
- configure(loadStories, module);
@@ -1,7 +0,0 @@
1
- import webpack from '../webpack.config.babel';
2
-
3
- export default async ({ config }) => ({
4
- ...config,
5
- module: webpack.module,
6
- resolve: webpack.resolve
7
- });
@@ -1,7 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "module": "esnext",
4
- "baseUrl": "./src"
5
- },
6
- "include": ["src/"]
7
- }