mirror of
https://github.com/wassname/talk.git
synced 2026-08-05 13:30:26 +08:00
[CORL-127] Custom CSS (#2194)
* feat: moved html-webpack-plugin to custom server templates in production * fix: fixed templates * fix: removed sri for the time being * fix: fixed up tests for new field name
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import OptimizeCssnanoPlugin from "@intervolga/optimize-cssnano-plugin";
|
||||
import CaseSensitivePathsPlugin from "case-sensitive-paths-webpack-plugin";
|
||||
import CompressionPlugin from "compression-webpack-plugin";
|
||||
import HtmlWebpackPlugin, { Options } from "html-webpack-plugin";
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import { identity } from "lodash";
|
||||
import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
||||
import path from "path";
|
||||
@@ -9,14 +9,12 @@ import WatchMissingNodeModulesPlugin from "react-dev-utils/WatchMissingNodeModul
|
||||
import TerserPlugin from "terser-webpack-plugin";
|
||||
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
|
||||
import webpack, { Configuration, Plugin } from "webpack";
|
||||
import WebpackAssetsManifest from "webpack-assets-manifest";
|
||||
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
|
||||
import ManifestPlugin from "webpack-manifest-plugin";
|
||||
|
||||
import { Config } from "./config";
|
||||
import { createClientEnv } from "./config";
|
||||
import paths from "./paths";
|
||||
import InterpolateHtmlPlugin from "./plugins/InterpolateHtmlPlugin";
|
||||
import PublicURIWebpackPlugin from "./plugins/PublicURIWebpackPlugin";
|
||||
|
||||
/**
|
||||
* filterPlugins will filter out null values from the array of plugins, allowing
|
||||
@@ -59,23 +57,16 @@ export default function createWebpackConfig(
|
||||
* ifProduction will only include the nodes if we're in production mode.
|
||||
*/
|
||||
const ifProduction = isProduction
|
||||
? <T extends {}>(...nodes: T[]) => nodes
|
||||
: <T extends {}>(...nodes: T[]) => [];
|
||||
? (...nodes: any[]) => nodes
|
||||
: (...nodes: any[]) => [];
|
||||
|
||||
const htmlWebpackConfig: Options = {
|
||||
minify: minimize && {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
};
|
||||
/**
|
||||
* ifNotProduction will only include the nodes if we're not in production
|
||||
* mode.
|
||||
*/
|
||||
const ifNotProduction = !isProduction
|
||||
? (...nodes: any[]) => nodes
|
||||
: (...nodes: any[]) => [];
|
||||
|
||||
const styleLoader = {
|
||||
loader: require.resolve("style-loader"),
|
||||
@@ -532,66 +523,50 @@ export default function createWebpackConfig(
|
||||
},
|
||||
plugins: filterPlugins([
|
||||
...baseConfig.plugins!,
|
||||
// Generates an `stream.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "stream.html",
|
||||
template: paths.appStreamHTML,
|
||||
chunks: ["stream"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `auth.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth.html",
|
||||
template: paths.appAuthHTML,
|
||||
chunks: ["auth"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `auth-callback.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth-callback.html",
|
||||
template: paths.appAuthCallbackHTML,
|
||||
chunks: ["authCallback"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `install.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "install.html",
|
||||
template: paths.appInstallHTML,
|
||||
chunks: ["install"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Generates an `admin.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "admin.html",
|
||||
template: paths.appAdminHTML,
|
||||
chunks: ["admin"],
|
||||
inject: "body",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
...ifNotProduction(
|
||||
// Generates an `stream.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "stream.html",
|
||||
template: paths.appStreamHTML,
|
||||
chunks: ["stream"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `auth.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth.html",
|
||||
template: paths.appAuthHTML,
|
||||
chunks: ["auth"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `auth-callback.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "auth-callback.html",
|
||||
template: paths.appAuthCallbackHTML,
|
||||
chunks: ["authCallback"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `install.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "install.html",
|
||||
template: paths.appInstallHTML,
|
||||
chunks: ["install"],
|
||||
inject: "body",
|
||||
}),
|
||||
// Generates an `admin.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "admin.html",
|
||||
template: paths.appAdminHTML,
|
||||
chunks: ["admin"],
|
||||
inject: "body",
|
||||
})
|
||||
),
|
||||
...ifProduction(
|
||||
// Inject the pieces we need here to resolve all the now relative url's
|
||||
// against the CDN if it's provided. It will inject the following into
|
||||
// the configuration blob on the page.
|
||||
new PublicURIWebpackPlugin(
|
||||
"{{ staticURI | dump | safe }}",
|
||||
"{{ staticURI }}"
|
||||
)
|
||||
new WebpackAssetsManifest({
|
||||
output: "asset-manifest.json",
|
||||
entrypoints: true,
|
||||
integrity: true,
|
||||
})
|
||||
),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: "asset-manifest.json",
|
||||
}),
|
||||
]),
|
||||
},
|
||||
/* Webpack config for our embed */
|
||||
@@ -622,40 +597,31 @@ export default function createWebpackConfig(
|
||||
},
|
||||
plugins: filterPlugins([
|
||||
...baseConfig.plugins!,
|
||||
...(isProduction
|
||||
? []
|
||||
: [
|
||||
// Generates an `embed.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "embed.html",
|
||||
template: paths.appEmbedHTML,
|
||||
inject: "head",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "story.html",
|
||||
template: paths.appEmbedStoryHTML,
|
||||
inject: "head",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "storyButton.html",
|
||||
template: paths.appEmbedStoryButtonHTML,
|
||||
inject: "head",
|
||||
...htmlWebpackConfig,
|
||||
}),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env),
|
||||
]),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: "embed-manifest.json",
|
||||
}),
|
||||
...ifNotProduction(
|
||||
// Generates an `embed.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "embed.html",
|
||||
template: paths.appEmbedHTML,
|
||||
inject: "head",
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "story.html",
|
||||
template: paths.appEmbedStoryHTML,
|
||||
inject: "head",
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "storyButton.html",
|
||||
template: paths.appEmbedStoryButtonHTML,
|
||||
inject: "head",
|
||||
})
|
||||
),
|
||||
...ifProduction(
|
||||
new WebpackAssetsManifest({
|
||||
output: "embed-asset-manifest.json",
|
||||
entrypoints: true,
|
||||
integrity: true,
|
||||
})
|
||||
),
|
||||
]),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import { Compiler, Plugin } from "webpack";
|
||||
|
||||
export default class InterpolateHtmlPlugin implements Plugin {
|
||||
private replacements: Record<string, string>;
|
||||
|
||||
constructor(replacements: Record<string, string>) {
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
public apply(compiler: Compiler) {
|
||||
// The following was modified from the original source:
|
||||
// https://github.com/jussikinnula/react-dev-utils/blob/9c290281877c026774c909b2900000c653f431a4/InterpolateHtmlPlugin.js
|
||||
// to support Webpack 4.
|
||||
compiler.hooks.compilation.tap("InterpolateHtmlPlugin", compilation => {
|
||||
const hooks = (HtmlWebpackPlugin as any).getHooks(compilation);
|
||||
hooks.afterTemplateExecution.tap("InterpolateHtmlPlugin", (data: any) => {
|
||||
// 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
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
import { AsyncSeriesWaterfallHook } from "tapable";
|
||||
import { Compiler, Plugin } from "webpack";
|
||||
|
||||
// Copied from @types/html-webpack-plugin
|
||||
interface HtmlTagObject {
|
||||
/**
|
||||
* Attributes of the html tag
|
||||
* E.g. `{'disabled': true, 'value': 'demo'}`
|
||||
*/
|
||||
attributes: {
|
||||
[attributeName: string]: string | boolean;
|
||||
};
|
||||
/**
|
||||
* Wether this html must not contain innerHTML
|
||||
* @see https://www.w3.org/TR/html5/syntax.html#void-elements
|
||||
*/
|
||||
voidTag: boolean;
|
||||
/**
|
||||
* The tag name e.g. `'div'`
|
||||
*/
|
||||
tagName: string;
|
||||
/**
|
||||
* Inner HTML The
|
||||
*/
|
||||
innerHTML?: string;
|
||||
}
|
||||
|
||||
type AlterAssetTagGroupsHook = AsyncSeriesWaterfallHook<{
|
||||
headTags: Array<HtmlTagObject | HtmlTagObject>;
|
||||
bodyTags: Array<HtmlTagObject | HtmlTagObject>;
|
||||
outputName: string;
|
||||
plugin: HtmlWebpackPlugin;
|
||||
}>;
|
||||
|
||||
export default class PublicURIWebpackPlugin implements Plugin {
|
||||
private configTemplate: string;
|
||||
private prefixTemplate: string;
|
||||
|
||||
constructor(configTemplate: string, prefixTemplate: string) {
|
||||
this.configTemplate = configTemplate;
|
||||
this.prefixTemplate = prefixTemplate;
|
||||
}
|
||||
|
||||
private prefixAttribute(attr: string | boolean) {
|
||||
if (!attr || typeof attr !== "string" || !attr.startsWith("/")) {
|
||||
return attr;
|
||||
}
|
||||
|
||||
return this.prefixTemplate + attr;
|
||||
}
|
||||
|
||||
private prefixTag = (tag: {
|
||||
tagName: string;
|
||||
attributes: Record<string, string | boolean>;
|
||||
}) => {
|
||||
switch (tag.tagName) {
|
||||
case "link":
|
||||
tag.attributes.href = this.prefixAttribute(tag.attributes.href);
|
||||
break;
|
||||
case "script":
|
||||
tag.attributes.src = this.prefixAttribute(tag.attributes.src);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
public apply = (compiler: Compiler) => {
|
||||
compiler.hooks.compilation.tap("PublicURIWebpackPlugin", compilation => {
|
||||
const hooks = (HtmlWebpackPlugin as any).getHooks(compilation);
|
||||
(hooks.alterAssetTagGroups as AlterAssetTagGroupsHook).tapAsync(
|
||||
"PublicURIWebpackPlugin",
|
||||
(htmlPluginData, cb) => {
|
||||
// Prefix all the asset's url's with the template.
|
||||
htmlPluginData.headTags.forEach(this.prefixTag);
|
||||
htmlPluginData.bodyTags.forEach(this.prefixTag);
|
||||
|
||||
// Insert the public path reference.
|
||||
htmlPluginData.bodyTags.unshift({
|
||||
tagName: "script",
|
||||
attributes: {
|
||||
type: "application/json",
|
||||
id: "config",
|
||||
},
|
||||
innerHTML: this.configTemplate,
|
||||
voidTag: false,
|
||||
});
|
||||
|
||||
return cb(null, htmlPluginData);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user