diff --git a/assets/config/webpack-plugins/dump-highlightjs-languages-data-plugin.js b/assets/config/webpack-plugins/dump-highlightjs-languages-data-plugin.js index df7f60f4..7c811fdc 100644 --- a/assets/config/webpack-plugins/dump-highlightjs-languages-data-plugin.js +++ b/assets/config/webpack-plugins/dump-highlightjs-languages-data-plugin.js @@ -1,46 +1,85 @@ /** - * Copyright (C) 2020 The Software Heritage developers + * Copyright (C) 2020-2022 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 */ 'use strict'; const fs = require('fs'); const path = require('path'); -const hljs = require('highlight.js'); -var stringify = require('json-stable-stringify'); +global.hljs = require('highlight.js'); +require('highlightjs-4d/dist/4d.min'); +require('highlightjs-sap-abap/dist/abap.min'); +require('highlightjs-alan/dist/alan.min'); +require('highlightjs-blade/dist/blade.min'); +require('highlightjs-chaos/dist/chaos.min'); +require('highlightjs-chapel/dist/chapel.min'); +require('highlightjs-cshtml-razor/dist/cshtml-razor.min'); +require('highlightjs-cpcdos/dist/cpc-highlight.min'); +require('highlightjs-cypher/dist/cypher.min'); +require('highlightjs-dafny/dist/dafny.min'); +require('highlightjs-dylan/dist/dylan.min'); +require('highlightjs-eta/dist/eta.min'); +require('highlightjs-extempore/dist/extempore.min'); +require('highlightjs-gdscript/dist/gdscript.min'); +require('highlightjs-gf/dist/gf.min'); +require('highlightjs-graphql')(hljs); +require('highlightjs-gsql/dist/gsql.min'); +require('highlightjs-hlsl/dist/hlsl.min'); +require('highlightjs-jolie/dist/jolie.min'); +hljs.registerLanguage('lean', require('highlightjs-lean')); +hljs.registerLanguage('lox', require('highlightjs-lox')); +require('highlightjs-mirc/mirc')(hljs); +hljs.registerLanguage('modelica', require('highlightjs-modelica')); +require('highlightjs-never/dist/never.min'); +hljs.registerLanguage('octave', require('highlightjs-octave').default); +require('highlightjs-oz/dist/oz.min'); +require('hightlightjs-papyrus/dist/papyrus.min'); +require('highlightjs-qsharp/dist/qsharp.min'); +require('highlightjs-redbol/dist/redbol.min'); +require('highlightjs-robot')(hljs); +require('highlightjs-robots-txt/dist/robots-txt.min'); +require('highlightjs-rpm-specfile')(hljs); +require('highlightjs-solidity/dist/solidity.min'); +require('highlightjs-svelte/dist/svelte.min'); +require('highlightjs-terraform')(hljs); +require('highlightjs-xsharp/dist/xsharp.min'); +require('highlightjs-zenscript/dist/zenscript.min'); +require('highlightjs-zig/dist/zig.min'); + +const stringify = require('json-stable-stringify'); // Simple webpack plugin to dump JSON data related to the set of // programming languages supported by the highlightjs library. // The JSON file is saved into swh-web static folder and will // be consumed by the backend django application. class DumpHighlightjsLanguagesDataPlugin { apply(compiler) { compiler.hooks.done.tap('DumpHighlightjsLanguagesDataPlugin', statsObj => { const outputPath = statsObj.compilation.compiler.outputPath; const hljsDataFile = path.join(outputPath, 'json/highlightjs-languages.json'); const languages = hljs.listLanguages(); const hljsLanguagesData = {'languages': languages}; const languageAliases = {}; for (const language of languages) { const languageData = hljs.getLanguage(language); if (!languageData.hasOwnProperty('aliases')) { continue; } for (const alias of languageData.aliases) { languageAliases[alias] = language; languageAliases[alias.toLowerCase()] = language; } } hljsLanguagesData['languages_aliases'] = languageAliases; fs.writeFileSync(hljsDataFile, stringify(hljsLanguagesData, {space: 4})); }); } }; module.exports = DumpHighlightjsLanguagesDataPlugin; diff --git a/assets/config/webpack.config.development.js b/assets/config/webpack.config.development.js index a83d09da..1b64e368 100644 --- a/assets/config/webpack.config.development.js +++ b/assets/config/webpack.config.development.js @@ -1,446 +1,462 @@ /** * Copyright (C) 2018-2021 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'); const DumpHighlightjsLanguagesDataPlugin = require('./webpack-plugins/dump-highlightjs-languages-data-plugin'); const ESLintPlugin = require('eslint-webpack-plugin'); // are we running webpack-dev-server ? const isDevServer = process.argv.find(v => v.includes('serve')) !== undefined; // 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) const cssLoaders = [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: !isDevServer } }, { loader: 'postcss-loader', options: { sourceMap: !isDevServer, postcssOptions: { plugins: [ // lint swh-web stylesheets ['stylelint', { 'config': { 'extends': 'stylelint-config-standard', 'rules': { 'indentation': 4, 'font-family-no-missing-generic-family-keyword': null, 'no-descending-specificity': null, 'selector-class-pattern': null }, 'ignoreFiles': ['node_modules/**/*.css', 'assets/src/thirdparty/**/*.css'] } }], // automatically add vendor prefixes to css rules 'autoprefixer', 'postcss-normalize', ['postcss-reporter', { clearReportedMessages: true }] ] } } } ]; // webpack development configuration module.exports = { // use caching to speedup incremental builds cache: { type: 'filesystem' }, // set mode to development mode: 'development', // workaround for https://github.com/webpack/webpack-dev-server/issues/2758 target: process.env.NODE_ENV === 'development' ? 'web' : 'browserslist', // 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: { client: { logging: 'warn', overlay: { warnings: true, errors: true }, progress: true }, devMiddleware: { publicPath: devServerPublicPath, stats: 'errors-only' }, host: '0.0.0.0', port: devServerPort, // enable to serve static assets not managed by webpack static: { directory: path.resolve('./'), watch: { ignored: /(node_modules|.tox|.mypy)/ } }, // 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, historyApiFallback: true, headers: { 'Access-Control-Allow-Origin': '*' } }, // set entries to the bundles we want to produce entry: bundles, // assets output configuration output: { path: path.resolve('./static/'), filename: 'js/[name].[contenthash].js', chunkFilename: 'js/[name].[contenthash].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', + snapshot: { + // fix webpack warning related to missing package.json file + managedPaths: [/^highlightjs-/] + }, // module import configuration module: { rules: [ { // 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: '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'] } } } }] }, { test: /\.ejs$/, use: [{ loader: 'ejs-compiled-loader', options: { htmlmin: true, htmlminOptions: { removeComments: true } } }] }, // expose jquery to the global context as $ and jQuery when importing it { test: require.resolve('jquery'), use: [{ loader: 'expose-loader', options: { 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: { globalName: 'hljs', 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+)?$/, type: 'asset/resource', generator: { filename: 'fonts/[name][ext][query]' } }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource', generator: { filename: 'fonts/[name][ext][query]' } }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource', generator: { filename: 'fonts/[name][ext][query]' } }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource', generator: { filename: 'fonts/[name][ext][query]' } }, { test: /\.otf(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource', generator: { filename: 'fonts/[name][ext][query]' } }, { test: /\.png$/, type: 'asset/resource', generator: { filename: 'img/thirdParty/[name][ext][query]' } }, { test: /\.ya?ml$/, type: 'json', use: 'yaml-loader' } ], // 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/*', '!json', '!json/*'] }), // 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].[contenthash].css', chunkFilename: 'css/[name].[contenthash].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({resourceRegExp: /^\.\/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/[name][ext]') } ] }), 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: { './assets/src/thirdparty/jquery.tabSlideOut/jquery.tabSlideOut.js': { 'spdxLicenseExpression': 'GPL-3.0', 'licenseFilePath': './assets/src/thirdparty/jquery.tabSlideOut/LICENSE' + }, + './node_modules/highlightjs-chapel/dist/chapel.min.js': { + 'spdxLicenseExpression': 'BSD-3-Clause', + 'licenseFilePath': './node_modules/highlightjs-chapel/LICENSE' + }, + './node_modules/highlightjs-mirc/mirc.js': { + 'spdxLicenseExpression': 'MIT', + 'licenseFilePath': './node_modules/highlightjs-mirc/LICENSE' + }, + './node_modules/highlightjs-never/dist/never.min.js': { + 'spdxLicenseExpression': 'MIT', + 'licenseFilePath': './node_modules/highlightjs-never/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' } ], 'https://piwik.inria.fr/matomo.js': [ { 'id': 'matomo.js', 'path': 'https://github.com/matomo-org/matomo/blob/master/js/piwik.js', 'spdxLicenseExpression': 'BSD-3-Clause', 'licenseFilePath': 'https://github.com/matomo-org/matomo/blob/master/js/LICENSE.txt' } ] } ) }), new ProgressBarPlugin({ format: chalk.cyan.bold('webpack build of swh-web assets') + ' [:bar] ' + chalk.green.bold(':percent') + ' (:elapsed seconds)', width: 50 }), new DumpHighlightjsLanguagesDataPlugin(), // Process all js files with eslint for consistent code style // and avoid bad js development practices. new ESLintPlugin({ overrideConfigFile: path.join(__dirname, '.eslintrc'), ignorePath: path.join(__dirname, '.eslintignore'), cache: true, emitWarning: true }) ], // webpack optimizations optimization: { // ensure the vendors bundle gets emitted in a single chunk splitChunks: { cacheGroups: { defaultVendors: { test: 'vendors', chunks: 'all', name: 'vendors', enforce: true } } } }, // disable webpack warnings about bundle sizes performance: { hints: false } }; diff --git a/assets/src/bundles/revision/diff-utils.js b/assets/src/bundles/revision/diff-utils.js index 040c34ce..a9507ff2 100644 --- a/assets/src/bundles/revision/diff-utils.js +++ b/assets/src/bundles/revision/diff-utils.js @@ -1,792 +1,792 @@ /** * 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 */ import 'waypoints/lib/jquery.waypoints'; import {swhSpinnerSrc} from 'utils/constants'; import {removeUrlFragment} from 'utils/functions'; import diffPanelTemplate from './diff-panel.ejs'; // number of changed files in the revision let changes = null; let nbChangedFiles = 0; // to track the number of already computed files diffs let nbDiffsComputed = 0; const noNewLineMarker = '' + '