Exporting webpack config as a function fails - webpack-4

I'am trying to handle development and production eviroment variables in my webpack configuration (see https://webpack.js.org/guides/production/), but it fails with
WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration should be an object.
package.json
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "./node_modules/.bin/webpack",
"start": "npm run build && node server.js"
},
"devDependencies": {
//...
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2",
"webpack-dev-middleware": "^3.4.0",
"webpack-hot-middleware": "^2.24.2"
}
}
webpack.config.js
const path = require('path'),
webpack = require('webpack'),
HtmlWebpackPlugin = require('html-webpack-plugin');
let config = {
entry: {
app: [
'./src/app/App.tsx', 'webpack-hot-middleware/client'
],
vendor: ['react', 'react-dom']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].bundle.js'
},
// ...
}
This export is working as expected without errors or warnings
module.exports = config; // everything is fine
But this fails
module.exports = function(env, argv) { // this errors
return config;
};
There is a similiar, but unanswered question here: webpack base config as a function doesn't work
It's a very mysterious behaviour, appreciate if anyone could help!

Well,it is working. I didn't notice that the error takes place on a total different spot of my code.
I was doing a tutorial about HMR with webpack and express. An it's this lines of code in the express setup which causes the trouble:
server.js
const webpackConfig = require('./webpack.config');
const compiler = webpack(webpackConfig);
//...
app.use(
require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: webpackConfig.output.publicPath
})
);
The webpackConfig is only getting a function without being called and so it's not returning an object. So adding parenthesis is all it took to make it work.
const webpackConfig = require('./webpack.config')();
//..

The documentation is a bit quirky. You properly forgot the set the env variable from package.json
For instance "start": "webpack --env.prod --", in package.json will pass {prod: true} as the env variable.
Hope this helps.
More info here: https://webpack.js.org/api/cli/#environment-options

Related

Unable to instrument expo app with istanbul or cypress/instrument

I have been trying this for a while now, without success.
I have an expo app and I am using cypress to test some use cases using the web version of the app (generated by expo).
However, I would like to generate some code coverage as well. I have read the official documentation for it, that recommends to babel-plugin-istanbul do to so, but it does not seem to work with expo.
I am not sure why this would not work.
Edit
I removed the files previously pointed here as they were not important.
Thanks for a wonderful documentation provided by a hero without a cape, I figured that my settings were wrong.
All I needed to do was:
install babel-plugin-istanbul
Update babel.config.js
module.exports = {
presets: ['babel-preset-expo'],
plugins: [
'istanbul',
[
'module-resolver',
{
root: ['.'],
extensions: [
'.js',
],
alias: {
'#': './',
},
},
],
],
};
update cypress/support/index.js
import '#cypress/code-coverage/support';
update cypress/plugins/index.js
module.exports = (on, config) => {
require('#cypress/code-coverage/task')(on, config);
// add other tasks to be registered here
// IMPORTANT to return the config object
// with the any changed environment variables
return config;
};
and voilà!

How can i load changes to my code in a Vue app?

I deployed a Django+VueJS app that uses django webpack loader in order to render Vue apps in my Django templates. I used Nginx and Gunicorn to deploy the app to a DigitalOcean VPS, everything works without any problem but i have some doubts on how to edit my components in production, since i'm fairly new to Vue
Here is my vue.config:
const BundleTracker = require("webpack-bundle-tracker");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const pages = {
'main': {
entry: './src/main.js',
chunks: ['chunk-vendors']
},
}
module.exports = {
pages: pages,
runtimeCompiler: true,
filenameHashing: false,
productionSourceMap: false,
publicPath: process.env.NODE_ENV === 'production'
? 'static/vue'
: 'http://localhost:8080/',
outputDir: '../django_vue_mpa/static/vue/',
chainWebpack: config => {
config.optimization
.splitChunks({
cacheGroups: {
moment: {
test: /[\\/]node_modules[\\/]moment/,
name: "chunk-moment",
chunks: "all",
priority: 5
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-vendors",
chunks: "all",
priority: 1
},
},
});
Object.keys(pages).forEach(page => {
config.plugins.delete(`html-${page}`);
config.plugins.delete(`preload-${page}`);
config.plugins.delete(`prefetch-${page}`);
})
config
.plugin('BundleTracker')
.use(BundleTracker, [{filename: '../vue_frontend/webpack-stats.json'}]);
// Uncomment below to analyze bundle sizes
// config.plugin("BundleAnalyzerPlugin").use(BundleAnalyzerPlugin);
config.resolve.alias
.set('__STATIC__', 'static')
config.devServer
.public('http://localhost:8080')
.host('localhost')
.port(8080)
.hotOnly(true)
.watchOptions({poll: 1000})
.https(false)
.headers({"Access-Control-Allow-Origin": ["*"]})
}
};
So in order to deploy the Vue part i did npm run build and npm created a bunch of files in my static directory. Now, every time i edit a component, in order to see the changes on the web, i do npm run build every time, which takes some time. Is this how am i supposed to do it? Or is there a shorter way?
I don't know about django, But I know about vue..
is this how am I supposed to do it?
For me, I don't suggest it, you can use your django as a backend for your frontend
that should mean you would have 2 servers running. 1 for your django and 1 for your vue app. use XHR request to access your django App, remember to handle CORS. IMHO I don't want vue to be used as a component based framework.
is there a shorter way.
YES, and this is how you do it.
add to package.json
{
...,
scripts: {
...,
'watch' : 'vue-cli-service build --watch --inline-vue',
...,
}
}
while using the following settings in vue.config.js
module.exports = {
'publicPath': '/django/path/to/public/folder',
'outputDir': '../dist',
'filenameHashing': false,
runtimeCompiler: true,
'css': {
extract: true,
},
}
i forgot about how publicPath and outputDir works..
but you can check it out here https://cli.vuejs.org/config/#publicpath
regarding the code on the package.json file..
you can check it here
https://github.com/vuejs/vue-cli/issues/1120#issuecomment-380902334

EmberJs: how to dynamicaly inject data into ENV?

I need to dynamically inject some data before build stage.
In my config/environment.js i have:
module.exports = function(environment) {
environment = 'production';
var ENV = {
APP: {
API_HOST: 'https://apihost1.com,
secret: 'key1'
}
};
return ENV;
};
How do i can to dynamically change "APP" object to something else:
APP: {
API_HOST: 'https://apihost2.com,
secret: 'key2'
}
before build according to an executing command in my package.json?
"scripts": {
"build": "$CONFIG_NAME ember build",
},
I was thinking about npm scripts but could not implement that.
Is anyone has a solution for it?
You could do something like this in config/environment:
const customConfig = require(`./config.${process.env.CONFIG_NAME}.json`);
module.exports = function(environment) {
environment = 'production';
var ENV = {
APP: {
...customConfig,
otherStuff: 'bla'
}
};
return ENV;
};
then you can run env CONFIG_NAME=foo ember build if you want to merge the content of the config/config.foo.json into the ENV.APP during the build.
env tho is a unix thing, this will work on Linux and MacOS as well as other unix systems.
For windows you can either set the env variable with PowerShell or install cross-env and do npm run cross-env CONFIG_NAME=foo ember build.

jest-haste-map: Haste module naming collision (AWS, RN)

I have a React-native project with AWS Amplify.
In the root directory, there is an amplify folder.
Inside this amplify folder, there is a backend folder, and a #current-cloud-backend folder.
These two are basically identical.
When I try to start my project with npm run start I receive this error:
The following files share their name; please adjust your hasteImpl:
* <rootDir>/amplify-backup/backend/function/cxLoyaltyMainAppVerifyAuthChallengeResponse/src/package.json
* <rootDir>/amplify/#current-cloud-backend/function/cxLoyaltyMainAppVerifyAuthChallengeResponse/src/package.json
And it is complaining that inside these two folders, each lambda function has it's own package.json, in which they are named identical to their counterpart folder.
What I have done so far
I have found many people mentioning to put modulePathIgnorePatterns: ['<rootDir>/build'] inside of my root package.json under jest. Some also say to put it inside of jest.config.js which I cannot find anywhere.
I have also tried creating a root rn-cli.config.js and added
module.exports = {
resolver: {
blacklistRE: blacklist( [
/node_modules\/.*\/node_modules\/react-native\/.*/,
] )
},
};
which also does not work.
I am really running out of ideas here, anyone have any ideas? Thank you
I am using the Expo CLI and was having the same problem.
The solution that worked for me:
metro.config.js at the root directory. (instead of rn-cli.config.js)
const blacklist = require('metro-config/src/defaults/blacklist');
module.exports = {
resolver: {
blacklistRE: blacklist([/#current-cloud-backend\/.*/]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
UPDATE 2022! Just change the folder of the previous answer. It´s no longer defaults/blacklist, but defaults/exclusionList. So the solution:
I am using the Expo CLI and was having the same problem.
The solution that worked for me:
metro.config.js at the root directory. (instead of rn-cli.config.js)
const blacklist = require('metro-config/src/defaults/exclusionList');
module.exports = {
resolver: {
blacklistRE: blacklist([/#current-cloud-backend\/.*/]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
Adding the below snippet in the metro.config.js file worked for me
I am using:
react-native-cli: 2.0.1
react-native: 0.63.4
amplify: 5.3.0
const exclusionList = require('metro- config/src/defaults/exclusionList');
module.exports = {
resolver: {
blacklistRE: exclusionList([/#current-cloud-backend\/.*/]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
Also, you will need to install metro-config as a dependency by running npm i -D metro-config
In my case I have a project managed with Expo and a rule to resolve files of type cjs. I only had to include the line:
defaultConfig.resolve.blacklistRE = blacklist([/#current-cloud-backend/.*/]);
Final result:
const { getDefaultConfig } = require("#expo/metro-config");
const blacklist = require('metro-config/src/defaults/exclusionList');
const defaultConfig = getDefaultConfig(__dirname);
defaultConfig.resolve.assetExts.push("cjs");
defaultConfig.resolve.blacklistRE = blacklist([/#current-cloud-backend\/.*/]);
module.exports = defaultConfig;

How to access Vuejs methods to test with Jest?

I have the following file: deposit-form.js.
With the following code:
new Vue({
el: '#app',
data: {
title: 'title',
depositForm: {
chosenMethod: 'online',
payMethods: [
{ text: 'Already paid via Venmo', value: 'venmo' },
{ text: 'Pay online', value: 'online' },
{ text: 'In-person payment', value: 'person' }
],
},
},
methods: {
submitDeposit: function() {
$.ajax({
url: 'http://localhost:8000/api/v1/deposit/',
type:'post',
data: $('#deposit-form').serialize(),
success: function() {
$('#content').fadeOut('slow', function() {
// Animation complete.
$('#msg-success').addClass('d-block');
});
},
error: function(e) {
console.log(e.responseText);
},
});
},
showFileName: function(event) {
var fileData = event.target.files[0];
var fileName = fileData.name;
$('#file-name').text('selected file: ' + fileName);
},
},
});
I'm having problems on how to setup Jest, how to import the VueJs functions inside 'methods' to make the tests with Jest.
How should be my code on the deposit-form.test.js ?
The first thing you need to do is export Vue app instance.
// deposit-form.js
import Vue from 'vue/dist/vue.common';
export default new Vue({
el: '#app',
data: {...},
...
});
Now you can use this code in your spec file. But now you need to have #app element before running tests. This can be done using the jest setup file. I will explain why it's needed. When you import your main file (deposit-form.js) into a test, an instance of Vue is created in your main file with new. Vue is trying to mount the application into #app element. But this element is not in your DOM. That is why you need to add this element just before running the tests.
In this file you also can import jQuery globally to use it in your tests without import separately.
// jest-env.js
import $ from 'jquery';
global.$ = $;
global.jQuery = $;
const mainAppElement = document.createElement('div');
mainAppElement.id = 'app';
document.body.appendChild(mainAppElement);
Jest setup file must be specified in the jest configuration section in package.json.
// package.json
{
...,
"dependencies": {
"jquery": "^3.3.1",
"vue": "^2.6.7"
},
"devDependencies": {
"#babel/core": "^7.0.0",
"#babel/plugin-transform-modules-commonjs": "^7.2.0",
"#babel/preset-env": "^7.3.4",
"#vue/test-utils": "^1.0.0-beta.29",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^24.1.0",
"babel-loader": "^8.0.5",
"babel-preset-vue": "^2.0.2",
"jest": "^24.1.0",
"vue-jest": "^3.0.3",
"vue-template-compiler": "^2.6.7",
"webpack": "^4.29.5",
"webpack-cli": "^3.2.3"
},
"scripts": {
"test": "./node_modules/.bin/jest --passWithNoTests",
"dev": "webpack --mode development --module-bind js=babel-loader",
"build": "webpack --mode production --module-bind js=babel-loader"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"vue"
],
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/babel-jest",
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
},
"setupFiles": [
"<rootDir>/jest-env.js"
]
}
}
Also, you probably need to configure Babel to use the features of ES6 in your projects and tests. This is not necessary if you follow the commonjs-style in your code. Basic .babelrc file contains next code:
// .babelrc
{
"presets": [
[
"#babel/preset-env",
{
"useBuiltIns": "entry",
"targets": {
"browsers": [
"last 2 versions"
]
}
}
],
"vue",
],
"plugins": [
"#babel/plugin-transform-modules-commonjs",
]
}
Now you can write your tests.
// deposit-form.test.js
import App from './deposit-form';
describe('Vue test sample.', () => {
afterEach(() => {
const mainElement = document.getElementById('app');
if (mainElement) {
mainElement.innerHTML = '';
}
});
it('Should mount to DOM.', () => {
// Next line is bad practice =)
expect(App._isMounted).toBeTruthy();
// You have access to your methods
App.submitDeposit();
});
});
My recommendation is to learn Vue Test Utils Guides and start to divide your code into components. With the current approach, you lose all the power of components and the ability to test vue-applications.
I updated my answer a bit. As I understood from the comment to the answer, you connect the libraries on the page as separate files. Here is my mistake. I didn't ask if the build system is being used. Code in my examples is written in the ECMA-2015 standard. But, unfortunately, browsers do not fully support it. You need an transpiler that converts our files into a format that is understandable for browsers. It sounds hard. But it's not a problem. I updated the contents of the file package.json in response. Now it only remains to create an input file for the assembly and run the assembly itself.
The input file is simple.
// index.js
import './deposit-form';
The build is started with the following commands from terminal.
# for development mode
$ yarn run dev
# or
$ npm run dev
# for production mode
$ yarn run build
# or
$ npm run build
The output file will be placed in the directory ./dist/. Now instead of separate files you need to connect only one. It contains all the necessary for the library and your code.
I used webpack to build. More information about it can be found in the documentation. Good example you can find in this article.