Sfoglia il codice sorgente

migrate to webpack 4 (#39)

tags/7.5
Stas Vilchik 6 anni fa
parent
commit
df91a4ec34

+ 55
- 0
server/sonar-web/config/InterpolateHtmlPlugin.js Vedi File

@@ -0,0 +1,55 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

// Copied from https://github.com/facebook/create-react-app/blob/master/packages/react-dev-utils/InterpolateHtmlPlugin.js

// This Webpack plugin lets us interpolate custom variables into `index.html`.
// Usage: `new InterpolateHtmlPlugin({ 'MY_VARIABLE': 42 })`
// Then, you can use %MY_VARIABLE% in your `index.html`.

// It works in tandem with HtmlWebpackPlugin.
// Learn more about creating plugins like this:
// https://github.com/ampedandwired/html-webpack-plugin#events

'use strict';
const escapeStringRegexp = require('escape-string-regexp');

class InterpolateHtmlPlugin {
constructor(replacements) {
this.replacements = replacements;
}

apply(compiler) {
compiler.hooks.compilation.tap('InterpolateHtmlPlugin', compilation => {
compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap('InterpolateHtmlPlugin', data => {
// Run HTML through a series of user-specified string replacements.
Object.keys(this.replacements).forEach(key => {
const value = this.replacements[key];
data.html = data.html.replace(
new RegExp('%' + escapeStringRegexp(key) + '%', 'g'),
value
);
});
});
});
}
}

module.exports = InterpolateHtmlPlugin;

+ 3
- 36
server/sonar-web/config/paths.js Vedi File

@@ -18,42 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
const path = require('path');
const fs = require('fs');

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebookincubator/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd());
function resolveApp(relativePath) {
return path.resolve(appDirectory, relativePath);
}

// We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebookincubator/create-react-app/issues/253.

// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders

// We will export `nodePaths` as an array of absolute paths.
// It will then be used by Webpack configs.
// Jest doesn’t need this because it already handles `NODE_PATH` out of the box.

const nodePaths = (process.env.NODE_PATH || '')
.split(process.platform === 'win32' ? ';' : ':')
.filter(Boolean)
.map(resolveApp);

// config after eject: we're in ./config/
module.exports = {
appBuild: resolveApp('build/webapp'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src/main/js'),
jsBuild: resolveApp('build/webapp/js'),
cssBuild: resolveApp('build/webapp/css'),
htmlBuild: resolveApp('build/webapp/index.html'),
appNodeModules: resolveApp('node_modules'),
ownNodeModules: resolveApp('node_modules'),
nodePaths
appBuild: path.join(__dirname, '../build/webapp'),
appPublic: path.join(__dirname, '../public'),
appHtml: path.join(__dirname, '../public/index.html')
};

+ 78
- 89
server/sonar-web/config/webpack.config.js Vedi File

@@ -1,20 +1,41 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/no-extraneous-dependencies */
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const webpack = require('webpack');
const InterpolateHtmlPlugin = require('./InterpolateHtmlPlugin');
const paths = require('./paths');

const cssMinimizeOptions = {
discardComments: { removeAll: true }
};

const cssLoader = ({ production, fast }) => ({
const cssLoader = ({ production }) => ({
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: production && !fast && cssMinimizeOptions,
minimize: production && cssMinimizeOptions,
url: false
}
});
@@ -33,35 +54,20 @@ const postcssLoader = () => ({
}
});

module.exports = ({ production = true, fast = false }) => ({
bail: production,

devtool: production ? (fast ? false : 'source-map') : 'cheap-module-source-map',
module.exports = ({ production = true }) => ({
mode: production ? 'production' : 'development',
devtool: production ? 'source-map' : 'cheap-module-source-map',
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ['.ts', '.tsx', '.js', '.json']
},
entry: {
vendor: [
!production && require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
!production && require.resolve('react-error-overlay'),
'lodash',
'd3-array',
'd3-hierarchy',
'd3-scale',
'd3-selection',
'd3-shape',
'react',
'react-dom'
].filter(Boolean),

app: [
'./src/main/js/app/utils/setPublicPath.js',
'./src/main/js/app/index.js',
'./src/main/js/components/SourceViewer/SourceViewer'
]
},
entry: [
!production && require.resolve('react-dev-utils/webpackHotDevClient'),
require.resolve('./polyfills'),
!production && require.resolve('react-error-overlay'),
'./src/main/js/app/utils/setPublicPath.js',
'./src/main/js/app/index.js'
].filter(Boolean),
output: {
path: paths.appBuild,
pathinfo: !production,
@@ -71,88 +77,71 @@ module.exports = ({ production = true, fast = false }) => ({
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /(node_modules|libs)/
},
{
test: /\.tsx?$/,
test: /(\.js$|\.ts(x?)$)/,
exclude: /(node_modules|libs)/,
use: [
{ loader: 'babel-loader' },
{
loader: 'awesome-typescript-loader',
options: {
transpileOnly: true,
useBabel: true,
useCache: true
}
loader: 'ts-loader',
options: { transpileOnly: true }
}
]
},
production
? {
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [cssLoader({ production, fast }), postcssLoader()]
})
}
: {
test: /\.css$/,
use: ['style-loader', cssLoader({ production, fast }), postcssLoader()]
},
{
test: /\.css$/,
use: [
production ? MiniCssExtractPlugin.loader : 'style-loader',
cssLoader({ production }),
postcssLoader()
].filter(Boolean)
},
{ test: require.resolve('lodash'), loader: 'expose-loader?_' },
{ test: require.resolve('react'), loader: 'expose-loader?React' },
{ test: require.resolve('react-dom'), loader: 'expose-loader?ReactDOM' }
].filter(Boolean)
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor' }),
// `allowExternal: true` to remove files outside of the current dir
production && new CleanWebpackPlugin([paths.appBuild], { allowExternal: true, verbose: false }),

production &&
new ExtractTextPlugin({
filename: production ? 'css/sonar.[chunkhash:8].css' : 'css/sonar.css'
}),
new CopyWebpackPlugin([
{
from: paths.appPublic,
to: paths.appBuild,
ignore: [paths.appHtml]
}
]),

!production && new InterpolateHtmlPlugin({ WEB_CONTEXT: '' }),
production &&
new MiniCssExtractPlugin({
filename: 'css/[name].[chunkhash:8].css',
chunkFilename: 'css/[name].[chunkhash:8].chunk.css'
}),

new HtmlWebpackPlugin({
inject: false,
template: paths.appHtml,
minify: production &&
!fast && {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
minify: production && {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}),

new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(production ? 'production' : 'development')
}),

production &&
!fast &&
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: { screw_ie8: true, warnings: false },
mangle: { screw_ie8: true },
output: { comments: false, screw_ie8: true }
}),
// keep `InterpolateHtmlPlugin` after `HtmlWebpackPlugin`
!production && new InterpolateHtmlPlugin({ WEB_CONTEXT: '' }),

!production && new webpack.HotModuleReplacementPlugin()
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
optimization: {
splitChunks: { chunks: 'all' }
}
});

+ 15
- 14
server/sonar-web/package.json Vedi File

@@ -57,10 +57,9 @@
"@types/react-router": "3.0.13",
"@types/react-select": "1.0.59",
"autoprefixer": "7.1.6",
"awesome-typescript-loader": "3.2.3",
"babel-core": "6.26.0",
"babel-jest": "22.0.6",
"babel-loader": "7.1.2",
"babel-loader": "7.1.4",
"babel-plugin-dynamic-import-node": "1.1.0",
"babel-plugin-syntax-dynamic-import": "6.18.0",
"babel-plugin-transform-class-properties": "^6.22.0",
@@ -71,40 +70,42 @@
"babel-preset-env": "1.6.1",
"babel-preset-react": "^6.22.0",
"chalk": "2.3.0",
"css-loader": "0.28.7",
"clean-webpack-plugin": "0.1.19",
"copy-webpack-plugin": "4.5.1",
"css-loader": "0.28.11",
"enzyme": "3.3.0",
"enzyme-adapter-react-16": "1.1.1",
"enzyme-to-json": "3.3.0",
"escape-string-regexp": "1.0.5",
"eslint": "4.17.0",
"eslint-plugin-import": "2.8.0",
"eslint-plugin-jsx-a11y": "6.0.2",
"eslint-plugin-promise": "3.6.0",
"eslint-plugin-react": "7.6.1",
"expose-loader": "0.7.3",
"extract-text-webpack-plugin": "3.0.1",
"expose-loader": "0.7.5",
"flow-bin": "^0.52.0",
"fs-extra": "0.30.0",
"html-webpack-plugin": "2.30.1",
"html-webpack-plugin": "3.0.6",
"jest": "22.0.6",
"lint-staged": "4.3.0",
"mini-css-extract-plugin": "0.2.0",
"postcss-calc": "6.0.1",
"postcss-custom-properties": "6.2.0",
"postcss-loader": "2.0.8",
"postcss-loader": "2.1.1",
"prettier": "1.10.2",
"react-dev-utils": "5.0.0",
"react-error-overlay": "1.0.7",
"react-test-renderer": "16.2.0",
"rimraf": "2.6.2",
"style-loader": "0.19.0",
"style-loader": "0.20.3",
"ts-jest": "22.0.1",
"ts-loader": "4.1.0",
"typescript": "2.7.1",
"typescript-eslint-parser": "13.0.0",
"webpack": "3.8.1",
"webpack-bundle-analyzer": "2.9.0",
"webpack-dev-server": "2.9.3"
"webpack": "4.1.1",
"webpack-bundle-analyzer": "2.11.1",
"webpack-dev-server": "3.1.1"
},
"scripts": {
"start": "node scripts/start.js",
"build-fast": "node scripts/build.js --fast",
"build": "node scripts/build.js",
"test": "node scripts/test.js",
"coverage": "npm test -- --coverage",

+ 13
- 40
server/sonar-web/scripts/build.js Vedi File

@@ -21,34 +21,15 @@
process.env.NODE_ENV = 'production';

const chalk = require('chalk');
const fs = require('fs-extra');
const rimrafSync = require('rimraf').sync;
const webpack = require('webpack');
const paths = require('../config/paths');
const sortBy = require('lodash/sortBy');
const formatSize = require('./utils/formatSize');
const getConfig = require('../config/webpack.config');

const fast = process.argv.some(arg => arg.indexOf('--fast') > -1);

const config = getConfig({ fast, production: true });

function clean() {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
console.log(chalk.cyan.bold('Cleaning output directories and files...'));

console.log(paths.appBuild + '/*');
rimrafSync(paths.appBuild + '/*');

console.log();
}
const config = getConfig({ production: true });

function build() {
if (fast) {
console.log(chalk.magenta.bold('Running fast build...'));
} else {
console.log(chalk.cyan.bold('Creating optimized production build...'));
}
console.log(chalk.cyan.bold('Creating optimized production build...'));
console.log();

webpack(config, (err, stats) => {
@@ -65,16 +46,17 @@ function build() {
}

const jsonStats = stats.toJson();
const withoutSourceMaps = jsonStats.assets.filter(asset => !asset.name.endsWith('.map'));

console.log('Assets:');
const assets = jsonStats.assets.slice();
assets.sort((a, b) => b.size - a.size);
assets.forEach(asset => {
let sizeLabel = formatSize(asset.size);
const leftPadding = ' '.repeat(Math.max(0, 8 - sizeLabel.length));
sizeLabel = leftPadding + sizeLabel;
console.log('', chalk.yellow(sizeLabel), asset.name);
});
console.log(`Biggest assets (${withoutSourceMaps.length} total):`);
sortBy(withoutSourceMaps, asset => -asset.size)
.slice(0, 5)
.forEach(asset => {
let sizeLabel = formatSize(asset.size);
const leftPadding = ' '.repeat(Math.max(0, 8 - sizeLabel.length));
sizeLabel = leftPadding + sizeLabel;
console.log('', chalk.yellow(sizeLabel), asset.name);
});
console.log();

const seconds = jsonStats.time / 1000;
@@ -85,13 +67,4 @@ function build() {
});
}

function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml
});
}

clean();
build();
copyPublicFolder();

+ 1
- 1
server/sonar-web/scripts/utils/formatSize.js Vedi File

@@ -24,5 +24,5 @@ module.exports = function(bytes) {
const k = 1000; // or 1024 for binary
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
return parseFloat((bytes / Math.pow(k, i)).toFixed(0)) + ' ' + sizes[i];
};

+ 2
- 1
server/sonar-web/tsconfig.json Vedi File

@@ -12,7 +12,8 @@
"lib": ["es2017", "dom"],
"module": "esnext",
"moduleResolution": "node",
"typeRoots": ["./src/main/js/typings", "./node_modules/@types"]
"typeRoots": ["./src/main/js/typings", "./node_modules/@types"],
"sourceMap": true
},
"include": ["./src/main/js/**/*"]
}

+ 840
- 412
server/sonar-web/yarn.lock
File diff soppresso perché troppo grande
Vedi File


+ 1
- 1
sonar-application/build.gradle Vedi File

@@ -123,7 +123,7 @@ zip.doFirst {
}
// Check the size of the archive
zip.doLast {
def minLength = 155000000
def minLength = 150000000
def maxLength = 170000000
def length = new File(distsDir, archiveName).length()
if (length < minLength)

Loading…
Annulla
Salva