Lazy Loading not work in WebPack Angular 2 project - javascript

I'm new to webPack and I have a problem with my project in Angular 2.
I'm trying to apply Lazy Loading on the modules, but I have always the same mistake for hours.
The error occurs when i need to load a child module. (During the rooting)
export const routes: Routes =
[
{ path: '', redirectTo: '/account', pathMatch: 'full' },
{ path: 'account', component: LoginComponent },
{ path: 'student', canLoad: [AuthGuard], loadChildren: './student.module/app.student.module#AppStudentModule' },
{ path: 'teacher', canLoad: [AuthGuard], loadChildren: './teacher.module/app.teacher.module#AppTeacherModule' },
{ path: 'administrative', canLoad: [AuthGuard], loadChildren: './secretary.module/app.secretary.module#AppSecretaryModule' },
{ path: 'unauthorized', component: PageUnauthorized },
{ path: 'terms', component: TermsComponent},
{ path: '**', component: PageNotFound }
];
I show you my current configuration:
My webpack.config.js:
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeJsPlugin = require('optimize-js-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
const root = path.join.bind(path, path.resolve(__dirname));
// Webpack Config
const webpackConfig = {
entry: {
app: './src/main.ts',
polyfills: './src/polyfills.ts',
vendor: './src/vendor.ts'
},
output: {
publicPath: '/public/js',
path: path.resolve(__dirname, './public/js')
},
plugins: [
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
root('./src'), // location of your src
{} // a map of your routes
),
new webpack.ProvidePlugin({
'lodash': 'lodash',
'moment': 'moment',
'moment-timezone': 'moment-timezone',
'angular2-mdl': 'angular2-mdl',
'jquery': 'jquery'
}),
new CopyWebpackPlugin([ { from: 'src/assets/i18n', to: '/assets/i18n' } ]),
new ExtractTextPlugin('../css/styles.css')
],
module: {
loaders: [
{
test: /\.ts$/,
loaders: [
'awesome-typescript-loader',
'angular2-router-loader',
'angular2-template-loader'
]
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader?minimize=true'
}),
include: [root('assets', 'css'), /node_modules/]
},
{
test: /\.scss$/,
loader: 'raw-loader!sass-loader'
},
{test: /\.html$/, loader: 'raw-loader'},
{ test: /\.(png|jpg|woff|woff2|eot|ttf|TTF|svg)$/, loader: 'url-loader?limit=100000' }
]
}
};
const defaultConfig = {
devtool: 'source-map',
output: {
filename: '[name].js',
sourceMapFilename: '[name].map',
chunkFilename: '[name]-chunk.js'
},
resolve: {
extensions: [ '.ts', '.js' ],
modules: [ path.resolve(__dirname, 'node_modules') ]
},
node: {
global: true,
crypto: 'empty',
__dirname: true,
__filename: true,
process: true,
Buffer: false,
clearImmediate: false,
setImmediate: false
}
};
const commonConfig = webpackMerge(defaultConfig, webpackConfig);
const prodConfig = {
devtool: 'source-map',
plugins: [
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
sequences : true, // join consecutive statemets with the “comma operator”
properties : true, // optimize property access: a["foo"] → a.foo
dead_code : true, // discard unreachable code
drop_debugger : true, // discard “debugger” statements
unsafe : false, // some unsafe optimizations (see below)
conditionals : true, // optimize if-s and conditional expressions
comparisons : true, // optimize comparisons
evaluate : true, // evaluate constant expressions
booleans : true, // optimize boolean expressions
loops : true, // optimize loops
unused : true, // drop unused variables/functions
hoist_funs : true, // hoist function declarations
hoist_vars : false, // hoist variable declarations
if_return : true, // optimize if-s followed by return/continue
join_vars : true, // join var declarations
cascade : true, // try to cascade `right` into `left` in sequences
side_effects : true, // drop side-effect-free statements
warnings : true, // warn about potentially dangerous optimizations/code
},
output: {
comments: false,
space_colon: false
}
}),
new OptimizeJsPlugin({
sourceMap: false
}),
new webpack.optimize.OccurrenceOrderPlugin()
]
};
const testConfig = {
devtool: 'inline-source-map',
module: {
rules: [
{test: /\.scss$/, loaders: ['raw-loader', 'postcss-loader', 'sass-loader'], exclude: [/\.global\.scss$/, /node_modules/]},
{test: /\.global\.scss$/, loaders: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], exclude: [/node_modules/]},
{test: /\.css$/, loaders: ['style-loader', 'css-loader'], include: [/node_modules/]},
{test: /\.css$/, loaders: ['to-string-loader', 'css-loader'], exclude: [/node_modules/]},
{test: /\.html$/, loader: 'raw-loader'},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
name: '../fonts/[name].[ext]',
publicPath: '/fonts/'
}
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader',
options: {
name: '../fonts/[name].[ext]',
publicPath: '/fonts/'
}
},
{
test: /\.ts$/,
use: [
{
loader: 'awesome-typescript-loader',
query: {
// use inline sourcemaps for "karma-remap-coverage" reporter
sourceMap: false,
inlineSourceMap: true,
compilerOptions: {
// Remove TypeScript helpers to be injected
// below by DefinePlugin
removeComments: true
}
},
},
'angular2-template-loader'
],
exclude: [/\.e2e\.ts$/]
},
{
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader',
exclude: [/spec-bundle\.js/, /node_modules/]
},
{
enforce: 'post',
test: /\.(js|ts)$/,
loader: 'istanbul-instrumenter-loader',
exclude: [
/spec-bundle\.js/,
/\.(e2e|spec)\.ts$/,
/node_modules/
]
}
]
}
};
const devConfig = {
devtool: 'cheap-module-source-map',
module: {
loaders: [
{
test: /\.ts$/,
loader: 'tslint-loader',
options: {
configFile: 'tslint.json'
},
exclude: [/\.(spec|e2e)\.ts$/, /node_modules/]
},
]
}
};
switch (process.env.NODE_ENV) {
case 'staging':
case 'production':
module.exports = webpackMerge(commonConfig, prodConfig);
break;
case 'test':
module.exports = webpackMerge(commonConfig, testConfig);
break;
case 'development':
default:
module.exports = webpackMerge(commonConfig, devConfig);
}
And this is my tsconfig.json:
{
"angularCompilerOptions": {
"entryModule": "src/app.module#AppModule",
"genDir": "./ngfactory",
"debug": false
},
"awesomeTypescriptLoaderOptions": {
"useWebpackText": true
},
"buildOnSave": false,
"compileOnSave": false,
"compilerOptions": {
"allowUnusedLabels": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"es6",
"dom"
],
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": false,
"noImplicitReturns": true,
"outDir": "public/js",
"rootDir": ".",
"sourceMap": true,
"suppressImplicitAnyIndexErrors": true,
"target": "ES5",
"typeRoots": [
"node_modules/#types"
],
"types": ["node", "jasmine"]
},
"exclude": [
"node_modules",
"public"
]
}
And so i have this error:
Please help me!!
Thanks

Related

Webpack 4 won't load files after renaming to jsx extension

I'm trying to import jsx files without using the file extensions, like import LandingPage from './components/LandingPage'; and as long as the original file is named LandingPage.js this works fine, but if I rename it to LandingPage.jsx it doesn't work anymore. I already added resolve.extensions: ['.js', '.jsx'] to my webpack config file and nothing changed. If I try to import a jsx file, I get this error:
ERROR in ./src/App.js
Module not found: Error: Can't resolve './components/LandingPage' in 'C:\Users\myself\Documents\Projects\react-app-starter\src'
webpack.config.js:
/* eslint-disable import/no-extraneous-dependencies */
const path = require('path');
const BrotliPlugin = require('brotli-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
require('dotenv-defaults').config({
path: `${__dirname}/.env`,
encoding: 'utf8',
defaults: `${__dirname}/.env.sample`,
});
const commonConfig = {
entry: './src/index.js',
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(js|jsx)?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.html$/,
exclude: /template\.html$/,
use: {
loader: 'html-loader',
options: {
minimize: true,
removeComments: false,
collapseWhitespace: true,
},
},
},
{
test: /\.(png|jpg|gif|ico)$/,
use: 'file-loader',
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
},
},
},
{
test: /\.geojson$/,
loader: 'json-loader',
},
],
},
plugins: [
new Dotenv({
defaults: `${__dirname}/.env.sample`,
path: `${__dirname}/.env`,
}),
new HtmlWebPackPlugin({
template: './src/template.html',
title: process.env.APP_TITLE,
filename: 'index.html',
favicon: './src/favicon.ico',
}),
],
};
if (process.env.BABEL_USE_MATERIAL_UI_ES_MODULES) {
commonConfig.resolve = {
alias: {
'#material-ui/core': '#material-ui/core/es',
},
};
}
module.exports = (env, argv = { mode: 'development' }) => {
switch (argv.mode) {
default:
case 'development': {
return {
...commonConfig,
devServer: {
compress: process.env.WEBPACK_DEV_SERVER_COMPRESS === 'true',
host: process.env.WEBPACK_DEV_SERVER_HOST,
open: process.env.WEBPACK_DEV_SERVER_OPEN === 'true',
port: process.env.WEBPACK_DEV_SERVER_PORT,
historyApiFallback: true,
},
devtool: 'eval-source-map',
};
}
case 'production': {
return {
...commonConfig,
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[contenthash].bundle.js',
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
...commonConfig.plugins,
new BrotliPlugin({
asset: '[path].br[query]',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
],
};
}
}
};
babel.config.js:
module.exports = (api) => {
api.cache(false);
const conditionalPresets = [];
const presets = [
'#babel/preset-react',
'#babel/preset-env',
...conditionalPresets,
];
const conditionalPlugins = (process.env.BABEL_USE_MATERIAL_UI_ES_MODULES === 'true') ?
[
[
'import', {
libraryName: '#material-ui/icons',
libraryDirectory: '',
camel2DashComponentName: false,
},
],
]
: [
[
'import',
{
libraryName: '#material-ui/core',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core',
],
[
'import',
{
libraryName: '#material-ui/core/colors',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core/colors',
],
[
'import',
{
libraryName: '#material-ui/core/styles',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core/styles',
],
[
'import',
{
libraryName: '#material-ui/icons',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/icons',
],
];
const plugins = [
'#babel/proposal-class-properties',
'#babel/syntax-dynamic-import',
'#babel/transform-runtime',
'#babel/plugin-transform-react-jsx',
...conditionalPlugins,
];
return {
presets,
plugins,
};
};
My build script: "dev": "webpack-dev-server --mode development --progress"

Separate the config API with dev and prod webpack

Please tell me, here is the config.js file in it, the exported variable with a reference to the API, but there is an API for the dev version, and there is a separate API for the prod version, as I understand it, you need to make 2 separate files, but how to make it in webpack so that when dev he used one file, and prod another?
Below added webpack configuration files, a project on react.js (if that makes any difference :) )
rules.js
const rules = [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.eot(\?v=\d+.\d+.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
name: '[name].[ext]',
},
},
],
},
{
test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream',
name: '[name].[ext]',
},
},
],
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
name: '[name].[ext]',
},
},
],
},
{
test: /\.(jpe?g|png|gif|ico)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
];
export default rules;
webpack.config.common.babel.js
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import Dotenv from 'dotenv-webpack';
import rules from './utils/rules';
const config = (env) => {
const PUBLIC_PATH = env.production ? '/' : '/';
return {
entry: {
index: './src/index.js'
},
output: {
publicPath: PUBLIC_PATH,
path: path.resolve(process.cwd(), 'dist'),
filename: env.production ? '[name].js' : '[name].bundle.js'
},
resolve: {
alias: {
'react-dom': '#hot-loader/react-dom',
app: path.resolve(process.cwd(), 'src/app/'),
store: path.resolve(process.cwd(), 'src/store/'),
ui: path.resolve(process.cwd(), 'src/ui/')
},
extensions: ['*', '.js', '.jsx', '.json', 'scss']
},
module: {
rules
},
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
plugins: [
new Dotenv({
systemvars: true
}),
new HtmlWebpackPlugin({
template: 'public/index.html',
favicon: 'public/favicon.ico',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
})
]
};
};
export default config;
webpack.config.dev.babel.js
import webpack from 'webpack';
import path from 'path';
import merge from 'webpack-merge';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import common from './webpack.config.common.babel';
const config = env => merge(common(env), {
devtool: 'cheap-module-eval-source-map',
target: 'web',
mode: 'development',
optimization: {
minimize: false
},
performance: {
hints: false
},
devServer: {
historyApiFallback: true,
contentBase: '../dist',
port: 3009,
hot: true,
clientLogLevel: 'none'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
{
test: /(\.css|\.scss|\.sass)$/,
use: [
{
loader: 'style-loader',
options: { singleton: true }
},
{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
/* eslint-disable global-require */
plugins: () => [require('autoprefixer')],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'src', 'scss')]
}
}
]
}
]
},
plugins: [
new webpack.NamedModulesPlugin(),
// new BundleAnalyzerPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.HotModuleReplacementPlugin()
]
});
export default config;
webpack.config.common.babel.js
import path from 'path';
import webpack from 'webpack';
import merge from 'webpack-merge';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import common from './webpack.config.common.babel';
const config = env => merge(common(env), {
devtool: 'source-map',
target: 'web',
mode: 'production',
optimization: {
minimize: true,
nodeEnv: 'production',
sideEffects: true,
concatenateModules: true,
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
module: {
rules: [
{
test: /(\.css|\.scss|\.sass)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
/* eslint-disable global-require */
plugins: () => [require('cssnano'), require('autoprefixer')],
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'src', 'scss')],
sourceMap: true,
},
},
],
},
],
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash].css',
})
],
});
export default config;
You can define two different npm scripts and use the --config flag for custom files (/file-paths):
"build:dev": "webpack --config ./webpack.client.dev.js",
"build:prod": "webpack --config ./webpack.client.prod.js"
For the common you have to use webpack-merge, for example:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
...
});

Unexpected character '#' You may need an appropriate loader to handle this file type

This is what I get when I try to run webpack: the error I get is:
"ERROR in ./v3/app/styles/main.scss
Module parse failed: /Users/vovina/widget-login-react/v3/app/styles/main.scss Unexpected character '#' (1:0)
You may need an appropriate loader to handle this file type."
it can't resolve #import, any ideas on this?
my webpack config is as follow:
const childProcess = require('child_process')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const trimEnd = require('lodash/trimEnd')
const webpack = require('webpack')
const path = require('path')
const ENV = {
NODE_ENV: process.env.NODE_ENV,
API: 'https://accounts' + (process.env.NODE_ENV === 'prd' ? '' : '-'
+ process.env.NODE_ENV),
BUILD_VERSION: trimEnd(childProcess.execSync('git rev-list HEAD --
count').toString(), '\n'),
BUILD_DATE: trimEnd(childProcess.execSync('git log --format="%cd" -n
1').toString(), '\n'),
BUILD_COMMIT_ID: trimEnd(childProcess.execSync('git log --format="%h"
-n 1').toString(), '\n')
}
const prodLikeEnvironment = process.env.NODE_ENV === 'stg' ||
process.env.NODE_ENV === 'prd'
const CSS_MAPS = !prodLikeEnvironment
module.exports = {
entry: {
init: [
'./app/init.js'
],
login: [
'./app/login.js'
],
authentication: [
'./v3/app/authenticator.js'
],
common: [
'./app/common.js'
]
},
target: 'web',
output: {
path: path.join(__dirname, 'dist', process.env.NODE_ENV),
pathinfo: true,
publicPath: '/assets/widgets/login/v2/',
filename: '[name].bundle.js',
chunkFilename: '[id].bundle.js',
libraryTarget: 'umd'
},
resolve: {
alias: {
'react': 'preact-compat',
'react-dom': 'preact-compat'
},
modules: [
path.resolve('./app'),
path.resolve('./node_modules')
]
},
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: ['babel-loader'],
exclude: [/bower_components/, /node_modules/]
},
{
// Transform our own .(scss|css) files with PostCSS and CSS-modules
test: /\.(scss|css)$/,
include: [path.resolve(__dirname, 'v3/app')],
options: {
sourceMap: true
},
loader: [
`style-loader?singleton`,
`css-loader?modules&importLoaders=1&localIdentName=
[local]${process.env.CSS_MODULES_IDENT ||
'_[hash:base64:5]'}&sourceMap=${CSS_MAPS}`,
'postcss-loader',
`sass-loader?sourceMap=${CSS_MAPS}`
].join('!')
},
{
test: /\.(scss|css)$/,
exclude: [path.resolve(__dirname, 'v3/app')],
options: {
sourceMap: true
},
loader: [
`style-loader?singleton`,
`css-loader?sourceMap=${CSS_MAPS}`,
`postcss-loader`,
`sass-loader?sourceMap=${CSS_MAPS}`
].join('!')
},
{
test: /\.(svg|eot|woff|woff2?|ttf|otf)$/,
use: 'base64-font-loader'
},
{
test: /.json$/,
loader: 'json'
},
{
test: /\.jpe?g$|\.gif$|\.png$/,
use: 'base64-image-loader'
},
{
test: /\.html?$/,
loader: 'html'
},
{
test: /\.js$/,
loader: 'strip-loader?strip[]=debug,strip[]=console.log,strip[]=console.debug,strip[]=console.info'
}
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true
// postcss: [
// autoprefixer({ browsers: 'last 2 versions' })
// ]
}),
new CopyWebpackPlugin([
{ from: 'public' }
]),
new ExtractTextPlugin('[name].bundle.css'),
new webpack.DefinePlugin({
ENV: JSON.stringify(ENV),
// Only used for react prod bundle. Refer to ENV.NODE_ENV for business
logic
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false
},
compress: {
unsafe_comps: true,
properties: true,
keep_fargs: false,
pure_getters: true,
collapse_vars: true,
unsafe: true,
warnings: false,
screw_ie8: true,
sequences: true,
dead_code: true,
drop_debugger: true,
comparisons: true,
conditionals: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
hoist_funs: true,
if_return: true,
join_vars: true,
cascade: true,
drop_console: true
}
})
]
}
You are using the (scss|css) twice in your configuration file.
Remove that and use the code posted blow:
Before using, you must first npm install raw-loader.
I think you've already installed the sass-loader.
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw-loader'
},
// // css global which not include in components
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
use: ExtractTextPlugin.extract({
use: 'raw-loader'
})
},
{
test: /\.scss$/,
include: helpers.root('src', 'app'),
use: ['raw-loader', 'sass-loader']
},
// // SASS global which not include in components
{
test: /\.scss$/,
exclude: helpers.root('src', 'app'),
use: ExtractTextPlugin.extract({
use: ['raw-loader', 'sass-loader']
})
}
Add my root()function.
var path = require('path');
var _root = path.resolve(__dirname, '..');
function root(args) {
args = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [_root].concat(args));
}
exports.root = root;
Hope this will work.

WebPack optimization with Awesome-Typescript-Loader not seeing much improvement

I'm trying to improve my WebPack performance so I switched out TS-Loader for Awesome-TypeScript-Loader, and my compile times only increased a tiny bit. I had heard ATL is supposed to be much faster, but I only shaved off maybe a few 100ms.
Here's my webpack.config.js:
'use strict';
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
const CleanWebpackPlugin = require('clean-webpack-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const WebpackNotifierPlugin = require('webpack-notifier');
const { CheckerPlugin, TsConfigPathsPlugin } = require('awesome-typescript-loader')
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
// borrowed from the ng2 seed
const entryPoints = ['inline', 'polyfills', 'sw-register', 'styles', 'vendor', 'app'];
const htmlConfig = {
template: 'client/_index.html',
filename: 'index.html',
// borrowed from the ng2 seed
chunksSortMode: (left, right) => {
const leftIndex = entryPoints.indexOf(left.names[0]);
const rightIndex = entryPoints.indexOf(right.names[0]);
if (leftIndex > rightIndex) {
return 1;
} else if (leftIndex < rightIndex) {
return -1;
} else {
return 0;
}
}
};
function isExternal(module) {
const match = typeof module.userRequest === 'string' &&
/node_modules/.test(module.userRequest.split('!').pop());
return match;
}
// const extractCSS = new ExtractTextPlugin({ filename: '[name].[hash].css', allChunks: true })
// const extractJS = new ExtractTextPlugin({ filename: '[name].[hash].js', allChunks: true })
module.exports = {
cache: true,
entry: {
polyfills: './client/app/polyfills.js',
// base: './client/base.js',
app: './client/app/app.ts'
},
output: {
// Absolute output directory
path: path.join(__dirname, '/dist/'),
// Output path from the view of the page
// Uses webpack-dev-server in development
publicPath: '/',
// Filename for entry points
// Only adds hash in build mode
filename: '[name].[hash].js',
// Filename for non-entry points
// Only adds hash in build mode
chunkFilename: '[name].[hash].js'
},
resolve: {
modules: [
path.resolve(__dirname, 'client'),
'node_modules'
],
extensions: ['.ts', '.js'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'vue-router$': 'vue-router/dist/vue-router.esm.js',
'vuex$': 'vuex/dist/vuex.esm.js',
'vuex-class$': 'vuex-class/dist/vuex-class.cjs.js',
'styles': path.resolve(__dirname, 'client/app/styles'),
'core': path.resolve(__dirname, 'client/app/core'),
'components': path.resolve(__dirname, 'client/app/components'),
'containers': path.resolve(__dirname, 'client/app/containers'),
'assets': path.resolve(__dirname, 'client/assets'),
'config': path.resolve(__dirname, 'client/config')
}
},
// Use 'source-map' for more accurate source mapping
// Use 'cheap-module-eval-source-map' for faster rebuilding
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{
// ES LINT LOADER
// Reference: https://github.com/MoOx/eslint-loader
// Lint ECMAScript
enforce: 'pre',
test: /\.js$/,
use: [
{ loader: 'eslint-loader', options: { cache: true } }
],
include: [path.resolve(__dirname, 'client')]
},
{
// TS LINT LOADER
// Reference: https://github.com/wbuchwalter/tslint-loader
// Lint TypeScript
enforce: 'pre',
test: /\.ts$/,
use: ['tslint-loader'],
include: [path.resolve(__dirname, 'client')]
},
{
// SOURCE MAP LOADER
// Reference: https://github.com/webpack-contrib/source-map-loader
// Extracts SourceMaps for source files that as added as sourceMappingURL comment
enforce: 'pre',
test: /\.(j|t|s?(a|c)s)s$/, // ts or js or css or scss or sass (or ass)
use: ['source-map-loader'],
exclude: [path.resolve(__dirname, 'node_modules/css-loader/lib/convert-source-map.js')] // There is a sourceMappingURL example in this file that triggers a warning
},
{
// JS LOADER
// Reference: https://github.com/babel/babel-loader
// Transpile .js files using babel-loader
// Compiles ES6 and ES7 into ES5 code
test: /\.js$/,
use: [
{ loader: 'babel-loader', options: { sourceMap: true, cacheDirectory: true } }
],
exclude: [path.resolve(__dirname, 'node_modules')]
},
{
// AWESOME TYPESCRIPT LOADER
// Reference: https://github.com/s-panferov/awesome-typescript-loader
// Transpile .ts files using awesome-typescript-loader
// Compiles TS into ES6 code
test: /\.ts$/,
use: [
// { loader: 'babel-loader', options: { sourceMap: true, cacheDirectory: true } },
// { loader: 'ts-loader' }
{ loader: 'awesome-typescript-loader', options: { sourceMap: true, useCache: true, useBabel: true, useTranspileModule: true } }
],
exclude: [path.resolve(__dirname, 'node_modules')]
},
{
// ASSET LOADER
// Reference: https://github.com/webpack/url-loader
// Copy png, jpg, jpeg, gif, svg, woff, woff2, ttf, eot files to output
// Rename the file using the asset hash
// Pass along the updated reference to your code
// You can add here any file extension you want to get copied to your output
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|mp4)([?]?.*)$/,
use: [
{ loader: 'file-loader', options: { name: 'assets/[name].[hash].[ext]' } }
]
},
{
// HTML LOADER
// Reference: https://github.com/webpack/html-loader
// Allow loading html through js
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: {
sourceMap: true,
exportAsEs6Default: true,
root: path.resolve(__dirname, 'client'),
// Look for and process the following tag:attr on html elements
attrs: [
'img:src',
'video:src'
]
}
}
]
},
{
// CSS LOADER
// Reference: https://github.com/webpack-contrib/css-loader
test: /\.css$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { sourceMap: true, importLoaders: 1 } },
{ loader: 'postcss-loader', options: { sourceMap: true } }
]
},
{
// SASS LOADER
// Reference: https://github.com/jtangelder/sass-loader
test: /\.s(a|c)ss$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { sourceMap: true, importLoaders: 2 } },
{ loader: 'postcss-loader', options: { sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } }
]
}
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
cache: true,
debug: true,
minimize: false
}),
new webpack.NamedModulesPlugin(),
new WebpackNotifierPlugin(),
new TsConfigPathsPlugin(),
new CheckerPlugin(),
new StyleLintPlugin({
files: ['./client/**/*.s?(a|c)ss']
}),
// Remove dist and build directories before building
new CleanWebpackPlugin(['dist']),
// extractCSS,
new CommonsChunkPlugin({
name: 'vendor',
// Only analyze the app entry point, we don't want to change the others
chunks: ['app'],
// Check if module is in node_modules
minChunks: (module) => {
return isExternal(module);
}
}),
new HtmlWebpackPlugin(htmlConfig)
],
devServer: {
historyApiFallback: true,
compress: false,
inline: true,
hot: true
}
};
And my tsconfig.json:
{
"compilerOptions": {
"allowJs": true,
"target": "ES2017",
"module": "ES2015",
"moduleResolution": "Node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"noEmitHelpers": true,
"importHelpers": true,
"pretty": true,
"alwaysStrict": true,
"lib": [
"DOM",
"ES2017",
"DOM.Iterable",
"ScriptHost"
],
"baseUrl": ".",
"paths": {
"styles/*": ["./client/app/styles/*"],
"core/*": ["./client/app/core/*"],
"components/*": ["./client/app/components/*"],
"containers/*": ["./client/app/containers/*"],
"assets/*": ["./client/assets/*"],
"config/*": ["./client/config/*"]
}
}
}
And the .babelrc file:
{
"presets": [
[
"env", {
"modules": false,
"useBuiltIns": true,
"debug": true,
"targets": {
"browsers": [
"last 1 Chrome version"
]
}
}]
]
}
I'm targeting ES2017 from TS and leaving it up to Babel to compile for our target browser; as in development we want the extra performance of native code, and less difference of the transpiled coutput.
It seems to be hung up on Babel, especially, as it gets bogged down in the "asset optimization" stage.
TS-loader + Babel: ~1500ms
ATL + Babel: ~1100ms
ATL - Babel: ~800ms
Also, I don't see any performance difference with ATL between useCache: true/false nor useTranspileModule: true/false. But I do see it making a TON of files in the .awcache folder, every time it recompiles (wondering if something is cache busting).
Running webpack-dev-server with params:
webpack-dev-server --watch --hot --progress --open
Using the latest versions of all packages, loaders, and tools as of this post.
Any help would be greatly appreciated!
Thanks!

Webpack babel config for both server and client javascript?

I'm trying to figure out how to have a single webpack config file that works for transforming both server (node.js) js and client js with the es2015 preset. Currently I have to specifically set "target: 'node'" for it to correctly process node-based files. If I don't, then webpack does the transformation based on the default "target: 'web'". It then reports errors because the 'mysql' module being imported clearly won't work for web.
How can I unify both into the same config file so that server and client js will be transformed separately? Or do I need separate configs entirely?
Sample webpack.config.js
'use strict';
var path = require('path');
var webpack = require('webpack');
module.exports = {
target: 'node',
resolve: {
root: path.resolve(__dirname),
extensions: ['', '.js']
},
entry: [
'babel-polyfill',
'./index.js',
],
output: {
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015']
}
}
]
}
};
Sample js code
import 'babel-polyfill';
import mysql from 'mysql';
class Test {
constructor () {
}
get read () {
}
};
You can pass multiple configs for Webpack to process at the same time. Simply return an array of configuration objects.
export default [
{
// config 1 here
},
{
// config 2 here
},
];
Extra tip: if you use .babel.js as extension for your config file, Webpack will run it through Babel for you, which allows you to use ES6 in your Webpack config.
Bonus: the snippet below
// Source: https://gist.github.com/Ambroos/f23d517a4261e52b4591224b4c8df826
import webpack from 'webpack';
import path from 'path';
import CleanPlugin from 'clean-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import AssetsPlugin from 'assets-webpack-plugin';
import CompressionPlugin from 'compression-webpack-plugin';
import autoprefixer from 'autoprefixer';
import rucksack from 'rucksack-css';
import cssnano from 'cssnano';
import moment from 'moment';
const sharedPlugins = [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /nl/),
new webpack.optimize.AggressiveMergingPlugin({}),
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.optimize.UglifyJsPlugin({
compress: {
drop_console: true,
screw_ie8: true,
sequences: true,
properties: true,
dead_code: true,
drop_debugger: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
if_return: true,
join_vars: true,
cascade: true,
negate_iife: true,
hoist_funs: true,
warnings: false,
},
mangle: {
screw_ie8: true,
},
output: {
screw_ie8: true,
preamble: '/* Website - ' + moment().format() + ' */',
},
}),
];
const sharedServerPlugins = [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /nl/),
new webpack.optimize.AggressiveMergingPlugin({}),
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.optimize.UglifyJsPlugin({
compress: {
drop_console: false,
screw_ie8: true,
sequences: true,
properties: true,
dead_code: true,
drop_debugger: false,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
if_return: true,
join_vars: true,
cascade: true,
negate_iife: true,
hoist_funs: true,
warnings: false,
},
mangle: {
screw_ie8: true,
},
output: {
screw_ie8: true,
preamble: '/* Website - ' + moment().format() + ' */',
},
}),
];
const PATHS = {
build: path.resolve(__dirname, '..', 'build'),
sourcemaps: path.resolve(__dirname, '..', 'build', 'sourcemaps'),
browserSource: path.resolve(__dirname, '..', 'src', 'browser', 'index.js'),
browserBuild: path.resolve(__dirname, '..', 'build', 'browser'),
serverSource: path.resolve(__dirname, '..', 'src', 'server', 'index.js'),
serverAssetsSource: path.resolve(__dirname, '..', 'src', 'server', 'assets', 'index.js'),
serverBuild: path.resolve(__dirname, '..', 'build', 'server'),
};
export default [
// Browser
{
entry: { browser: PATHS.browserSource },
output: {
path: PATHS.browserBuild,
filename: 's/[chunkhash].js',
chunkFilename: 's/async-[chunkhash].js',
publicPath: '/',
sourceMapFilename: '../sourcemaps/browser/[file].map',
},
devtool: 'hidden-source-map',
plugins: [
new AssetsPlugin({
prettyPrint: true,
path: path.resolve(PATHS.build, 'browserAssets'),
filename: 'index.js',
processOutput: assets => `module.exports = ${JSON.stringify(assets, null, ' ')};`,
}),
new CleanPlugin([PATHS.browserBuild, PATHS.sourcemaps], path.resolve(PATHS.build, 'browserAssets')),
new ExtractTextPlugin('s/[contenthash].css'),
new CompressionPlugin({
asset: '{file}.gz',
algorithm: 'gzip',
regExp: /\.js$|\.html$|\.css$|\.svg$|\.eot$|\.xml$/,
threshold: 1400,
minRation: 0.8,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK_ENV: JSON.stringify('browser'),
APP_ENV: (process.env.APP_ENV && JSON.stringify(process.env.APP_ENV)) || undefined,
},
}),
].concat(sharedPlugins),
externals: [
{ browserConfig: 'var websiteBrowserConfig' },
{ programs: 'var programs' },
],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.json$/,
loader: 'json',
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract(
'style',
[
'css?importLoaders=2&localIdentName=css-module-[hash:base64]',
'postcss',
'sass',
]
),
},
{
test: /\.(gif|png|jpe?g|svg|ico)$/i,
loaders: [
'url?limit=1400&name=s/i/[sha512:hash:base64:16].[ext]',
'image-webpack?bypassOnDebug',
],
},
{
test: /isotope\-|fizzy\-ui\-utils|desandro\-|masonry|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports?define=>false&this=>window',
},
{
test: /flickity/,
loader: 'imports?define=>false&this=>window',
},
{
test: /node_modules\/unipointer/,
loader: 'imports?define=>undefined',
},
],
},
postcss: () => {
return [rucksack, autoprefixer, cssnano];
},
},
// Server assets
{
entry: { assets: PATHS.serverAssetsSource },
target: 'node',
output: {
path: PATHS.browserBuild,
libraryTarget: 'commonjs',
filename: '../serverAssets/index.js',
publicPath: '/',
},
plugins: [
// assetsWriter,
new CompressionPlugin({
asset: '{file}.gz',
algorithm: 'gzip',
regExp: /\.js$|\.html$|\.css$|\.svg$|\.eot$|\.xml$/,
threshold: 1400,
minRation: 0.8,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK_ENV: JSON.stringify('assets'),
APP_ENV: (process.env.APP_ENV && JSON.stringify(process.env.APP_ENV)) || undefined,
},
}),
].concat(sharedPlugins),
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.json$/,
loader: 'json',
},
{
test: /\.(gif|png|jpe?g|svg|ico)$/i,
loaders: [
'url?limit=1400&name=s/i/[sha512:hash:base64:16].[ext]',
'image-webpack?bypassOnDebug',
],
},
],
},
},
// Server
{
entry: PATHS.serverSource,
target: 'node',
output: {
path: PATHS.build,
libraryTarget: 'commonjs',
filename: 'server/server.js',
publicPath: '/s/',
sourceMapFilename: 'sourcemaps/browser/[file].map',
},
externals: [
{ serverAssets: '../serverAssets/index.js' },
{ browserAssets: '../browserAssets/index.js' },
{ vrtConfig: '../../env_vars.js' },
/^(?!\.|\/).+/i,
/webpack-assets\.json/,
],
plugins: [
new CleanPlugin(PATHS.serverBuild),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK_ENV: JSON.stringify('server'),
APP_ENV: (process.env.APP_ENV && JSON.stringify(process.env.APP_ENV)) || undefined,
},
}),
].concat(sharedServerPlugins),
node: {
__dirname: false,
__filename: false,
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
},
{
test: /\.json$/,
loader: 'json',
},
],
},
},
];
That is the config we use in one of our sites with a partially shared codebase, and partially shared assets. It consists of three Webpack builds in one:
Browser
Server assets (images/fonts/... that will be referenced in server-generated HTML)
Server code (Node)
The server code has a few special properties:
target: 'node' (Webpack needs this)
output.libraryTarget: 'commonjs' (makes Webpack use commonjs for unbundled libs)
externals: [ /^(?!\.|\/).+/i, ] (makes Webpack not bundle anything in node_modules, or anything that is not a relative path (starting with . or /)
This combination makes Webpack only process your own code, and access other modules and libraries through require. Which means your dependencies using native bindings won't break as they won't be bundled.

Categories

Resources