Integration of back-end webpack with Vue-CLI 3 - vue.js
I'm developing a full-stack application using Vue-CLI 3. The backend is written in TypeScript and so requires compilation etc. Currently I'm just using ts-node to run the backend app directly which works, but it would be much cleaner if I could also webpack the backend app to give a single server.js rather than multiple sprawling typescript files all over the place.
The problem is that the way I'd do this normally is have a single webpack.base.js and then include that in different webpack config files. I can't really do that because a lot of the webpack configuration is hidden away in vue-cli-service
Ended up pretty much having a base webpack config and extending client and server webpack configuration.
My app root webpack.config.js looks like this:
const { config: client } = require('./src/client/webpack.config');
const { config: server } = require('./src/server/webpack.config');
/**
* Webpack Build File
*
* This webpack configuration is used for only building the client and server
* bundles. It imports both of these from their respective directories, but
* allows for overrides if required.
*
* Other dev tools such as watching, hot module reloading etc. has been split
* out into other config files
*
* #param {object} env Webpack `env` object
*/
module.exports = ({ mode = 'development' } = {}) => ([
{
...client({ mode }, process.env),
},
{
...server({ mode }, process.env),
},
]);
My src/client/webpack.config.js looks like this:
const { resolve: _resolve, sep } = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TSConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackRootElementPlugin = require('html-webpack-root-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const { DefinePlugin } = require('webpack');
const paths = {
src: {
rootDir: _resolve(__dirname) + sep,
app: _resolve(__dirname, 'app') + sep,
},
dist: {
rootDir: _resolve(__dirname, '..', '..', 'dist'),
app: _resolve(__dirname, '..', '..', 'dist', 'public'),
},
};
/**
* Client webpack build configuration.
*
* This webpack config produces a bundle for the client-side application only.
*
* #param {object} webpackEnv Webpack env object (basically any/all options passed in via the CLI)
* #param {object} processEnv Process env object (environment variables from process.env)
*/
const config = ({ mode = 'none' }, { APP_NAME = '', BASE_URL = '/' } = {}) => ({
name: 'client',
target: 'web',
mode,
entry: {
app: paths.src.app + 'main.ts',
},
output: {
path: paths.dist.app,
},
optimization: {
runtimeChunk: 'single',
},
resolve: {
extensions: ['.ts', '.js', '.vue'],
plugins: [
new TSConfigPathsPlugin({
configFile: paths.src.rootDir + 'tsconfig.json',
}),
],
},
context: paths.src.rootDir,
module: {
rules: [
{
test: /\.tsx?$/,
include: paths.src.rootDir,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
options: {
happyPackMode: true,
},
},
],
},
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.s?[ac]ss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
{
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
loader: 'file-loader',
options: {
name: 'assets/[name].[hash].[ext]',
esModule: false,
},
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
favicon: paths.src.app + 'assets/logo.png',
title: APP_NAME,
}),
new HtmlWebpackRootElementPlugin('app'),
new VueLoaderPlugin(),
new DefinePlugin({
'process.env.BASE_URL': JSON.stringify(BASE_URL),
}),
],
});
module.exports = {
config,
paths,
};
and my src/server/webpack.config.js looks like this:
const { resolve: _resolve, sep } = require('path');
const TSConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
const { IgnorePlugin } = require('webpack');
const paths = {
src: _resolve(__dirname) + sep,
dist: _resolve(__dirname, '..', '..', 'dist'),
};
/**
* NestJs uses a custom wrapper around require() that allows it to show a
* warning when some extra package needs to be installed. This causes problems
* with webpack, so we're blacklisting packages we're not using with the
* IgnorePlugin below.
*
* To de-blacklist a package, just remove it from this array.
*/
const nestBlacklist = [
'^cache-manager$',
'^#nestjs/microservices$',
// packages below are required from microservices
'^amqp-connection-manager$',
'^amqplib$',
'^grpc$',
'^mqtt$',
'^nats$',
'^redis$',
];
/**
* Server webpack build configuration.
*
* This webpack config produces a bundle for the server-side application only.
*
* #param {object} webpackEnv Webpack env object (basically any/all options passed in via the CLI)
* #param {object} processEnv Process env object (environment variables from process.env)
*/
const config = ({ mode = 'none' }) => ({
name: 'server',
mode,
target: 'node',
entry: paths.src + 'main.ts',
externals: [nodeExternals()],
output: {
path: paths.dist,
filename: 'server.js',
},
resolve: {
extensions: ['.ts', '.js'],
plugins: [
new TSConfigPathsPlugin({
configFile: './tsconfig.build.json',
}),
],
},
context: paths.src,
optimization: {
minimize: false,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.ts$/,
include: paths.src,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
options: {
happyPackMode: true,
},
},
],
},
],
},
plugins: [
new IgnorePlugin({
contextRegExp: /#nestjs/,
resourceRegExp: new RegExp(nestBlacklist.join('|')),
}),
],
node: {
__dirname: false,
},
});
module.exports = {
config,
paths,
};
This allows me to have distinct server and client side compilation configuration, but in such a way that's re-usable in other configurations. So, for webpack dev server - I have webpack.dev.config.js which contains the following:
const client = require('./src/client/webpack.config');
const server = require('./src/server/webpack.config');
const { HotModuleReplacementPlugin } = require('webpack');
const {
SERVER_PORT = 3000,
} = process.env;
/**
* Watch settings are the same between client and server, so we're keeping them
* here for consistency
*/
const watchConfig = {
watch: true,
watchOptions: {
ignored: /node_modules/,
},
};
/**
* Development Webpack Build File
*
* This webpack configuration extends `webpack.config.js` and builds on it to
* provide hot module replacement, watch moide and a dev server for the
* client-side code
*
* Other dev tools such as watching, hot module reloading etc. has been split
* out into other config files
*
* #param {object} env Webpack `env` object
*/
module.exports = ({ mode = 'development' } = {}) => ([
{
...client.config({ mode }, process.env),
...watchConfig,
devServer: {
contentBase: client.paths.dist.app,
historyApiFallback: {
rewrites: [
{ from: /./, to: '/index.html' },
],
},
port: 8000,
host: '0.0.0.0',
hot: true,
hotOnly: true,
proxy: {
'/api': `http://localhost:${SERVER_PORT}/`,
},
},
},
{
...server.config({ mode }, process.env),
...watchConfig,
plugins: [
...server.config({ mode }, process.env).plugins,
new HotModuleReplacementPlugin(),
],
},
]);
This config provides a setup that produces a server.js which then serves up a directory (in this case '/public'`) as it's static files dir.
Related
Module parse failed: Unexpected character '#' even with shebang loader
I am trying to use https://github.com/dipson88/treeselectjs in my polymer 3 application. Though, when I try to import it in one of my js file like import Treeselect from 'treeselectjs' it throws the following error: Module parse failed: Unexpected character '#' (1:2341) File was processed with these loaders: my webpack is : const merge = require('webpack-merge'); const flowDefaults = require('./webpack.generated.js'); module.exports = merge(flowDefaults, { mode: 'production', module: { rules: [ { test: /\.svg$/, use: { loader: 'html-loader', options: { minimize: true } } }, { test: /\.mjs.js$/, loader: 'string-replace-loader', options: { search: '#', replace: '', } }, { test: /\.js$/, loader: ['shebang-loader','ify-loader'] } ] }, stats: { warnings: false } }); How can I resolve this
How to distinguish files which have the same name but in different sub-folders in sourcemap?
In my VUE project, there are several templates which have the same filename but located in different source sub-folders. I'm using webpack 3.12 to build it, and the devtool set to 'cheap-module-eval-source-map'. After I run webpack-dev-server 2.11.1 to debug it, all template source files are put into the root folder 'webpack://' of the browser's sourcemap, so ONLY one of these files can exist, others are lost, I can't debug them. Is there a way to make these files co-existing in the sourcemap? module.exports = { plugins: [ new VueLoaderPlugin() ], context: path.resolve(__dirname, '../'), entry: { app: ['babel-polyfill', './src/main.js'] }, output: { path: config.build.assetsRoot, filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, resolve: { extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.esm.js', '#': resolve('src'), } }, devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: vueLoaderConfig }, { test: /\.js$/, loader: 'babel-loader', include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('media/[name].[hash:7].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] }, // these devServer options should be customized in /config/index.js devServer: { clientLogLevel: 'warning', historyApiFallback: { rewrites: [{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, ], }, hot: true, contentBase: false, // since we use CopyWebpackPlugin. compress: true, host: HOST || config.dev.host, port: PORT || config.dev.port, open: config.dev.autoOpenBrowser, overlay: config.dev.errorOverlay ? { warnings: false, errors: true } : false, publicPath: config.dev.assetsPublicPath, proxy: config.dev.proxyTable, quiet: true, // necessary for FriendlyErrorsPlugin watchOptions: { poll: config.dev.poll, } } }
At last, I changed 'cheap-module-eval-source-map' to 'module-eval-source-map', the relative path of source files are kept. I've read the official document of devtool, it says "it doesn't have column mappings, it only maps line numbers." if "cheap" mode used, I don't understand why the relative path is discarded. eval-cheap-source-map - Similar to eval-source-map, each module is executed with eval(). It is "cheap" because it doesn't have column mappings, it only maps line numbers. It ignores SourceMaps from Loaders and only display transpiled code similar to the eval devtool. eval-cheap-module-source-map - Similar to eval-cheap-source-map, however, in this case Source Maps from Loaders are processed for better results. However Loader Source Maps are simplified to a single mapping per line.
This worked for me when using vue-cli-service serve: https://github.com/vuejs/vue-cli/issues/2978#issuecomment-577364101 In vue.config.js: let path = require('path') module.exports = { configureWebpack: config => { if (process.env.NODE_ENV === 'development') { // See available sourcemaps: // https://webpack.js.org/configuration/devtool/#devtool config.devtool = 'eval-source-map' // console.log(`NOTICE: vue.config.js directive: ${config.devtool}`) config.output.devtoolModuleFilenameTemplate = info => { let resPath = path.normalize(info.resourcePath) let isVue = resPath.match(/\.vue$/) let isGenerated = info.allLoaders let generated = `webpack-generated:///${resPath}?${info.hash}` let vuesource = `vue-source:///${resPath}` return isVue && isGenerated ? generated : vuesource } config.output.devtoolFallbackModuleFilenameTemplate = 'webpack:///[resource-path]?[hash]' } }, }
building vuejs plugin with webpack
ERROR in ./src/Loader.vue Module parse failed: C:\test\vuePlugin\src\Loader.vue Unexpected token (1:0) You may need an appropriate loader to handle this file type. SyntaxError: Unexpected token (1:0) at Parser.pp$4.raise (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:2221:15) at Parser.pp.unexpected (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:603:10) at Parser.pp$3.parseExprAtom (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1822:12) at Parser.pp$3.parseExprSubscripts (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1715:21) at Parser.pp$3.parseMaybeUnary (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1692:19) at Parser.pp$3.parseExprOps (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1637:21) at Parser.pp$3.parseMaybeConditional (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1620:21) at Parser.pp$3.parseMaybeAssign (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1597:21) at Parser.pp$3.parseExpression (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:1573:21) at Parser.pp$1.parseStatement (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:727:47) at Parser.pp$1.parseTopLevel (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:638:25) at Parser.parse (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:516:17) at Object.parse (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\acorn\dist\acorn.js:3098:39) at Parser.parse (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\lib\Parser.js:902:15) at NormalModule. (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\lib\NormalModule.js:104:16) at NormalModule.onModuleBuild (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\webpack-core\lib\NormalModuleMixin.js:310:10) at nextLoader (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\webpack-core\lib\NormalModuleMixin.js:275:25) at C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\webpack-core\lib\NormalModuleMixin.js:259:5 at Storage.finished (C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\enhanced-resolve\lib\CachedInputFileSystem.js:38:16) at C:\Users\shubham-sharma2\AppData\Roaming\npm\node_modules\webpack\node_modules\graceful-fs\graceful-fs.js:78:16 at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:511:3) # multi main "webpack --config --display-error-details ./webpack.config.js" const webpack = require('webpack'); const merge = require('webpack-merge'); const path = require('path'); var config = { entry:path.resolve(__dirname,'src/plugin.js'), output: { path: path.resolve( __dirname,'/dist/') }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader', include: __dirname, exclude: /node_modules/ }, { test: /\.vue$/, loader: 'vue-loader' } ] },resolve: { extensions: ['.vue', '.js', '.jsx',''] },plugins:[ new webpack.optimize.UglifyJsPlugin( { minimize : true, sourceMap : false, mangle: true, compress: { warnings: false } } )] }; config.node={ fs:'empty' }; module.exports = [ merge(config, { entry: path.resolve(__dirname,'src/plugin.js'), output: { filename: 'loader.min.js', libraryTarget: 'window', library: 'Loader', } }), merge(config, { entry: path.resolve(__dirname , 'src/Loader.vue'), output: { filename: 'loader.js', libraryTarget: 'umd', library: 'Loader', umdNamedDefine: true } }) // Config 1: For browser environment // merge(commonConfig, { // }), // Config 2: For Node-based development environments // merge(commonConfig, { // }) ];
Add const VueLoaderPlugin = require('vue-loader/lib/plugin') to the top of your webpack config, and new VueLoaderPlugin() in your plugin section. And for your style blocks, add: // this will apply to both plain `.css` files // AND `<style>` blocks in `.vue` files { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] } You might also try: extensions: ['', '.vue', '.js', '.jsx'] I have seen the order become an issue.
karma-coverage includes node_modules
I have a karma config which for my unit test and code-cov. My project dir looks like below RootFOlder -karma.config.js -webpack.test.config.js -src/ --test.ts --components ---ButonComponent ----Buttoncomponent.spec.ts And my karma.config is below // Karma configuration module.exports = function (config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['jasmine'], // list of files / patterns to load in the browser files: [ 'src/test.ts', 'src/components/**/*.component.ts', ], // list of files / patterns to exclude exclude: [ 'node_modules', './src/tsconfig.spec.json' ], plugins: [ require('karma-jasmine'), require("karma-coverage"), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-webpack'), require('karma-sourcemap-loader'), require('ts-loader') ], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { 'src/components/**/*.ts': ['coverage'], 'src/components/**/*.component.ts': ['webpack', 'sourcemap'], 'src/test.ts': ['webpack', 'sourcemap'], }, webpack: require('./webpack.test.config'), coverageReporter: { reporters: [ { type: 'text', subdir: 'text' }, { type: 'html', subdir: 'report-html' }, ] }, ... ... }); } And my webpack. module.exports = { devtool: 'inline-source-map', mode: 'development', target: 'node', resolve: { extensions: ['.ts', '.js'] }, module: { rules: [ { test: /\.ts$/, loaders: ['ts-loader', 'angular-router-loader', 'angular2-template-loader'] } .... .... ] }, plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'test') }), // Removes warnings regarding Critical dependency new webpack.ContextReplacementPlugin( /\#angular(\\|\/)core(\\|\/)f?esm5/, path.join(__dirname, './src') ) ], node: { console: false, global: true, process: true, Buffer: false, setImmediate: false } } The problem is after i run my test, my coverage covers the bundled node_modules. The file coverage endup being hude running in MBytes and my coverage low. Pls how do i exclude the node_modules from my coverage? Any help is appreciated .
By default you shouldn't even need to mention node_modules in the exclude path. Try removing it and see if the coverage is corrected? If not, try adding this to the preprocessors: '!node_modules/**/*.*': ['coverage']
Webpack and Express - Critical Dependencies Warning
I have the following webpack.config.ts: var webpack = require( 'webpack' ); var path = require( 'path' ); module.exports = { entry: [ './api/bin/www.ts' ], output: { path: path.resolve( __dirname, './dist/api' ), filename: 'index.js' }, module: { loaders: [ { test: /\.ts$/, loader: 'awesome-typescript-loader' }, { test: /\.json$/, loader: 'json-loader' } ] }, resolve: { extensions: [ '', '.js', '.ts' ] }, target: 'node', node: { console: true, fs: 'empty', net: 'empty', tls: 'empty' } }; When I run webpack I get a warning about a dependency: WARNING in ./~/express/lib/view.js Critical dependencies: 78:29-56 the request of a dependency is an expression # ./~/express/lib/view.js 78:29-56 The express server I start with this is no more than a Hello World example and functions as should but I am concerned about this warning. My googlefu hasn't revealed any passable solutions. I have seen one particular instance of this problem but the solutions were to bypass the warning by not showing it.
Use webpack-node-externals. const nodeExternals = require('webpack-node-externals'); { target: 'node', externals: [nodeExternals()], } https://www.npmjs.com/package/webpack-node-externals
For those that only need to remove the express due to the view lib as mentioned here you can also explicitly target express in externals from your webpack config. externals: [{ 'express': { commonjs: 'express' } }]
My warning only got fixed with: module.exports = { target: 'node', externals: { "express": "require('express')" } }
Instead of excluding all of the npm dependencies to be bundled with nodeExternals you can also exclude only express by natively requiring it by replacing import express from 'express'; // Or const express = require('express'); To const express = __non_webpack_require__('express'); That will suppress the warning caused by express