diff --git a/package.json b/package.json index a706d77e..8ca0c27c 100644 --- a/package.json +++ b/package.json @@ -1,139 +1,139 @@ { "name": "swh-web", - "version": "0.0.243", + "version": "0.0.244", "description": "Static assets management for swh-web", "scripts": { "build-dev": "NODE_ENV=development webpack --config ./swh/web/assets/config/webpack.config.development.js --colors", "build-test": "NODE_ENV=test webpack --config ./swh/web/assets/config/webpack.config.development.js --colors", "start-dev": "NODE_ENV=development nodemon --watch swh/web/api --watch swh/web/browse --watch swh/web/templates --watch swh/web/common --watch swh/web/settings --watch swh/web/assets/config --ext py,html,js --exec \"webpack-dev-server --info=false --config ./swh/web/assets/config/webpack.config.development.js --colors\"", "build": "NODE_ENV=production webpack --config ./swh/web/assets/config/webpack.config.production.js --colors", "mochawesome": "mochawesome-merge cypress/mochawesome/results/*.json > cypress/mochawesome/mochawesome.json && marge -o cypress/mochawesome/report cypress/mochawesome/mochawesome.json", "eslint": "eslint -c swh/web/assets/config/.eslintrc --fix swh/web/assets/** cypress/integration/** cypress/plugins/** cypress/support/**", "preinstall": "npm -v || (SWH_WEB=$PWD && cd /tmp && yarn add npm && cd node_modules/npm && yarn link && cd $SWH_WEB && yarn link npm)", "nyc-report": "nyc report --reporter=lcov" }, "repository": { "type": "git", "url": "https://forge.softwareheritage.org/source/swh-web" }, "author": "The Software Heritage developers", "license": "AGPL-3.0-or-later", "dependencies": { "@babel/runtime-corejs3": "^7.10.3", "@mdi/font": "^5.3.45", "@sentry/browser": "^5.18.1", "admin-lte": "^3.0.5", "ansi_up": "^4.0.4", "bootstrap": "^4.5.0", "chosen-js": "^1.8.7", "clipboard": "^2.0.6", "core-js": "^3.6.5", "d3": "^5.16.0", "datatables.net-responsive-bs4": "^2.2.5", "dompurify": "^2.0.12", "highlight.js": "^10.1.1", "highlightjs-line-numbers.js": "^2.8.0", "html-encoder-decoder": "^1.3.8", "iframe-resizer": "^4.2.11", "jquery": "^3.5.1", "js-cookie": "^2.2.1", "js-year-calendar": "^1.0.2", "mathjax": "^3.0.5", "mocha-junit-reporter": "^2.0.0", "notebookjs": "^0.4.2", "object-fit-images": "^3.2.4", "org": "^0.2.0", "pdfjs-dist": "^2.4.456", "popper.js": "^1.16.1", "showdown": "^1.9.1", "typeface-alegreya": "0.0.69", "typeface-alegreya-sans": "^0.0.72", "validate.js": "^0.13.1", "waypoints": "^4.0.1", "whatwg-fetch": "^3.0.0" }, "devDependencies": { "@babel/core": "^7.10.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-runtime": "^7.10.3", "@babel/preset-env": "^7.10.3", "@cypress/code-coverage": "^3.8.1", "autoprefixer": "^9.8.4", "axios": "^0.19.2", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "babel-plugin-istanbul": "^6.0.0", "bootstrap-loader": "^3.0.4", "cache-loader": "^4.1.0", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^6.0.2", "css-loader": "^3.6.0", "cypress": "^4.9.0", "cypress-multi-reporters": "^1.4.0", "ejs": "^3.1.3", "eslint": "^7.3.1", "eslint-loader": "^4.0.2", "eslint-plugin-chai-friendly": "^0.6.0", "eslint-plugin-cypress": "^2.11.1", "eslint-plugin-import": "^2.22.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "exports-loader": "^1.1.0", "expose-loader": "^1.0.0", "file-loader": "^6.0.0", "imports-loader": "^1.1.0", "istanbul-lib-coverage": "^3.0.0", "mini-css-extract-plugin": "^0.9.0", "mocha": "^8.0.1", "mochawesome": "^6.1.1", "mochawesome-merge": "^4.1.0", "mochawesome-report-generator": "^5.1.0", "node-sass": "^4.14.1", "nodemon": "^2.0.4", "nyc": "^15.1.0", "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", "postcss-normalize": "^9.0.0", "postcss-reporter": "^6.0.1", "progress-bar-webpack-plugin": "^2.1.0", "resolve-url-loader": "^3.1.1", "robotstxt-webpack-plugin": "^7.0.0", "sass-loader": "^8.0.2", "schema-utils": "^2.7.0", "script-loader": "^0.7.2", "spdx-expression-parse": "^3.0.1", "style-loader": "^1.2.1", "stylelint": "^13.6.1", "stylelint-config-standard": "^20.0.0", "terser-webpack-plugin": "^3.0.6", "url-loader": "^4.1.0", "webpack": "^4.43.0", "webpack-bundle-tracker": "^0.4.3", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.0" }, "resolutions": { "jquery": "^3.5.1" }, "browserslist": [ "cover 99.5%", "not dead" ], "postcss": { "plugins": { "autoprefixer": {}, "postcss-normalize": {} } }, "nyc": { "report-dir": "cypress/coverage", "exclude": [ "swh/web/assets/src/bundles/vendors/index.js", "swh/web/assets/src/thirdparty/**/*.js" ] }, "engines": { "node": ">=8.9.0" } } diff --git a/swh/web/assets/config/webpack.config.development.js b/swh/web/assets/config/webpack.config.development.js index 7ee4c4de..6d5666eb 100644 --- a/swh/web/assets/config/webpack.config.development.js +++ b/swh/web/assets/config/webpack.config.development.js @@ -1,433 +1,448 @@ /** * Copyright (C) 2018-2020 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ // webpack configuration for compiling static assets in development mode // import required node modules and webpack plugins const chalk = require('chalk'); const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const BundleTracker = require('webpack-bundle-tracker'); const RobotstxtPlugin = require('robotstxt-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin').CleanWebpackPlugin; const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const FixSwhSourceMapsPlugin = require('./webpack-plugins/fix-swh-source-maps-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const GenerateWebLabelsPlugin = require('./webpack-plugins/generate-weblabels-webpack-plugin'); const ProgressBarPlugin = require('progress-bar-webpack-plugin'); // are we running webpack-dev-server ? const isDevServer = process.argv.find(v => v.includes('webpack-dev-server')); // webpack-dev-server configuration const devServerPort = 3000; const devServerPublicPath = 'http://localhost:' + devServerPort + '/static/'; // set publicPath according if we are using webpack-dev-server to serve // our assets or not const publicPath = isDevServer ? devServerPublicPath : '/static/'; const nodeModules = path.resolve(__dirname, '../../../../node_modules/'); // collect all bundles we want to produce with webpack var bundles = {}; const bundlesDir = path.join(__dirname, '../src/bundles'); fs.readdirSync(bundlesDir).forEach(file => { bundles[file] = ['bundles/' + file + '/index.js']; }); // common loaders for css related assets (css, sass) let cssLoaders = [ MiniCssExtractPlugin.loader, { loader: 'cache-loader' }, { loader: 'css-loader', options: { sourceMap: !isDevServer } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: !isDevServer, plugins: [ // lint swh-web stylesheets require('stylelint')({ 'config': { 'extends': 'stylelint-config-standard', 'rules': { 'indentation': 4, 'font-family-no-missing-generic-family-keyword': null, 'no-descending-specificity': null }, 'ignoreFiles': ['node_modules/**/*.css', 'swh/web/assets/src/thirdparty/**/*.css'] } }), // automatically add vendor prefixes to css rules require('autoprefixer')(), require('postcss-normalize')(), require('postcss-reporter')({ clearReportedMessages: true }) ] } } ]; // webpack development configuration module.exports = { // use caching to speedup incremental builds cache: true, // set mode to development mode: 'development', // use eval source maps when using webpack-dev-server for quick debugging, // otherwise generate source map files (more expensive) devtool: isDevServer ? 'eval' : 'source-map', // webpack-dev-server configuration devServer: { clientLogLevel: 'warning', port: devServerPort, publicPath: devServerPublicPath, // enable to serve static assets not managed by webpack contentBase: path.resolve('./'), // we do not use hot reloading here (as a framework like React needs to be used in order to fully benefit from that feature) // and prefer to fully reload the frontend application in the browser instead hot: false, inline: true, historyApiFallback: true, headers: { 'Access-Control-Allow-Origin': '*' }, compress: true, stats: 'errors-only', overlay: { warnings: true, errors: true } }, // set entries to the bundles we want to produce entry: bundles, // assets output configuration output: { path: path.resolve('./static/'), filename: 'js/[name].[chunkhash].js', chunkFilename: 'js/[name].[chunkhash].js', publicPath: publicPath, // each bundle will be compiled as a umd module with its own namespace // in order to easily use them in django templates library: ['swh', '[name]'], libraryTarget: 'umd' }, // module resolving configuration resolve: { // alias pdfjs to its minified version alias: { 'pdfjs-dist': 'pdfjs-dist/build/pdf.min.js' }, // configure base paths for resolving modules with webpack modules: [ 'node_modules', path.resolve(__dirname, '../src') ] }, stats: 'errors-warnings', // module import configuration module: { rules: [ { // Preprocess all js files with eslint for consistent code style // and avoid bad js development practices. enforce: 'pre', test: /\.js$/, exclude: /node_modules/, use: [{ loader: 'eslint-loader', options: { configFile: path.join(__dirname, '.eslintrc'), ignorePath: path.join(__dirname, '.eslintignore'), cache: true, emitWarning: true } }] }, { // Use babel-loader in order to use es6 syntax in js files // but also advanced js features like async/await syntax. // All code get transpiled to es5 in order to be executed // in a large majority of browsers. test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'cache-loader' }, { loader: 'babel-loader', options: { presets: [ // use env babel presets to benefit from es6 syntax ['@babel/preset-env', { // Do not transform es6 module syntax to another module type // in order to benefit from dead code elimination (aka tree shaking) // when running webpack in production mode 'loose': true, 'modules': false }] ], plugins: [ // use babel transform-runtime plugin in order to use aync/await syntax ['@babel/plugin-transform-runtime', { 'regenerator': true }], // use other babel plugins to benefit from advanced js features (es2017) '@babel/plugin-syntax-dynamic-import' ], env: { test: { plugins: ['istanbul'] } } } }] }, // expose jquery to the global context as $ and jQuery when importing it { test: require.resolve('jquery'), use: [{ loader: 'expose-loader', options: { - exposes: ['$', 'jQuery'] + exposes: [ + { + globalName: '$', + override: true + }, + { + globalName: 'jQuery', + override: true + } + ] } }] }, // expose highlightjs to the global context as hljs when importing it { test: require.resolve('highlight.js'), use: [{ loader: 'expose-loader', options: { - exposes: 'hljs' + exposes: { + globalName: 'hljs', + override: true + } } }] }, { test: require.resolve('js-cookie'), use: [{ loader: 'expose-loader', options: { - exposes: 'Cookies' + exposes: { + globalName: 'Cookies', + override: true + } } }] }, // css import configuration: // - first process it with postcss // - then extract it to a dedicated file associated to each bundle { test: /\.css$/, use: cssLoaders }, // sass import configuration: // - generate css with sass-loader // - process it with postcss // - then extract it to a dedicated file associated to each bundle { test: /\.scss$/, use: cssLoaders.concat([ { loader: 'sass-loader', options: { sourceMap: !isDevServer } } ]) }, // web fonts import configuration { test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/' } }] }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/' } }] }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/' } }] }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/' } }] }, { test: /\.otf(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'fonts/' } }] }, { test: /\.png$/, use: [{ loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'img/thirdParty/' } }] } ], // tell webpack to not parse already minified files to speedup build process noParse: [path.resolve(nodeModules, 'pdfjs-dist/build/pdf.min.js'), path.resolve(nodeModules, 'mathjax/es5/tex-mml-chtml.js')] }, // webpack plugins plugins: [ // cleanup previously generated assets new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: ['**/*', '!xml', '!xml/*', '!img', '!img/*', '!img/logos', '!img/logos/*', '!img/icons', '!img/icons/*'] }), // needed in order to use django_webpack_loader new BundleTracker({ filename: './static/webpack-stats.json' }), // for generating the robots.txt file new RobotstxtPlugin({ policy: [{ userAgent: '*', disallow: '/api/' }] }), // for extracting all stylesheets in separate css files new MiniCssExtractPlugin({ filename: 'css/[name].[chunkhash].css', chunkFilename: 'css/[name].[chunkhash].css' }), // fix generated asset sourcemaps to workaround a Firefox issue new FixSwhSourceMapsPlugin(), // define some global variables accessible from js code new webpack.DefinePlugin({ __STATIC__: JSON.stringify(publicPath) }), // needed in order to use bootstrap 4.x new webpack.ProvidePlugin({ Popper: ['popper.js', 'default'], Alert: 'exports-loader?Alert!bootstrap/js/dist/alert', Button: 'exports-loader?Button!bootstrap/js/dist/button', Carousel: 'exports-loader?Carousel!bootstrap/js/dist/carousel', Collapse: 'exports-loader?Collapse!bootstrap/js/dist/collapse', Dropdown: 'exports-loader?Dropdown!bootstrap/js/dist/dropdown', Modal: 'exports-loader?Modal!bootstrap/js/dist/modal', Popover: 'exports-loader?Popover!bootstrap/js/dist/popover', Scrollspy: 'exports-loader?Scrollspy!bootstrap/js/dist/scrollspy', Tab: 'exports-loader?Tab!bootstrap/js/dist/tab', Tooltip: 'exports-loader?Tooltip!bootstrap/js/dist/tooltip', Util: 'exports-loader?Util!bootstrap/js/dist/util' }), // needed in order to use pdf.js new webpack.IgnorePlugin(/^\.\/pdf.worker.js$/), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(nodeModules, 'pdfjs-dist/build/pdf.worker.min.js'), to: path.resolve(__dirname, '../../../../static/js/') }, { from: path.resolve(nodeModules, 'mathjax/es5/output/chtml/fonts/woff-v2/**'), to: path.resolve(__dirname, '../../../../static/fonts/'), flatten: true } ] }), new GenerateWebLabelsPlugin({ outputType: 'json', exclude: ['mini-css-extract-plugin', 'bootstrap-loader'], srcReplace: { './node_modules/pdfjs-dist/build/pdf.min.js': './node_modules/pdfjs-dist/build/pdf.js', './node_modules/admin-lte/dist/js/adminlte.min.js': './node_modules/admin-lte/dist/js/adminlte.js' }, licenseOverride: { './swh/web/assets/src/thirdparty/jquery.tabSlideOut/jquery.tabSlideOut.js': { 'spdxLicenseExpression': 'GPL-3.0', 'licenseFilePath': './swh/web/assets/src/thirdparty/jquery.tabSlideOut/LICENSE' } }, additionalScripts: Object.assign( { 'js/pdf.worker.min.js': [ { 'id': 'pdfjs-dist/build/pdf.worker.js', 'path': './node_modules/pdfjs-dist/build/pdf.worker.js', 'spdxLicenseExpression': 'Apache-2.0', 'licenseFilePath': './node_modules/pdfjs-dist/LICENSE' } ], '/jsreverse/': [ { 'id': 'jsreverse', 'path': '/jsreverse/', 'spdxLicenseExpression': 'AGPL-3.0-or-later', 'licenseFilePath': './LICENSE' } ] } ) }), new ProgressBarPlugin({ format: chalk.cyan.bold('webpack build of swh-web assets') + ' [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)', width: 50 }) ], // webpack optimizations optimization: { // ensure the vendors bundle gets emitted in a single chunk splitChunks: { cacheGroups: { vendors: { test: 'vendors', chunks: 'all', name: 'vendors', enforce: true } } } }, // disable webpack warnings about bundle sizes performance: { hints: false } };