未验证 提交 50fab2de 编写于 作者: P Peter Pan 提交者: GitHub

fix extrema cannot display correctly when there are multiple values with same...

fix extrema cannot display correctly when there are multiple values with same step in scalar page (#981)

* chore: update dependencies

* fix(scalar): cannot display minmax values when more then 1 point in same step

* chore: update snowpack to 3.0

* chore: update snowpack to 3.0
上级 2afd6f89
node_modules
dist
packages/icons/components
packages/wasm/dist
license-header.js
*.d.ts
......@@ -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/**/*']
}
]
};
......@@ -12,7 +12,6 @@ coverage
.next
# production
build
dist
# misc
......@@ -27,4 +26,4 @@ yarn-error.log*
lerna-debug.log*
# output
/output
output
node_modules
dist
out
wasm
output
packages/icons/components
packages/wasm/dist
license-header.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 =>
......
......@@ -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": {
......
../../LICENSE
\ No newline at end of file
# 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
```
#!/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 <command> [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();
}
});
});
{
"compilerOptions": {
"target": "es5",
"lib": [
"esnext",
"esnext.asynciterable",
"dom"
],
"moduleResolution": "node",
"esModuleInterop": true,
"declaration": true,
"skipLibCheck": true,
"outDir": "dist"
}
}
/**
* 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']
};
......@@ -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;
/**
* 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);
......@@ -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 = '';
......@@ -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
};
......@@ -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.');
};
......@@ -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 `
<script type="module">
import env from '${baseUri}/__snowpack__/env.local.js'; window.__snowpack_env__ = env;
import * as env from '${baseUri}/__snowpack__/env.local.js'; window.__snowpack_env__ = env;
</script>
`;
}
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.');
};
......@@ -16,42 +16,60 @@
/* eslint-disable no-console */
import express from 'express';
import middleware from './middleware';
import chalk from 'chalk';
import moment from 'moment';
const host = process.env.HOST || 'localhost';
const port = Number.parseInt(process.env.PORT || '', 10) || 8998;
const apiUrl = process.env.API_URL || '/api';
export default class Logger {
name = '';
startTime;
export interface Options {
host?: string;
port?: number;
apiUrl?: string;
}
constructor(name) {
this.name = name;
this.start();
}
const server = express();
async function start(options?: Options) {
const config = Object.assign({host, port, apiUrl}, options);
server.use(config.apiUrl, middleware());
const s = server.listen(config.port, config.host, () => {
process.send?.('ready');
console.log(`> Ready on http://${config.host}:${config.port}${config.apiUrl}`);
process.on('SIGINT', () => {
s.close((err: Error | undefined) => {
if (err) {
throw err;
}
process.exit(0);
});
});
});
}
#prepend() {
let ret = `[${moment().format('HH:mm:ss')}] `;
if (this.name) {
ret += `[${this.name}] `;
}
return chalk.gray(ret);
}
if (require.main === module) {
start();
}
log(msg) {
console.log(this.#prepend() + msg);
}
info(msg) {
this.log(msg);
}
warn(msg) {
this.log(chalk.yellow(msg));
}
export {start, middleware};
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));
}
}
/**
* 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
};
......@@ -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();
/**
* 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
};
/**
* 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;
};
......@@ -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};
......@@ -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",
......
......@@ -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__'
}
};
......@@ -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<GraphRef, GraphProps>(
<Content>
<iframe
ref={iframe}
src={`${PUBLIC_PATH}/netron/index.html`}
src={PUBLIC_PATH + netron}
frameBorder={0}
scrolling="no"
marginWidth={0}
......
......@@ -17,11 +17,10 @@
import React, {FunctionComponent, Suspense, useMemo} from 'react';
import type {WithStyled} from '~/utils/style';
import icons from '@visualdl/icons';
import styled from 'styled-components';
import useClassNames from '~/hooks/useClassNames';
const PUBLIC_PATH: string = import.meta.env.SNOWPACK_PUBLIC_PATH;
const Wrapper = styled.i`
speak: none;
font-style: normal;
......@@ -39,7 +38,7 @@ type IconProps = {
};
const Icon: FunctionComponent<IconProps & WithStyled> = ({type, onClick, className}) => {
const Svg = useMemo(() => React.lazy(() => import(`${PUBLIC_PATH}/icons/${type}.js`)), [type]);
const Svg = useMemo(() => React.lazy(() => icons(type)), [type]);
const classNames = useClassNames('vdl-icon', `icon-${type}`, className, [type, className]);
......
......@@ -131,9 +131,9 @@ const ScalarChart: FunctionComponent<ScalarChartProps> = ({
const getTooltipTableData = useCallback(
(series: number[]) => {
const idx = xAxisMap[xAxis];
const points = nearestPoint(smoothedDatasets ?? [], runs, idx, series[idx]).map((point, index) => ({
const points = nearestPoint(smoothedDatasets ?? [], runs, idx, series[idx]).map(point => ({
...point,
...datasetRanges?.[index]
...datasetRanges?.[runs.findIndex(run => run.label === point.run.label)]
}));
const sort = sortingMethodMap[sortingMethod];
const sorted = sort(points, series);
......
......@@ -41,17 +41,18 @@ const Wrapper = styled.div`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0.15em 0;
}
}
th {
font-size: 1.166666667em;
font-weight: bold;
padding: 0.15em 0.285714286em;
padding: 0 0.285714286em;
}
td {
padding: 0.15em 0.333333333em;
padding: 0 0.333333333em;
&.run-indicator > span {
${size(12, 12)}
......
......@@ -36,20 +36,31 @@ if (import.meta.env.MODE === 'production' && TELEMETRY_ID) {
})();
}
ReactDOM.render(
<React.StrictMode>
<GlobalStyle />
<React.Suspense fallback={<BodyLoading />}>
<Provider store={store}>
<App />
</Provider>
</React.Suspense>
</React.StrictMode>,
document.getElementById('root')
);
function render() {
ReactDOM.render(
<React.StrictMode>
<GlobalStyle />
<React.Suspense fallback={<BodyLoading />}>
<Provider store={store}>
<App />
</Provider>
</React.Suspense>
</React.StrictMode>,
document.getElementById('root')
);
// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.
// Learn more: https://www.snowpack.dev/#hot-module-replacement
if (import.meta.hot) {
import.meta.hot.accept();
// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.
// Learn more: https://www.snowpack.dev/#hot-module-replacement
if (import.meta.hot) {
import.meta.hot.accept();
}
}
if (import.meta.env.MODE === 'development' && !import.meta.env.DEMO) {
import('@visualdl/mock').then(async ({initMock}) => {
await initMock();
render();
});
} else {
render();
}
......@@ -120,7 +120,7 @@ const Graph: FunctionComponent = () => {
const {data, loading} = useRequest<BlobResponse>(files ? null : '/graph/graph');
useEffect(() => {
if (data?.data.size) {
if (data?.data?.size) {
setFiles([new File([data.data], data.filename || 'unknown_model')]);
}
}, [data]);
......
......@@ -36,13 +36,14 @@ import {
import EventEmitter from 'eventemitter3';
const BASE_URI: string = import.meta.env.SNOWPACK_PUBLIC_BASE_URI;
const env: ImportMeta['env'] = JSON.parse(JSON.stringify(import.meta.env));
export default class WebWorker implements IWorker {
private listeners: Listeners = {};
private onceListeners: Listeners = {};
private worker: Worker | null = null;
private emitter: EventEmitter | null = null;
env = import.meta.env;
env = env;
constructor(name: string) {
const workerPath = `${BASE_URI}/_dist_/worker`;
......@@ -50,7 +51,7 @@ export default class WebWorker implements IWorker {
if (checkWorkerModuleSupport()) {
this.worker = new Worker(`${workerPath}/worker.js`, {type: 'module'});
this.worker.addEventListener('message', this.listener.bind(this));
this.emit<InitializeData>('INITIALIZE', {name, env: import.meta.env});
this.emit<InitializeData>('INITIALIZE', {name, env});
} else {
this.emitter = new EventEmitter();
this.emitter.addListener('message', this.listener.bind(this));
......
......@@ -22,9 +22,7 @@ import initWasm from '@visualdl/wasm';
type FuncNames = Exclude<keyof typeof funcs, 'default'>;
const runner: Runner = async worker => {
const PUBLIC_PATH = worker.env.SNOWPACK_PUBLIC_PATH;
await initWasm(`${PUBLIC_PATH}/wasm/visualdl.wasm`);
await initWasm();
worker.on<{name: FuncNames; params: unknown[]}>('RUN', ({name, params}) => {
try {
......
......@@ -16,11 +16,23 @@
import * as React from 'react';
import App from '../src/App';
// import App from '../src/App';
import {expect} from 'chai';
import {render} from '@testing-library/react';
test('renders learn react link', () => {
const {getByText} = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
describe('<App>', () => {
// it('renders learn react link', () => {
// const {getByText} = render(<App />);
// const linkElement = getByText(/learn react/i);
// expect(document.body.contains(linkElement));
// });
it('test demo', () => {
const {getByText} = render(
<div>
<a>hello world!</a>
</div>
);
const linkElement = getByText(/hello world!/i);
expect(document.body.contains(linkElement));
});
});
......@@ -7,14 +7,26 @@
"exclude": [
"node_modules"
],
"extends": "@snowpack/app-scripts-react/tsconfig.base.json",
"compilerOptions": {
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "node",
"jsx": "preserve",
"baseUrl": "./",
"isolatedModules": true,
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"types": [
"mocha",
"snowpack-env"
],
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"importsNotUsedAsValues": "error",
"esModuleInterop": true,
"paths": {
"*": [
"web_modules/.types/*"
],
"~/*": [
"./src/*"
]
......
/**
* 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.
*/
declare module '@visualdl/netron' {
const ref: string;
export default ref;
}
......@@ -17,7 +17,36 @@
/* Use this file to declare any custom file extensions for importing */
/* Use this folder to also add/extend a package d.ts file, if needed. */
/* CSS MODULES */
declare module '*.module.css' {
const classes: {[key: string]: string};
export default classes;
}
declare module '*.module.scss' {
const classes: {[key: string]: string};
export default classes;
}
declare module '*.module.sass' {
const classes: {[key: string]: string};
export default classes;
}
declare module '*.module.less' {
const classes: {[key: string]: string};
export default classes;
}
declare module '*.module.styl' {
const classes: {[key: string]: string};
export default classes;
}
/* CSS */
declare module '*.css';
declare module '*.scss';
declare module '*.sass';
declare module '*.less';
declare module '*.styl';
/* IMAGES */
declare module '*.svg' {
const ref: string;
export default ref;
......@@ -42,7 +71,5 @@ declare module '*.png' {
const ref: string;
export default ref;
}
declare module '*.webp' {
const ref: string;
export default ref;
}
/* CUSTOM: ADD YOUR OWN HERE */
/**
* 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.
*/
import webTestRunnerPlugin from '@snowpack/web-test-runner-plugin';
process.env.NODE_ENV = 'test';
export default {
plugins: [webTestRunnerPlugin()]
};
......@@ -18,12 +18,15 @@
import IO from './io';
import type {Worker} from './types';
import {fileURLToPath} from 'url';
import getPort from 'get-port';
import mkdirp from 'mkdirp';
import path from 'path';
import rimraf from 'rimraf';
import {spawn} from 'child_process';
const cwd = path.dirname(fileURLToPath(import.meta.url));
const host = '127.0.0.1';
const publicPath = '/visualdl';
const pages = [
......@@ -39,7 +42,7 @@ const pages = [
'high-dimensional',
'hyper-parameter'
];
const dataDir = path.resolve(__dirname, '../data');
const dataDir = path.resolve(cwd, '../data');
async function start() {
rimraf.sync(dataDir);
......@@ -65,7 +68,7 @@ async function start() {
publicPath
],
{
cwd: path.resolve(__dirname, '../logs'),
cwd: path.resolve(cwd, '../logs'),
stdio: ['ignore', 'pipe', 'pipe']
}
);
......
......@@ -17,9 +17,12 @@
import {Request, Response} from 'express';
import IO from './builder/io';
import {fileURLToPath} from 'url';
import meta from './data/meta.json';
import path from 'path';
const cwd = path.dirname(fileURLToPath(import.meta.url));
function notFound(res: Response) {
res.status(404).send({
status: 1,
......@@ -42,7 +45,7 @@ export default async (req: Request, res: Response) => {
return notFound(res);
}
res.sendFile(path.join(__dirname, 'data/data', data.uri, data.filename), {
res.sendFile(path.join(cwd, 'data/data', data.uri, data.filename), {
headers: data.headers
});
};
......@@ -22,29 +22,30 @@
"url": "https://github.com/PaddlePaddle/VisualDL.git",
"directory": "frontend/packages/demo"
},
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "rimraf dist data && ts-node --script-mode builder/index.ts && tsc && cpy data dist/ --parents",
"build": "rimraf dist data && node --loader ts-node/esm --es-module-specifier-resolution=node builder/index.ts && tsc && cpy data dist/ --parents",
"test": "echo \"Error: no test specified\" && exit 0"
},
"devDependencies": {
"@types/express": "4.17.11",
"@types/express": "4.17.12",
"@types/mkdirp": "1.0.1",
"@types/node": "15.3.0",
"@types/node": "15.12.1",
"@types/node-fetch": "2.5.10",
"@types/rimraf": "3.0.0",
"cpy-cli": "3.1.1",
"get-port": "5.1.1",
"mime-types": "2.1.30",
"mime-types": "2.1.31",
"mkdirp": "1.0.4",
"node-fetch": "2.6.1",
"rimraf": "3.0.2",
"ts-node": "9.1.1",
"typescript": "4.2.4"
"ts-node": "10.0.0",
"typescript": "4.3.2"
},
"peerDependencies": {
"express": "^4.17.1"
......
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"target": "ESNext",
"module": "ESNext",
"lib": [
"esnext",
"esnext.asynciterable"
......
/**
* 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 svgs multipass
import {ensureDir, remove} from 'fs-extra';
import babel from '@babel/core';
import camelCase from 'camelcase';
import {fileURLToPath} from 'url';
import fs from 'fs/promises';
import path from 'path';
import {optimize as svgo} from 'svgo';
import svgr from '@svgr/core';
const cwd = path.dirname(fileURLToPath(import.meta.url));
const src = path.resolve(cwd, './icons');
const components = path.resolve(cwd, './components');
const svgs = path.resolve(cwd, './svgs');
async function transform(file, minified = false) {
const basename = path.basename(file, '.svg');
const jsx = await svgr.default(
await fs.readFile(file, 'utf-8'),
{
plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'],
icon: true,
svgProps: {
fill: 'currentColor',
className: 'vdl-icon'
}
},
{componentName: camelCase(basename).replace(/./, w => w.toUpperCase())}
);
const result = await babel.transformAsync(jsx, {
filename: basename + '.jsx',
presets: ['@babel/preset-react'],
minified
});
return result.code;
}
async function optimize(file) {
const result = svgo(await fs.readFile(file, 'utf-8'), {
multipass: true
});
return result.data;
}
async function build() {
await remove(components);
await ensureDir(components);
await remove(svgs);
await ensureDir(svgs);
const files = await fs.readdir(src);
for (const file of files) {
if (path.extname(file) === '.svg') {
const filePath = path.join(src, file);
const js = await transform(filePath, true);
await fs.writeFile(path.join(components, path.basename(file, '.svg') + '.js'), js, 'utf-8');
const svg = await optimize(filePath);
await fs.writeFile(path.join(svgs, file), svg, 'utf-8');
}
}
}
build();
/**
* 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.
*/
import type React from 'react';
export default function (
type: string
): Promise<{default: React.ForwardRefRenderFunction<SVGSVGElement, React.SVGAttributes<SVGSVGElement>>}>;
/**
* 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 default type => import(`./components/${type}.js`);
{
"name": "@visualdl/cli",
"name": "@visualdl/icons",
"version": "2.2.0-1",
"description": "A platform to visualize the deep learning process and result.",
"keywords": [
......@@ -20,32 +20,34 @@
"repository": {
"type": "git",
"url": "https://github.com/PaddlePaddle/VisualDL.git",
"directory": "frontend/packages/cli"
"directory": "frontend/packages/icons"
},
"scripts": {
"build": "tsc",
"dev": "cross-env NODE_ENV=development ts-node index.ts",
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 0"
},
"bin": "dist/index.js",
"types": "dist/index.d.ts",
"main": "index.js",
"module": "index.js",
"type": "module",
"types": "index.d.ts",
"files": [
"dist"
"index.js",
"index.d.ts",
"svgs",
"components"
],
"dependencies": {
"@visualdl/server": "2.2.0-1",
"open": "8.0.9",
"ora": "5.4.0",
"pm2": "4.5.6",
"yargs": "17.0.1"
"scripts": {
"build": "node build.js",
"test": "echo \"Error: no test specified\" && exit 0"
},
"devDependencies": {
"@types/node": "15.3.0",
"@types/yargs": "16.0.2",
"cross-env": "7.0.3",
"ts-node": "9.1.1",
"typescript": "4.2.4"
"@babel/core": "7.14.3",
"@babel/preset-react": "7.13.13",
"@svgr/core": "5.5.0",
"@svgr/plugin-jsx": "5.5.0",
"@svgr/plugin-svgo": "5.5.0",
"camelcase": "6.2.0",
"fs-extra": "10.0.0",
"svgo": "2.3.0"
},
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0"
},
"engines": {
"node": ">=12",
......
# VisualDL FrontEnd Mock
Mock data for development.
## Build
```bash
yarn build
```
[net]
# Testing
# batch=1
# subdivisions=1
# Training
batch=64
subdivisions=16
width=608
height=608
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
learning_rate=0.001
burn_in=1000
max_batches = 500200
policy=steps
steps=400000,450000
scales=.1,.1
[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky
# Downsample
[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=128
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=64
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=256
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=512
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
# Downsample
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
######################
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky
[convolutional]
batch_normalize=1
filters=512
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky
[convolutional]
size=1
stride=1
pad=1
filters=255
activation=linear
[yolo]
mask = 6,7,8
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
[route]
layers = -4
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[upsample]
stride=2
[route]
layers = -1, 61
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky
[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky
[convolutional]
size=1
stride=1
pad=1
filters=255
activation=linear
[yolo]
mask = 3,4,5
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
[route]
layers = -4
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[upsample]
stride=2
[route]
layers = -1, 36
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky
[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky
[convolutional]
size=1
stride=1
pad=1
filters=255
activation=linear
[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1
......@@ -14,21 +14,24 @@
* limitations under the License.
*/
import {Request, Response} from 'express';
import fs from 'fs/promises';
import audio1 from '../../assets/audio/1.mp3';
import audio2 from '../../assets/audio/2.wav';
import audio3 from '../../assets/audio/3.mp3';
import audio4 from '../../assets/audio/4.wav';
import audio5 from '../../assets/audio/5.wav';
import audio6 from '../../assets/audio/6.mp3';
import audio7 from '../../assets/audio/7.wav';
import mime from 'mime-types';
import path from 'path';
const audios = ['1.mp3', '2.wav', '3.mp3', '4.wav', '5.wav', '6.mp3', '7.wav'];
const audios = [audio1, audio2, audio3, audio4, audio5, audio6, audio7];
export default async (req: Request, res: Response) => {
const index = (+req.query.index ?? 0) % audios.length;
const file = path.resolve(__dirname, '../../assets/audio', audios[index]);
const result = await fs.readFile(file);
export default async req => {
const index = +(req.query.index ?? 0) % audios.length;
const result = await fetch(audios[index]);
const response = new Response(await result.arrayBuffer());
const contentType = mime.contentType(audios[index]);
if (contentType) {
res.setHeader('Content-Type', contentType);
response.headers.set('Content-Type', contentType);
}
return result;
return response;
};
......@@ -15,4 +15,3 @@
*/
export default ['embeddings', 'scalar', 'image', 'text', 'graph', 'pr_curve', 'roc_curve'];
// export default ['embeddings', 'scalar', 'image', 'audio', 'text', 'graph', 'histogram', 'pr_curve', 'roc_curve'];
......@@ -14,24 +14,23 @@
* limitations under the License.
*/
import {Request, Response} from 'express';
import fs from 'fs/promises';
import path from 'path';
import iris from '../../assets/embedding/metadata/metadata-edited.tsv';
import word2vec from '../../assets/embedding/metadata/word2vec_10000_200d_labels.tsv';
const metadatas = {
'Word2Vec 10K': 'word2vec_10000_200d_labels.tsv',
Iris: 'metadata-edited.tsv'
'Word2Vec 10K': word2vec,
Iris: iris
};
export default async (req: Request, res: Response) => {
export default async req => {
const metadata = metadatas[req.query.name + ''];
res.setHeader('Content-Type', 'text/tab-separated-values');
if (!metadata) {
res.status(404);
return '';
return new Response('', {status: 404});
}
const file = path.resolve(__dirname, '../../assets/embedding/metadata', metadata);
const result = await fs.readFile(file);
return result.toString('utf-8');
const result = await fetch(metadata);
return new Response(await result.text(), {
headers: {
'Content-Type': 'text/tab-separated-values'
}
});
};
......@@ -14,24 +14,23 @@
* limitations under the License.
*/
import {Request, Response} from 'express';
import fs from 'fs/promises';
import path from 'path';
import iris from '../../assets/embedding/tensor/iris_tensors.bytes';
import word2vec from '../../assets/embedding/tensor/word2vec_10000_200d_tensors.bytes';
const tensors = {
'Word2Vec 10K': 'word2vec_10000_200d_tensors.bytes',
Iris: 'iris_tensors.bytes'
'Word2Vec 10K': word2vec,
Iris: iris
};
export default async (req: Request, res: Response) => {
export default async req => {
const tensor = tensors[req.query.name + ''];
res.setHeader('Content-Type', 'application/octet-stream');
if (!tensor) {
res.status(404);
return Buffer.from(new ArrayBuffer(0));
return new Response(new ArrayBuffer(0), {status: 404});
}
const file = path.resolve(__dirname, '../../assets/embedding/tensor', tensor);
const result = await fs.readFile(file);
return result;
const result = await fetch(tensor);
return new Response(await result.arrayBuffer(), {
headers: {
'Content-Type': 'application/octet-stream'
}
});
};
......@@ -14,14 +14,18 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-var-requires */
// cSpell:disable
const {middleware} = require('@visualdl/mock');
const {createProxyMiddleware} = require('http-proxy-middleware');
import graph from '../../assets/graph/yolov3.cfg';
module.exports = {
middleware: process.env.MOCK
? () => createProxyMiddleware({target: process.env.MOCK, changeOrigin: true})
: middleware,
pathname: '/api'
export default async () => {
const result = await fetch(graph);
return new Response(await result.arrayBuffer(), {
status: 200,
headers: {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename="yolov3.cfg"'
}
});
};
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册