build.js: Move our front end build tool from webpack to esbuild
We don't need the `$ESLINT`/`$STYLELINT` env variables any more to coordinate between build.js and webpack-config.js, as everything is in build.js now. Update HACKING.md accordingly. Implement our own watch mode due to the deficiencies of esbuild's [1]. This is similar to podman's [2], but with recursive watching. Because the approach for redefining breakpoints changed, update pixel tests for the spacing noise. Co-Authored-By: Martin Pitt <mpitt@redhat.com> Fixes #17629 [1] https://github.com/evanw/esbuild/issues/1204#issuecomment-1482909251 [2] https://github.com/cockpit-project/cockpit-podman/pull/1243
This commit is contained in:
parent
a06d1e8ae6
commit
fe89f2eff5
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
["@babel/env", {
|
||||
"targets": {
|
||||
"chrome": "85",
|
||||
"firefox": "77",
|
||||
"safari": "13.4",
|
||||
"edge": "85"
|
||||
}
|
||||
}],
|
||||
"@babel/preset-react"
|
||||
]
|
||||
}
|
|
@ -6,10 +6,8 @@
|
|||
},
|
||||
"extends": [
|
||||
"eslint:recommended", "standard", "standard-jsx", "standard-react",
|
||||
"plugin:flowtype/recommended",
|
||||
"plugin:jsx-a11y/recommended"
|
||||
],
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "2022"
|
||||
},
|
||||
|
|
17
HACKING.md
17
HACKING.md
|
@ -208,11 +208,10 @@ Most rule violations can be automatically fixed by running:
|
|||
|
||||
Rules configuration can be found in the `.eslintrc.json` file.
|
||||
|
||||
During fast iterative development, you can also choose to not run eslint. This
|
||||
can speed up the build and avoid build failures due to ill-formatted comments,
|
||||
unused identifiers, and other JavaScript-related issues:
|
||||
|
||||
make ESLINT=0
|
||||
During fast iterative development, you can also choose to not run eslint, by
|
||||
running `./build.js` with the `-e`/`--no-eslint` option. This
|
||||
speeds up the build and avoid build failures due to ill-formatted comments,
|
||||
unused identifiers, and other JavaScript-related issues.
|
||||
|
||||
## Running stylelint
|
||||
|
||||
|
@ -231,11 +230,9 @@ Some rule violations can be automatically fixed by running:
|
|||
|
||||
Rules configuration can be found in the `.stylelintrc.json` file.
|
||||
|
||||
During fast iterative development, you can also choose to not run stylelint.
|
||||
This speeds up the build and avoids build failures due to ill-formatted CSS
|
||||
or other issues:
|
||||
|
||||
make STYLELINT=0
|
||||
During fast iterative development, you can also choose to not run stylelint, by
|
||||
running `./build.js` with the `-s`/`--no-stylelint` option. This speeds up the
|
||||
build and avoids build failures due to ill-formatted CSS or other issues.
|
||||
|
||||
## Working on your local machine: Web server
|
||||
|
||||
|
|
278
build.js
278
build.js
|
@ -1,88 +1,262 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import child_process from 'child_process';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import process from 'process';
|
||||
|
||||
import { getFiles, getTestFiles, all_subdirs } from './files.js';
|
||||
|
||||
const production = process.env.NODE_ENV === 'production';
|
||||
const useWasm = os.arch() != 'x64';
|
||||
|
||||
// ensure node_modules is present and up to date
|
||||
child_process.spawnSync('tools/node-modules', ['make_package_lock_json'], { stdio: 'inherit' });
|
||||
|
||||
// List of directories to use when resolving import statements
|
||||
const nodePaths = ['pkg/lib'];
|
||||
|
||||
// context options for distributed pages in dist/
|
||||
const pkgOptions = {
|
||||
...!production ? { sourcemap: "external" } : {},
|
||||
bundle: true,
|
||||
external: ['*.woff', '*.woff2', '*.jpg', '*.svg', '../../assets*'], // Allow external font files which live in ../../static/fonts
|
||||
legalComments: 'external', // Move all legal comments to a .LEGAL.txt file
|
||||
loader: {
|
||||
".js": "jsx",
|
||||
".py": "text",
|
||||
".sh": "text",
|
||||
},
|
||||
minify: production,
|
||||
nodePaths,
|
||||
outbase: './pkg',
|
||||
outdir: "./dist",
|
||||
target: ['es2020'],
|
||||
};
|
||||
|
||||
// context options for qunit tests in qunit/
|
||||
const qunitOptions = {
|
||||
bundle: true,
|
||||
minify: false,
|
||||
nodePaths,
|
||||
outbase: './pkg',
|
||||
outdir: "./qunit",
|
||||
loader: {
|
||||
".sh": "text",
|
||||
},
|
||||
};
|
||||
|
||||
const parser = (await import('argparse')).default.ArgumentParser();
|
||||
parser.add_argument('-c', '--config', { help: "Path to webpack.config.js", default: "webpack.config.js" });
|
||||
parser.add_argument('-r', '--rsync', { help: "rsync bundles to ssh target after build", metavar: "HOST" });
|
||||
parser.add_argument('-w', '--watch', { action: 'store_true', help: "Enable watch mode" });
|
||||
parser.add_argument('-e', '--no-eslint', { action: 'store_true', help: "Disable eslint linting" });
|
||||
parser.add_argument('-s', '--no-stylelint', { action: 'store_true', help: "Disable stylelint linting" });
|
||||
parser.add_argument('-e', '--no-eslint', { action: 'store_true', help: "Disable eslint linting", default: production });
|
||||
parser.add_argument('-s', '--no-stylelint', { action: 'store_true', help: "Disable stylelint linting", default: production });
|
||||
parser.add_argument('onlydir', { nargs: '?', help: "The pkg/<DIRECTORY> to build (eg. base1, shell, ...)", metavar: "DIRECTORY" });
|
||||
const args = parser.parse_args();
|
||||
|
||||
if (args.no_eslint) {
|
||||
process.env.ESLINT = "0";
|
||||
} else if (args.watch) {
|
||||
process.env.ESLINT = "1";
|
||||
}
|
||||
|
||||
if (args.no_stylelint) {
|
||||
process.env.STYLELINT = "0";
|
||||
} else if (args.watch) {
|
||||
process.env.STYLELINT = "1";
|
||||
}
|
||||
|
||||
if (args.onlydir?.includes('/')) {
|
||||
if (args.onlydir?.includes('/'))
|
||||
parser.error("Directory must not contain '/'");
|
||||
}
|
||||
|
||||
if (useWasm && args.watch)
|
||||
parser.error("watch mode is not supported with esbuild-wasm");
|
||||
|
||||
if (args.onlydir)
|
||||
process.env.ONLYDIR = args.onlydir;
|
||||
if (args.rsync)
|
||||
process.env.RSYNC = args.rsync;
|
||||
|
||||
const cwd = process.cwd();
|
||||
const config_path = path.resolve(cwd, args.config);
|
||||
// keep cockpit.js as global external, except on base1 (as that's what exports it), and kdump (for testing that bundling works)
|
||||
const cockpitJSResolvePlugin = {
|
||||
name: 'cockpit-js-resolve',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^cockpit$/ }, args => {
|
||||
if (args.resolveDir.endsWith('/base1') || args.resolveDir.endsWith('/kdump'))
|
||||
return null;
|
||||
return { path: args.path, namespace: 'external-global' };
|
||||
});
|
||||
|
||||
function process_result(err, stats) {
|
||||
// process.stdout.write(stats.toString({colors: true}) + "\n");
|
||||
build.onLoad({ filter: /.*/, namespace: 'external-global' },
|
||||
args => ({ contents: `module.exports = ${args.path}` }));
|
||||
},
|
||||
};
|
||||
|
||||
if (err) {
|
||||
console.log(JSON.stringify(err));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (args.watch) {
|
||||
const info = stats.toJson();
|
||||
const time = new Date().toTimeString().split(' ')[0];
|
||||
process.stdout.write(`${time} Build succeeded, took ${info.time / 1000}s\n`);
|
||||
}
|
||||
|
||||
// Failure exit code when compilation fails
|
||||
if (stats.hasErrors() || stats.hasWarnings())
|
||||
console.log(stats.toString("normal"));
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
if (!args.watch)
|
||||
process.exit(1);
|
||||
// similar to fs.watch(), but recursively watches all subdirectories
|
||||
function watch_dirs(dir, on_change) {
|
||||
const callback = (ev, dir, fname) => {
|
||||
// only listen for "change" events, as renames are noisy
|
||||
if (ev !== "change")
|
||||
return;
|
||||
on_change(path.join(dir, fname));
|
||||
};
|
||||
|
||||
fs.watch(dir, {}, (ev, path) => callback(ev, dir, path));
|
||||
|
||||
// watch all subdirectories in dir
|
||||
const d = fs.opendirSync(dir);
|
||||
let dirent;
|
||||
while ((dirent = d.readSync()) !== null) {
|
||||
if (dirent.isDirectory())
|
||||
watch_dirs(path.join(dir, dirent.name), on_change);
|
||||
}
|
||||
d.closeSync();
|
||||
}
|
||||
|
||||
async function build() {
|
||||
// dynamic imports which need node_modules
|
||||
const config = (await import(config_path)).default;
|
||||
const webpack = (await import('webpack')).default;
|
||||
const cockpit_rsync = (await import('./pkg/lib/cockpit-rsync-plugin.js'));
|
||||
const copy = (await import('esbuild-plugin-copy')).default;
|
||||
const esbuild = (await import(useWasm ? 'esbuild-wasm' : 'esbuild')).default;
|
||||
const sassPlugin = (await import('esbuild-sass-plugin')).sassPlugin;
|
||||
const replace = (await import('esbuild-plugin-replace')).replace;
|
||||
|
||||
if (args.rsync) {
|
||||
process.env.RSYNC = args.rsync;
|
||||
config.plugins.push(new cockpit_rsync.CockpitRsyncWebpackPlugin({ source: "dist/" + (args.onlydir || "") }));
|
||||
}
|
||||
const cleanPlugin = (await import('./pkg/lib/esbuild-cleanup-plugin.js')).cleanPlugin;
|
||||
const cockpitCompressPlugin = (await import('./pkg/lib/esbuild-compress-plugin.js')).cockpitCompressPlugin;
|
||||
const cockpitPoEsbuildPlugin = (await import('./pkg/lib/cockpit-po-plugin.js')).cockpitPoEsbuildPlugin;
|
||||
const cockpitRsyncEsbuildPlugin = (await import('./pkg/lib/cockpit-rsync-plugin.js')).cockpitRsyncEsbuildPlugin;
|
||||
const cockpitTestHtmlPlugin = (await import('./pkg/lib/esbuild-test-html-plugin.js')).cockpitTestHtmlPlugin;
|
||||
const eslintPlugin = (await import('./pkg/lib/esbuild-eslint-plugin.js')).eslintPlugin;
|
||||
const stylelintPlugin = (await import('./pkg/lib/esbuild-stylelint-plugin.js')).stylelintPlugin;
|
||||
|
||||
const compiler = webpack(config);
|
||||
const { entryPoints, assetFiles, redhat_fonts } = getFiles(args.onlydir);
|
||||
const tests = getTestFiles();
|
||||
const testEntryPoints = tests.map(test => "pkg/" + test + ".js");
|
||||
|
||||
if (args.watch) {
|
||||
compiler.hooks.watchRun.tap("WebpackInfo", compilation => {
|
||||
const time = new Date().toTimeString().split(' ')[0];
|
||||
process.stdout.write(`${time} Build started\n`);
|
||||
const pkgFirstPlugins = [
|
||||
cleanPlugin({ subdir: args.onlydir }),
|
||||
];
|
||||
|
||||
const pkgPlugins = [
|
||||
...args.no_stylelint ? [] : [stylelintPlugin({ filter: /pkg\/.*\.(css?|scss?)$/ })],
|
||||
...args.no_eslint ? [] : [eslintPlugin({ filter: /pkg\/.*\.(jsx?|js?)$/ })],
|
||||
cockpitJSResolvePlugin,
|
||||
// Redefine grid breakpoints to count with our shell
|
||||
// See https://github.com/patternfly/patternfly-react/issues/3815 and
|
||||
// [Redefine grid breakpoints] section in pkg/lib/_global-variables.scss for explanation
|
||||
replace({
|
||||
include: /\.css$/,
|
||||
values: {
|
||||
'576px': '236px',
|
||||
'768px': '428px',
|
||||
'992px': '652px',
|
||||
'1200px': '876px',
|
||||
'1450px': '1100px',
|
||||
}
|
||||
}),
|
||||
sassPlugin({
|
||||
loadPaths: [...nodePaths, 'node_modules'],
|
||||
quietDeps: true,
|
||||
async transform(source, resolveDir, path) {
|
||||
if (path.includes('patternfly-4-cockpit.scss')) {
|
||||
return source
|
||||
.replace(/url.*patternfly-icons-fake-path.*;/g, 'url("../base1/fonts/patternfly.woff") format("woff");')
|
||||
.replace(/@font-face[^}]*patternfly-fonts-fake-path[^}]*}/g, '');
|
||||
}
|
||||
return source;
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
const getTime = () => new Date().toTimeString().split(' ')[0];
|
||||
|
||||
const pkgLastPlugins = [
|
||||
cockpitPoEsbuildPlugin({
|
||||
subdirs: args.onlydir ? [args.onlydir] : all_subdirs,
|
||||
// login page does not have cockpit.js, but reads window.cockpit_po
|
||||
wrapper: subdir => subdir == "static" ? "window.cockpit_po = PO_DATA;" : undefined,
|
||||
}),
|
||||
// Esbuild will only copy assets that are explicitly imported and used
|
||||
// in the code. This is a problem for index.html and manifest.json which are not imported
|
||||
copy({ assets: [...assetFiles, ...redhat_fonts] }),
|
||||
// cockpit-ws cannot currently serve compressed login page
|
||||
...production ? [cockpitCompressPlugin({ subdir: args.onlydir, exclude: /\/static/ })] : [],
|
||||
|
||||
{
|
||||
name: 'notify-end',
|
||||
setup(build) {
|
||||
build.onEnd(() => console.log(`${getTime()}: Build finished`));
|
||||
}
|
||||
},
|
||||
|
||||
...args.rsync ? [cockpitRsyncEsbuildPlugin({ source: "dist/" + (args.onlydir || '') })] : [],
|
||||
];
|
||||
|
||||
if (useWasm) {
|
||||
// build each entry point individually, as otherwise it runs out of memory
|
||||
// See https://github.com/evanw/esbuild/issues/3006
|
||||
const numEntries = entryPoints.length;
|
||||
for (const [index, entryPoint] of entryPoints.entries()) {
|
||||
console.log("building", entryPoint);
|
||||
const context = await esbuild.context({
|
||||
...pkgOptions,
|
||||
entryPoints: [entryPoint],
|
||||
plugins: [
|
||||
...(index === 0 ? pkgFirstPlugins : []),
|
||||
...pkgPlugins,
|
||||
...(index === numEntries - 1 ? pkgLastPlugins : []),
|
||||
],
|
||||
});
|
||||
|
||||
await context.rebuild();
|
||||
context.dispose();
|
||||
}
|
||||
|
||||
// build all tests in one go, they are small enough
|
||||
console.log("building qunit tests");
|
||||
const context = await esbuild.context({
|
||||
...qunitOptions,
|
||||
entryPoints: testEntryPoints,
|
||||
plugins: [
|
||||
...args.no_stylelint ? [] : [stylelintPlugin({ filter: /pkg\/.*\.(css?|scss?)$/ })],
|
||||
...args.no_eslint ? [] : [eslintPlugin({ filter: /pkg\/.*\.(jsx?|js?)$/ })],
|
||||
cockpitTestHtmlPlugin({ testFiles: tests }),
|
||||
],
|
||||
});
|
||||
compiler.watch(config.watchOptions, process_result);
|
||||
|
||||
await context.rebuild();
|
||||
context.dispose();
|
||||
} else {
|
||||
compiler.run(process_result);
|
||||
// with native esbuild, build everything in one go, that's fastest
|
||||
const pkgContext = await esbuild.context({
|
||||
...pkgOptions,
|
||||
entryPoints,
|
||||
plugins: [...pkgFirstPlugins, ...pkgPlugins, ...pkgLastPlugins],
|
||||
});
|
||||
|
||||
const qunitContext = await esbuild.context({
|
||||
...qunitOptions,
|
||||
entryPoints: testEntryPoints,
|
||||
plugins: [
|
||||
...args.no_stylelint ? [] : [stylelintPlugin({ filter: /pkg\/.*\.(css?|scss?)$/ })],
|
||||
...args.no_eslint ? [] : [eslintPlugin({ filter: /pkg\/.*\.(jsx?|js?)$/ })],
|
||||
cockpitTestHtmlPlugin({ testFiles: tests }),
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all([pkgContext.rebuild(), qunitContext.rebuild()]);
|
||||
} catch (e) {
|
||||
if (!args.watch)
|
||||
process.exit(1);
|
||||
// ignore errors in watch mode
|
||||
}
|
||||
|
||||
if (args.watch) {
|
||||
const on_change = async path => {
|
||||
console.log("change detected:", path);
|
||||
await Promise.all([pkgContext.cancel(), qunitContext.cancel()]);
|
||||
try {
|
||||
await Promise.all([pkgContext.rebuild(), qunitContext.rebuild()]);
|
||||
} catch (e) {} // ignore in watch mode
|
||||
};
|
||||
|
||||
watch_dirs('pkg', on_change);
|
||||
// wait forever until Control-C
|
||||
await new Promise(() => {});
|
||||
}
|
||||
|
||||
pkgContext.dispose();
|
||||
qunitContext.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
import path from 'path';
|
||||
|
||||
const info = {
|
||||
entries: [
|
||||
"base1/cockpit.js",
|
||||
"apps/apps.jsx",
|
||||
"kdump/kdump.js",
|
||||
// do *not* call this metrics/metrics -- uBlock origin etc. like to block metrics.{css,js}
|
||||
"metrics/index.js",
|
||||
|
||||
"networkmanager/networkmanager.jsx",
|
||||
"networkmanager/firewall.jsx",
|
||||
|
||||
"playground/index.js",
|
||||
"playground/exception.js",
|
||||
"playground/metrics.js",
|
||||
"playground/pkgs.js",
|
||||
"playground/plot.js",
|
||||
"playground/react-patterns.js",
|
||||
"playground/service.js",
|
||||
"playground/speed.js",
|
||||
"playground/test.js",
|
||||
"playground/translate.js",
|
||||
"playground/preloaded.js",
|
||||
"playground/notifications-receiver.js",
|
||||
"playground/journal.jsx",
|
||||
|
||||
"selinux/selinux.js",
|
||||
"shell/shell.js",
|
||||
"sosreport/sosreport.jsx",
|
||||
"static/login.js",
|
||||
"storaged/storaged.jsx",
|
||||
|
||||
"systemd/services.jsx",
|
||||
"systemd/logs.jsx",
|
||||
"systemd/overview.jsx",
|
||||
"systemd/terminal.jsx",
|
||||
"systemd/hwinfo.jsx",
|
||||
|
||||
"packagekit/updates.jsx",
|
||||
"users/users.js",
|
||||
],
|
||||
|
||||
tests: [
|
||||
"base1/test-base64",
|
||||
"base1/test-browser-storage",
|
||||
"base1/test-cache",
|
||||
"base1/test-chan",
|
||||
"base1/test-dbus-address",
|
||||
"base1/test-dbus-framed",
|
||||
"base1/test-dbus",
|
||||
"base1/test-echo",
|
||||
"base1/test-events",
|
||||
"base1/test-external",
|
||||
"base1/test-file",
|
||||
"base1/test-format",
|
||||
"base1/test-framed-cache",
|
||||
"base1/test-framed",
|
||||
"base1/test-http",
|
||||
"base1/test-journal-renderer",
|
||||
"base1/test-locale",
|
||||
"base1/test-location",
|
||||
"base1/test-metrics",
|
||||
"base1/test-no-jquery",
|
||||
"base1/test-permissions",
|
||||
"base1/test-promise",
|
||||
"base1/test-protocol",
|
||||
"base1/test-series",
|
||||
"base1/test-spawn-proc",
|
||||
"base1/test-spawn",
|
||||
"base1/test-stream",
|
||||
"base1/test-user",
|
||||
"base1/test-utf8",
|
||||
"base1/test-websocket",
|
||||
|
||||
"kdump/test-config-client",
|
||||
|
||||
"networkmanager/test-utils",
|
||||
|
||||
"shell/machines/test-machines",
|
||||
|
||||
"storaged/test-util",
|
||||
],
|
||||
|
||||
files: [
|
||||
"apps/index.html",
|
||||
"apps/default.png",
|
||||
|
||||
"kdump/index.html",
|
||||
|
||||
"metrics/index.html",
|
||||
|
||||
"networkmanager/index.html",
|
||||
"networkmanager/firewall.html",
|
||||
|
||||
"packagekit/index.html",
|
||||
|
||||
"playground/index.html",
|
||||
"playground/exception.html",
|
||||
"playground/hammer.gif",
|
||||
"playground/metrics.html",
|
||||
"playground/pkgs.html",
|
||||
"playground/plot.html",
|
||||
"playground/react-patterns.html",
|
||||
"playground/service.html",
|
||||
"playground/speed.html",
|
||||
"playground/test.html",
|
||||
"playground/translate.html",
|
||||
"playground/preloaded.html",
|
||||
"playground/notifications-receiver.html",
|
||||
"playground/journal.html",
|
||||
|
||||
"selinux/index.html",
|
||||
|
||||
"shell/images/server-error.png",
|
||||
"shell/images/server-large.png",
|
||||
"shell/images/server-small.png",
|
||||
"shell/images/cockpit-icon.svg",
|
||||
"shell/images/bg-plain.jpg",
|
||||
"shell/index.html",
|
||||
"shell/shell.html",
|
||||
|
||||
"sosreport/index.html",
|
||||
"sosreport/sosreport.png",
|
||||
|
||||
"static/login.html",
|
||||
|
||||
"storaged/index.html",
|
||||
"storaged/images/storage-array.png",
|
||||
"storaged/images/storage-disk.png",
|
||||
|
||||
"systemd/index.html",
|
||||
"systemd/logs.html",
|
||||
"systemd/services.html",
|
||||
"systemd/terminal.html",
|
||||
"systemd/hwinfo.html",
|
||||
|
||||
"users/index.html",
|
||||
]
|
||||
};
|
||||
|
||||
const srcdir = process.env.SRCDIR || '.';
|
||||
const nodedir = path.relative(process.cwd(), path.resolve(srcdir, "node_modules"));
|
||||
|
||||
export const all_subdirs = Array.from(new Set(info.entries.map(key => key.split('/')[0])));
|
||||
|
||||
const redhat_fonts = [
|
||||
"Text-updated-Bold", "Text-updated-BoldItalic", "Text-updated-Italic", "Text-updated-Medium", "Text-updated-MediumItalic", "Text-updated-Regular",
|
||||
"Display-updated-Black", "Display-updated-BlackItalic", "Display-updated-Bold", "Display-updated-BoldItalic",
|
||||
"Display-updated-Italic", "Display-updated-Medium", "Display-updated-MediumItalic", "Display-updated-Regular",
|
||||
"Mono-updated-Bold", "Mono-updated-BoldItalic", "Mono-updated-Italic", "Mono-updated-Medium", "Mono-updated-MediumItalic", "Mono-updated-Regular",
|
||||
].map(name => {
|
||||
const subdir = 'RedHat' + name.split('-')[0];
|
||||
const fontsdir = '@patternfly/patternfly/assets/fonts/RedHatFont-updated';
|
||||
|
||||
return {
|
||||
// Rename the RedHat*-updated files to not contain the 'updated' string so as to keep compatibility with external plugins
|
||||
// which expect the non-updated font file names in `static` folder
|
||||
from: path.resolve(nodedir, fontsdir, subdir, 'RedHat' + name + '.woff2'),
|
||||
to: 'static/fonts/RedHat' + name.replace("-updated", "") + ".woff2"
|
||||
};
|
||||
});
|
||||
|
||||
const pkgfile = suffix => `${srcdir}/pkg/${suffix}`;
|
||||
export const getFiles = subdir => {
|
||||
/* Qualify all the paths in entries */
|
||||
const entryPoints = [];
|
||||
info.entries.forEach(key => {
|
||||
if (subdir && key.indexOf(subdir) !== 0)
|
||||
return;
|
||||
|
||||
entryPoints.push(pkgfile(key));
|
||||
});
|
||||
|
||||
/* Qualify all the paths in files listed */
|
||||
const files = [];
|
||||
info.files.forEach(value => {
|
||||
if (!subdir || value.indexOf(subdir) === 0)
|
||||
files.push({ from: pkgfile(value), to: path.dirname(value) });
|
||||
});
|
||||
if (subdir) {
|
||||
const manifest = subdir + "/manifest.json";
|
||||
files.push({ from: pkgfile(manifest), to: subdir });
|
||||
} else {
|
||||
all_subdirs.forEach(subdir => {
|
||||
const manifest = subdir + "/manifest.json";
|
||||
files.push({ from: pkgfile(manifest), to: subdir });
|
||||
});
|
||||
}
|
||||
|
||||
return ({ entryPoints, assetFiles: files, redhat_fonts });
|
||||
};
|
||||
|
||||
export const getTestFiles = () => info.tests;
|
|
@ -1 +1 @@
|
|||
Subproject commit 2f20e8b647feed92d4a418cfe7d7c35ddb95183e
|
||||
Subproject commit 282c8d0a987c3dbd6929603fe6a9b381d8a9ed2f
|
27
package.json
27
package.json
|
@ -25,20 +25,16 @@
|
|||
"xterm-addon-canvas": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/eslint-parser": "^7.13.14",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"argparse": "^2.0.1",
|
||||
"axe-core": "^3.5.2",
|
||||
"babel-loader": "^8.1.0",
|
||||
"chrome-remote-interface": "^0.32.1",
|
||||
"compression-webpack-plugin": "^9.2.0",
|
||||
"copy-webpack-plugin": "^6.1.0",
|
||||
"css-loader": "^5.2.0",
|
||||
"css-minimizer-webpack-plugin": "^3.0.1",
|
||||
"cssnano-preset-lite": "^2.0.1",
|
||||
"date-fns": "^2.22.1",
|
||||
"esbuild": "0.17.13",
|
||||
"esbuild-wasm": "0.17.13",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-replace": "^1.3.0",
|
||||
"esbuild-sass-plugin": "2.6.0",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-config-standard-jsx": "^11.0.0",
|
||||
|
@ -51,26 +47,15 @@
|
|||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"eslint-webpack-plugin": "^2.5.3",
|
||||
"gettext-parser": "^2.0.0",
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"htmlparser": "^1.7.7",
|
||||
"jed": "^1.1.1",
|
||||
"mini-css-extract-plugin": "^1.4.0",
|
||||
"null-loader": "^4.0.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"sass": "^1.59.3",
|
||||
"sass-loader": "^12.1.0",
|
||||
"sizzle": "^2.3.5",
|
||||
"strict-loader": "^1.2.0",
|
||||
"string-replace-loader": "^3.0.0",
|
||||
"stylelint": "^14.8.5",
|
||||
"stylelint-config-standard": "^25.0.0",
|
||||
"stylelint-config-standard-scss": "^4.0.0",
|
||||
"stylelint-webpack-plugin": "^3.3.0",
|
||||
"terser-webpack-plugin": "^5.1.3",
|
||||
"webpack": "^5.38.0",
|
||||
"webpack-cli": "^4.7.0"
|
||||
"stylelint-formatter-pretty": "^3.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"eslint": "eslint --ext .js --ext .jsx pkg/ test/common/",
|
||||
|
|
|
@ -39,7 +39,7 @@ $(DIST_STAMP): $(srcdir)/package-lock.json $(PKG_INPUTS)
|
|||
@rm -f $(DIST_STAMP)
|
||||
$(V_BUNDLE) cd $(srcdir) && NODE_ENV='$(NODE_ENV)' tools/termschutz ./build.js
|
||||
|
||||
EXTRA_DIST += webpack.config.js package.json package-lock.json
|
||||
EXTRA_DIST += build.js files.js package.json package-lock.json
|
||||
|
||||
# This is how the qunit tests get included. We need to prevent automake from
|
||||
# seeing them during ./autogen.sh, but need make to find them at compile time.
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import fs from "fs";
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
||||
const srcdir = process.env.SRCDIR || '.';
|
||||
const libdir = path.resolve(srcdir, "pkg", "lib");
|
||||
|
||||
export const cockpitTestHtmlPlugin = ({ testFiles }) => ({
|
||||
name: 'CockpitTestHtmlPlugin',
|
||||
setup(build) {
|
||||
build.onEnd(async () => {
|
||||
const data = fs.readFileSync(path.resolve(libdir, "qunit-template.html"), "utf8");
|
||||
testFiles.forEach(file => {
|
||||
const test = path.parse(file).name;
|
||||
const output = _.template(data.toString())({
|
||||
title: test,
|
||||
builddir: file.split("/").map(() => "../").join(""),
|
||||
script: test + '.js',
|
||||
});
|
||||
const outdir = './qunit/' + path.dirname(file);
|
||||
const outfile = test + ".html";
|
||||
|
||||
fs.mkdirSync(outdir, { recursive: true });
|
||||
fs.writeFileSync(path.resolve(outdir, outfile), output);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
|
@ -20,13 +20,13 @@
|
|||
<html class="pf-theme-dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.title %>.css" type="text/css" />
|
||||
<script src="<%= htmlWebpackPlugin.options.builddir %>dist/base1/cockpit.js"></script>
|
||||
<script src="<%= htmlWebpackPlugin.options.script %>"></script>
|
||||
<title><%= title %></title>
|
||||
<link rel="stylesheet" href="<%= title %>.css" type="text/css" />
|
||||
<script src="<%= builddir %>dist/base1/cockpit.js"></script>
|
||||
<script src="<%= script %>"></script>
|
||||
</head>
|
||||
<body class="pf-m-redhat-font pf-m-tabular-nums">
|
||||
<h1 id="qunit-header"><%= htmlWebpackPlugin.options.title %></h1>
|
||||
<h1 id="qunit-header"><%= title %></h1>
|
||||
<h2 id="qunit-banner"></h2><div id="qunit-testrunner-toolbar"></div>
|
||||
<h2 id="qunit-userAgent"></h2><ol id="qunit-tests"></ol>
|
||||
<div id="qunit-fixture">test markup, will be hidden</div>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ed665cbb10919c86588acb16104ad09c8b99672c
|
||||
Subproject commit aa18584cad30d98e719eb6ad6dfc5638251452a8
|
|
@ -82,7 +82,7 @@ dist_licenses = {} # Files: dirglob → set(licenses)
|
|||
|
||||
for directory, _subdirs, files in os.walk(f'{BASE_DIR}/dist'):
|
||||
for file in files:
|
||||
if '.LICENSE.txt' not in file:
|
||||
if '.LEGAL.txt' not in file:
|
||||
continue
|
||||
|
||||
full_filename = os.path.join(directory, file)
|
||||
|
@ -96,6 +96,9 @@ for directory, _subdirs, files in os.walk(f'{BASE_DIR}/dist'):
|
|||
contents = license_file.read()
|
||||
|
||||
for comment in contents.split('\n\n'):
|
||||
if (comment.strip() == "" or "Bundled license information:" in comment):
|
||||
continue
|
||||
|
||||
licenses = find_patterns(license_patterns, comment)
|
||||
if not licenses:
|
||||
raise SystemError('Can not determine licenses of:\n%s' % comment)
|
||||
|
@ -119,7 +122,7 @@ for directory, _subdirs, files in os.walk(f'{BASE_DIR}/dist'):
|
|||
|
||||
for pattern in set.union(set(license_patterns), set(copyright_patterns)):
|
||||
if pattern not in used_patterns:
|
||||
# We'll have no LICENSE.txt files in that dev builds
|
||||
# We'll have no LEGAL.txt files in that dev builds
|
||||
# so of course we won't use any of the patterns
|
||||
if os.getenv('NODE_ENV') == 'development' or os.getenv('IGNORE_UNUSED_PATTERNS'):
|
||||
continue
|
||||
|
|
|
@ -1,431 +0,0 @@
|
|||
/* --------------------------------------------------------------------
|
||||
* Fill in module info here.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import Copy from 'copy-webpack-plugin';
|
||||
import Html from 'html-webpack-plugin';
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import CompressionPlugin from 'compression-webpack-plugin';
|
||||
import TerserJSPlugin from 'terser-webpack-plugin';
|
||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
||||
import ESLintPlugin from 'eslint-webpack-plugin';
|
||||
import StylelintPlugin from 'stylelint-webpack-plugin';
|
||||
import { CockpitPoWebpackPlugin } from './pkg/lib/cockpit-po-plugin.js';
|
||||
|
||||
const info = {
|
||||
entries: [
|
||||
"base1/cockpit.js",
|
||||
"apps/apps.jsx",
|
||||
"kdump/kdump.js",
|
||||
// do *not* call this metrics/metrics -- uBlock origin etc. like to block metrics.{css,js}
|
||||
"metrics/index.js",
|
||||
|
||||
"networkmanager/networkmanager.jsx",
|
||||
"networkmanager/firewall.jsx",
|
||||
|
||||
"playground/index.js",
|
||||
"playground/exception.js",
|
||||
"playground/metrics.js",
|
||||
"playground/pkgs.js",
|
||||
"playground/plot.js",
|
||||
"playground/react-patterns.js",
|
||||
"playground/service.js",
|
||||
"playground/speed.js",
|
||||
"playground/test.js",
|
||||
"playground/translate.js",
|
||||
"playground/preloaded.js",
|
||||
"playground/notifications-receiver.js",
|
||||
"playground/journal.jsx",
|
||||
|
||||
"selinux/selinux.js",
|
||||
"shell/shell.js",
|
||||
"sosreport/sosreport.jsx",
|
||||
"static/login.js",
|
||||
"storaged/storaged.jsx",
|
||||
|
||||
"systemd/services.jsx",
|
||||
"systemd/logs.jsx",
|
||||
"systemd/overview.jsx",
|
||||
"systemd/terminal.jsx",
|
||||
"systemd/hwinfo.jsx",
|
||||
|
||||
"packagekit/updates.jsx",
|
||||
"users/users.js",
|
||||
],
|
||||
|
||||
tests: [
|
||||
"base1/test-base64",
|
||||
"base1/test-browser-storage",
|
||||
"base1/test-cache",
|
||||
"base1/test-chan",
|
||||
"base1/test-dbus-address",
|
||||
"base1/test-dbus-framed",
|
||||
"base1/test-dbus",
|
||||
"base1/test-echo",
|
||||
"base1/test-events",
|
||||
"base1/test-external",
|
||||
"base1/test-file",
|
||||
"base1/test-format",
|
||||
"base1/test-framed-cache",
|
||||
"base1/test-framed",
|
||||
"base1/test-http",
|
||||
"base1/test-journal-renderer",
|
||||
"base1/test-locale",
|
||||
"base1/test-location",
|
||||
"base1/test-metrics",
|
||||
"base1/test-no-jquery",
|
||||
"base1/test-permissions",
|
||||
"base1/test-promise",
|
||||
"base1/test-protocol",
|
||||
"base1/test-series",
|
||||
"base1/test-spawn-proc",
|
||||
"base1/test-spawn",
|
||||
"base1/test-stream",
|
||||
"base1/test-user",
|
||||
"base1/test-utf8",
|
||||
"base1/test-websocket",
|
||||
|
||||
"kdump/test-config-client",
|
||||
|
||||
"networkmanager/test-utils",
|
||||
|
||||
"shell/machines/test-machines",
|
||||
|
||||
"storaged/test-util",
|
||||
],
|
||||
|
||||
files: [
|
||||
"apps/index.html",
|
||||
"apps/default.png",
|
||||
|
||||
"kdump/index.html",
|
||||
|
||||
"metrics/index.html",
|
||||
|
||||
"networkmanager/index.html",
|
||||
"networkmanager/firewall.html",
|
||||
|
||||
"packagekit/index.html",
|
||||
|
||||
"playground/index.html",
|
||||
"playground/exception.html",
|
||||
"playground/hammer.gif",
|
||||
"playground/metrics.html",
|
||||
"playground/pkgs.html",
|
||||
"playground/plot.html",
|
||||
"playground/react-patterns.html",
|
||||
"playground/service.html",
|
||||
"playground/speed.html",
|
||||
"playground/test.html",
|
||||
"playground/translate.html",
|
||||
"playground/preloaded.html",
|
||||
"playground/notifications-receiver.html",
|
||||
"playground/journal.html",
|
||||
|
||||
"selinux/index.html",
|
||||
|
||||
"shell/images/server-error.png",
|
||||
"shell/images/server-large.png",
|
||||
"shell/images/server-small.png",
|
||||
"shell/images/cockpit-icon.svg",
|
||||
"shell/images/bg-plain.jpg",
|
||||
"shell/index.html",
|
||||
"shell/shell.html",
|
||||
|
||||
"sosreport/index.html",
|
||||
"sosreport/sosreport.png",
|
||||
|
||||
"static/login.html",
|
||||
|
||||
"storaged/index.html",
|
||||
"storaged/images/storage-array.png",
|
||||
"storaged/images/storage-disk.png",
|
||||
|
||||
"systemd/index.html",
|
||||
"systemd/logs.html",
|
||||
"systemd/services.html",
|
||||
"systemd/terminal.html",
|
||||
"systemd/hwinfo.html",
|
||||
|
||||
"users/index.html",
|
||||
]
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Implementation
|
||||
*/
|
||||
|
||||
process.traceDeprecation = true;
|
||||
|
||||
/* These can be overridden, typically from the Makefile.am */
|
||||
const srcdir = process.env.SRCDIR || '.';
|
||||
const libdir = path.resolve(srcdir, "pkg" + path.sep + "lib");
|
||||
const nodedir = path.relative(process.cwd(), path.resolve(srcdir, "node_modules"));
|
||||
const section = process.env.ONLYDIR;
|
||||
|
||||
/* A standard nodejs and webpack pattern */
|
||||
const production = process.env.NODE_ENV === 'production';
|
||||
|
||||
/* Default to disable eslint for faster production builds */
|
||||
const eslint = process.env.ESLINT ? (process.env.ESLINT !== '0') : !production;
|
||||
|
||||
/* Default to disable csslint for faster production builds */
|
||||
const stylelint = process.env.STYLELINT ? (process.env.STYLELINT !== '0') : !production;
|
||||
|
||||
const pkgfile = suffix => `${srcdir}/pkg/${suffix}`;
|
||||
|
||||
/* Qualify all the paths in entries */
|
||||
const entry = {};
|
||||
info.entries.forEach(key => {
|
||||
if (section && key.indexOf(section) !== 0)
|
||||
return;
|
||||
|
||||
entry[key.replace(/\..*$/, '')] = pkgfile(key);
|
||||
});
|
||||
|
||||
const all_sections = Array.from(new Set(info.entries.map(key => key.split('/')[0])));
|
||||
|
||||
/* Qualify all the paths in files listed */
|
||||
const files = [];
|
||||
info.files.forEach(value => {
|
||||
if (!section || value.indexOf(section) === 0)
|
||||
files.push({ from: pkgfile(value), to: value });
|
||||
});
|
||||
if (section) {
|
||||
const manifest = section + "/manifest.json";
|
||||
files.push({ from: pkgfile(manifest), to: manifest });
|
||||
} else {
|
||||
all_sections.forEach(section => {
|
||||
const manifest = section + "/manifest.json";
|
||||
files.push({ from: pkgfile(manifest), to: manifest });
|
||||
});
|
||||
}
|
||||
|
||||
info.files = files;
|
||||
|
||||
// main font for all our pages
|
||||
const redhat_fonts = [
|
||||
"Text-updated-Bold", "Text-updated-BoldItalic", "Text-updated-Italic", "Text-updated-Medium", "Text-updated-MediumItalic", "Text-updated-Regular",
|
||||
"Display-updated-Black", "Display-updated-BlackItalic", "Display-updated-Bold", "Display-updated-BoldItalic",
|
||||
"Display-updated-Italic", "Display-updated-Medium", "Display-updated-MediumItalic", "Display-updated-Regular",
|
||||
"Mono-updated-Bold", "Mono-updated-BoldItalic", "Mono-updated-Italic", "Mono-updated-Medium", "Mono-updated-MediumItalic", "Mono-updated-Regular",
|
||||
].map(name => {
|
||||
const subdir = 'RedHat' + name.split('-')[0];
|
||||
const fontsdir = '@patternfly/patternfly/assets/fonts/RedHatFont-updated';
|
||||
|
||||
return {
|
||||
// Rename the RedHat*-updated files to not contain the 'updated' string so as to keep compatibility with external plugins
|
||||
// which expect the non-updated font file names in `static` folder
|
||||
from: path.resolve(nodedir, fontsdir, subdir, 'RedHat' + name + '.woff2'),
|
||||
to: 'static/fonts/RedHat' + name.replace("-updated", "") + ".woff2"
|
||||
};
|
||||
});
|
||||
|
||||
const plugins = [
|
||||
new Copy({ patterns: info.files }),
|
||||
new MiniCssExtractPlugin({ filename: "[name].css" }),
|
||||
new CockpitPoWebpackPlugin({
|
||||
subdirs: section ? [section] : all_sections,
|
||||
// login page does not have cockpit.js, but reads window.cockpit_po
|
||||
wrapper: subdir => subdir == "static" ? "window.cockpit_po = PO_DATA;" : undefined,
|
||||
}),
|
||||
];
|
||||
|
||||
if (production) {
|
||||
plugins.push(new CompressionPlugin({
|
||||
test: /\.(css|html|js|txt)$/,
|
||||
deleteOriginalAssets: true,
|
||||
exclude: [
|
||||
'/test-[^/]+.$', // don't compress test cases
|
||||
'^static/[^/]+$', // cockpit-ws cannot currently serve compressed login page
|
||||
'\\.html$', // HTML pages get patched by cockpit-ws, can't be compressed
|
||||
].map(r => new RegExp(r)),
|
||||
}));
|
||||
}
|
||||
|
||||
if (eslint) {
|
||||
plugins.push(new ESLintPlugin({ extensions: ["js", "jsx"] }));
|
||||
}
|
||||
|
||||
if (stylelint) {
|
||||
plugins.push(new StylelintPlugin({
|
||||
context: "pkg/" + (section || ""),
|
||||
}));
|
||||
}
|
||||
|
||||
if (!section || section.startsWith('static'))
|
||||
plugins.push(new Copy({ patterns: redhat_fonts }));
|
||||
|
||||
/* Fill in the tests properly */
|
||||
info.tests.forEach(test => {
|
||||
if (!section || test.indexOf(section) === 0) {
|
||||
entry['../qunit/' + test] = pkgfile(test + ".js");
|
||||
plugins.push(new Html({
|
||||
title: path.basename(test),
|
||||
filename: '../qunit/' + test + ".html",
|
||||
template: libdir + path.sep + "qunit-template.html",
|
||||
builddir: test.split("/").map(() => "../").join(""),
|
||||
script: path.basename(test + '.js'),
|
||||
inject: false,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const aliases = {
|
||||
"font-awesome": path.resolve(nodedir, 'font-awesome-sass/assets/stylesheets'),
|
||||
};
|
||||
|
||||
export default {
|
||||
mode: production ? 'production' : 'development',
|
||||
resolve: {
|
||||
alias: aliases,
|
||||
modules: [libdir, nodedir],
|
||||
extensions: ["*", ".js", ".json"]
|
||||
},
|
||||
resolveLoader: {
|
||||
modules: [nodedir, './pkg/lib'],
|
||||
},
|
||||
entry,
|
||||
plugins,
|
||||
|
||||
// cockpit.js gets included via <script> (except on base1 and kdump), everything else should be bundled
|
||||
externals: [
|
||||
({ context, request }, callback) => {
|
||||
if (request === "cockpit" && !context.includes("/base1") && !context.includes("/kdump"))
|
||||
callback(null, "cockpit");
|
||||
else
|
||||
callback();
|
||||
}
|
||||
],
|
||||
|
||||
devtool: production ? false : "source-map",
|
||||
stats: "errors-warnings",
|
||||
|
||||
// disable noisy warnings about exceeding the recommended size limit
|
||||
performance: {
|
||||
maxAssetSize: 20000000,
|
||||
maxEntrypointSize: 20000000,
|
||||
},
|
||||
|
||||
watchOptions: {
|
||||
ignored: /node_modules/
|
||||
},
|
||||
|
||||
optimization: {
|
||||
minimize: production,
|
||||
minimizer: [
|
||||
new TerserJSPlugin(),
|
||||
new CssMinimizerPlugin({
|
||||
minimizerOptions: {
|
||||
preset: ['lite']
|
||||
}
|
||||
})
|
||||
],
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /\/node_modules\/.*\//, // exclude external dependencies
|
||||
loader: 'strict-loader' // Adds "use strict"
|
||||
},
|
||||
/* these modules need to be babel'ed, they cause bugs in their dist'ed form */
|
||||
{
|
||||
test: /\/node_modules\/.*(react-table).*\.js$/,
|
||||
use: "babel-loader"
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
// exclude external dependencies; it's too slow, and they are already plain JS except the above
|
||||
// also exclude unit tests, we don't need it for them, just a waste and makes failures harder to read
|
||||
exclude: /\/node_modules|\/test-[^/]*\.js/,
|
||||
use: "babel-loader"
|
||||
},
|
||||
{
|
||||
test: /patternfly-4-cockpit.scss$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: !production,
|
||||
url: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'string-replace-loader',
|
||||
options: {
|
||||
multiple: [
|
||||
{
|
||||
search: /src: ?url\("patternfly-icons-fake-path\/pficon[^}]*/g,
|
||||
replace: "src:url('fonts/patternfly.woff')format('woff');",
|
||||
},
|
||||
{
|
||||
search: /@font-face[^}]*patternfly-fonts-fake-path[^}]*}/g,
|
||||
replace: '',
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: !production,
|
||||
sassOptions: {
|
||||
quietDeps: true,
|
||||
outputStyle: production ? 'compressed' : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.s?css$/,
|
||||
exclude: /patternfly-(4-)?cockpit.scss/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: !production,
|
||||
url: false
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: !production,
|
||||
sassOptions: {
|
||||
quietDeps: true,
|
||||
outputStyle: production ? 'compressed' : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
// See https://github.com/patternfly/patternfly-react/issues/3815 and
|
||||
// [Redefine grid breakpoints] section in pkg/lib/_global-variables.scss for more details
|
||||
// Components which are using the pf-global--breakpoint-* variables should import scss manually
|
||||
// instead off the automatically imported CSS stylesheets
|
||||
test: /\.css$/,
|
||||
include: stylesheet => {
|
||||
return (
|
||||
stylesheet.includes('@patternfly/react-styles/css/components/Table/') ||
|
||||
stylesheet.includes('@patternfly/react-styles/css/components/Page/') ||
|
||||
stylesheet.includes('@patternfly/react-styles/css/components/Toolbar/')
|
||||
);
|
||||
},
|
||||
use: ["null-loader"]
|
||||
},
|
||||
// inlined scripts
|
||||
{
|
||||
test: /\.(sh|py)$/,
|
||||
use: "raw-loader"
|
||||
},
|
||||
],
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue