diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/.babelrc b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/.babelrc new file mode 100644 index 0000000000000000000000000000000000000000..18151f1f8f709838202b6231652ea8cec184527e --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"] +} \ No newline at end of file diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/.prettierrc b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/.prettierrc new file mode 100644 index 0000000000000000000000000000000000000000..e3d2acb00457084b2f6cccafb8c95740e0344485 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/.prettierrc @@ -0,0 +1,13 @@ +{ + "parser": "typescript", + "semi": true, + "singleQuote": true, + "jsxSingleQuote": false, + "bracketSpacing": true, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "all", + "proseWrap": "always", + "endOfLine": "lf", + "printWidth": 120 +} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/package.json b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/package.json new file mode 100644 index 0000000000000000000000000000000000000000..bcd95816016e78f3a02187ee0d3e69e6cabf3ee3 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/package.json @@ -0,0 +1,49 @@ +{ + "name": "fe", + "version": "1.0.0", + "main": "webpack.config.js", + "scripts": { + "dev": "webpack serve --config webpack.dev.js", + "buildWin": "cross-env NODE_ENV=production webpack && copy dist\\index.html ..\\server\\static\\", + "prettier": "prettier --config ./.prettierrc --write ./src/**/*.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@babel/core": "^7.27.3", + "@babel/preset-env": "^7.27.2", + "@babel/preset-react": "^7.27.1", + "babel-loader": "^10.0.0", + "clean-webpack-plugin": "^4.0.0", + "cross-env": "^7.0.3", + "css-loader": "^7.1.2", + "file-loader": "^6.2.0", + "html-loader": "^5.1.0", + "html-webpack-plugin": "^5.6.3", + "inline-chunk-html-plugin": "^1.1.1", + "less": "^4.3.0", + "less-loader": "^12.3.0", + "mini-css-extract-plugin": "^2.9.2", + "prettier": "^3.5.3", + "style-loader": "^4.0.0", + "ts-loader": "^9.5.2", + "typescript": "^5.8.3", + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "4.15.1" + }, + "dependencies": { + "ahooks": "^3.8.5", + "antd": "^5.25.3", + "axios": "^1.9.0", + "echarts": "^5.6.0", + "echarts-for-react": "^3.0.2", + "lodash": "^4.17.21", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "use-sync-external-store": "^1.5.0", + "zustand": "^5.0.5" + } +} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/public/index.html b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/public/index.html new file mode 100644 index 0000000000000000000000000000000000000000..a0c678cdf146296b73a19afc2e04fe1aaab32448 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/public/index.html @@ -0,0 +1,10 @@ + + + + + Graph + + +
+ + \ No newline at end of file diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/index.less b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/index.less new file mode 100644 index 0000000000000000000000000000000000000000..2d10e24d0616249373f6e376747bd4e245448d82 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/index.less @@ -0,0 +1,34 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +.warpper { + background-color: #eeeeee; + height: 100%; +} + +.controller { + padding: 16px 12px; + background-color: white; +} + +.title { + font-size: 16px; + font-weight: 500; + text-align: center; + padding-block: 6px; + background-color: #f1f1f1; +} \ No newline at end of file diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/index.tsx b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ee2a7a787517fdcb504a1ea8162299b6b9363ca4 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/index.tsx @@ -0,0 +1,216 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +import React, { useEffect, useState, memo } from 'react'; +import { shallow, useShallow } from 'zustand/shallow'; +import { message } from 'antd'; +import { isEmpty } from 'lodash'; +import './index.less'; +import { useGlobalStore } from '../store'; +import { DIMENSIONS_OPTIONS, HEATMAP_TYPE } from '../common/constant'; +import SelectWithLabel from '../common/SelectWithLabel'; +import useController from './useController'; +import type { SelectOptionType } from '../common/type'; +import type { ControllerProps, ValuesResponseType, ValuesRequestParamsType } from './type'; + +const Controller: React.FC = (props: ControllerProps) => { + const { metrics } = props; + const useControllerInstance = useController(); + const { metric, stat, dimension, dimensionValue, heatMapType, setContextState } = useGlobalStore( + useShallow((state) => ({ + metric: state.metric, + stat: state.stat, + dimension: state.dimension, + dimensionValue: state.dimensionValue, + heatMapType: state.heatMapType, + setContextState: state.setContextState, + })), + shallow, + ); + const [metricsMapStats, setMetricsMapStats] = useState>>({}); + const [metricsNameList, setMetricsNameList] = useState([]); + const [statNameList, setStatNameList] = useState([]); + const [dimensionValueList, setDimensionValueList] = useState(); + + useEffect(() => { + if (metrics) { + const metricsMapStats: Record> = {}; + const metricsNameList: Array = []; + metrics.forEach(({ name, stats }) => { + metricsMapStats[name] = stats.map((stat) => { + return { + label: stat, + value: stat, + }; + }); + metricsNameList.push({ label: name, value: name }); + }); + + const selecrMetric = metricsNameList?.[0]?.value; + const statNameList: Array = metricsMapStats[selecrMetric]; + const selectedStat = statNameList?.[0]?.value; + setMetricsMapStats(metricsMapStats); + // 初始化指标 + setMetricsNameList(metricsNameList); + // 初始化统计量 + setStatNameList(statNameList); + setContextState({ metric: selecrMetric, stat: selectedStat }); + // 初始化维度,默认选中step维度 + const params: ValuesRequestParamsType = { + metric: selecrMetric, + stat: selectedStat, + dimension: DIMENSIONS_OPTIONS[0].value, + value: 0, + }; + updateDimensionValueList(params); + } + }, [metrics]); + + useEffect(() => { + if (!metric || !stat || !dimension || !dimensionValue) { + return; + } + const params = { + metric, + stat, + dimension, + value: dimensionValue, + }; + const loadGraphData = async (params) => { + setContextState({ loadingHeatMap: true }); + const result = await useControllerInstance.loadGraphData(params); + setContextState({ loadingHeatMap: false }); + if (result.error) { + message.error(result?.error); + return; + } + setContextState({ + heatMaphData: result?.data, + trendData: { dimensions: [], values: [] }, + dimX: ' ', + dimY: ' ', + }); + }; + loadGraphData(params); + }, [metric, stat, dimension, dimensionValue]); + + const updateDimensionValueList = async (params: ValuesRequestParamsType) => { + const result: ValuesResponseType = await useControllerInstance.loadDimensionValueList(params); + if (result.error) { + message.error(result.error); + return; + } + if (!isEmpty(result)) { + const dimensionValueList = Object.entries(result?.data || {}).map(([key, value]) => { + return { + value: key, + label: value, + }; + }); + setContextState({ + dimension: params.dimension, + dimensionValue: dimensionValueList?.[0]?.value, + }); + setDimensionValueList(dimensionValueList); + } + }; + + // 指标选择 + const onSelectMetricChange = (value: string) => { + const statNameList = metricsMapStats[value]; + const selectedStat = statNameList?.[0]?.value; + setContextState({ metric: value, stat: selectedStat }); + setStatNameList(statNameList); + }; + + // 统计量选择 + const onSelectStatChange = (value: string) => { + setContextState({ stat: value }); + }; + + // 维度选择 + const onSelectDimensionChange = (value: string) => { + const params: ValuesRequestParamsType = { + metric, + stat, + dimension: value, + value: 0, + } as ValuesRequestParamsType; + updateDimensionValueList(params); + }; + // 维度值选择 + const onSelectDimensionValueChange = (value: string) => { + setContextState({ dimensionValue: value }); + }; + + return ( +
+
设置
+
+ + + + + + setContextState({ + heatMapType: value, + }) + } + options={HEATMAP_TYPE} + /> +
+
+ ); +}; + +export default memo(Controller); diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/type/index.d.ts b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/type/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d396a0bd142596cd4930555b633d07e234387ad --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/type/index.d.ts @@ -0,0 +1,39 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +export interface ControllerProps { + metrics: Array; +} + +export interface ValuesResponseType { + data?: { + [string]: string; + }; + error?: string; +} + +export interface HeatmapDataResponseType { + data?: Array; + error?: string; +} + +export interface ValuesRequestParamsType { + metric: string; + stat: string; + dimension: string; + value: number; +} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/useController.ts b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/useController.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2ff9d1f32ae41f327ad27ebd7ffac45e1475475 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/controller/useController.ts @@ -0,0 +1,59 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +import request from '../utils/request'; +import { ValuesResponseType, ValuesRequestParamsType, HeatmapDataResponseType } from './type'; + +const useController = () => { + const loadGraphData = async (params: ValuesRequestParamsType) => { + try { + const result = await request({ + url: 'heatmap_data', + method: 'GET', + params, + }) as unknown as HeatmapDataResponseType; + return result; + } catch (error) { + return { + error: '网络异常:获取维度值列表失败', + }; + } + }; + const loadDimensionValueList = async (params: ValuesRequestParamsType) => { + if (!params.dimension || !params.metric || !params.stat) { + return {}; + } + try { + const result: ValuesResponseType = (await request({ + url: 'values', + method: 'GET', + params, + })) as unknown as ValuesResponseType; + return result; + } catch (error) { + return { + error: '网络异常:获取维度值列表失败', + }; + } + }; + return { + loadGraphData, + loadDimensionValueList, + }; +}; + +export default useController; diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/index.tsx b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e7b152719b9137b34bfda8670660716bb256e9e5 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/index.tsx @@ -0,0 +1,26 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import MonVis from './main'; + +const rootElement = document.getElementById('root'); +if (rootElement) { + const root = ReactDOM.createRoot(rootElement); + root.render(); +} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/store/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/store/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..79b5345c0732045975a3ccdbedf1cdd57bdfc364 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/store/index.ts @@ -0,0 +1,39 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +import { createWithEqualityFn } from 'zustand/traditional'; +import { CONTINUOUS } from '../common/constant'; +import type { ContextStateType } from '../common/type'; + +// 使用createWithEqualityFn而不是create +export const useGlobalStore = createWithEqualityFn( + (set) => ({ + heatMaphData: [], + trendData: { dimensions: [], values: [] }, + metric: '', + stat: '', + dimension: '', + dimensionValue: '', + heatMapType: CONTINUOUS, + dimX: '', + dimY: '', + loadingHeatMap: false, + loadingLineChart: false, + setContextState: (newState) => set((state) => ({ ...state, ...newState })), + }), + Object.is, // 指定默认的相等性函数,可以是浅层比较 +); diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/utils/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/utils/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d899bc2456624d58fc7f3e24364e2831f2c41ff1 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/utils/index.ts @@ -0,0 +1,27 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ +export const formatSegmentLabel = (min, max) => { + const format = (value) => { + if (value === Infinity) return '∞'; + if (value === -Infinity) return '-∞'; + return value.toExponential(2); + }; + + if (min === -Infinity) return `< ${format(max)}`; + if (max === Infinity) return `> ${format(min)}`; + return `${format(min)} ~ ${format(max)}`; +}; diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/utils/request.ts b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/utils/request.ts new file mode 100644 index 0000000000000000000000000000000000000000..84c23efb6d96aa88883dec5570062d93ccdfd3a3 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/src/utils/request.ts @@ -0,0 +1,91 @@ +/* Copyright (c) 2025, Huawei Technologies. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import axios, { AxiosResponse, AxiosError } from 'axios'; +interface RequestOptions { + url: string; // 请求地址 + method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; // 请求方法,默认为 GET + data?: any; // 请求体数据(适用于 POST、PUT) + params?: any; // URL 参数(适用于 GET) + headers?: Record; // 自定义请求头 + timeout?: number; // 超时时间(毫秒),默认 10 秒 +} +interface ApiResponse { + success: boolean; // 是否成功 + data?: T; // 响应数据 + error?: string; // 错误信息 +} +export default async function request(options: RequestOptions): Promise> { + const { url, method = 'GET', data = null, params = null, headers = {}, timeout = 60000 * 3 } = options; + + try { + const controller = new AbortController(); + const signal = controller.signal; + const timeoutId = setTimeout(() => controller.abort(), timeout); + const response: AxiosResponse = await axios({ + url, + method, + data, + params, + headers: { + 'Content-Type': 'application/json', // 默认 Content-Type + ...headers, // 自定义请求头覆盖默认值 + }, + maxBodyLength: Infinity, // 允许发送大文件 + signal, // 绑定信号以支持超时 + }); + + clearTimeout(timeoutId); + if (response.status >= 200 && response.status < 300) { + return response.data as ApiResponse; + } else { + return { + success: false, + error: `HTTP Error: ${response.status} - ${response.statusText}`, + }; + } + } catch (error) { + if (axios.isAxiosError(error)) { + const axiosError = error as AxiosError; + if (axiosError.code === 'ECONNABORTED') { + return { + success: false, + error: '请求超时,请稍后重试。', + }; + } else if (axiosError.response) { + const { status, statusText } = axiosError.response; + return { + success: false, + error: `HTTP Error: ${status} - ${statusText}`, + }; + } else if (axiosError.request) { + return { + success: false, + error: '网络错误,未收到服务器响应。', + }; + } else { + return { + success: false, + error: axiosError.message || '未知错误', + }; + } + } else { + return { + success: false, + error: (error as Error).message || '未知错误', + }; + } + } +} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/tsconfig.json b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..15fb526ae25d57334e4719f957c99682989b3240 --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/tsconfig.json @@ -0,0 +1,40 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ +{ + "compilerOptions": { + "jsx": "react", + "downlevelIteration": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "inlineSourceMap": true, + "lib": [ + "dom", + "ES2022", + "dom.iterable" + ], + "moduleResolution": "node", + "module": "ES2022", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitOverride": true, + "skipLibCheck": true, + "strict": true, + "noImplicitAny": false, + "target": "es2018" + } +} \ No newline at end of file diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/webpack.config.js b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/webpack.config.js new file mode 100644 index 0000000000000000000000000000000000000000..e52a714e3b4c1c07ba668d738f2dbea423d9025c --- /dev/null +++ b/plugins/tensorboard-plugins/tb_graph_ascend/monvis_plugin/fe/webpack.config.js @@ -0,0 +1,81 @@ +/* ------------------------------------------------------------------------- + Copyright (c) 2025, Huawei Technologies. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const InlineChunkHtmlPlugin = require('inline-chunk-html-plugin'); + +module.exports = { + entry: { + app: './src/index.tsx', + }, + output: { + filename: 'index.js', + path: path.resolve(__dirname, 'dist'), + }, + module: { + rules: [ + { + test: /\.(html)$/, + use: { + loader: 'html-loader', + }, + }, + { + test: /\.tsx?$/, + use: { + loader: 'ts-loader', + options: { + transpileOnly: true, + }, + }, + exclude: /node_modules/, + }, + { + test: /\.css$/i, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.less$/i, + use: [ + 'style-loader', // 将 JS 字符串生成为