diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..382b281490592acd47e8f94931857558ff05ecb6 --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,6 @@ +node_modules +dist +packages/icons/components +packages/wasm/dist +license-header.js +*.d.ts diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 370daa2a28b932e9f54437dd4c40d3af92274988..2fdc4560987c76e175b4aeff98969a268d9b9ecf 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -22,10 +22,9 @@ module.exports = { }, extends: ['plugin:prettier/recommended'], parserOptions: { - ecmaVersion: 2018, + ecmaVersion: 2020, sourceType: 'module' }, - ignorePatterns: ['node_modules/', 'dist/', 'output/', 'license-header.js'], plugins: ['license-header'], rules: { 'no-console': 'warn', @@ -34,7 +33,7 @@ module.exports = { }, overrides: [ { - files: ['packages/cli/**/*', 'packages/mock/**/*', 'packages/demo/**/*', 'packages/server/**/*'], + files: ['packages/cli/**/*', 'packages/demo/**/*', 'packages/server/**/*'], extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], parser: '@typescript-eslint/parser', rules: { @@ -71,6 +70,9 @@ module.exports = { 'react/prop-types': 'off', 'react/react-in-jsx-scope': 'off' } + }, + { + files: ['packages/icons/**/*'] } ] }; diff --git a/frontend/.gitignore b/frontend/.gitignore index 107316ec6829c51f709b2571f96b3fcb828e0ed6..5c15507d2c438e753be03f488ef19850e89d595e 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -12,7 +12,6 @@ coverage .next # production -build dist # misc @@ -27,4 +26,4 @@ yarn-error.log* lerna-debug.log* # output -/output +output diff --git a/frontend/.prettierignore b/frontend/.prettierignore index 6336fdd77aaeb0a260cf0fe9e3f3b6ac37a516c6..a26f8e01e3c19e1d4797435fcd97506a548c4283 100644 --- a/frontend/.prettierignore +++ b/frontend/.prettierignore @@ -1,5 +1,5 @@ +node_modules dist -out -wasm -output +packages/icons/components +packages/wasm/dist license-header.js diff --git a/frontend/lint-staged.config.js b/frontend/lint-staged.config.js index ef5471e1134e55a8c98dea807c83ea5ed223bd8d..987a0a04aba5475bdb66c0a389f449049ef86818 100644 --- a/frontend/lint-staged.config.js +++ b/frontend/lint-staged.config.js @@ -26,8 +26,7 @@ const getPackages = filenames => module.exports = { // lint all files when global package.json or eslint config changes. - './(package.json|.eslintrc.js)': () => - `eslint --ext .tsx,.jsx.ts,.js --ignore-path ${path.join(__dirname, '.gitignore')} ${__dirname}`, + './(package.json|.eslintrc.js)': () => `eslint --ext .tsx,.jsx.ts,.js ${__dirname}`, // check types when ts file or package.json changes. './(packages/*/package.json|packages/*/**/*.ts?(x))': filenames => diff --git a/frontend/package.json b/frontend/package.json index c61d2ff94fbb3b53cf0f78398d9c229b86eedf4f..4fb11c809f43de4536f1317775057707723a350c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,8 +29,14 @@ "scripts": { "bootstrap": "lerna bootstrap", "build": "./scripts/build.sh", + "build:core": "yarn workspace @visualdl/core build", + "build:demo": "yarn workspace @visualdl/demo build", "clean": "rimraf output packages/*/dist packages/wasm/target", - "lint": "eslint --ext .tsx,.jsx.ts,.js --ignore-path .gitignore .", + "dev": "yarn dev:core", + "dev:core": "yarn workspace @visualdl/core dev", + "dev:demo": "yarn workspace @visualdl/server dev:demo", + "dev:server": "yarn workspace @visualdl/server dev", + "lint": "eslint --ext .tsx,.jsx.ts,.js,.mjs .", "format": "prettier --write \"**/*.{ts,tsx,js,jsx}\"", "test": "yarn workspaces run test", "prepublishOnly": "yarn lint && yarn test && yarn build", @@ -38,19 +44,19 @@ "version": "yarn format && git add -A" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "4.24.0", - "@typescript-eslint/parser": "4.24.0", - "eslint": "7.26.0", + "@typescript-eslint/eslint-plugin": "4.26.0", + "@typescript-eslint/parser": "4.26.0", + "eslint": "7.28.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-license-header": "0.2.0", "eslint-plugin-prettier": "3.4.0", - "eslint-plugin-react": "7.23.2", + "eslint-plugin-react": "7.24.0", "eslint-plugin-react-hooks": "4.2.0", "lerna": "4.0.0", "lint-staged": "11.0.0", - "prettier": "2.3.0", + "prettier": "2.3.1", "rimraf": "3.0.2", - "typescript": "4.2.4", + "typescript": "4.3.2", "yarn": "1.22.10" }, "engines": { diff --git a/frontend/packages/cli/LICENSE b/frontend/packages/cli/LICENSE deleted file mode 120000 index 30cff7403da04711c46979a06f6bf8eb10ee088a..0000000000000000000000000000000000000000 --- a/frontend/packages/cli/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/frontend/packages/cli/README.md b/frontend/packages/cli/README.md deleted file mode 100644 index 8c2c3eb90b4ca4ec2d4aca7f91a5b12b6e7c24ad..0000000000000000000000000000000000000000 --- a/frontend/packages/cli/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# VisualDL FrontEnd cli - -## Usage - -> nodejs ≥ 10 and npm ≥ 6 are required. - -```bash -npm install -g @visualdl/cli -# or -yarn global add @visualdl/cli -``` - -Then you can start visualdl server by - -```bash -visualdl start --backend="http://127.0.0.1:8040" -``` - -To stop visualdl server, just type - -```bash -visualdl stop -``` - -For more usage infomation, please type - -```bash -visualdl -h -``` diff --git a/frontend/packages/cli/index.ts b/frontend/packages/cli/index.ts deleted file mode 100755 index a6475a72530f72ad85c064d2cc72b508c741101c..0000000000000000000000000000000000000000 --- a/frontend/packages/cli/index.ts +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env node - -/** - * Copyright 2020 Baidu Inc. 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. - */ - -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable no-console */ - -import ora, {Ora} from 'ora'; - -import ecosystem from '@visualdl/server/ecosystem.config'; -import pm2 from 'pm2'; - -const app = ecosystem.apps[0]; - -const argv = require('yargs') - .usage('Usage: $0 [options]') - .command('start', 'Start VisualDL server') - .command('stop', 'Stop VisualDL server') - .example( - '$0 start --backend="http://172.17.0.82:8040"', - 'Start VisualDL server with backend address http://172.17.0.82:8040' - ) - .alias('p', 'port') - .nargs('p', 1) - .nargs('port', 1) - .describe('p', 'Port of server') - .nargs('host', 1) - .describe('host', 'Host of server') - .alias('b', 'backend') - .nargs('b', 1) - .nargs('backend', 1) - .describe('b', 'Backend API address') - .boolean('demo') - .describe('demo', 'Run in demo mode') - .boolean('open') - .describe('open', 'Open browser when server is ready') - .help('h') - .alias('h', 'help') - .epilog('Visit https://github.com/PaddlePaddle/VisualDL for more information.').argv; - -const command = argv._[0]; - -const exit = () => { - console.log('Command not found, use -h or --help for help'); - process.exit(1); -}; - -const exitIfError = (err?: Error, exitCode = 1, spinner?: Ora) => { - if (!err) { - return; - } - if (spinner) { - spinner.fail('Error!'); - } - console.error(err); - process.exit(exitCode); -}; - -if (!command) { - exit(); -} - -const banner = ` - -█████ █████ ███ ████ ██████████ █████ -░░███ ░░███ ░░░ ░░███ ░░███░░░░███ ░░███ - ░███ ░███ ████ █████ █████ ████ ██████ ░███ ░███ ░░███ ░███ - ░███ ░███ ░░███ ███░░ ░░███ ░███ ░░░░░███ ░███ ░███ ░███ ░███ - ░░███ ███ ░███ ░░█████ ░███ ░███ ███████ ░███ ░███ ░███ ░███ - ░░░█████░ ░███ ░░░░███ ░███ ░███ ███░░███ ░███ ░███ ███ ░███ █ - ░░███ █████ ██████ ░░████████░░████████ █████ ██████████ ███████████ - ░░░ ░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░░░░░ - - - -`; - -pm2.connect(err => { - exitIfError(err, 2); - - pm2.list((err, list) => { - exitIfError(err, 2); - - const host = argv.host || 'localhost'; - const port = Number.parseInt(argv.port, 10) || 8999; - const url = `http://${host}:${port}`; - - if (command === 'start') { - if (list.find(item => item.name === app.name)) { - exitIfError(new Error('VisualDL server is already running'), 1); - } - const spinner = ora('Starting VisualDL server...').start(); - pm2.start( - { - ...app, - env: { - ...app.env, - HOST: host, - PORT: port + '', - BACKEND: argv.backend, - DEMO: argv.demo ? '1' : '' - } - }, - err => { - pm2.disconnect(); - exitIfError(err, 2, spinner); - spinner.succeed('Starting VisualDL server... Done'); - console.log(banner); - console.log(`> VisualDL server is running at ${url}`); - if (argv.open) { - console.log(' Opening your browser for you...'); - const open = require('open'); - open(url); - } - } - ); - } else if (command === 'stop') { - if (!list.find(item => item.name === app.name)) { - exitIfError(new Error('VisualDL server is not running'), 1); - } - const spinner = ora('Stopping VisualDL server...').start(); - pm2.delete(app.name, err => { - exitIfError(err, 2); - const end = (err?: Error) => { - pm2.disconnect(); - exitIfError(err, 2); - - spinner.succeed('Stopping VisualDL server... Done'); - console.log('> VisualDL server stopped'); - console.log(' See you next time'); - process.exit(0); - }; - pm2.list((err, newList) => { - exitIfError(err, 2); - if (!newList.length) { - pm2.killDaemon(end); - } else { - end(); - } - }); - }); - } else { - exit(); - } - }); -}); diff --git a/frontend/packages/cli/tsconfig.json b/frontend/packages/cli/tsconfig.json deleted file mode 100644 index 0b96ab5e5ba052f562902d3d9286583db84364b3..0000000000000000000000000000000000000000 --- a/frontend/packages/cli/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "esnext", - "esnext.asynciterable", - "dom" - ], - "moduleResolution": "node", - "esModuleInterop": true, - "declaration": true, - "skipLibCheck": true, - "outDir": "dist" - } -} diff --git a/frontend/packages/core/.browserslistrc b/frontend/packages/core/.browserslistrc deleted file mode 100644 index 20d320ea39ca35f13405452a75102d8972621636..0000000000000000000000000000000000000000 --- a/frontend/packages/core/.browserslistrc +++ /dev/null @@ -1 +0,0 @@ -supports es6-module diff --git a/frontend/packages/core/.gitignore b/frontend/packages/core/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7081ce64b1b1cb854736cef14ad90bf1a839751d --- /dev/null +++ b/frontend/packages/core/.gitignore @@ -0,0 +1 @@ +public/netron diff --git a/frontend/packages/core/babel.config.js b/frontend/packages/core/babel.config.js deleted file mode 100644 index e3275faf73fbc6bb3fa3bfdf1b94cce92b71ef2e..0000000000000000000000000000000000000000 --- a/frontend/packages/core/babel.config.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Baidu Inc. 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. - */ - -// cSpell:words esmodules - -module.exports = { - extends: '@snowpack/app-scripts-react/babel.config.json', - presets: [ - [ - '@babel/preset-env', - { - targets: { - esmodules: true - }, - bugfixes: true, - modules: false - } - ] - ], - plugins: ['styled-components', '@babel/plugin-proposal-class-properties', 'emotion'] -}; diff --git a/frontend/packages/core/builder/cdn.js b/frontend/packages/core/builder/cdn.js index c0041a1a78b78dc2056f17f6bfe8e8a86121d2d0..532b2c9b849885ffdb5ef3422cf319c9ca1579a9 100644 --- a/frontend/packages/core/builder/cdn.js +++ b/frontend/packages/core/builder/cdn.js @@ -14,32 +14,18 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable no-console */ +// cspell:words baiducloud -const path = require('path'); -const fs = require('fs/promises'); -const {BosClient} = require('@baiducloud/sdk'); -const mime = require('mime-types'); +import {BosClient} from '@baiducloud/sdk'; +import Logger from './log.js'; +import fs from 'fs/promises'; +import mime from 'mime-types'; +import path from 'path'; -const endpoint = process.env.BOS_ENDPOINT || 'https://bos.bj.baidubce.com'; -const ak = process.env.BOS_AK; -const sk = process.env.BOS_SK; - -const version = process.env.CDN_VERSION || 'latest'; - -const config = { - endpoint, - credentials: { - ak, - sk - } -}; +const logger = new Logger('CDN'); const bucket = 'visualdl-static'; -const client = new BosClient(config); - async function getFiles(dir) { const result = []; try { @@ -57,14 +43,20 @@ async function getFiles(dir) { } } } catch (e) { - console.error(e); + logger.error(e); } return result; } async function push(directory, options) { - if (!ak || !sk) { - console.error('No AK and SK specified!'); + logger.start(); + + const version = options?.version ?? 'latest'; + + logger.process(`pushing to CDN with version "${version}"...`); + + if (!options?.ak || !options?.sk) { + logger.error('No AK and SK specified!'); process.exit(1); } @@ -83,24 +75,51 @@ async function push(directory, options) { size: stats.size }); } else { - console.error(`${directory} does not exist!`); + logger.error(`${directory} does not exist!`); process.exit(1); } } catch (e) { - console.error(e); + logger.error(e); process.exit(1); } + + const config = { + endpoint: options?.endpoint ?? 'https://bos.bj.baidubce.com', + credentials: { + ak: options.ak, + sk: options.sk + } + }; + const client = new BosClient(config); + + const q = []; for (const file of files) { (function (f) { - client - .putObjectFromFile(bucket, `assets/${version}/${f.filename}`, f.name, { - 'Content-Length': f.size, - 'Content-Type': `${f.mime}` + q.push( + new Promise((resolve, reject) => { + client + .putObjectFromFile(bucket, `assets/${version}/${f.filename}`, f.name, { + 'Content-Length': f.size, + 'Content-Type': `${f.mime}` + }) + .then(() => { + logger.info([f.filename, f.mime, f.size].join(', ')); + resolve(); + }) + .catch(error => { + logger.error(f + error + ''); + reject(); + }); }) - .then(() => console.log([f.name, f.mime, f.size].join(', '))) - .catch(error => console.error(f, error)); + ); })(file); } + try { + await Promise.all(q); + logger.end('CDN Pushed.'); + } catch { + logger.error('Some errors occurred when pushing to CDN.'); + } } -module.exports = push; +export default push; diff --git a/frontend/packages/core/builder/dev-server.js b/frontend/packages/core/builder/dev-server.js deleted file mode 100644 index 44ec2af4a193fe532304da3ed1e126b47f538fa5..0000000000000000000000000000000000000000 --- a/frontend/packages/core/builder/dev-server.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2020 Baidu Inc. 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. - */ - -/* eslint-disable @typescript-eslint/no-var-requires */ - -const express = require('express'); -const mock = require('./mock'); -const icons = require('./icons'); -const netron = require('./netron'); -const wasm = require('./wasm'); - -const argv = require('yargs').nargs('port', 1).number('port').nargs('host', 1).argv; - -const app = express(); - -app.use(mock.pathname, mock.middleware()); - -app.use(icons.pathname, icons.middleware()); - -app.use(netron.pathname, express.static(netron.root, {index: false})); - -app.get(`${wasm.pathname}/${wasm.out}`, (_req, res) => { - res.type('application/wasm'); - res.sendFile(wasm.source); -}); - -app.listen(argv.port, argv.host); diff --git a/frontend/packages/core/builder/environment.js b/frontend/packages/core/builder/env.js similarity index 70% rename from frontend/packages/core/builder/environment.js rename to frontend/packages/core/builder/env.js index 9c3224fea971bb284ca0e2edd863d5fd5fdeaed9..b16da41aa6a459071c47c2af6654fbe9d662aba0 100644 --- a/frontend/packages/core/builder/environment.js +++ b/frontend/packages/core/builder/env.js @@ -14,10 +14,12 @@ * limitations under the License. */ +// cspell:words tongji + // This file is used to generate environment variables which used by the app // PUBLIC_PATH is for assets, can be set to a CDN address -process.env.SNOWPACK_PUBLIC_PATH = process.env.CDN_VERSION +export const SNOWPACK_PUBLIC_PATH = process.env.CDN_VERSION ? `https://visualdl-static.cdn.bcebos.com/assets/${process.env.CDN_VERSION}` : process.env.PUBLIC_PATH === '/' || !process.env.PUBLIC_PATH ? '' @@ -25,9 +27,9 @@ process.env.SNOWPACK_PUBLIC_PATH = process.env.CDN_VERSION // BASE_URI is for env, router and workers. Must be local address which starts with a `/` or empty string // if it is not set and PUBLIC_PATH is not a CDN address, it will be set to the same value of PUBLIC_PATH -process.env.SNOWPACK_PUBLIC_BASE_URI = - process.env.SNOWPACK_PUBLIC_PATH.startsWith('/') || process.env.PUBLIC_PATH === '' - ? process.env.SNOWPACK_PUBLIC_PATH +export const SNOWPACK_PUBLIC_BASE_URI = + SNOWPACK_PUBLIC_PATH.startsWith('/') || process.env.PUBLIC_PATH === '' + ? SNOWPACK_PUBLIC_PATH : process.env.BASE_URI === '/' || !process.env.BASE_URI ? '' : process.env.BASE_URI; @@ -35,19 +37,21 @@ process.env.SNOWPACK_PUBLIC_BASE_URI = // API_URL is for api requests // it will be set to `${BASE_URI}/api` by default // if it is set to a absolute address refer to another hostname, CORS headers must be set -process.env.SNOWPACK_PUBLIC_API_URL = process.env.API_URL || `${process.env.SNOWPACK_PUBLIC_BASE_URI}/api`; +export const SNOWPACK_PUBLIC_API_URL = process.env.API_URL || `${SNOWPACK_PUBLIC_BASE_URI}/api`; // TELEMETRY_ID is for Baidu Tongji // set to an empty string will disable telemetry -process.env.SNOWPACK_PUBLIC_TELEMETRY_ID = process.env.TELEMETRY_ID || ''; +export const SNOWPACK_PUBLIC_TELEMETRY_ID = process.env.TELEMETRY_ID || ''; // API_TOKEN_KEY is for vdl-service // if it is set, api requests will add an additional header `X-VisualDL-Instance-ID` from the token key in query string -process.env.SNOWPACK_PUBLIC_API_TOKEN_KEY = process.env.API_TOKEN_KEY || ''; +export const SNOWPACK_PUBLIC_API_TOKEN_KEY = process.env.API_TOKEN_KEY || ''; // supported languages -process.env.SNOWPACK_PUBLIC_LANGUAGES = process.env.LANGUAGES || 'en,zh'; +export const SNOWPACK_PUBLIC_LANGUAGES = process.env.LANGUAGES || 'en,zh'; // default language -process.env.SNOWPACK_PUBLIC_DEFAULT_LANGUAGE = process.env.DEFAULT_LANGUAGE || 'en'; +export const SNOWPACK_PUBLIC_DEFAULT_LANGUAGE = process.env.DEFAULT_LANGUAGE || 'en'; // theme -process.env.SNOWPACK_PUBLIC_THEME = process.env.THEME || ''; +export const SNOWPACK_PUBLIC_THEME = process.env.THEME || ''; +// demo +export const DEMO = ''; diff --git a/frontend/packages/core/builder/icons.js b/frontend/packages/core/builder/icons.js index 1c7311149bf31e8d602a5dbe8d144136c35c7ecd..82a5bf48aeee0f8509bc22db6f2a7a33e8033c2f 100644 --- a/frontend/packages/core/builder/icons.js +++ b/frontend/packages/core/builder/icons.js @@ -14,76 +14,48 @@ * limitations under the License. */ -/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/no-var-requires */ - -const path = require('path'); -const fs = require('fs/promises'); -const {default: svgr} = require('@svgr/core'); -const babel = require('@babel/core'); -const {camelCase} = require('lodash'); -const {ensureDir} = require('fs-extra'); - -const root = path.resolve(__dirname, '../public/icons'); -const pathname = '/icons'; -const dist = path.resolve(__dirname, '../dist'); -const dest = path.join(dist, pathname); - -async function transform(file, minified) { - const basename = path.basename(file, '.svg'); - let jsx = await svgr( - await fs.readFile(file, 'utf-8'), - { - icon: true, - svgProps: { - fill: 'currentColor', - className: 'vdl-icon' - } - }, - {componentName: camelCase(basename).replace(/./, w => w.toUpperCase())} - ); - jsx = jsx.replace('import * as React from "react";', 'import React from "../web_modules/react.js";'); - const result = await babel.transformAsync(jsx, { - filename: basename + '.jsx', - presets: ['@babel/preset-react'], - minified - }); - return result.code; -} - -async function build() { - await ensureDir(dest); - const files = await fs.readdir(root); +import Logger from './log.js'; +import {fileURLToPath} from 'url'; +import fs from 'fs/promises'; +import path from 'path'; + +const logger = new Logger('Icons'); + +const cwd = path.dirname(fileURLToPath(import.meta.url)); +const iconsPath = path.resolve(cwd, '../dist/__snowpack__/link/packages/icons/components'); +const reactPath = path.resolve(cwd, '../dist/__snowpack__/pkg/react.js'); +const relativePath = path.relative(iconsPath, reactPath).replace(/\\\\/g, '/'); + +export default async function build() { + logger.start(); + logger.process('building icons...'); + const files = await fs.readdir(iconsPath); + const q = []; for (const file of files) { - if (path.extname(file) === '.svg') { - const js = await transform(path.join(root, file), false); - await fs.writeFile(path.join(dest, path.basename(file, '.svg') + '.js'), js, 'utf-8'); - } + (function (f) { + const filename = path.join(iconsPath, f); + if (path.extname(filename) === '.js') { + q.push( + new Promise(async (resolve, reject) => { + try { + const content = await fs.readFile(filename, 'utf-8'); + await fs.writeFile( + filename, + content.replace('import*as React from"react";', `import React from"${relativePath}";`) + ); + resolve(); + } catch { + reject(); + } + }) + ); + } + })(file); + } + try { + await Promise.all(q); + logger.end('Icons built.'); + } catch { + logger.error('Some errors occurred when building icons.'); } - console.log('Icons copied!'); -} - -if (require.main === module) { - build(); } - -const middleware = () => { - return async (req, res) => { - const file = path.join(root, req.path.replace(/\.js$/, '.svg')); - if ((await fs.stat(file)).isFile()) { - if (req.path.endsWith('.js')) { - res.type('js'); - res.send(await transform(file, false)); - } else { - res.type(req.path.split('.').pop()); - res.send(await fs.readFile(file)); - } - } - }; -}; - -module.exports = { - middleware, - root, - pathname -}; diff --git a/frontend/packages/core/builder/inject-env.js b/frontend/packages/core/builder/inject-env.js index 11c2c7b69f1da35a937c1e39bc3ce9a243dd8ec8..361e502ddcbf96ef923e7768deffd19fd96cfa42 100644 --- a/frontend/packages/core/builder/inject-env.js +++ b/frontend/packages/core/builder/inject-env.js @@ -14,17 +14,30 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ +import Logger from './log.js'; +import {fileURLToPath} from 'url'; +import fs from 'fs/promises'; +import path from 'path'; -const path = require('path'); -const fs = require('fs/promises'); +const logger = new Logger('Inject env'); -const ENV_INJECT = 'const env = globalThis.__snowpack_env__ || {}; export default env;'; - -const dest = path.resolve(__dirname, '../dist/__snowpack__'); +const cwd = path.dirname(fileURLToPath(import.meta.url)); +const dest = path.resolve(cwd, '../dist/__snowpack__'); const envFile = path.join(dest, 'env.js'); -module.exports = async () => { +async function envInjectTemplate(env) { + return `const env = globalThis.__snowpack_env__ || {}; export const ${Object.keys(env) + .map(key => `${key}=env["${key}"]`) + .join(',')};`; +} + +export default async () => { + logger.start(); + const env = await import(envFile); + const envInject = await envInjectTemplate(env); + logger.process('renaming env.js to env.local.js...'); await fs.rename(envFile, path.join(dest, 'env.local.js')); - await fs.writeFile(envFile, ENV_INJECT, 'utf-8'); + logger.process('regenerating env.js...'); + await fs.writeFile(envFile, envInject, 'utf-8'); + logger.end('Env injected.'); }; diff --git a/frontend/packages/core/builder/inject-template.js b/frontend/packages/core/builder/inject-template.js index 36b87280ddea8abb22f3edbf901128d2349f12eb..18d5adda00cd6f139ff783d2ead28791971ad094 100644 --- a/frontend/packages/core/builder/inject-template.js +++ b/frontend/packages/core/builder/inject-template.js @@ -14,25 +14,31 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ +import {SNOWPACK_PUBLIC_BASE_URI, SNOWPACK_PUBLIC_PATH} from './env.js'; -const path = require('path'); -const fs = require('fs/promises'); -const {minify} = require('html-minifier'); +import Logger from './log.js'; +import {fileURLToPath} from 'url'; +import fs from 'fs/promises'; +import {minify} from 'html-minifier'; +import path from 'path'; -const dist = path.resolve(__dirname, '../dist'); +const logger = new Logger('Inject template'); + +const cwd = path.dirname(fileURLToPath(import.meta.url)); + +const dist = path.resolve(cwd, '../dist'); const input = path.join(dist, 'index.html'); const output = path.join(dist, 'index.tpl.html'); function envProviderTemplate(baseUri) { return ` `; } -const ENV_PROVIDER = envProviderTemplate(process.env.SNOWPACK_PUBLIC_BASE_URI); +const ENV_PROVIDER = envProviderTemplate(SNOWPACK_PUBLIC_BASE_URI); const ENV_TEMPLATE_PROVIDER = envProviderTemplate('%BASE_URI%'); function injectProvider(content, provider) { @@ -42,7 +48,7 @@ function injectProvider(content, provider) { function prependPublicPath(content, publicPath) { return content.replace(/\b(src|href)=(['"]?)([^'"\s>]*)/gi, (_matched, attr, quote, url) => { - if (/^\/(_dist_|__snowpack__|web_modules|favicon.ico)\b/.test(url)) { + if (/^\/(_dist_|__snowpack__|web_modules|favicon.ico|imported-styles.css)\b/.test(url)) { url = publicPath + url; } return attr + '=' + quote + url; @@ -63,12 +69,18 @@ async function writeMinified(file, content) { ); } -module.exports = async () => { +export default async () => { + logger.start(); + logger.process('injecting env to index.html...'); const index = await fs.readFile(input, 'utf-8'); - const indexWithPublicPath = prependPublicPath(index, process.env.SNOWPACK_PUBLIC_PATH); + const indexWithPublicPath = prependPublicPath(index, SNOWPACK_PUBLIC_PATH); const injected = injectProvider(indexWithPublicPath, ENV_PROVIDER); + logger.process('minifying index.html...'); await writeMinified(input, injected); + logger.process('injecting env to index.tpl.html...'); const template = prependPublicPath(index, '%PUBLIC_URL%'); const injectedTemplate = injectProvider(template, ENV_TEMPLATE_PROVIDER); + logger.process('minifying index.tpl.html...'); await writeMinified(output, injectedTemplate); + logger.end('Template injected.'); }; diff --git a/frontend/packages/core/builder/log.js b/frontend/packages/core/builder/log.js new file mode 100644 index 0000000000000000000000000000000000000000..ce430d2e66aedeccec0d2262818166a01e83cda4 --- /dev/null +++ b/frontend/packages/core/builder/log.js @@ -0,0 +1,75 @@ +/** + * Copyright 2020 Baidu Inc. 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. + */ + +/* eslint-disable no-console */ + +import chalk from 'chalk'; +import moment from 'moment'; + +export default class Logger { + name = ''; + startTime; + + constructor(name) { + this.name = name; + this.start(); + } + + #prepend() { + let ret = `[${moment().format('HH:mm:ss')}] `; + if (this.name) { + ret += `[${this.name}] `; + } + return chalk.gray(ret); + } + + log(msg) { + console.log(this.#prepend() + msg); + } + + info(msg) { + this.log(msg); + } + + warn(msg) { + this.log(chalk.yellow(msg)); + } + + error(msg) { + this.log(chalk.red(msg)); + } + + process(msg) { + this.warn('! ' + msg); + } + + start() { + this.startTime = new Date().valueOf(); + } + + end(msg) { + const endTime = new Date().valueOf(); + this.log( + '✔ ' + + msg + + chalk.gray(` [${Math.round(moment.duration(endTime - this.startTime).asSeconds() * 100) / 100}s]`) + ); + } + + complete(msg) { + this.log(chalk.green.bold.underline(msg)); + } +} diff --git a/frontend/packages/core/builder/netron.js b/frontend/packages/core/builder/netron.js deleted file mode 100644 index 2889e406387d098a485c1a446e55def2affcabbb..0000000000000000000000000000000000000000 --- a/frontend/packages/core/builder/netron.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2020 Baidu Inc. 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. - */ - -/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/no-var-requires */ - -const path = require('path'); -const fs = require('fs-extra'); - -const root = path.dirname(require('enhanced-resolve').sync(__dirname, '@visualdl/netron') || ''); -const pathname = '/netron'; -const dist = path.resolve(__dirname, '../dist'); -const dest = path.join(dist, pathname); - -async function build() { - await fs.ensureDir(dest); - await fs.copy(root, dest, {preserveTimestamps: true}); - console.log('Netron copied!'); -} - -if (require.main === module) { - build(); -} - -module.exports = { - root, - dest, - pathname -}; diff --git a/frontend/packages/core/builder/post-build.js b/frontend/packages/core/builder/post-build.js index 13cc65038d51bd1a4efc9b9640772b6807d42d76..c6b85f22ab44159a39e65aa3951a30f470d44afe 100644 --- a/frontend/packages/core/builder/post-build.js +++ b/frontend/packages/core/builder/post-build.js @@ -14,28 +14,41 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ +import Logger from './log.js'; +import buildIcons from './icons.js'; +import {config} from 'dotenv'; +import {fileURLToPath} from 'url'; +import injectEnv from './inject-env.js'; +import injectTemplate from './inject-template.js'; +import path from 'path'; +import pushCdn from './cdn.js'; -require('dotenv').config(); -require('./environment'); +const logger = new Logger('Post Build'); -const path = require('path'); -const pushCdn = require('./cdn'); -const injectTemplate = require('./inject-template'); -const injectEnv = require('./inject-env'); - -const dist = path.resolve(__dirname, '../dist'); +config(); +const cwd = path.dirname(fileURLToPath(import.meta.url)); +const dist = path.resolve(cwd, '../dist'); async function main() { + logger.info('Post-build Start'); + await injectTemplate(); await injectEnv(); + await buildIcons(); + if (process.env.CDN_VERSION) { await pushCdn(dist, { + version: process.env.CDN_VERSION, + endpoint: process.env.BOS_ENDPOINT, + ak: process.env.BOS_AK, + sk: process.env.BOS_SK, exclude: ['index.html', 'index.tpl.html', '__snowpack__/env.local.js'] }); } + + logger.complete('▶ Post-build Complete!'); } main(); diff --git a/frontend/packages/core/builder/wasm.js b/frontend/packages/core/builder/wasm.js deleted file mode 100644 index d84eab3db0c15025f0a6c02b6e58bc51d873d7e6..0000000000000000000000000000000000000000 --- a/frontend/packages/core/builder/wasm.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2020 Baidu Inc. 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. - */ - -/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/no-var-requires */ - -const path = require('path'); -const fs = require('fs-extra'); - -const root = path.dirname(require('enhanced-resolve').sync(__dirname, '@visualdl/wasm')); -const source = path.join(root, 'index_bg.wasm'); -const dist = path.resolve(__dirname, '../dist'); -const pathname = '/wasm'; -const out = 'visualdl.wasm'; -const dest = path.join(dist, pathname, out); - -async function build() { - await fs.ensureDir(path.join(dist, pathname)); - await fs.copy(source, dest, {preserveTimestamps: true}); - console.log('WebAssembly copied!'); -} - -if (require.main === module) { - build(); -} - -module.exports = { - dest, - source, - pathname, - out -}; diff --git a/frontend/packages/core/index.d.ts b/frontend/packages/core/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a3a960947a5f35e4d6d14ae2a7953570d9b9ca6 --- /dev/null +++ b/frontend/packages/core/index.d.ts @@ -0,0 +1,26 @@ +/** + * Copyright 2020 Baidu Inc. 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 env: { + SNOWPACK_PUBLIC_PATH: string; + SNOWPACK_PUBLIC_BASE_URI: string; + SNOWPACK_PUBLIC_API_URL: string; + SNOWPACK_PUBLIC_TELEMETRY_ID: string; + SNOWPACK_PUBLIC_API_TOKEN_KEY: string; + SNOWPACK_PUBLIC_LANGUAGES: string; + SNOWPACK_PUBLIC_DEFAULT_LANGUAGE: string; + SNOWPACK_PUBLIC_THEME: string; +}; diff --git a/frontend/packages/core/jest.config.js b/frontend/packages/core/index.js similarity index 81% rename from frontend/packages/core/jest.config.js rename to frontend/packages/core/index.js index c916531c56fbcca4534cec8ca87cfecf40e33942..fbaf7b024bdb7b06d9445bce2674f1dfd0606b83 100644 --- a/frontend/packages/core/jest.config.js +++ b/frontend/packages/core/index.js @@ -14,8 +14,6 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ +import * as env from './builder/env.js'; -module.exports = { - ...require('@snowpack/app-scripts-react/jest.config.js')() -}; +export {env}; diff --git a/frontend/packages/core/package.json b/frontend/packages/core/package.json index 3bd572989d04f201551319e4b1e6254ff3983c6c..86626ba7136dab7ea851c16fe12c1a55b3fbc279 100644 --- a/frontend/packages/core/package.json +++ b/frontend/packages/core/package.json @@ -24,37 +24,42 @@ }, "scripts": { "dev": "snowpack dev", + "dev:reload": "yarn dev --reload", "build": "snowpack build && node builder/post-build.js", - "start": "yarn dev --reload", - "test": "jest --passWithNoTests" + "snowpack": "snowpack", + "test": "web-test-runner \"test/**/*.tsx\"" }, - "main": "dist/index.html", + "type": "module", + "main": "index.js", + "types": "index.d.ts", "files": [ - "dist", - "builder/environment.js" + "index.js", + "index.d.ts", + "dist" ], "dependencies": { "@tippyjs/react": "4.2.5", + "@visualdl/icons": "2.2.0-1", "@visualdl/netron": "2.2.0-1", "@visualdl/wasm": "2.2.0-1", "bignumber.js": "9.0.1", "classnames": "2.3.1", "d3": "6.7.0", - "d3-format": "3.0.0", + "d3-format": "3.0.1", "echarts": "4.9.0", "echarts-gl": "1.1.2", "eventemitter3": "4.0.7", "file-saver": "2.0.5", - "i18next": "20.2.4", + "i18next": "20.3.1", "i18next-browser-languagedetector": "6.1.1", "i18next-fetch-backend": "3.0.0", "jszip": "3.6.0", "lodash": "4.17.21", - "mime-types": "2.1.30", + "mime-types": "2.1.31", "moment": "2.29.1", "nprogress": "0.2.0", "numeric": "1.2.6", - "polished": "4.1.2", + "polished": "4.1.3", "query-string": "7.0.0", "react": "17.0.2", "react-content-loader": "6.0.3", @@ -62,69 +67,60 @@ "react-dnd-html5-backend": "14.0.0", "react-dom": "17.0.2", "react-helmet": "6.1.0", - "react-i18next": "11.8.15", + "react-i18next": "11.10.0", "react-input-range": "1.3.0", - "react-is": "17.0.2", "react-rangeslider": "2.2.0", "react-redux": "7.2.4", "react-router-dom": "5.2.0", - "react-spinners": "0.10.6", + "react-spinners": "0.11.0", "react-table": "7.7.0", "react-table-sticky": "1.1.3", "react-toastify": "7.0.4", "redux": "4.1.0", "styled-components": "5.3.0", "swr": "0.5.6", - "three": "0.128.0", + "three": "0.129.0", "tippy.js": "6.3.1", "umap-js": "1.3.3" }, "devDependencies": { - "@babel/core": "7.14.3", - "@babel/plugin-proposal-class-properties": "7.13.0", - "@babel/preset-env": "7.14.2", - "@babel/preset-react": "7.13.13", "@baiducloud/sdk": "1.0.0-rc.28", "@simbathesailor/use-what-changed": "2.0.0", - "@snowpack/app-scripts-react": "1.12.6", - "@snowpack/plugin-dotenv": "2.0.5", - "@snowpack/plugin-optimize": "0.2.10", - "@snowpack/plugin-run-script": "2.3.0", - "@svgr/core": "5.5.0", - "@testing-library/jest-dom": "5.12.0", + "@snowpack/plugin-dotenv": "2.1.0", + "@snowpack/plugin-optimize": "0.2.13", + "@snowpack/plugin-react-refresh": "2.5.0", + "@snowpack/plugin-typescript": "1.2.1", + "@snowpack/web-test-runner-plugin": "0.2.2", "@testing-library/react": "11.2.7", - "@types/d3": "6.5.0", + "@types/chai": "4.2.18", + "@types/d3": "6.7.0", "@types/d3-format": "2.0.0", "@types/echarts": "4.9.7", "@types/file-saver": "2.0.2", - "@types/jest": "26.0.23", - "@types/loadable__component": "5.13.3", - "@types/lodash": "4.14.169", + "@types/lodash": "4.14.170", "@types/mime-types": "2.1.0", "@types/nprogress": "0.2.0", "@types/numeric": "1.2.1", - "@types/react": "17.0.6", - "@types/react-dom": "17.0.5", + "@types/react": "17.0.9", + "@types/react-dom": "17.0.6", "@types/react-helmet": "6.1.1", "@types/react-rangeslider": "2.2.3", "@types/react-redux": "7.1.16", "@types/react-router-dom": "5.1.7", - "@types/react-table": "7.7.0", + "@types/react-table": "7.7.1", "@types/snowpack-env": "2.3.3", "@types/styled-components": "5.1.9", - "@types/three": "0.128.0", + "@types/three": "0.129.1", "@visualdl/mock": "2.2.0-1", - "babel-plugin-styled-components": "1.12.0", - "dotenv": "9.0.2", + "@web/test-runner": "0.13.5", + "chai": "4.3.4", + "chalk": "4.1.1", + "dotenv": "10.0.0", "enhanced-resolve": "5.8.2", - "express": "4.17.1", - "fs-extra": "10.0.0", "html-minifier": "4.0.0", - "http-proxy-middleware": "2.0.0", - "jest": "26.6.3", - "snowpack": "2.18.5", - "typescript": "4.2.4", - "yargs": "17.0.1" + "snowpack": "3.5.5", + "snowpack-plugin-copy": "1.0.1", + "typescript": "4.3.2" }, "engines": { "node": ">=14", diff --git a/frontend/packages/core/snowpack.config.js b/frontend/packages/core/snowpack.config.js index 7e624a809a7b7412ef51746fcf3ab621c8520839..c7330dc4aa3b742dc00a316c25b4282fd71a6552 100644 --- a/frontend/packages/core/snowpack.config.js +++ b/frontend/packages/core/snowpack.config.js @@ -14,63 +14,118 @@ * limitations under the License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ +// cspell:words pnpify svgs entrypoints -require('./builder/environment'); -const mock = require('./builder/mock'); -const icons = require('./builder/icons'); -const netron = require('./builder/netron'); -const wasm = require('./builder/wasm'); +import * as env from './builder/env.js'; -const port = Number.parseInt(process.env.PORT || 3000, 10); -const devServer = { - port: port + 1, - host: '127.0.0.1' -}; +import {fileURLToPath} from 'url'; +import fs from 'fs'; +import path from 'path'; +import resolve from 'enhanced-resolve'; + +const cwd = path.dirname(fileURLToPath(import.meta.url)); +const workspaceRoot = path.resolve(cwd, '../../'); + +function isWorkspace() { + try { + const packageJson = fs.readFileSync(path.resolve(workspaceRoot, './package.json'), 'utf-8'); + return !!JSON.parse(packageJson).workspaces; + } catch { + return false; + } +} + +const iconsPath = path.dirname(resolve.sync(cwd, '@visualdl/icons')); +const netronPath = path.dirname(resolve.sync(cwd, '@visualdl/netron')); +const wasmPath = path.dirname(resolve.sync(cwd, '@visualdl/wasm')); +const mockPath = path.dirname(resolve.sync(cwd, '@visualdl/mock')); +const dest = path.resolve(cwd, './dist/__snowpack__/link/packages'); -module.exports = { - extends: '@snowpack/app-scripts-react', +/** @type {import("snowpack").SnowpackUserConfig } */ +export default { + cwd, + workspaceRoot: isWorkspace() ? workspaceRoot : undefined, + mount: { + src: '/_dist_', + public: { + url: '/', + static: true + } + }, + routes: [ + { + match: 'routes', + src: '.*', + dest: '/index.html' + } + ], + env, + alias: { + '~': './src' + }, plugins: [ + '@snowpack/plugin-react-refresh', '@snowpack/plugin-dotenv', + [ + '@snowpack/plugin-typescript', + { + /* Yarn PnP workaround: see https://www.npmjs.com/package/@snowpack/plugin-typescript */ + ...(process.versions.pnp ? {tsc: 'yarn pnpify tsc'} : {}) + } + ], [ '@snowpack/plugin-optimize', { - minifyHTML: false, // we will do it later in post-build + minifyHTML: false, preloadModules: true, - target: ['chrome79', 'firefox67', 'safari11.1', 'edge79'] // browsers support es module + preloadCSS: true, + target: ['chrome79', 'firefox67', 'safari11.1', 'edge79'] } ], [ - '@snowpack/plugin-run-script', + 'snowpack-plugin-copy', { - cmd: 'node builder/icons.js && node builder/netron.js && node builder/wasm.js', - watch: `node builder/dev-server.js --port ${devServer.port} --host ${devServer.host}`, - output: 'dashboard' + patterns: [ + { + source: ['components/*.js', 'svgs/*.svg'], + destination: path.join(dest, 'icons'), + options: { + cwd: iconsPath, + parents: true + } + }, + { + source: [path.join(netronPath, '**/*')], + destination: path.join(dest, 'netron/dist') + }, + { + source: [path.join(wasmPath, '*.{js,wasm}')], + destination: path.join(dest, 'wasm/dist') + }, + { + source: ['./{data,assets}/**/*'], + destination: path.join(dest, 'mock'), + options: { + cwd: mockPath, + parents: true + } + } + ] } ] ], - install: ['@visualdl/wasm'], - alias: { - '~': './src' - }, - proxy: { - ...[mock.pathname, icons.pathname, netron.pathname, wasm.pathname].reduce((m, pathname) => { - m[ - process.env.SNOWPACK_PUBLIC_BASE_URI + pathname - ] = `http://${devServer.host}:${devServer.port}${pathname}`; - return m; - }, {}) - }, devOptions: { hostname: process.env.HOST || 'localhost', - port + port: Number.parseInt(process.env.PORT || 3000, 10) + }, + packageOptions: { + polyfillNode: true, + knownEntrypoints: ['chai', '@testing-library/react'] }, buildOptions: { out: 'dist', - baseUrl: '/', // set it in post-build - clean: true - }, - installOptions: { - polyfillNode: true + baseUrl: '/', + clean: true, + metaUrlPath: '__snowpack__' } }; diff --git a/frontend/packages/core/src/components/GraphPage/Graph.tsx b/frontend/packages/core/src/components/GraphPage/Graph.tsx index ab06f4492489daca55f517281bfa611528dcd41a..82a1f45a580e8779fd729815e660388db2f37770 100644 --- a/frontend/packages/core/src/components/GraphPage/Graph.tsx +++ b/frontend/packages/core/src/components/GraphPage/Graph.tsx @@ -21,6 +21,7 @@ import {contentHeight, position, primaryColor, rem, size, transitionProps} from import ChartToolbox from '~/components/ChartToolbox'; import HashLoader from 'react-spinners/HashLoader'; import logo from '~/assets/images/netron.png'; +import netron from '@visualdl/netron'; import styled from 'styled-components'; import {toast} from 'react-toastify'; import useTheme from '~/hooks/useTheme'; @@ -287,7 +288,7 @@ const Graph = React.forwardRef(