core-js is large in bundle (using 62kB) - javascript

I'm reducing my js bundle size and stumbled upon core-js. It takes around 62kB which represents ~24% of the whole package.
I tried using #babel/preset-env, but wasn't able to shrink the size any further. Not sure if I'm using the "right" settings:
'#babel/preset-env',
{
targets: {
browsers: ['>1%'],
},
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
},
The full webpack.config.js
const path = require('path');
const webpack = require('webpack');
const dotenv = require('dotenv');
const copyWebpackPlugin = require('copy-webpack-plugin');
const bundleOutputDir = './dist';
/* eslint-disable import/no-extraneous-dependencies */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// const CompressionPlugin = require('compression-webpack-plugin');
module.exports = (env) => {
// get project ID (environment)
const projectID = process.env.PROJECT_ID;
if (!projectID || projectID === undefined) {
throw new Error('Need env variable PROJECT_ID');
}
const isDevEnvironment = !(projectID === 'production-project');
const isDevBuild = !(env && env.prod);
const analyzeBundle = env && env.analyze;
// call dotenv and it will return an Object with a parsed key
const dotEnv = isDevEnvironment ? dotenv.config({ path: './.env.development' }).parsed : dotenv.config().parsed;
// reduce it to a nice object, the same as before
const envKeys = Object.keys(dotEnv).reduce((prev, next) => {
const updatedPrev = prev;
updatedPrev[`process.env.${next}`] = JSON.stringify(dotEnv[next]);
return updatedPrev;
}, {});
envKeys['process.env.PROJECT_ID'] = JSON.stringify(projectID);
// need to remove quotes from env
const publicURL = 'https:/mysite.com'
const plugins = [new webpack.DefinePlugin(envKeys), new ForkTsCheckerWebpackPlugin()];
if (isDevBuild) {
// eslint-disable-next-line new-cap
plugins.push(new webpack.SourceMapDevToolPlugin(), new copyWebpackPlugin([{ from: 'dev/' }]));
} else {
// Don't need to enable compressinon plugin as Firebase Hosting automatically zips the files for us
// plugins.push(new CompressionPlugin());
}
if (analyzeBundle) {
// eslint-disable-next-line global-require
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins.push(new BundleAnalyzerPlugin());
}
const babelPlugins = [
// syntax sugar found in React components
'#babel/proposal-class-properties',
'#babel/proposal-object-rest-spread',
// transpile JSX/TSX to JS
[
'#babel/plugin-transform-react-jsx',
{
// we use Preact, which has `Preact.h` instead of `React.createElement`
pragma: 'h',
pragmaFrag: 'Fragment',
},
],
[
'transform-react-remove-prop-types',
{
removeImport: !isDevBuild,
},
],
];
if (!isDevBuild) {
babelPlugins.push(['transform-remove-console', { exclude: ['error', 'warn'] }]);
}
return [
{
entry: './src/index.ts',
output: {
filename: 'widget.js',
path: path.resolve(bundleOutputDir),
publicPath: isDevBuild ? '' : publicURL,
},
devServer: {
host: '0.0.0.0', // your ip address
port: 8080,
disableHostCheck: true,
contentBase: bundleOutputDir,
open: 'google chrome',
},
plugins,
optimization: {
minimize: !isDevBuild,
nodeEnv: 'production',
mangleWasmImports: true,
removeAvailableModules: true,
usedExports: true,
sideEffects: true,
providedExports: true,
concatenateModules: true,
},
mode: isDevBuild ? 'development' : 'production',
module: {
rules: [
// packs PNG's discovered in url() into bundle
{
test: /\.(jpe?g|png|webp)$/i,
use: [
{
loader: 'responsive-loader',
options: {
// eslint-disable-next-line global-require
adapter: require('responsive-loader/sharp'),
// sizes: [160, 320, 640, 960, 1280],
name: '[path][name]-[width].[ext]',
sourceMap: isDevBuild,
},
},
],
},
{ test: /\.svg/, use: ['#svgr/webpack'] },
{
test: /\.(css)$/i,
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
},
},
{
// allows import CSS as modules
loader: 'css-loader',
options: {
modules: {
// css class names format
localIdentName: '[name]-[local]-[hash:base64:5]',
},
sourceMap: isDevBuild,
},
},
],
},
{
test: /\.(scss)$/i,
use: [
{
loader: 'style-loader',
options: { injectType: 'singletonStyleTag' },
},
{
// allows import CSS as modules
loader: 'css-loader',
options: {
modules: {
// css class names format
localIdentName: '[name]-[local]-[hash:base64:5]',
},
sourceMap: isDevBuild,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: isDevBuild,
},
},
],
},
// use babel-loader for TS and JS modeles,
// starting v7 Babel babel-loader can transpile TS into JS,
// so no need for ts-loader
// note, that in dev we still use tsc for type checking
{
test: /\.(js|ts|tsx|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'#babel/preset-env',
{
targets: {
browsers: ['>1%'],
},
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
},
],
[
// enable transpiling ts => js
'#babel/typescript',
// tell babel to compile JSX using into Preact
{ jsxPragma: 'h' },
],
],
plugins: babelPlugins,
},
},
],
},
],
},
resolve: {
extensions: ['*', '.js', '.ts', '.tsx'],
plugins: [new TsconfigPathsPlugin()],
alias: {
react: 'preact/compat',
'react-dom': 'preact/compat',
images: path.join(__dirname, 'images'),
sharedimages: path.resolve(__dirname, '../../packages/shared/src/resources'),
},
},
},
];
};

It looks like the targets property for #babel/preset-env is no longer used and instead the browserlist is recommended to include the list of supported browsers.
https://babeljs.io/docs/en/babel-preset-env#browserslist-integration

Related

WARNING in Unknown plugin: imageminSvgo. Did you forget to install the plugin?

This is the Warning that I receive from Webpack, despite installing the imageminSvgo plugin.
I have used it within the Image Minimizer Plugin as imageminSvgo, but Webpack doesn't seem to detect it.
I would really appreciate some help in knowing how to use this plugin in my project in the best way.
Here are my webpack.config.js configs.
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
// const imageminSvgo = require('imagemin-svgo')
const mode = "production";
module.exports = {
devtool: false,
stats: "errors-warnings",
resolve: {
alias: {
CssFolder: path.resolve(__dirname, "src/stylesheets/"),
JsFolder: path.resolve(__dirname, "src/javascripts/"),
ImgFolder: path.resolve(__dirname, "src/images/"),
},
},
entry: { TOAA: "./src/index.js" },
mode: mode,
devServer: {
port: 8080,
// contentBase: "./build",
hot: true,
// overlay: true,
compress: true,
historyApiFallback: true,
},
output: {
filename: "[name]-[contenthash].bundle.js",
path: path.resolve(__dirname, "build"),
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
plugins: [
// imageminSvgo({
// plugins: [
// {
// name: "removeViewBox",
// active: false,
// },
// ],
// }),
new CompressionPlugin(),
new HtmlWebpackPlugin(
{ template: "./public/index.html" },
{ inject: true }
),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename:
mode === "production"
? "[name]-[contenthash].bundle.css"
: "[name].css",
}),
new ImageMinimizerPlugin({
minimizerOptions: {
// Lossless optimization with custom option
// Feel free to experiment with options for better result for you
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
["imageminSvgo", { removeViewBox: true }],
// Svgo configuration here https://github.com/svg/svgo#configuration
[
"svgo",
{
plugins: [
{
name: "removeViewBox",
active: false,
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
},
},
{
name: "preset-default",
params: {
overrides: {
// customize plugin options
convertShapeToPath: {
convertArcs: true,
},
// disable plugins
convertPathData: false,
},
},
},
],
},
],
],
},
}),
new NodePolyfillPlugin(),
],
module: {
rules: [
{
test: /\.css$/i,
use: [
{ loader: MiniCssExtractPlugin.loader, options: {} },
{ loader: "css-loader", options: { importLoaders: 1 } },
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"autoprefixer",
{
overrideBrowserslist: ["last 3 versions", "ie >9"],
},
],
],
},
},
},
],
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.(jpe?g|png|gif|svg|jpg)$/i,
type: "asset",
},
{
test: /\.html$/i,
loader: "html-loader",
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
};
Please let me know if additional info is required.
Try reinstall imagemin forcing the installation of the plugins. Use something like this: npm install -g imagemin-cli#3.0.0 --unsafe-perm=true --allow-root

how to make webpack import polyfills only for a specific build?

I just managed to make webpack create two separete builds one for es5 and another for es6.
See below the config file:
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const es5Config = merge(common,{
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es5.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
template: "./src/template.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['#babel/preset-env', {
modules: false,
useBuiltIns: 'entry',
targets: {
browsers: [
"IE 11"
],
},
}],
],
},
},
}],
},
});
const es6Config = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es6.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
template: "./src/template.html",
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true
}
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['#babel/preset-env', {
modules: false,
useBuiltIns: "usage",
targets: {
browsers: [
'Chrome >= 60',
'Safari >= 10.1',
'iOS >= 10.3',
'Firefox >= 54',
'Edge >= 15',
],
},
}],
],
},
},
}],
},
});
module.exports = [es5Config, es6Config];
The issue is now that I wanted webpack to import the polyfills only for the es5 build. and use usebuilins set to usage did not work to polyfill anything.
Am I probably using it wrong maybe something related to the the node_modules package??
So I am just importing the polyfills in the main file like:
import "core-js/stable";
import "regenerator-runtime/runtime";
How can I make these imports to be added only for the es5 build? Or how can I include polyfills/imports from webpack?
And extra question if anyone knows, how can I use the usebuiltins with "usage" correctly? Cause so far even thought the polifylls are added for my main file I still get errors for Symbols in IE11, for example.
I figured it out. here is the webpack config:
common:
const path = require("path");
module.exports = {
entry: {
main: "./src/index.js",
vendor: "./src/vendor.js"
},
module: {
rules: [
{
test: /\.html$/,
use: ["html-loader"]
},
{
test: /\.(svg|png|jpg|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name].[hash].[ext]",
outputPath: "imgs"
}
}
}
]
}
};
prod:
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const es5Config = merge(common,{
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es5.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: (module) => {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace('#', '')}`;
},
},
},
},
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
filename: 'main.aspx',
template: "./src/main.html",
// minify: {
// removeAttributeQuotes: true,
// collapseWhitespace: true,
// removeComments: true
// }
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
new BundleAnalyzerPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
{
test: /\.m?js$/,
exclude: /node_modules/,
//exclude: /node_modules\/(?!(\#pnp)\/).*/,
use: {
loader: 'babel-loader',
options: {
//configFile : './es5.babel.config.json',
presets: [
['#babel/preset-env', {
modules: false,
useBuiltIns: false,
targets: {
browsers: [
"IE 11"
],
},
}],
],
},
},
}],
},
});
const es6Config = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.es6.js",
path: path.resolve(__dirname, "dist")
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: (module) => {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace('#', '')}`;
},
},
},
},
minimizer: [
new OptimizeCssAssetsPlugin(),
new TerserPlugin(),
new HtmlWebpackPlugin({
filename: 'main_es6.html',
template: "./src/main.html",
// minify: {
// removeAttributeQuotes: true,
// collapseWhitespace: true,
// removeComments: true
// }
}),
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name].[contentHash].css" }),
new CleanWebpackPlugin(),
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
]
},
],
},
});
module.exports = [ es5Config, es6Config];
babel.config.json:
{
"plugins": [
[
"#babel/plugin-transform-runtime",
{
"absoluteRuntime": true,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
so this combined with the cdn polyfill works to load the polifylls only for IE11. it also has it's own build.
The only issue here is that the resulting output will have separated files and the es5 build should have nomodule in all it's scripts. Also for the es6 all should have module.
I then have to go and manually add those which I can easily make a custom template to handle that.
But then the scripts for the polyfill is removed and I still have to manually merge the html files. Anyone knows how to handle that?
edit:
1 - for the attributes you can use HtmlWebpackPlugin hooks () or ScriptExtHtmlWebpackPlugin to place the attributes in the tags.
find some code with hooks here:
const HtmlWebpackPlugin = require('html-webpack-plugin');
class es5TagTransformerHook {
apply (compiler) {
compiler.hooks.compilation.tap('MyPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync(
'es5TagTransformerHook', stacktraces
async (data, cb) => {
// Manipulate the content
// data.html += 'The Magic Footer'
// Tell webpack to move on
data.bodyTags.forEach(t=>t.tagName === 'script'?t.attributes.nomodule = true:null)
cb(null, data)
}
)
})
}
}
module.exports = es5TagTransformerHook
2 - for merging the resulting html I ended up using this gist:
https://gist.github.com/agoldis/21782f3b9395f78d28dce23e3b6ddd56

How to resolve to a non default javascript file

I am using webpack and typescript in my SPA along with the oidc-client npm package.
which has a structure like this:
oidc-client.d.ts
oidc-client.js
oidc-client.rsa256.js
When I import the oidc-client in my typescript file as below:
import oidc from 'oidc-client';
I want to import the rsa256 version and not the standard version, but I am unsure how to do this.
in my webpack config I have tried to use the resolve function but not I am not sure how to use this properly:
const path = require('path');
const ForkTsChecker = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const FileManagerPlugin = require('filemanager-webpack-plugin');
const shell = require('shelljs');
const outputFolder = 'dist';
const destinationFolder = '../SPA/content';
if (shell.test('-d', outputFolder)) {
shell.rm('-rf', `${outputFolder}`);
}
if (shell.test('-d', destinationFolder)) {
shell.rm('-rf', `${destinationFolder}`);
}
module.exports = (env, options) => {
//////////////////////////////////////
/////////// HELP /////////////////////
/////////////////////////////////////
resolve: {
oidc = path.resolve(__dirname, 'node_modules\\oidc-client\\dist\\oidc- client.rsa256.slim.min.js')
};
const legacy = GenerateConfig(options.mode, "legacy", { "browsers": "> 0.5%, IE 11" });
return [legacy];
}
function GenerateConfig(mode, name, targets) {
return {
entry: `./src/typescript/main.${name}.ts`,
output: {
filename: `main.${name}.js`,
path: path.resolve(__dirname, `${outputFolder}`),
publicPath: '/'
},
resolve: {
extensions: ['.js', '.ts']
},
stats: {
children: false
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.ts\.html?$/i,
loader: 'vue-template-loader',
},
{
test: /\.vue$/i,
loader: 'vue-loader'
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'#babel/env',
{
"useBuiltIns": "usage",
"corejs": { version: 3, proposals: true },
"targets": targets
}
],
"#babel/typescript"
],
plugins: [
"#babel/transform-typescript",
[
"#babel/proposal-decorators",
{
"legacy": true
}
],
"#babel/plugin-proposal-optional-chaining",
"#babel/plugin-proposal-nullish-coalescing-operator",
"#babel/proposal-class-properties",
"#babel/proposal-numeric-separator",
"babel-plugin-lodash",
]
}
}
}
]
},
devServer: {
contentBase: path.join(__dirname, outputFolder),
compress: true,
hot: true,
index: `index.${name}.html`,
open: 'chrome'
},
plugins: [
new ForkTsChecker({
vue: true
}),
new HtmlWebpackPlugin({
filename: `index.${name}.html`,
template: 'src/index.html',
title: 'Project Name'
}),
new MiniCssExtractPlugin({ filename: 'app.css', hot: true }),
new VueLoaderPlugin()
]
};
}
Please add an alias to specify the exact .js module you'd like to import, like this for example:
resolve: {
extensions: ['.ts','.js'],
modules: ['node_modules'],
alias: {
'oidc-client': 'oidc-client-js/dist/oidc-client.rsa256.slim'
}
}
If you don't specify an alias, webpack/ will follow node module resolution and we'll eventually find your module by going to
library oidc-client-js in node_modules and follow package.json main property which is "lib/oidc-client.min.js" and that's not what you want.

Webpack build does not work with crawlers

My build works perfectly fine in browsers (even legacy v40 < chrome). It does not work with any crawler I have tried so far. There's the same error which strangely only happens in crawlers (such as googlebot) which makes it incredibly difficult to pinpoint the issue.
I tried
Disabling production build to see unminified errors, but crawlers then refuse to load the js file as it is too big
Running the site in as many browsers as I can (except IE) it works in all of them.
Disabling preloading for crawlers (renders the app's html using headless chrome)
This is the error that googlebot sees:
You can test this yourself at https://wavedistrict.com
Webpack config:
const { resolve } = require("path")
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin")
const WebpackPwaManifest = require("webpack-pwa-manifest")
const webpackMerge = require("webpack-merge")
const Visualizer = require("webpack-visualizer-plugin")
const isProduction = process.env.NODE_ENV === "production"
/**
* Variable for the project root.
* Change this when moving the configuration files
*/
const projectRoot = resolve(__dirname)
const sourceFolder = resolve(projectRoot, "src")
const tsFolder = resolve(sourceFolder, "ts")
const buildFolder = resolve(projectRoot, "build")
const publicFolder = resolve(projectRoot, "public")
const htmlTemplateFile = resolve(publicFolder, "index.html")
const tsconfigPath = resolve(projectRoot, "tsconfig.json")
const tslintPath = resolve(projectRoot, "tslint.json")
const tsLoader = {
loader: "ts-loader",
options: {
compilerOptions: {
module: "esnext",
target: "es5",
allowSyntheticDefaultImports: true,
},
transpileOnly: true,
configFile: tsconfigPath,
allowTsInNodeModules: true,
},
}
const babelLoader = {
loader: "babel-loader",
}
const workerRule = {
test: /\.worker\.ts$/,
use: {
loader: "worker-loader",
},
}
const babelRule = {
test: /\.(js|ts|tsx)$/,
use: [babelLoader],
}
const sassRule = {
test: /\.scss$/,
use: [
isProduction
? MiniCssExtractPlugin.loader
: {
loader: "style-loader",
options: {
singleton: true,
},
},
{ loader: "css-loader" },
{
loader: "sass-loader",
options: {
data: "#import './ts/modules/core/styles/_.scss';",
includePaths: [sourceFolder],
},
},
],
}
/** #type {import('webpack').Configuration} */
const baseConfig = {
context: projectRoot,
entry: [
"babel-polyfill",
"url-search-params-polyfill",
resolve(tsFolder, "init"),
],
output: {
filename: "js/[name].js",
path: buildFolder,
publicPath: "/",
},
module: {
rules: [workerRule, babelRule, sassRule],
},
resolve: {
modules: ["node_modules"],
extensions: [".js", ".ts", ".tsx", ".scss"],
alias: {
modules: resolve(tsFolder, "modules"),
common: resolve(tsFolder, "common"),
},
mainFields: ["jsnext:main", "module", "main"],
},
plugins: [
new CopyWebpackPlugin([
{
from: publicFolder,
ignore: [htmlTemplateFile],
},
]),
new CleanWebpackPlugin(buildFolder, { root: projectRoot, verbose: false }),
/**new ForkTsCheckerWebpackPlugin({
tslint: tslintPath,
tsconfig: tsconfigPath,
}),**/
],
stats: {
children: false,
entrypoints: false,
modules: false,
},
}
if (process.argv.includes("--stats")) {
if (baseConfig.plugins) {
baseConfig.plugins.push(new Visualizer())
}
}
const devConfig = webpackMerge(baseConfig, {
mode: "development",
plugins: [
new HtmlWebpackPlugin({
template: htmlTemplateFile,
chunksSortMode: "dependency",
}),
],
devtool: "inline-source-map",
devServer: {
hot: false,
historyApiFallback: true,
},
})
const prodConfig = webpackMerge(baseConfig, {
mode: "production",
optimization: {
minimize: true,
nodeEnv: "production",
},
plugins: [
new WebpackPwaManifest({
name: "WaveDistrict",
short_name: "WaveDistrict",
description: "",
background_color: "#091F35",
theme_color: "#00ba8c",
orientation: "any",
icons: [
{
src: resolve(publicFolder, "img/logo.svg"),
sizes: [48, 72, 96, 128, 144, 192, 256, 512],
destination: "icons",
},
{
src: resolve(publicFolder, "img/logo.png"),
sizes: [48, 72, 96, 128, 144, 192, 256, 512],
destination: "icons",
},
],
}),
new MiniCssExtractPlugin({
filename: "css/[name].css",
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: htmlTemplateFile,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
new HtmlWebpackInlineSourcePlugin(),
],
performance: {
maxAssetSize: 500000,
},
devtool: "source-map",
})
module.exports = isProduction ? prodConfig : devConfig
Babel config (needed to convert ES6 node_modules to ES5):
const babelEnv = {
targets: {
chrome: "41", // For googlebot
},
}
/** Keep track of all conflicting dependencies here */
const nonES5Deps = ["qs", "querystring", "query-string", "decko"]
module.exports = function(api) {
api.cache(true)
return {
exclude: [],
include: (path) => {
if (nonES5Deps.some((p) => path.match(p))) {
return true
}
if (path.match(/node_modules/)) return false
return true
},
presets: [
["#babel/preset-env", babelEnv],
"#babel/preset-react",
"#babel/preset-typescript",
],
plugins: [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-transform-arrow-functions",
[
"#babel/plugin-proposal-decorators",
{
legacy: true,
},
],
[
"#babel/plugin-proposal-class-properties",
{
loose: true,
},
],
],
}
}
So what's going on here? How can I debug the issue when it only appears in crawlers?
I have discovered the issue with the help of a friend. It appears Googlebot (and other crawlers) does not support the AudioContext object, hence undefined is not a function.
Modifying my code to check for it and safely disabling functionality if it doesn't exist has solved the problem.

Ng2-translate incompatible with webpack

Good morning guys.
I'm using the ng2-translate plugin in my application, running tns run ios works perfectly.
But when running with the command tns run ios --bundle --env.uglify --env.aot the webpack does the copying without giving any error, but the error app when opening:
CONSOLE LOG file:///app/vendor.js:1:1200993:
CONSOLE ERROR file:///app/vendor.js:1:28276: ERROR TypeError: this.http.get(this.prefix+"/"+e+this.suffix).map is not a function. (In 'this.http.get(this.prefix+"/"+e+this.suffix).map(function(e){return e.json()})', 'this.http.get(this.prefix+"/"+e+this.suffix).map' is undefined)
CONSOLE ERROR file:///app/vendor.js:1:1125775: bootstrap: ERROR BOOTSTRAPPING ANGULAR
CONSOLE ERROR file:///app/vendor.js:1:1125775: bootstrap: this.http.get(this.prefix+"/"+e+this.suffix).map is not a function. (In 'this.http.get(this.prefix+"/"+e+this.suffix).map(function(e){return e.json()})', 'this.http.get(this.prefix+"/"+e+this.suffix).map' is undefined)
getTranslation#file:///app/vendor.js:1:886381
getTranslation#file:///app/vendor.js:1:887491
retrieveTranslations#file:///app/vendor.js:1:887380
setDefaultLang#file:///app/vendor.js:1:886824
n#file:///app/bundle.js:1:88782
ka#file:///app/vendor.js:1:110925
Has anyone had this problem before?
I already tested this plugin with nativescript: https://www.npmjs.com/package/#ngx-translate/core but I did not succeed.
If you are using JSON files for storing strings, make sure those are included in Webpack's copy plug-in config.
Yes it is added in the webpack, this is my current configuration:
const { join, relative, resolve, sep } = require("path");
const webpack = require("webpack");
const nsWebpack = require("nativescript-dev-webpack");
const nativescriptTarget = require("nativescript-dev-webpack/nativescript-
target");
const { nsReplaceBootstrap } = require("nativescript-dev-
webpack/transformers/ns-replace-bootstrap");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const { NativeScriptWorkerPlugin } = require("nativescript-worker-
loader/NativeScriptWorkerPlugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const { AngularCompilerPlugin } = require("#ngtools/webpack");
module.exports = env => {
// Add your custom Activities, Services and other Android app components here.
const appComponents = [
"tns-core-modules/ui/frame",
"tns-core-modules/ui/frame/activity",
];
const platform = env && (env.android && "android" || env.ios && "ios");
if (!platform) {
throw new Error("You need to provide a target platform!");
}
const projectRoot = __dirname;
// Default destination inside platforms/<platform>/...
const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot));
const appResourcesPlatformDir = platform === "android" ? "Android" : "iOS";
const {
// The 'appPath' and 'appResourcesPath' values are fetched from
// the nsconfig.json configuration file
// when bundling with `tns run android|ios --bundle`.
appPath = "app",
appResourcesPath = "app/App_Resources",
// You can provide the following flags when running 'tns run android|ios'
aot, // --env.aot
snapshot, // --env.snapshot
uglify, // --env.uglify
report, // --env.report
sourceMap, // --env.sourceMap
} = env;
const appFullPath = resolve(projectRoot, appPath);
const appResourcesFullPath = resolve(projectRoot, appResourcesPath);
const entryModule = `${nsWebpack.getEntryModule(appFullPath)}.ts`;
const entryPath = `.${sep}${entryModule}`;
const ngCompilerPlugin = new AngularCompilerPlugin({
hostReplacementPaths: nsWebpack.getResolver([platform, "tns"]),
platformTransformers: aot ? [nsReplaceBootstrap(() => ngCompilerPlugin)] : null,
mainPath: resolve(appPath, entryModule),
tsConfigPath: join(__dirname, "tsconfig.tns.json"),
skipCodeGeneration: !aot,
sourceMap: !!sourceMap,
});
const config = {
mode: uglify ? "production" : "development",
context: appFullPath,
watchOptions: {
ignored: [
appResourcesFullPath,
// Don't watch hidden files
"**/.*",
]
},
target: nativescriptTarget,
entry: {
bundle: entryPath,
},
output: {
pathinfo: false,
path: dist,
libraryTarget: "commonjs2",
filename: "[name].js",
globalObject: "global",
},
resolve: {
extensions: [".ts", ".js", ".scss", ".css"],
// Resolve {N} system modules from tns-core-modules
modules: [
resolve(__dirname, "node_modules/tns-core-modules"),
resolve(__dirname, "node_modules"),
"node_modules/tns-core-modules",
"node_modules",
],
alias: {
'~': appFullPath
},
symlinks: true
},
resolveLoader: {
symlinks: false
},
node: {
// Disable node shims that conflict with NativeScript
"http": false,
"timers": false,
"setImmediate": false,
"fs": "empty",
"__dirname": false,
},
devtool: sourceMap ? "inline-source-map" : "none",
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
name: "vendor",
chunks: "all",
test: (module, chunks) => {
const moduleName = module.nameForCondition ?
module.nameForCondition() : '';
return /[\\/]node_modules[\\/]/.test(moduleName) ||
appComponents.some(comp => comp === moduleName);
},
enforce: true,
},
}
},
minimize: !!uglify,
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
parallel: true,
cache: true,
output: {
comments: false,
},
compress: {
// The Android SBG has problems parsing the output
// when these options are enabled
'collapse_vars': platform !== "android",
sequences: platform !== "android",
}
}
})
],
},
module: {
rules: [
{
test: new RegExp(entryPath),
use: [
// Require all Android app components
platform === "android" && {
loader: "nativescript-dev-webpack/android-app-components-loader",
options: { modules: appComponents }
},
{
loader: "nativescript-dev-webpack/bundle-config-loader",
options: {
angular: true,
loadCss: !snapshot, // load the application css if in debug mode
}
},
].filter(loader => !!loader)
},
{ test: /\.html$|\.xml$/, use: "raw-loader" },
// tns-core-modules reads the app.css and its imports using css-loader
{
test: /[\/|\\]app\.css$/,
use: {
loader: "css-loader",
options: { minimize: false, url: false },
}
},
{
test: /[\/|\\]app\.scss$/,
use: [
{ loader: "css-loader", options: { minimize: false, url: false } },
"sass-loader"
]
},
// Angular components reference css files and their imports using raw-loader
{ test: /\.css$/, exclude: /[\/|\\]app\.css$/, use: "raw-loader" },
{ test: /\.scss$/, exclude: /[\/|\\]app\.scss$/, use: ["raw-loader", "resolve-url-loader", "sass-loader"] },
{
test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
use: [
"nativescript-dev-webpack/moduleid-compat-loader",
"#ngtools/webpack",
]
},
// Mark files inside `#angular/core` as using SystemJS style dynamic imports.
// Removing this will cause deprecation warnings to appear.
{
test: /[\/\\]#angular[\/\\]core[\/\\].+\.js$/,
parser: { system: true },
},
],
},
plugins: [
// Define useful constants like TNS_WEBPACK
new webpack.DefinePlugin({
"global.TNS_WEBPACK": "true",
"process": undefined,
}),
// Remove all files from the out dir.
new CleanWebpackPlugin([`${dist}/**/*`]),
// Copy native app resources to out dir.
new CopyWebpackPlugin([
{
from: `${appResourcesFullPath}/${appResourcesPlatformDir}`,
to: `${dist}/App_Resources/${appResourcesPlatformDir}`,
context: projectRoot
},
]),
// Copy assets to out dir. Add your own globs as needed.
new CopyWebpackPlugin([
{ from: "fonts/**" },
{ from: 'styles/css/**' },
{ from: "assets/i18n/*.json"},
{ from: "**/*.jpg" },
{ from: "**/*.png" },
], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }),
// Generate a bundle starter script and activate it in package.json
new nsWebpack.GenerateBundleStarterPlugin([
"./vendor",
"./bundle",
]),
// For instructions on how to set up workers with webpack
// check out https://github.com/nativescript/worker-loader
new NativeScriptWorkerPlugin(),
ngCompilerPlugin,
// Does IPC communication with the {N} CLI to notify events when running in watch mode.
new nsWebpack.WatchStateLoggerPlugin(),
],
};
if (report) {
// Generate report files for bundles content
config.plugins.push(new BundleAnalyzerPlugin({
analyzerMode: "static",
openAnalyzer: false,
generateStatsFile: true,
reportFilename: resolve(projectRoot, "report", `report.html`),
statsFilename: resolve(projectRoot, "report", `stats.json`),
}));
}
if (snapshot) {
config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({
chunk: "vendor",
angular: true,
requireModules: [
"reflect-metadata",
"#angular/platform-browser",
"#angular/core",
"#angular/common",
"#angular/router",
"nativescript-angular/platform-static",
"nativescript-angular/router",
],
projectRoot,
webpackConfig: config,
}));
}
return config;
};

Categories