Webpack Failed to compile: loader issues with optional chaining - node.js

Created a react application with create-react-app along with typescript as the template.
Trying to use optional chaining:
const inputRef = useRef<HTMLInputElement | null>(null);
const handleClick = () => {
if(inputRef) {
inputRef?.current?.click();
}
};
On load my application gives the error:
Here is my webpack.config.js file:
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function(webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
// In development, we always serve from the root. This makes config easier.
const publicPath = isEnvProduction
? paths.servedPath
: isEnvDevelopment && '/';
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = isEnvProduction
? publicPath.slice(0, -1)
: isEnvDevelopment && '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
});
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
].filter(Boolean),
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
// Add /* filename */ comments to generated require()s in the output.
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
? 'static/js/[name].[chunkhash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? 'static/js/[name].[chunkhash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
// We use "/" in development.
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
},
optimization: {
minimize: isEnvProduction,
minimizer: [
// This is only used in production mode
new TerserPlugin({
terserOptions: {
parse: {
// we want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending futher investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true,
sourceMap: shouldUseSourceMap,
}),
// This is only used in production mode
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
// `inline: false` forces the sourcemap to be output into a
// separate file
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
}
: false,
},
}),
],
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
name: false,
},
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: true,
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'#svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
cacheCompression: isEnvProduction,
compact: isEnvProduction,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: isEnvProduction,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(paths.appPath),
new webpack.DefinePlugin(env.stringified),
// This is necessary to emit hot updates (currently CSS only):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
isEnvDevelopment && new CaseSensitivePathsPlugin(),
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
isEnvProduction &&
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: publicUrl + '/index.html',
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*',
],
watch: paths.appSrc,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
performance: false,
};
};

You haven't stated what version of create-react-app you're using, and the latest version should support optional chaining out of the box, but you can try adding support for optional chaining directly by including the associated babel plugin #babel/plugin-proposal-optional-chaining.
First npm i #babel/plugin-proposal-optional-chaining --save-dev.
Then update your webpack config module.rules for babel-loader.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'#svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
// Try adding this plugin
require.resolve('#babel/plugin-proposal-optional-chaining')
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
cacheCompression: isEnvProduction,
compact: isEnvProduction,
},
}

Related

Webpack properly compiles, but updates not recognized from webpack-dev-server

I am trying to enable webpack-dev-server's hot-reload functionality on a multi-page site based at local.www.my-domain.com. The website is running locally inside a docker container.
The pages are written in php and rendered on Twig templates. Each page inherits from a master template, and each page's bundle is also conditionally rendered on this master template using a relative path (I do not inject anything from webpack itself). The bundles are loaded appropriately and run fine with this webpack config:
const path = require("path");
const TerserPlugin = require('terser-webpack-plugin');
const crawler = require('./crawlPaths.js');
const pages = crawler.getPaths('');
module.exports = {
mode: 'development',
entry: pages.reduce((config, page) => {
config[page.filePath + page.fileName] = `./js/${page.filePath + page.fileName}.${page.ext}`;
return config;
}, {}),
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../web/dist/bundles'),
library: {
type: 'umd',
},
},
devtool: 'inline-source-map',
optimization: {
splitChunks: {
chunks: 'all',
},
minimize: false,
minimizer: [
new TerserPlugin()
],
},
}
Note: I call a function called crawler.getPaths() in the config to retrieve pages that I want rendered; the page objects have this structure:
{
ext: 'js',
fileName: 'file',
filePath: '_snippets/',
templatePath: '_snippets/file.twig',
templateExt: 'twig'
}
However, I am having trouble introducing webpack-dev-server so that the pages update automatically. I tweaked the src attribute of the script tag that renders the bundle to instead point to a full path: http://local.www.my-domain-com:8080/web/dist/bundles/<name>.js.
Then I added a publicPath key to webpack's output object and added a devServer, watch, and watchOptions objects as well.
The bundles load appropriately when I visit http://local.www.my-domain-com:8080/web/dist/bundles/<name>.js. When the corresponding bundle is updated, webpack-dev-server does recompile. However, the recompiled page is not sent to http://local.www.my-domain-com:8080/web/dist/bundles/<name>.js; I need to hit refresh to see the update.
Furthermore, when I visit local.www.my-domain.com/path/, the corresponding :8080/web/dist/bundles/<name>.js bundle is visible and loaded in the 'Sources' tab of Chrome dev-tools, but the bundle is never run nor updated.
Here is the config I am using for the webpack-dev-server:
const path = require("path");
const TerserPlugin = require('terser-webpack-plugin');
const crawler = require('./crawlPaths.js');
const pages = crawler.getPaths('');
module.exports = {
mode: 'development',
entry: pages.reduce((config, page) => {
config[page.filePath + page.fileName] = `./js/${page.filePath + page.fileName}.${page.ext}`;
return config;
}, {}),
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../web/dist/bundles'),
library: {
type: 'umd',
},
publicPath: "http://local.www.my-domain-com:8080/web/dist/bundles",
},
devtool: 'inline-source-map',
optimization: {
splitChunks: {
chunks: 'all',
},
minimize: false,
minimizer: [
new TerserPlugin()
],
},
devServer: {
static: '../web/dist/bundles',
proxy: {
'/': {
target: "http://local.www.my-domain.com",
}
},
allowedHosts: ".my-domain.com",
host: '0.0.0.0',
hot: true,
},
watchOptions: {
aggregateTimeout: 300,
poll: 1000,
ignored: /node_modules/,
},
watch: true,
}
The webpack-dev-server is a proxy for the website's Nginx server, which is running inside a docker container.
Any ideas what I need to do to get this running? And please let me know if I can provide anymore information :)

Jest Modules Do Not Import Correctly

Dealing with modules in Jest has been a nightmare.
Aside from all the other issues encountered, there is one I have yet to be able to work through.
It looks as certain imports are getting imported incorrectly.
Consider the following trivial example
import * as jp from 'jsonpath'
console.log('paths: ' + jp.paths)
when running my app code in the browser, this prints
paths: function(obj, string, count) {
assert.ok(obj instanceof Object, "obj needs to be an object");
assert.ok(string, "we need a path");
var results = this.nodes(obj, string, count)
.map(function(r) { return r.path });
return results;
}
when running the same code from a Jest test, I get:
console.log
paths: undefined
upon debugging the test and inspecting the jp object in the debugger, I see:
Needless to say this does not look correct. When running console.log(jp) from the browser, I see
It seems like Jest is screwing up the module prototype somehow.
My jest.config.js:
const esModules = ['quasar/lang', 'lodash-es'].join('|');
/* eslint-env node */
module.exports = {
globals: {
__DEV__: true,
// TODO: Remove if resolved natively https://github.com/vuejs/vue-jest/issues/175
'vue-jest': {
pug: { doctype: 'html' },
},
},
setupFilesAfterEnv: ['<rootDir>/test/jest/jest.setup.ts'],
// noStackTrace: true,
// bail: true,
// cache: false,
// verbose: true,
// watch: true,
collectCoverage: false,
coverageDirectory: '<rootDir>/test/jest/coverage',
collectCoverageFrom: [
'<rootDir>/src/**/*.vue',
'<rootDir>/src/**/*.js',
'<rootDir>/src/**/*.ts',
'<rootDir>/src/**/*.jsx',
'<rootDir>/src/**/*.tsx',
],
coveragePathIgnorePatterns: ['/node_modules/', '.d.ts$'],
coverageThreshold: {
global: {
// branches: 50,
// functions: 50,
// lines: 50,
// statements: 50
},
},
testMatch: [
// Matches tests in any subfolder of 'src' or into 'test/jest/__tests__'
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
'<rootDir>/test/jest/__tests__/**/*.(spec|test).+(ts|js)?(x)',
'<rootDir>/src/**/*.jest.(spec|test).+(ts|js)?(x)',
],
// Extension-less imports of components are resolved to .ts files by TS,
// grating correct type-checking in test files.
// Being 'vue' the first moduleFileExtension option, the very same imports
// will be resolved to .vue files by Jest, if both .vue and .ts files are
// in the same folder.
// This guarantee a great dev experience both for testing and type-checking.
// See https://github.com/vuejs/vue-jest/issues/188#issuecomment-620750728
moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'ts', 'tsx'],
moduleNameMapper: {
'^quasar$': 'quasar/dist/quasar.common.js',
'^~/(.*)$': '<rootDir>/$1',
'^src/(.*)$': '<rootDir>/src/$1',
'^app/(.*)$': '<rootDir>/$1',
'^components/(.*)$': '<rootDir>/src/components/$1',
'^layouts/(.*)$': '<rootDir>/src/layouts/$1',
'^pages/(.*)$': '<rootDir>/src/pages/$1',
'^assets/(.*)$': '<rootDir>/src/assets/$1',
'^boot/(.*)$': '<rootDir>/src/boot/$1',
'.*css$': '#quasar/quasar-app-extension-testing-unit-jest/stub.css',
},
transform: {
// See https://jestjs.io/docs/en/configuration.html#transformignorepatterns-array-string
[`^(${esModules}).+\\.js$`]: 'babel-jest',
'^.+\\.(ts|js|html)$': 'ts-jest',
// vue-jest uses find-babel-file, which searches by this order:
// (async) .babelrc, .babelrc.js, package.json, babel.config.js
// (sync) .babelrc, .babelrc.js, babel.config.js, package.json
// https://github.com/tleunen/find-babel-config/issues/33
'.*\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
},
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
testEnvironment: 'jsdom'
};
Any suggestions on how to resolve this?
Thank you.

webpack 5 maxing out on RAM when bundling for karma tests

I recently updated webpack to version 5, and now when compiling a bundle for karma tests my system is maxing out of RAM and I get the JavaScript heap out of memory error. This happens both on my local machine that has 8gb RAM, and also on a gitlab runner hosted on digital ocean which has 4gb RAM.
Locally, if I kill all other apps, and set the memory allocation options node --max-old-space-size=4096 testscript.js I can sometimes get past the bundling, so I think it currently requires around 4gb+ RAM just to bundle, and this worries me. On the gitlab runner, linux terminates the docker container and I get the "killed" error
Before updating to weback 5 and therefore also before the memory issue my webpack configuration looked a bit differently, because webpack 5 doesn't include node libs by default, I have had to add some polyfills.
These are new in the configuration
crypto: false,
fs: false,
http: false,
path: require.resolve('path-browserify'),
stream: require.resolve('stream-browserify')
and these:
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
new webpack.ProvidePlugin({ process: 'process/browser' }),
This is what the final webpack configuration for the karma bundling looks like:
const path = require('path');
const webpack = require('webpack');
const { excludeNodeModulesExcept } = require('../src/webpack/config');
const moduleListToTranspileForIE11 = ['debug', 'loader-utils', 'query-string', 'split-on-first', 'strict-uri-encode'];
const devModuleListToTranspileForIE11 = ['fetch-mock', 'proxy-polyfill', 'sinon', 'tr46', 'webidl-conversions', 'whatwg-url'];
module.exports = {
context: path.resolve(__dirname, '..'),
devtool: 'source-map',
entry: [
'./test/karma-webpack.main.js'
],
externals: {
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'css-loader', 'postcss-loader']
},
{
test: /\.js$/,
exclude: excludeNodeModulesExcept([].concat(devModuleListToTranspileForIE11, moduleListToTranspileForIE11)),
use: {
loader: 'babel-loader',
options: {
plugins: [
['#babel/plugin-transform-for-of', { assumeArray: true }]
],
presets: [
['#babel/preset-env', {}]
],
cacheDirectory: true // Note: for faster rebuilds
}
}
},
{
test: /\.svg$/,
loader: 'svg-react-loader'
}
]
},
mode: process.env.NODE_ENV || 'development',
resolveLoader: {
modules: [
'node_modules',
path.resolve(__dirname, '../src/webpack/loader')
]
},
output: {
path: path.resolve(__dirname, '../output/test'),
publicPath: '/',
filename: 'karma-webpack.js'
},
resolve: {
fallback: {
crypto: false,
fs: false,
http: false,
path: require.resolve('path-browserify'),
stream: require.resolve('stream-browserify')
}
},
performance: {
hints: false
},
plugins: [
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
new webpack.ProvidePlugin({ process: 'process/browser' }),
new webpack.IgnorePlugin(/style\/demoHack\/\w+\.css/),
new webpack.IgnorePlugin(/app\.css/),
new webpack.IgnorePlugin(/config\/output\/build\/clientConfig\.json/),
new webpack.IgnorePlugin(/output\/local\/\w+\.json/)
]
};
The entry point ./test/karma-webpack.main.js looks like this:
require('./mocha.main');
var appReq = require.context('../src', true, /\.js$/);
appReq.keys().filter(function (key) {
return ['./react.main.js', './src/helper/polyfill.js'].indexOf(key) === -1 && !key.startsWith('./tool') && !key.startsWith('./src/test');
}).map(appReq);
// Only start mocha in browser.
if (!window.__karma__) {
window.mocha.run();
}
and is based on: https://github.com/FormidableLabs/full-stack-testing/blob/master/test/client/main.js
This seems to be including all mocha (file.test.js) files in the bundling, which makes it very slow, as those files also includes a lot of devDependencies. However, this was not a problem prior to the update. I have tried filtering out all files that are .test.js - then the bundling is fast, and I don't see any memory issues, however, karma is not running any tests then, the test suite is simply empty.
I am looking for advice with anything that could look suspicious about this configuration, and why it cannot bundle with less than 4gb memory available.

WebPack 4 SplitChunksPlugin for NodeJS

I'm producing bundles for a NodeJS 8.x (AWS Lambda) and would like to selectively minify chunks (business code to remain non-minified and third party libraries to be minified to save on bundle size).
I've configured Webpack as below but it main bundle of mine doesn't work because it is not built to be able to require the other files (one file for each npm dependency).
How can I configure WebPack to produce chunks which can require each other in NodeJS?
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: (module) => {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName}`;
},
},
},
},
Instead of minifying the output chunks selectively, I opted to not use the SplitChunksPlugin at all and instead minify in a custom loader as part of the compilation.
This makes it possible to cherry pick exactly which input files (Webpack calls them assets) you want to minify and which ones you don't.
npm install terser --save-dev
tools/es-minify-loader.js
const Terser = require('terser');
module.exports = function esMinifyLoader(source) {
const output = Terser.minify(source, { compress: true, mangle: true });
return output.code;
};
webpack.config.js
...
module: {
rules: [
{
exclude: [
/dont-minify-this-file\.js/,
/some-library-or-folder-you-dont-want-minified/i,
],
test: [
/\.js$/
],
use: [
{
loader: path.resolve(__dirname, 'tools/es-minify-loader.js'),
}
]
}
]
},
...

webpack sass-loader and `MiniCssExtractPlugin.loader` did not compile scss file into css

Is there anything wrong with my webpack config? I followed tutorials online everywhere says the same way to config. But I think I must have missed something.
webpack.common.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
// const HtmlWebpackPlugin = require('html-webpack-plugin');
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
library: './src/library.js',
HelloComp: './src/components/HelloComponent/HelloComponent.vue',
Bye: './src/components/ByeComponent/ByeComponent.vue',
MiniC: './src/components/MiniC/MiniC.vue',
TestAb: './src/components/test-ab/test-ab.vue'
},
plugins: [
new CleanWebpackPlugin(['dist'])
// Do not really need it as no live app is here
// new HtmlWebpackPlugin({
// title: 'Production'
// })
],
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
library: 'library',
libraryTarget: 'umd'
},
externals: {
'vue': {
root: 'vue',
commonjs2: 'vue',
commonjs: 'vue',
amd: 'vue',
umd: 'vue'
},
'vue-router': {
root: 'vue-router',
commonjs2: 'vue-router',
commonjs: 'vue-router',
amd: 'vue-router',
umd: 'vue-router'
}
// ,
// 'style-loader': {
// root: 'style-loader',
// commonjs2: 'style-loader',
// commonjs: 'style-loader',
// amd: 'style-loader',
// umd: 'style-loader'
// },
// 'vue-hot-reload-api': {
// root: 'vue-hot-reload-api',
// commonjs2: 'vue-hot-reload-api',
// commonjs: 'vue-hot-reload-api',
// amd: 'vue-hot-reload-api',
// umd: 'vue-hot-reload-api'
// },
// 'vue-loader': {
// root: 'vue-loader',
// commonjs2: 'vue-loader',
// commonjs: 'vue-loader',
// amd: 'vue-loader',
// umd: 'vue-loader'
// }
}
}
webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// const path = require('path')
// function resolve (dir) {
// return path.join(__dirname, '..', dir)
// }
const configedAnalyzer = new BundleAnalyzerPlugin({
// Can be `server`, `static` or `disabled`.
// In `server` mode analyzer will start HTTP server to show bundle report.
// In `static` mode single HTML file with bundle report will be generated.
// In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`.
analyzerMode: 'static',
// Host that will be used in `server` mode to start HTTP server.
analyzerHost: '127.0.0.1',
// Port that will be used in `server` mode to start HTTP server.
analyzerPort: 8887,
// Path to bundle report file that will be generated in `static` mode.
// Relative to bundles output directory.
reportFilename: './../report/bundle_anlaysis.html',
// Module sizes to show in report by default.
// Should be one of `stat`, `parsed` or `gzip`.
// See "Definitions" section for more information.
defaultSizes: 'gzip',
// Automatically open report in default browser
openAnalyzer: true,
// If `true`, Webpack Stats JSON file will be generated in bundles output directory
generateStatsFile: true,
// Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`.
// Relative to bundles output directory.
statsFilename: 'stats.json',
// Options for `stats.toJson()` method.
// For example you can exclude sources of your modules
// from stats file with `source: false` option.
// See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null,
// Log level. Can be 'info', 'warn', 'error' or 'silent'.
logLevel: 'info'
})
module.exports = merge(common, {
mode: 'production',
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
},
// IMPORTANT: All js load should come AFTER vue-loader!!!
{
test: /\.(js|vue)$/,
use: 'eslint-loader',
enforce: 'pre' // kick in before other loader... Q: also before vue-loader I suppose? maybe better move it to top?
},
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
filename: '[name].css'
}
},
'css-loader', // translates CSS into CommonJS
'sass-loader' // compiles Sass to CSS, using Node Sass by default
]
},
// {
// test: /\.css$/,
// use: [
// 'vue-style-loader',
// 'css-loader'
// ]
// },
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
// {
// test: /\.scss$/,
// use: [
// MiniCssExtractPlugin.loader,
// 'style-loader', // creates style nodes from JS strings
// 'css-loader', // translates CSS into CommonJS
// 'sass-loader' // compiles Sass to CSS, using Node Sass by default
// ]
// },
{
test: /\.js$/,
use: 'babel-loader'
}
]
},
plugins: [
// new webpack.HotModuleReplacementPlugin(),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
new CopyWebpackPlugin([{
// NOTE: it does not really do anything, unless we have a asset folder, that needed no compression
from: './static/',
to: './static/',
toType: 'dir'
}]),
// NOTE: honestly, this did not help reduce prod bundle size... but for wtw:
// https://webpack.js.org/plugins/module-concatenation-plugin/
new webpack.optimize.ModuleConcatenationPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
// NOTE: disable when needed, its just to analyze code
configedAnalyzer
],
stats: {
// Examine all modules
maxModules: Infinity,
// Display bailout reasons
optimizationBailout: true
}
});
To reproduce
git clone https://github.com/adamchenwei/boilerplate-webpack-babel-sass-storybook-vuejs
npm install
./p
siily me. I forgot to actually let the webpack to see the scss file by adding entry to it. and basically just do #import '...' in library-global-styles.scss. But would love to know how to do to break them down depends on file name though...
entry: {
...
'library-global-styles': './src/library-global-styles.scss'
},

Resources