Enable development build for webui (#4496)

This commit is contained in:
liuzhe-lz 2022-01-25 14:02:10 +08:00 коммит произвёл GitHub
Родитель b4e3942669
Коммит d56e1bd945
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 290 добавлений и 262 удалений

Просмотреть файл

@ -74,6 +74,10 @@ stages:
yarn --cwd ts/webui eslint
displayName: ESLint (WebUI)
- script: |
yarn --cwd ts/webui tsc
displayName: Type Check (WebUI)
# To reduce debug cost, steps are sorted differently on each platform,
# so that a bug in any module will cause at least one platform to fail quickly.

Просмотреть файл

@ -17,7 +17,7 @@ steps:
- task: NodeTool@0
inputs:
versionSpec: 16.3.0
versionSpec: 16.13.2
displayName: Configure Node.js version
- script: |

Просмотреть файл

@ -23,7 +23,7 @@ import traceback
from zipfile import ZipFile
node_version = 'v16.3.0'
node_version = 'v16.13.2'
yarn_version = 'v1.22.10'
def _get_jupyter_lab_version():
@ -177,7 +177,10 @@ def compile_ts(release):
_print('Building web UI')
_yarn('ts/webui')
_yarn('ts/webui', 'build')
if release:
_yarn('ts/webui', 'release')
else:
_yarn('ts/webui', 'build')
_print('Building JupyterLab extension')
if release:

Просмотреть файл

@ -84,7 +84,7 @@
"http-signature": ">=1.3.6"
},
"engines": {
"node": "^16.3.0"
"node": "^16.13.2"
},
"nyc": {
"include": [

Просмотреть файл

@ -25,6 +25,7 @@
"@typescript-eslint/no-namespace": 0,
"@typescript-eslint/consistent-type-assertions": 0,
"@typescript-eslint/no-inferrable-types": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-use-before-define": [2, "nofunc"],
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }],

Просмотреть файл

@ -1,14 +0,0 @@
'use strict';
// This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/en/webpack.html
module.exports = {
process() {
return 'module.exports = {};';
},
getCacheKey() {
// The output is always the same.
return 'cssTransform';
},
};

Просмотреть файл

@ -1,31 +0,0 @@
'use strict';
const path = require('path');
// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html
module.exports = {
process(src, filename) {
const assetFilename = JSON.stringify(path.basename(filename));
if (filename.match(/\.svg$/)) {
return `const React = require('react');
module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: React.forwardRef((props, ref) => ({
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: ref,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
})),
};`;
}
return `module.exports = ${assetFilename};`;
},
};

Просмотреть файл

@ -91,7 +91,8 @@
"proxy": "http://localhost:12138",
"scripts": {
"start": "node --max-old-space-size=3072 scripts/start.js",
"build": "node --max-old-space-size=3072 scripts/build.js",
"build": "node --max-old-space-size=3072 scripts/developmentBuild.js",
"release": "node --max-old-space-size=3072 scripts/productionBuild.js",
"test": "node --max-old-space-size=3072 scripts/test.js",
"eslint": "npx eslint ./ --ext .tsx,.ts",
"stylelint": "npx stylelint **/*{.css,.scss}",

Просмотреть файл

@ -1,189 +0,0 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const path = require('path');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const configFactory = require('../config/webpack.config');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('./formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// Generate configuration
const config = configFactory('production');
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
return measureFileSizesBeforeBuild(paths.appBuild);
})
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
)
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
}
console.log('Creating an optimized production build...');
const compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
if (err) {
if (!err.message) {
return reject(err);
}
messages = formatWebpackMessages({
errors: [err.message],
warnings: [],
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
);
}
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
if (
process.env.CI &&
(typeof process.env.CI !== 'string' ||
process.env.CI.toLowerCase() !== 'false') &&
messages.warnings.length
) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
return resolve({
stats,
previousFileSizes,
warnings: messages.warnings,
});
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,84 @@
'use strict';
const resolve = require('resolve');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const getClientEnvironment = require('./env');
const paths = require('./paths');
const publicPath = './';
const publicUrl = '.';
const env = getClientEnvironment(publicUrl);
const config = {
mode: 'development',
devtool: 'cheap-module-source-map',
entry: [ paths.appIndexJs ],
output: {
path: paths.appBuild,
pathinfo: true,
filename: 'static/js/bundle.js',
chunkFilename: 'static/js/[name].chunk.js',
publicPath: publicPath,
},
optimization: { minimize: false },
resolve: {
modules: [ 'node_modules' ],
extensions: paths.moduleFileExtensions.map(ext => `.${ext}`),
},
module: {
strictExportPresence: true,
rules: [
{
oneOf: [
{
test: [ /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/ ],
loader: require.resolve('url-loader'),
options: { limit: 10000, name: 'static/media/[name].[hash:8].[ext]' },
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: { cacheDirectory: true, cacheCompression: false, compact: false },
},
{
test: /\.css$/,
use: [
require.resolve('style-loader'),
require.resolve('css-loader'),
],
sideEffects: true,
},
{
test: /\.(scss|sass)$/,
use: [
require.resolve('style-loader'),
require.resolve('css-loader'),
require.resolve('sass-loader'),
],
sideEffects: true,
},
{
loader: require.resolve('file-loader'),
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: { name: 'static/media/[name].[hash:8].[ext]' },
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({ inject: true, template: paths.appHtml }),
new MonacoWebpackPlugin({ languages: [ 'json' ] }),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
new ModuleNotFoundPlugin(paths.appPath),
],
performance: { hints: false },
}
module.exports = () => config;

Просмотреть файл

@ -0,0 +1,78 @@
'use strict';
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
process.on('unhandledRejection', err => { throw err; });
require('./config/env');
const fsPromises = require('fs/promises');
const fsExtra = require('fs-extra');
const chalk = require('react-dev-utils/chalk');
const printBuildError = require('react-dev-utils/printBuildError');
const webpack = require('webpack');
const configFactory = require('./config/webpack.config.dev');
const paths = require('./config/paths');
const formatWebpackMessages = require('./formatWebpackMessages');
async function main() {
await fsPromises.rm(paths.appBuild, { force: true, recursive: true });
await fsExtra.copy(paths.appPublic, paths.appBuild, { // fs.cp() seems not work
dereference: true,
filter: file => (file !== paths.appHtml)
});
console.log('Creating an development build...');
const config = configFactory('production');
const compiler = webpack(config);
const result = await asyncRun(compiler);
const { errors, warnings } = formatWebpackMessages(result);
if (errors.length) {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(new Error(errors[0]));
process.exit(1);
}
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
if (isCi()) {
process.exit(1);
}
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
}
function asyncRun(compiler) {
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
resolve({ err, stats });
});
});
}
function isCi() {
if (!process.env.CI) {
return false;
}
if (typeof process.env.CI !== 'string') {
return true;
}
return process.env.CI.toLowerCase() !== 'false';
}
main().catch(err => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
});

Просмотреть файл

@ -116,15 +116,22 @@ function formatMessage(message) {
return message.trim();
}
function formatWebpackMessages(json) {
const formattedErrors = json.errors.map(formatMessage);
const formattedWarnings = json.warnings.map(formatMessage);
const result = { errors: formattedErrors, warnings: formattedWarnings };
if (result.errors.some(isLikelyASyntaxError)) {
// If there are any syntax errors, show just them.
result.errors = result.errors.filter(isLikelyASyntaxError);
}
return result;
function formatWebpackMessages({ err, stats }) {
let json;
if (err) {
json = { errors: [ err.message ?? 'Unknown error' ], warnings: [ ] };
} else {
json = stats.toJson({ all: false, warnings: true, errors: true });
}
const formattedErrors = json.errors.map(formatMessage);
const formattedWarnings = json.warnings.map(formatMessage);
const result = { errors: formattedErrors, warnings: formattedWarnings };
if (result.errors.some(isLikelyASyntaxError)) {
// If there are any syntax errors, show just them.
result.errors = result.errors.filter(isLikelyASyntaxError);
}
return result;
}
module.exports = formatWebpackMessages;

Просмотреть файл

@ -0,0 +1,93 @@
'use strict';
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
process.on('unhandledRejection', err => { throw err; });
require('./config/env');
const fsPromises = require('fs/promises');
const fsExtra = require('fs-extra');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
const chalk = require('react-dev-utils/chalk');
const printBuildError = require('react-dev-utils/printBuildError');
const webpack = require('webpack');
const formatWebpackMessages = require('./formatWebpackMessages');
const configFactory = require('./config/webpack.config');
const paths = require('./config/paths');
async function main() {
await checkBrowsers(paths.appPath, process.stdout.isTTY);
const previousFileSizes = await FileSizeReporter.measureFileSizesBeforeBuild(paths.appBuild);
await fsPromises.rm(paths.appBuild, { force: true, recursive: true });
await fsExtra.copy(paths.appPublic, paths.appBuild, { // fs.cp() seems not work
dereference: true,
filter: file => (file !== paths.appHtml)
});
console.log('Creating an optimized production build...');
const config = configFactory('production');
const compiler = webpack(config);
const result = await asyncRun(compiler);
const { errors, warnings } = formatWebpackMessages(result);
if (errors.length) {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(new Error(errors[0]));
process.exit(1);
}
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
if (isCi()) {
process.exit(1);
}
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
FileSizeReporter.printFileSizesAfterBuild(
result.stats,
previousFileSizes,
paths.appBuild,
512 * 1024, // WARN_AFTER_BUNDLE_GZIP_SIZE
1024 * 1024 // WARN_AFTER_CHUNK_GZIP_SIZE
);
}
function asyncRun(compiler) {
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
resolve({ err, stats });
});
});
}
function isCi() {
if (!process.env.CI) {
return false;
}
if (typeof process.env.CI !== 'string') {
return true;
}
return process.env.CI.toLowerCase() !== 'false';
}
main().catch(err => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
});

Просмотреть файл

@ -12,7 +12,7 @@ process.on('unhandledRejection', err => {
});
// Ensure environment variables are read.
require('../config/env');
require('./config/env');
const fs = require('fs');
@ -28,9 +28,9 @@ const {
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const paths = require('./config/paths');
const configFactory = require('./config/webpack.config');
const createDevServerConfig = require('./config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;

Просмотреть файл

@ -13,7 +13,7 @@ process.on('unhandledRejection', err => {
});
// Ensure environment variables are read.
require('../config/env');
require('./config/env');
const jest = require('jest');

Просмотреть файл

@ -1,9 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});