diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000000000000000000000000000000000000..b5195cc371ea3f20e4b97922cd8b823011ce836f --- /dev/null +++ b/.babelrc @@ -0,0 +1,15 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "targets": { + "chrome": "49", + "ie": "11" + } + } + ], + "@babel/preset-react" + ] +} diff --git a/.gitignore b/.gitignore index bca833d82ca5713b93b6e4d3f4c25d13b1560bf0..7f7cc510883a8996c8bda611a3628d9d07656ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /coverage # production +/build /dist # misc @@ -24,4 +25,4 @@ yarn-error.log* node_modules .eslintcache package-lock.json -folder.txt +structure.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..9dd2a5a6812235a424fef0b12db2abb356fbfa8a --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2024, wuruipeng + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/app/general/common.js b/app/general/common.js new file mode 100644 index 0000000000000000000000000000000000000000..4881978670cdee0cd6f6bbe89050e663940c0d97 --- /dev/null +++ b/app/general/common.js @@ -0,0 +1,39 @@ +/* + * @Author: wuruipeng + * @Date: 2024-04-08 09:57:24 + * @LastEditors: wuruipeng + * @LastEditTime: 2024-04-10 17:01:17 + * @description: 全局公共方法 + */ +const { app, dialog } = require('electron') + +// 退出程序确定方法 +let willQuitApp = false // 前置判断条件,是否确认退出程序 +const confirmExit = event => { + if (!willQuitApp) { + event.preventDefault() // 取消默认行为 + dialog + .showMessageBox({ + type: 'warning', + title: 'Tips', + message: 'Are you sure quit this App ?', + buttons: ['OK', 'Cancel'] + }) + .then(result => { + const response = result.response + if (response === 0) { + willQuitApp = true + app.quit() + } else { + willQuitApp = false + } + }) + .catch(() => { + willQuitApp = false + }) + } +} + +module.exports = { + confirmExit +} diff --git a/app/general/ipcEvent.js b/app/general/ipcEvent.js new file mode 100644 index 0000000000000000000000000000000000000000..a2a88e27a84870e02def634987e5b62e8c71afad --- /dev/null +++ b/app/general/ipcEvent.js @@ -0,0 +1,15 @@ +/* + * @Author: wuruipeng + * @Date: 2024-04-08 09:57:33 + * @LastEditors: wuruipeng + * @LastEditTime: 2024-04-08 10:02:41 + * @description: 监听渲染进程交互主进程方法 + */ + +const { app, ipcMain } = require('electron') + +// 关闭应用方法 +ipcMain.on('close-app', () => { + // 调用 app.quit() 方法来关闭应用程序 + app.quit() +}) diff --git a/app/main.js b/app/main.js new file mode 100644 index 0000000000000000000000000000000000000000..6abb4b69033f417294fe5a84b27aa1d10b529269 --- /dev/null +++ b/app/main.js @@ -0,0 +1,123 @@ +/* + * @Author: wuruipeng + * @Date: 2024-03-06 17:39:06 + * @LastEditors: wuruipeng + * @LastEditTime: 2024-04-11 14:47:50 + * @description: 主进程配置 + */ + +const { app, screen, BrowserWindow, Menu, dialog } = require('electron') +const path = require('path') +const { confirmExit } = require('./general/common.js') +require('./general/ipcEvent.js') // 配置监听ipc信息事件 + +// 初始化主进程 +function initApp(rootPath) { + // 菜单模板 + const template = [ + { + label: 'Setting', + submenu: [ + { label: 'Change Serve/Local', accelerator: 'CmdOrCtrl+S' }, + { label: 'Toggle DevTools', role: 'toggleDevTools' }, + { type: 'separator' }, + { label: 'Quit', role: 'quit' } + ] + }, + { + label: 'Window', + submenu: [ + { label: 'Reload', role: 'reload' }, + { label: 'Zoom In', role: 'zoomIn' }, + { label: 'Zoom Out', role: 'zoomOut' }, + { label: 'Toggle Fullscreen', role: 'togglefullscreen' } + ] + }, + { + label: 'Help', + submenu: [{ label: `About ${app.name}`, role: 'about' }] + } + ] + // 基础窗口信息配置 + const createWindow = function () { + const { width, height } = screen.getPrimaryDisplay().workAreaSize // 自适应默认尺寸 + const mainWindow = new BrowserWindow({ + width, + height, + x: 0, + y: 0, + webPreferences: { + devTools: true, // 打开开发者工具 + nodeIntegration: true, // 使用nodejs整合模块 + contextIsolation: true, // 上下文隔离 + preload: path.join(__dirname, 'preload.js') // 设定html内预加载script文件 + } + }) + // 切换应用功能方法 + const changePage = (type = null) => { + switch (type) { + case 'web': + mainWindow.loadURL('http://localhost:3000') // 在线功能加载 React 服务的 URL + // 需要启动服务的项目,同时监听加载失败事件 + mainWindow.webContents.on( + 'did-fail-load', + (event, errorCode, errorDescription) => { + // 如果errorCode为-105(net::ERR_NAME_NOT_RESOLVED),则表示服务不可用 + if (errorCode === -105) { + dialog.showErrorBox('Can not use this server') + } else { + dialog.showErrorBox(`Load Failed:${errorDescription}`) + } + } + ) + break + default: + mainWindow.loadFile( + path.join(__dirname, '..', rootPath, 'index.html') + ) // 使用相对路径加载 HTML 文件 + break + } + } + // 自定义应用切换事件 + template[0].submenu[0].submenu = [ + { + label: 'Online Web', + click: () => { + changePage('web') + } + }, + { + label: 'Local Page', + click: () => { + changePage() + } + } + ] + // 定义并执行菜单配置 + const menu = Menu.buildFromTemplate(template) + Menu.setApplicationMenu(menu) + changePage('web') + // 窗口关闭前进行确认验证 + mainWindow.on('close', event => { + confirmExit(event) + }) + } + // 应用加载完成配置 + app.on('ready', createWindow) + app.on('activate', () => { + createWindow() + }) + // 退出程序提示 + app.on('before-quit', event => { + confirmExit(event) + }) +} + +try { + // 开发模式 配置electron主进程热更新,可支持css,js,html热更 + require('electron-reloader')(module, {}) + initApp('public') +} catch (_) { + // 生产模式 electron-reloader依赖在打包后运行引用会造成读取不到的错误,从而定位到catch事件中,在catch中应去掉对该依赖的加载 + initApp('build') +} diff --git a/app/preload.js b/app/preload.js new file mode 100644 index 0000000000000000000000000000000000000000..8da3fdf96543808745e697251b98ab37e8ae067d --- /dev/null +++ b/app/preload.js @@ -0,0 +1,29 @@ +/* + * @Author: wuruipeng + * @Date: 2024-04-08 09:55:51 + * @LastEditors: wuruipeng + * @LastEditTime: 2024-04-08 16:06:34 + * @description: 渲染进程预加载脚本(用于搭建传递主进程与渲染进程的属性使用和方法) + */ + +const { contextBridge, ipcRenderer } = require('electron') +const common = require('./general/common.js') + +// 可发送通信消息至主进程控制调用 +contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer) +// 将需要暴露给渲染进程的 API 注册到一个受信任的预定义对象中(可返回更新值至渲染进程) +contextBridge.exposeInMainWorld( + 'resultAPI', // 对象名称 + { + // 定义一个 invoke 方法,用于从渲染进程向主进程发送消息,并等待主进程返回结果 + invoke: (channel, data) => { + // 在这里使用 ipcRenderer 发送消息给主进程,并等待主进程返回结果 + return ipcRenderer.invoke(channel, data) + }, + // 监听主进程发送的消息 + on: (channel, listener) => { + ipcRenderer.on(channel, listener) + } + } +) +contextBridge.exposeInMainWorld('common', common) diff --git a/build/electron-builder.json b/build/electron-builder.json deleted file mode 100644 index 3f9fab8ac68200fe09badbbaa6ae21c7d239d9f4..0000000000000000000000000000000000000000 --- a/build/electron-builder.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "appId": "com.example.myapp", - "productName": "ElectronProject", - "copyright": "Copyright © 2020-2024 WRP", - "directories": { - "output": "dist" - }, - "files": [ - { - "from": "build/", - "to": "build/" - }, - "main.js", - "index.html", - "package.json" - ], - "win": { - "target": ["nsis"] - }, - "mac": { - "target": ["dmg"] - }, - "linux": { - "target": ["AppImage"] - }, - "nsis": { - "oneClick": false, - "perMachine": true, - "allowElevation": true, - "allowToChangeInstallationDirectory": true, - "contents": [ - { - "x": 130, - "y": 220, - "type": "link", - "path": "/Applications" - }, - { - "x": 410, - "y": 220, - "type": "file" - } - ] - } -} diff --git a/node-folder.js b/config-structure.js similarity index 82% rename from node-folder.js rename to config-structure.js index 54c653e06a77a6c704349324680934f1af7c63f5..5b610726727bf8f6adfd2a6b58eb7da6ba49f633 100644 --- a/node-folder.js +++ b/config-structure.js @@ -1,3 +1,10 @@ +/* + * @Author: wuruipeng + * @Date: 2024-04-03 10:32:46 + * @LastEditors: wuruipeng + * @LastEditTime: 2024-04-08 11:13:13 + * @description:项目目录结构生成配置 + */ const fs = require('fs') const path = require('path') @@ -56,13 +63,18 @@ function generateDirectoryText( const folderPath = './' // 指定要排除的文件夹列表 -const excludedFolders = ['.git', 'node_modules', '.eslintcache', 'folder.txt'] +const excludedFolders = [ + '.git', + 'node_modules', + '.eslintcache', + 'structure.txt' +] const directoryText = generateDirectoryText(folderPath, '', excludedFolders) // 指定要保存目录文本的文件路径 -const outputPath = 'folder.txt' +const outputPath = 'structure.txt' fs.writeFileSync(outputPath, directoryText) -console.log(`目录信息已保存到 ${outputPath} 文件中。`) +console.log(`Structure information has saved to ${outputPath}`) diff --git a/index.html b/index.html deleted file mode 100644 index 3d65494de087254c8afc3bd46b4f95f2b07d9e8c..0000000000000000000000000000000000000000 --- a/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Electron - - - Electron - - diff --git a/jest-preset.json b/jest-preset.json new file mode 100644 index 0000000000000000000000000000000000000000..afddcf43c351f31cb0b2b84ceea027f4875d5265 --- /dev/null +++ b/jest-preset.json @@ -0,0 +1,6 @@ +{ + "preset": "react", + "transformIgnorePatterns": [ + "node_modules/(?!(@history/index|@api.*|@utils/index|@style.*)/)" + ] +} diff --git a/main.js b/main.js deleted file mode 100644 index 76074d5cf316a2c20440292e03e0656f3b68b76c..0000000000000000000000000000000000000000 --- a/main.js +++ /dev/null @@ -1,15 +0,0 @@ -const { app, BrowserWindow } = require('electron') - -const createWindow = () => { - const win = new BrowserWindow({ - width: 800, - height: 600, - webPreferences: { - nodeIntegration: true - } - }) - - win.loadFile('index.html') -} - -app.whenReady().then(createWindow) diff --git a/package.json b/package.json index dbfef70c3bbf3c8d052202d02434d942fa4cfd21..c82ec47921ec0f0dd2d7ae8c841e973153ffc6fa 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,125 @@ { - "name": "electron-project", + "author": "WuRuipeng", + "license": "ISC", + "name": "mtlh-application", "version": "1.0.0", "description": "electron-project", - "main": "main.js", + "main": "app/main.js", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "axios": "^1.6.5", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-redux": "^9.0.4", + "react-router-dom": "^6.21.1", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0" + }, "scripts": { - "start": "electron .", + "app": "electron .", + "serve": "webpack serve --mode development", + "build": "webpack --mode development", + "serve:build": "serve build", "pack": "electron-builder", - "structure": "node node-folder.js", + "pack:all": "npm run serve:build && npm run pack && serve build", + "jest": "jest", + "structure": "node config-structure.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "https://gitee.com/wuruipeng_admin/my-application-development.git" }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, "devDependencies": { + "@babel/core": "^7.22.9", + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "@babel/preset-env": "^7.23.8", + "@babel/preset-react": "^7.23.3", + "@types/react": "^18.2.79", + "@types/react-dom": "^18.2.25", + "babel-loader": "^9.1.3", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^3.6.0", "electron": "^25.5.0", - "electron-builder": "^24.6.3" + "electron-builder": "^24.6.3", + "electron-reloader": "^1.2.3", + "html-webpack-plugin": "^5.6.0", + "jest": "26.6.0", + "less": "^4.2.0", + "less-loader": "^12.2.0", + "style-loader": "^4.0.0", + "ts-loader": "^9.5.1", + "typescript": "^5.4.5", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.0.4" + }, + "build": { + "asar": false, + "appId": "com.example.mtlh", + "productName": "MTLH", + "copyright": "Copyright © 2020-2024 WRP", + "icon": "build/logo256.png", + "directories": { + "output": "dist" + }, + "extraResources": [ + { + "from": "server/", + "to": "server/", + "filter": [ + "**/*", + "!**/node_modules/**", + "!package-lock/*.json" + ] + } + ], + "files": [ + "app/**/*", + "build/**/*", + "package.json" + ], + "win": { + "target": [ + "nsis" + ] + }, + "mac": { + "target": [ + "dmg" + ] + }, + "linux": { + "target": [ + "AppImage" + ] + }, + "nsis": { + "oneClick": false, + "perMachine": true, + "allowElevation": true, + "allowToChangeInstallationDirectory": true + }, + "extends": null }, - "keywords": [], - "author": "", - "license": "ISC" + "engines": { + "node": ">=16" + } } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000000000000000000000000000000000000..cb1889536387249582de68204d830237100b4674 --- /dev/null +++ b/public/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + + Application + + + +
+ diff --git a/public/logo192.png b/public/logo192.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 Binary files /dev/null and b/public/logo192.png differ diff --git a/public/logo256.png b/public/logo256.png new file mode 100644 index 0000000000000000000000000000000000000000..41a1abd48b5da10e96a1793a452ddf090838d03b Binary files /dev/null and b/public/logo256.png differ diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000000000000000000000000000000000000..ed99a00dd8bf8fec305ceb5ecd806656739a0c67 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo256.png", + "type": "image/png", + "sizes": "256x256" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..e9e57dc4d41b9b46e05112e9f45b7ea6ac0ba15e --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000000000000000000000000000000000000..3962804f79e1ef159c02103dc3f434c4995ffd78 --- /dev/null +++ b/server/index.js @@ -0,0 +1,46 @@ +/* + * @Author: wuruipeng + * @Date: 2024-04-08 10:47:57 + * @LastEditors: wuruipeng + * @LastEditTime: 2024-04-11 11:36:29 + * @description:本地koa服务配置入口文件 + */ + +const Koa = require('koa') +const app = new Koa() +// 引入post请求中间件 +const bodyparser = require('koa-bodyparser') +// 引入解决跨域组件 +const cors = require('koa2-cors') +// 引入表单校验模块 +const koaBouncer = require('koa-bouncer') +// 引入session模块 +const session = require('koa-session') + +// 使用post请求中间件 +app.use(bodyparser()) +// 使用解决跨域组件 +app.use(cors()) +// 使用表单校验模块 +app.use(koaBouncer.middleware()) +// 设置session签名,防止客户端修改(加密) +app.keys = ['session secret', 'anthor secret'] +// 设置session配置项 +const SESSION_CONFIG = { + key: 'sid', // 设置cookie的key名 + maxAge: 86400000, // 有效期,默认一天 + httpOnly: true, // 仅服务端修改 + signed: true // 签名cookie +} +// 使用session模块注册配置 +app.use(session(SESSION_CONFIG, app)) + +// 中间件配置 +app.use(async ctx => { + // 获取访问次数 + let n = ctx.session.count || 0 + ctx.session.count = ++n // 存储对应session的信息 + ctx.body += `第${n}次访问该网站` +}) + +app.listen('3001') diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000000000000000000000000000000000000..6669ed75c1a9e1839bb2af109a6054dc1b1c664b --- /dev/null +++ b/server/package.json @@ -0,0 +1,36 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "Local NodeJs Server", + "main": "index.js", + "scripts": { + "serve": "node index.js", + "serve:nodemon": "nodemon index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "koa", + "mongodb" + ], + "author": "WuRuipeng", + "license": "ISC", + "dependencies": { + "@koa/router": "^10.1.1", + "jsonwebtoken": "^8.5.1", + "koa": "^2.13.4", + "koa-bodyparser": "^4.3.0", + "koa-bouncer": "^6.0.0", + "koa-jwt": "^4.0.3", + "koa-logger": "^3.2.1", + "koa-multer": "^1.0.2", + "koa-onerror": "^4.2.0", + "koa-session": "^6.2.0", + "koa-static": "^5.0.0", + "koa2-cors": "^2.0.6", + "mongodb": "^4.4.0", + "mongoose": "^6.2.4", + "nodemon": "^3.1.0", + "sequelize": "^6.17.0", + "trek-captcha": "^0.4.0" + } +} diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..21d01243aea03386d5da65c8cf4d14a3444fdc7e --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import '@/styles/index.less' +import { Root, createRoot } from 'react-dom/client' + +const rootElement = document.getElementById('root') +if (rootElement) { + const root: Root = createRoot(rootElement) + root.render( + + Main Theme + + ) +} else { + console.error('Cannot find the rootElement!') +} diff --git a/src/setupTests.js b/src/setupTests.js new file mode 100644 index 0000000000000000000000000000000000000000..8f2609b7b3e0e3897ab3bcaad13caf6876e48699 --- /dev/null +++ b/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/src/styles/index.less b/src/styles/index.less new file mode 100644 index 0000000000000000000000000000000000000000..bf6d0e8ef8ea1fb0f46fdd5f6bc0aa52aae159bb --- /dev/null +++ b/src/styles/index.less @@ -0,0 +1,51 @@ +/* Typography */ +html, +body, +#root { + width: 100%; + height: 100%; + padding: 0; + margin: 0; + font: inherit; + font-family: Arial, sans-serif; +} + +/* Box sizing */ +*, +*:before, +*:after { + box-sizing: border-box; +} + +/* Links */ +a { + color: #007bff; + text-decoration: none; + transition: color 0.3s ease; +} + +a:hover { + color: #0056b3; +} + +/* Lists */ +ul, +ol { + margin: 0; + padding: 0; +} + +/* Form elements */ +input, +button, +textarea, +select { + font-family: inherit; + font-size: inherit; +} + +/* Images */ +img { + max-width: 100%; + height: auto; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..f0ccea136ca281099843bd4be24e1ebd8a2d855b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "lib": ["dom", "dom.iterable", "es2015", "es2016", "es2017", "esnext"], + "strict": true, + "declaration": true, + "sourceMap": true, + "noEmit": false, + "outDir": "./build", + "rootDir": "./src", + "baseUrl": "./src", + "importHelpers": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictNullChecks": true, + "preserveConstEnums": true, + "resolveJsonModule": true, + "typeRoots": ["./node_modules/@types"], + "allowJs": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "isolatedModules": true, + "jsx": "react" + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["app", "node_modules", "build", "dist", "public", "server"], + "paths": { + "@/*": ["src/*"] + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000000000000000000000000000000000000..fadbdb625bc34eaaeef5403113ba8ef573b1a209 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,86 @@ +const path = require('path') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') + +module.exports = { + mode: 'development', + entry: './src/index.tsx', + devServer: { + static: { + directory: path.join(__dirname, 'public') + }, + compress: true, + port: 3000, + open: false, + allowedHosts: 'auto', + client: { + logging: 'info', + overlay: true, + progress: true + }, + proxy: [ + { + context: ['/api'], + target: 'http://localhost:3001', + secure: false, + changeOrigin: true, + pathRewrite: { '^/api': '' } + } + ] + }, + resolve: { + alias: { + '@': path.resolve(__dirname, 'src/') + }, + extensions: ['.tsx', '.ts', '.jsx', '.js'] + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader' + } + }, + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + use: { + loader: 'ts-loader', + options: { + transpileOnly: false + } + } + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }, + { + test: /\.less$/, + use: ['style-loader', 'css-loader', 'less-loader'] + } + ] + }, + plugins: [ + new HtmlWebpackPlugin({ + template: './public/index.html' + }), + new CopyWebpackPlugin({ + patterns: [ + { + from: 'public', + to: '', + globOptions: { + ignore: ['**/index.html'] + } + } + ] + }) + ], + output: { + path: path.resolve(__dirname, 'build'), + filename: 'bundle.js' + } +}