I want to use jsdom within a Gatsby project, in order to be able to load and render a separate webpage within a React component.
However, when I try to build with Gatsby, I get a series of Webpack errors which look like this:
undefined failed
Can't resolve 'child_process' in
'/Users/john/WebstormProjects/analytics/web/node_modules/jsdom/lib/jsdom/living/xhr'
I think this may be because Webpack is using the 'web' target mode and not 'node'. I've tried putting a webpack.config.js file in the root directory:
module.exports = {
target: 'node',
}
However, this didn't do anything. I also tried to use onCreateWebpackConfig, but I'm not sure if this is correct, as I couldn't get this to work either:
//gatsby-node.ts
export { onCreateWebpackConfig } from "./src/gatsby/on-create-webpack-config";
// ./src/gatsby/on-create-webpack-config
export const onCreateWebpackConfig = ({ actions } : any) => {
actions.setWebpackConfig({
target: 'node',
});
}
I have 2 questions:
Is it possible to use jsdom in a Gatsby project and if so, what do I need to do? If it's just to use the target mode, then:
How do I set the target mode in Gatsby to be node and not web?
How do I set the target mode in Gatsby to be node and not web?
It looks like you can alter the webpack config to set the target:
// gatsby-node.js
exports.onCreateWebpackConfig = ({ actions, getConfig }) => {
const config = getConfig();
config.target = 'node';
actions.replaceWebpackConfig(config);
};
https://www.gatsbyjs.com/docs/how-to/custom-configuration/add-custom-webpack-config/#modifying-the-babel-loader
Related
I'm trying to use react-gtm-module in my Gatsby project, I used the library #loadable/component to load this module in my component. So, when I run gatsby develop, I get the error TagManager.initialize is not a function
This is the code:
import loadable from '#loadable/component';
const TagManager = loadable(() => import('react-gtm-module'));
export const setupGtm = () => {
if (typeof window !== 'undefined') {
TagManager.initialize({
gtmId: 'GTM-ID',
});
}
};
I would really like to use the react-gtm-module because I already have several codes already pre-configured, does anyone know how to use no gatsby?
Thanks!!
For this to run properly, you have to install the Gatsby plugin called Google Tagmanager inside your gatsby project, you can do that. by running
npm install gatsby-plugin-google-tagmanager
When you are done, you have to get the plugin set up in your gatsby-config.js or gatsby-config.ts file.
Check out this doc from Gatsby for more info.
Don't use the React-based dependency. It's far way easy to use the native Gatsby plugins for that since they will insert the script in the needed places automatically. I would suggest get riding off that snippet and using gatsby-plugin-google-tagmanager:
// In your gatsby-config.js
plugins: [
{
resolve: "gatsby-plugin-google-tagmanager",
options: {
id: "YOUR_GOOGLE_TAGMANAGER_ID",
// Include GTM in development.
//
// Defaults to false meaning GTM will only be loaded in production.
includeInDevelopment: false,
// datalayer to be set before GTM is loaded
// should be an object or a function that is executed in the browser
//
// Defaults to null
defaultDataLayer: { platform: "gatsby" },
// Specify optional GTM environment details.
gtmAuth: "YOUR_GOOGLE_TAGMANAGER_ENVIRONMENT_AUTH_STRING",
gtmPreview: "YOUR_GOOGLE_TAGMANAGER_ENVIRONMENT_PREVIEW_NAME",
dataLayerName: "YOUR_DATA_LAYER_NAME",
// Name of the event that is triggered
// on every Gatsby route change.
//
// Defaults to gatsby-route-change
routeChangeEventName: "YOUR_ROUTE_CHANGE_EVENT_NAME",
// Defaults to false
enableWebVitalsTracking: true,
},
},
]
You can omit the options you won't use.
I made a page which pulls data from Contentful. The data is pulling correctly, but buttons which use functions from methods don't work. Live updating of variables (for example, using v-model) doesn't work either.
I see this error in the console:
I think this error is the problem. Does anyone know what's wrong? I have no clue how to solve it :(
My contentful.js:
const contentful = require('contentful')
const client = contentful.createClient({
space: process.env.CONTENTFUL_ENV_SPACE_ID,
accessToken: process.env.CONTENTFUL_ENV_ACCESS_TOKEN
})
module.exports = client
Code which pulls data:
export default {
layout: "landing_page",
asyncData() {
return client
.getEntries({
content_type: "landingPage"
})
.then(entries => {
return { contentfulData: entries.items[0].fields };
});
},
computed: {
styles() {
return landingPageCss;
}
},
components: {
priceBox,
contact,
home,
aboutUs,
footerDiv
}
};
The best approach is used dotenv package to that. Set your env keys in .env file.
nuxt.config.js file should contain:
const env = require('dotenv').config()
export default {
mode: 'universal',
...
env: env.parsed,
...
}
Look at this video: https://codecourse.com/watch/using-env-files-with-nuxt
If you use dotenv you need to do following steps:
npm install --save-dev #nuxtjs/dotenv
Then you install it as an module. Note here if you using Nuxt.js older then v2.9 then you ahve to go to nuxt.config.js and put your code into the module section:
...
module: [
'#nuxtjs/dotenv'
]
...
If there is no module section then create one.
If you using newer then v2.9 then you put it into the buildModules
...
buildModules: [
'#nuxtjs/dotenv'
]
...
Your variables that are saved in the .env file are now accessable through context.env or process.env
I am developing React Native application that includes different configurations for different possible clients, in a file such as src/config/config.js. These configurations are quite complex. The file is structured based on the client name as key, and the values as the object entries, e.g.:
export default {
fooClient: {
apiUrl: "https://foo.example.com/",
barClient: {
apiUrl: "https://bar.example.com/"
}
}
Of course, there are many other option keys.
When building the app, I know for which client I want to do this, by specifying an Android build variant, e.g.:
ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
For security reasons, I don't want keys of other clients to be included in the config file though. What are my options to remove all other client configs from this file before I build the app and ship it to a client?
I thought about the following: I modify the packager so that it strips out the keys that do not correspond to the current build variant.
I now have a transformer plugin for Metro that does the following:
const upstreamTransformer = require('metro-react-native-babel-transformer');
module.exports.transform = function(src, filename, options) {
if (typeof src === 'object') {
// handle RN >= 0.46
({ src, filename, options } = src);
}
if (filename.endsWith('config.js')) {
console.log('Transforming ' + filename);
let srcStripped = src.replace(';', '').replace('export default ', '');
let configObj = JSON.parse(srcStripped);
// TODO: get the build variant and strip all keys that we do not need from configObj
return upstreamTransformer.transform({
src: 'export default ' + JSON.stringify(configObj) + ';',
filename: filename,
options
});
} else {
return upstreamTransformer.transform({ src, filename, options });
}
};
But how do I know which build variant is being used?
If this seems like an XY problem, I am happy to explore alternatives to building the configuration dynamically. I cannot, however, use environment variables, since the configuration will be too complex for it to be just a list of .env keys.
You shouldn't use Metro transform this way. It's not clean and it may lead to missing configuration and/or damaged syntax sooner or later.
What I have done and suggest you, is to create 3 different configuration files under src/config/; one file for fooClient.js, one file for barClient.js and one last file with common configuration client.js. All files will export default configuration objects, but inside each fooClient and barClient, you'll use deepmerge module to merge client.js config:
client.js:
export default {
commonSettingA: "...",
commonSettings: {
...
}
...
}
fooClient.js:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://foo.example.com/",
}
]);
barClient.js:
import merge from 'deepmerge';
import config from './config';
export default merge.all([
config,
{
apiUrl: "https://bar.example.com/",
}
]);
Then you can use an environment variable to pass the needed configuration and create a propriate metro resolve; #react-native-community/cli does not pass command line arguments to metro config script. You can use process.argv to parse it by yourself, but it's not worth it.
Here is how you can create a resolve inside metro.config.js using environment variable:
const path = require("path");
module.exports = {
projectRoot: path.resolve(__dirname),
resolver: {
sourceExts: ['js', 'jsx', 'ts', 'tsx'],
extraNodeModules: {
// Local aliases
"#config": path.resolve(__dirname, "src/config", `${process.env.METRO_VARIANT}Client.js`)
}
}
};
Using this resolve, you'll have to import the configuration like this:
import config from '#config';
Then you add 2 package.json scripts, one for fooClient and one for barClient:
{
...
"scripts": {
"run:android:foo": "METRO_VARIANT=foo ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo",
"run:android:bar": "METRO_VARIANT=bar ENVFILE=.env npx react-native run-android --variant barDebug --appIdSuffix bar",
...
}
...
}
Then you just run the needed script:
yarn run:android:foo # will build with fooClient.js
yarn run:android:bar # will build with barClient.js
Can't you just add a "duplicated" parameter to the command?
Like so
METRO_VARIANT=fooDebug ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo
And get it with process.env.METRO_VARIANT
You might not want to make your setup much more complex. Depending on the number of the number of different clients, you might wanna go with multiple schemas/variants per client, but that is probably way overkill.
I'm usig Snap.svg in Vue project, generated with Vue cli 3.
Snap is added to vue.config.js as follows:
module.exports = {
chainWebpack: config => {
config.module
.rule("i18n")
.resourceQuery(/blockType=i18n/)
.type("javascript/auto")
.use("i18n")
.loader("#kazupon/vue-i18n-loader")
.end();
config.module
.rule("snapsvg")
.test(require.resolve("snapsvg"))
.use("imports-loader?this=>window,fix=>module.exports=0")
.loader("imports-loader")
.end();
}
};
As well as to main.js
const snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);
I'm using snap in my components without local import.
var s = Snap(this.paper["svg-wrap"]);
The library works as excepted, svg is generated, however i keep getting Eslint errors.
error: 'Snap' is not defined (no-undef) at src\components\subpageBanner.vue:246:21:
I want to keep using Eslint in all components, but configure it to ingore this kind of errors.
Is it possible?
If its a single file, then you can put the following line at the top of your file.
/*eslint no-undef: "warning"*/
I used this tutorial: https://github.com/gatsbyjs/gatsby/blob/master/docs/docs/environment-variables.md
Steps I followed:
1) install dotenv#4.0.0
2) Create two files in root folder: ".env.development" and ".env.production"
3) "follow their setup instructions" (example on dotenv npm docs)
In gatsby-config.js:
const fs = require('fs');
const dotenv = require('dotenv');
const envConfig =
dotenv.parse(fs.readFileSync(`.env.${process.env.NODE_ENV}`));
for (var k in envConfig) {
process.env[k] = envConfig[k];
}
Unfortunately, when i run gatsby develop, NODE_ENV isn't set yet:
error Could not load gatsby-config
Error: ENOENT: no such file or directory, open 'E:\Front-End Projects\Gatsby\sebhewelt.com\.env.undefined'
It works when I set it manually:
dotenv.parse(fs.readFileSync(`.env.development`));
I need environment variables in gatsby-config because I put sensitive data in this file:
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: envConfig.CONTENTFUL_SPACE_ID,
accessToken: envConfig.CONTENTFUL_ACCESS_TOKEN
}
}
How to make it work?
PS: Additional question - As this made me think, I know I shouldn't put passwords and tokens on github, but as netlify builds from github, is there other safe way?
I had a similar issue, I created 2 files in the root ".env.development" and ".env.production" but was still not able to access the env file variables - it was returning undefined in my gatsby-config.js file.
Got it working by npm installing dotenv and doing this:
1) When running gatsby develop process.env.NODE_ENV was returning undefined, but when running gatsby build it was returning 'production' so I define it here:
let env = process.env.NODE_ENV || 'development';
2) Then I used dotenv but specify the filepath based on the process.env.NODE_ENV
require('dotenv').config({path: `./.env.${env}`});
3) Then you can access your variables for your config:
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `${process.env.CONTENTFUL_ID}`,
accessToken: `${process.env.CONTENTFUL_TOKEN}`,
},
},
],
}
You should only use env files when you're comfortable checking those into git. For passwords/tokens/etc. add them to Netlify or whatever build tool you use through their dashboard.
These you can access in gatsby-config.js & gatsby-node.js via process.env.ENV_VARIABLE.
You can't access environment variables added this way in the browser however. For this you'll need to use .env.development & .env.production.
I really dislike the .env.production file pattern, our build system sets up and uses env variables and having extra build steps to write those into a file is weird. But Gatsby only whitelists GATSBY_ of the env vars, with no obvious way of adding your own.
But doing that isn't so hard, you can do it by adding something like this in the gatsby-node.js file:
exports.onCreateWebpackConfig = ({ actions, getConfig }) => {
const config = getConfig();
// Allow process.env.MY_WHITELIST_PREFIX_* environment variables
const definePlugin = config.plugins.find(p => p.definitions);
for (const [k, v] of Object.entries(process.env)) {
if (k.startsWith("MY_WHITELIST_PREFIX_")) {
definePlugin.definitions[`process.env.${k}`] = JSON.stringify(v);
}
}
actions.replaceWebpackConfig(config);
};
After doing a few searches, I found that we can set environment variables through netlify website, here are the steps:
Under your own netlify console platform, please go to settings
Choose build & deploy tab (can be found on sidebar)
Choose environment sub-tab option
Click edit variables and add/put your credentials in
Done!