mirror of
https://github.com/tlsnotary/explorer.git
synced 2026-01-08 20:38:02 -05:00
chore: add boilerplate for react + redux
This commit is contained in:
3
.babelrc
Normal file
3
.babelrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
||||||
|
}
|
||||||
36
.eslintrc
Normal file
36
.eslintrc
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"extends": ["prettier", "plugin:@typescript-eslint/recommended"],
|
||||||
|
"plugins": ["prettier", "@typescript-eslint"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": 1,
|
||||||
|
"@typescript-eslint/no-var-requires": 0,
|
||||||
|
"@typescript-eslint/ban-ts-comment": 0,
|
||||||
|
"no-undef": "error",
|
||||||
|
"padding-line-between-statements": "error"
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": "latest"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"webextensions": true,
|
||||||
|
"es6": true,
|
||||||
|
"browser": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": "typescript"
|
||||||
|
},
|
||||||
|
"ignorePatterns": [
|
||||||
|
"node_modules",
|
||||||
|
"zip",
|
||||||
|
"build",
|
||||||
|
"wasm",
|
||||||
|
"tlsn",
|
||||||
|
"util",
|
||||||
|
"webpack.config.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -0,0 +1,4 @@
|
|||||||
|
**/node_modules
|
||||||
|
**/.DS_Store
|
||||||
|
.idea
|
||||||
|
build
|
||||||
|
|||||||
8
.prettierignore
Normal file
8
.prettierignore
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
build
|
||||||
|
node_modules
|
||||||
|
wasm
|
||||||
|
tlsn
|
||||||
|
postcss.config.js
|
||||||
|
webpack.config.js
|
||||||
|
*.json
|
||||||
|
*.scss
|
||||||
9
.prettierrc.json
Normal file
9
.prettierrc.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
||||||
13555
package-lock.json
generated
Normal file
13555
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
@@ -4,7 +4,8 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"build": "NODE_ENV=production webpack --config webpack.config.js",
|
||||||
|
"dev": "NODE_ENV=development webpack-dev-server --config webpack.config.js --hot"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -15,5 +16,71 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/tlsnotary/explorer/issues"
|
"url": "https://github.com/tlsnotary/explorer/issues"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"charwise": "^3.0.1",
|
||||||
|
"classnames": "^2.3.2",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"isomorphic-fetch": "^3.0.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-redux": "^8.1.2",
|
||||||
|
"react-router": "^6.15.0",
|
||||||
|
"react-router-dom": "^6.15.0",
|
||||||
|
"redux": "^4.2.1",
|
||||||
|
"redux-logger": "^3.0.6",
|
||||||
|
"redux-thunk": "^2.4.2",
|
||||||
|
"tailwindcss": "^3.3.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.20.12",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
|
"@babel/preset-env": "^7.20.2",
|
||||||
|
"@babel/preset-react": "^7.18.6",
|
||||||
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||||
|
"@types/chrome": "^0.0.202",
|
||||||
|
"@types/node": "^20.4.10",
|
||||||
|
"@types/react": "^18.0.26",
|
||||||
|
"@types/react-dom": "^18.0.10",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
|
"@types/redux-logger": "^3.0.9",
|
||||||
|
"@types/webextension-polyfill": "^0.10.7",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-loader": "^9.1.2",
|
||||||
|
"babel-preset-react-app": "^10.0.1",
|
||||||
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"css-loader": "^6.7.3",
|
||||||
|
"eslint": "^8.31.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-config-react-app": "^7.0.1",
|
||||||
|
"eslint-plugin-flowtype": "^8.0.3",
|
||||||
|
"eslint-plugin-import": "^2.27.4",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
"eslint-plugin-react": "^7.32.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"fs-extra": "^11.1.0",
|
||||||
|
"html-loader": "^4.2.0",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"postcss-loader": "^7.3.3",
|
||||||
|
"postcss-preset-env": "^9.1.1",
|
||||||
|
"prettier": "^3.0.2",
|
||||||
|
"react-refresh": "^0.14.0",
|
||||||
|
"react-refresh-typescript": "^2.0.7",
|
||||||
|
"sass": "^1.57.1",
|
||||||
|
"sass-loader": "^13.2.0",
|
||||||
|
"source-map-loader": "^3.0.1",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"terser-webpack-plugin": "^5.3.6",
|
||||||
|
"ts-loader": "^9.4.2",
|
||||||
|
"type-fest": "^3.5.2",
|
||||||
|
"typescript": "^4.9.4",
|
||||||
|
"webpack": "^5.75.0",
|
||||||
|
"webpack-cli": "^4.10.0",
|
||||||
|
"webpack-dev-server": "^4.11.1"
|
||||||
|
},
|
||||||
"homepage": "https://github.com/tlsnotary/explorer#readme"
|
"homepage": "https://github.com/tlsnotary/explorer#readme"
|
||||||
}
|
}
|
||||||
|
|||||||
4
postcss.config.js
Normal file
4
postcss.config.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
const tailwindcss = require("tailwindcss");
|
||||||
|
module.exports = {
|
||||||
|
plugins: ["postcss-preset-env", tailwindcss],
|
||||||
|
};
|
||||||
45
src/components/Button/index.scss
Normal file
45
src/components/Button/index.scss
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
.button {
|
||||||
|
@apply bg-slate-100;
|
||||||
|
@apply text-slate-500;
|
||||||
|
@apply font-bold;
|
||||||
|
@apply px-2 py-0.5;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply text-slate-600;
|
||||||
|
@apply bg-slate-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
@apply text-slate-700;
|
||||||
|
@apply bg-slate-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
@apply bg-primary/[0.8];
|
||||||
|
@apply text-white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply bg-primary/[0.9];
|
||||||
|
@apply text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
@apply bg-primary;
|
||||||
|
@apply text-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
@apply opacity-50;
|
||||||
|
@apply select-none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply text-slate-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
@apply text-slate-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/components/Button/index.tsx
Normal file
41
src/components/Button/index.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React, { ButtonHTMLAttributes, ReactElement } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import './index.scss';
|
||||||
|
import Icon from '../Icon';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
className?: string;
|
||||||
|
btnType?: 'primary' | 'secondary' | '';
|
||||||
|
loading?: boolean;
|
||||||
|
} & ButtonHTMLAttributes<HTMLButtonElement>;
|
||||||
|
|
||||||
|
export default function Button(props: Props): ReactElement {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
btnType = '',
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
disabled,
|
||||||
|
loading,
|
||||||
|
// Must select all non-button props here otherwise react-dom will show warning
|
||||||
|
...btnProps
|
||||||
|
} = props;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
'flex flex-row flex-nowrap items-center',
|
||||||
|
'h-10 px-4 button transition-colors',
|
||||||
|
{
|
||||||
|
'button--primary': btnType === 'primary',
|
||||||
|
'button--secondary': btnType === 'secondary',
|
||||||
|
'cursor-default': disabled || loading,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={!disabled && !loading ? onClick : undefined}
|
||||||
|
disabled={disabled || loading}
|
||||||
|
{...btnProps}>
|
||||||
|
{loading ? <Icon className="animate-spin" fa="fa-solid fa-spinner" size={2} /> : children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
0
src/components/Icon/index.scss
Normal file
0
src/components/Icon/index.scss
Normal file
37
src/components/Icon/index.tsx
Normal file
37
src/components/Icon/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React, { MouseEventHandler, ReactElement, ReactNode } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import './index.scss';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
url?: string;
|
||||||
|
fa?: string;
|
||||||
|
className?: string;
|
||||||
|
size?: number;
|
||||||
|
onClick?: MouseEventHandler;
|
||||||
|
children?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Icon(props: Props): ReactElement {
|
||||||
|
const { url, size = 1, className = '', fa, onClick, children } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'bg-contain bg-center bg-no-repeat icon',
|
||||||
|
{
|
||||||
|
'cursor-pointer': onClick,
|
||||||
|
},
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
backgroundImage: url ? `url(${url})` : undefined,
|
||||||
|
width: !fa ? `${size}rem` : undefined,
|
||||||
|
height: !fa ? `${size}rem` : undefined,
|
||||||
|
}}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{!url && !!fa && <i className={fa} style={{ fontSize: `${size}rem` }} />}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/index.tsx
Normal file
22
src/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import 'isomorphic-fetch';
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDOM from 'react-dom';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import store from './store';
|
||||||
|
import App from './pages/App';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
|
if ((module as any).hot) {
|
||||||
|
(module as any).hot.accept();
|
||||||
|
}
|
||||||
10
src/pages/App/index.scss
Normal file
10
src/pages/App/index.scss
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
$fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
|
||||||
|
|
||||||
|
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome";
|
||||||
|
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/brands";
|
||||||
|
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/solid";
|
||||||
|
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/regular";
|
||||||
23
src/pages/App/index.tsx
Normal file
23
src/pages/App/index.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React, { ReactElement } from 'react';
|
||||||
|
import "./index.scss";
|
||||||
|
import Button from '../../components/Button';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { asyncIncrementCounter, incrementCounter, useCounter, useLoading } from '../../store/counter';
|
||||||
|
|
||||||
|
export default function App(): ReactElement {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const counter = useCounter();
|
||||||
|
const loading = useLoading();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app flex flex-col gap-4">
|
||||||
|
{`Clicked ${counter} times`}
|
||||||
|
<Button className="w-fit" onClick={() => dispatch(incrementCounter())}>
|
||||||
|
Increment
|
||||||
|
</Button>
|
||||||
|
<Button className="w-fit" onClick={() => dispatch(asyncIncrementCounter(1000))} loading={loading}>
|
||||||
|
Wait 1s + Increment
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
80
src/store/counter.ts
Normal file
80
src/store/counter.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import deepEqual from 'fast-deep-equal';
|
||||||
|
import type { AppRootState } from './index';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
export enum ActionType {
|
||||||
|
Increment = 'counter/increment',
|
||||||
|
SetLoading = 'counter/setLoading',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Action<payload = any> = {
|
||||||
|
type: ActionType;
|
||||||
|
payload: payload;
|
||||||
|
error?: boolean;
|
||||||
|
meta?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
value: number;
|
||||||
|
loading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initState: State = {
|
||||||
|
value: 0,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const incrementCounter = (): Action => ({
|
||||||
|
type: ActionType.Increment,
|
||||||
|
payload: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setLoading = (loading = false): Action<boolean> => ({
|
||||||
|
type: ActionType.SetLoading,
|
||||||
|
payload: loading,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const asyncIncrementCounter = (timeout = 1000): any => async (dispatch: Dispatch, getState: () => AppRootState) => {
|
||||||
|
dispatch(setLoading(true));
|
||||||
|
await new Promise(r => setTimeout(r, timeout));
|
||||||
|
dispatch(incrementCounter());
|
||||||
|
dispatch(setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function counter(state = initState, action: Action): State {
|
||||||
|
switch (action.type) {
|
||||||
|
case ActionType.Increment:
|
||||||
|
return handleIncrement(state, action);
|
||||||
|
case ActionType.SetLoading:
|
||||||
|
return handleSetLoading(state, action);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleIncrement(state: State, action: Action): State {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
value: state.value + 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSetLoading(state: State, action: Action<boolean>): State {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCounter() {
|
||||||
|
return useSelector((state: AppRootState) => {
|
||||||
|
return state.counter.value;
|
||||||
|
}, deepEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLoading() {
|
||||||
|
return useSelector((state: AppRootState) => {
|
||||||
|
return state.counter.loading;
|
||||||
|
}, deepEqual);
|
||||||
|
}
|
||||||
32
src/store/index.ts
Normal file
32
src/store/index.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { applyMiddleware, combineReducers, createStore } from 'redux';
|
||||||
|
import thunk from 'redux-thunk';
|
||||||
|
import { createLogger } from 'redux-logger';
|
||||||
|
import counter from './counter';
|
||||||
|
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
counter,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AppRootState = ReturnType<typeof rootReducer>;
|
||||||
|
|
||||||
|
const createStoreWithMiddleware =
|
||||||
|
process.env.NODE_ENV === 'development'
|
||||||
|
? applyMiddleware(
|
||||||
|
thunk,
|
||||||
|
createLogger({
|
||||||
|
collapsed: true,
|
||||||
|
}),
|
||||||
|
)(createStore)
|
||||||
|
: applyMiddleware(
|
||||||
|
thunk,
|
||||||
|
)(createStore);
|
||||||
|
|
||||||
|
function configureAppStore() {
|
||||||
|
return createStoreWithMiddleware(
|
||||||
|
rootReducer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = configureAppStore();
|
||||||
|
|
||||||
|
export default store;
|
||||||
12
static/index.html
Normal file
12
static/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>Popup</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<div id="modal-root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
12
tailwind.config.js
Normal file
12
tailwind.config.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: '#243f5f',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"noEmit": false,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["build", "node_modules"]
|
||||||
|
}
|
||||||
169
webpack.config.js
Executable file
169
webpack.config.js
Executable file
@@ -0,0 +1,169 @@
|
|||||||
|
var webpack = require("webpack"),
|
||||||
|
path = require("path"),
|
||||||
|
CopyWebpackPlugin = require("copy-webpack-plugin"),
|
||||||
|
HtmlWebpackPlugin = require("html-webpack-plugin"),
|
||||||
|
TerserPlugin = require("terser-webpack-plugin");
|
||||||
|
var { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
var ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
|
||||||
|
var ReactRefreshTypeScript = require("react-refresh-typescript");
|
||||||
|
|
||||||
|
const ASSET_PATH = process.env.ASSET_PATH || "/";
|
||||||
|
|
||||||
|
var alias = {};
|
||||||
|
|
||||||
|
var fileExtensions = [
|
||||||
|
"jpg",
|
||||||
|
"jpeg",
|
||||||
|
"png",
|
||||||
|
"gif",
|
||||||
|
"eot",
|
||||||
|
"otf",
|
||||||
|
"svg",
|
||||||
|
"ttf",
|
||||||
|
"woff",
|
||||||
|
"woff2",
|
||||||
|
];
|
||||||
|
|
||||||
|
const isDevelopment = process.env.NODE_ENV !== "production";
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
mode: process.env.NODE_ENV || "development",
|
||||||
|
ignoreWarnings: [
|
||||||
|
/Circular dependency between chunks with runtime/,
|
||||||
|
/ResizeObserver loop completed with undelivered notifications/
|
||||||
|
],
|
||||||
|
entry: {
|
||||||
|
index: path.join(__dirname, "src", "index.tsx"),
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: "[name].bundle.js",
|
||||||
|
path: path.resolve(__dirname, "build"),
|
||||||
|
clean: true,
|
||||||
|
publicPath: ASSET_PATH,
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
// look for .css or .scss files
|
||||||
|
test: /\.(css|scss)$/,
|
||||||
|
// in the `src` directory
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "style-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "css-loader",
|
||||||
|
options: { importLoaders: 1 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "postcss-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: "sass-loader",
|
||||||
|
options: {
|
||||||
|
sourceMap: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: new RegExp(".(" + fileExtensions.join("|") + ")$"),
|
||||||
|
type: "asset/resource",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
// loader: 'file-loader',
|
||||||
|
// options: {
|
||||||
|
// name: '[name].[ext]',
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
loader: "html-loader",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ts|tsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: require.resolve("ts-loader"),
|
||||||
|
options: {
|
||||||
|
getCustomTransformers: () => ({
|
||||||
|
before: [isDevelopment && ReactRefreshTypeScript()].filter(
|
||||||
|
Boolean
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
transpileOnly: isDevelopment,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx)$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: "source-map-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: require.resolve("babel-loader"),
|
||||||
|
options: {
|
||||||
|
plugins: [
|
||||||
|
isDevelopment && require.resolve("react-refresh/babel"),
|
||||||
|
].filter(Boolean),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: alias,
|
||||||
|
extensions: fileExtensions
|
||||||
|
.map((extension) => "." + extension)
|
||||||
|
.concat([".js", ".jsx", ".ts", ".tsx", ".css"]),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
isDevelopment && new ReactRefreshWebpackPlugin(),
|
||||||
|
new CleanWebpackPlugin({ verbose: false }),
|
||||||
|
new webpack.ProgressPlugin(),
|
||||||
|
// expose and write the allowed env vars on the compiled bundle
|
||||||
|
new webpack.EnvironmentPlugin(["NODE_ENV"]),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: path.join(__dirname, "static", "index.html"),
|
||||||
|
filename: "index.html",
|
||||||
|
chunks: ["index"],
|
||||||
|
cache: false,
|
||||||
|
}),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
Buffer: ['buffer', 'Buffer'],
|
||||||
|
}),
|
||||||
|
].filter(Boolean),
|
||||||
|
infrastructureLogging: {
|
||||||
|
level: "info",
|
||||||
|
},
|
||||||
|
// Required by wasm-bindgen-rayon, in order to use SharedArrayBuffer on the Web
|
||||||
|
// Ref:
|
||||||
|
// - https://github.com/GoogleChromeLabs/wasm-bindgen-rayon#setting-up
|
||||||
|
// - https://web.dev/i18n/en/coop-coep/
|
||||||
|
devServer: {
|
||||||
|
headers: {
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
options.devtool = "cheap-module-source-map";
|
||||||
|
} else {
|
||||||
|
options.optimization = {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
extractComments: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = options;
|
||||||
Reference in New Issue
Block a user