I'm trying to set up env vars on my Svelte app to hide an API key.
I followed the instructions in this article [https://medium.com/dev-cafe/how-to-setup-env-variables-to-your-svelte-js-app-c1579430f032].
Here's the structure of my rollup.config.js
import { config as configDotenv } from 'dotenv';
import replace from '#rollup/plugin-replace';
configDotenv();
export default {
...
plugins: [
replace({
__myapp: JSON.stringify({
env: {
isProd: production,
amplitude_api_key : process.env.amplitude_api_key
}
})
}),
]}
When I try to access the env var by calling: __myapp.env.API_KEY
I get this error: __myapp is not defined
It seems that the nesting is the problem. I was able to get it work using this syntax:
replace({
'process.env.isProd': production,
'process.env.amplitude_api_key': process.env.amplitude_api_key
}),
and then use process.env.isProd in your app. Of course, if you like the __myapp thing, you could use __myapp instead of process on the left side of the replace function in your rollup config.
Even though this thread is solved, I want to point out that your remark "to hide an API key" is invalid because .env on clientside is always parsing right into your sourcecode. So in other words: your api-key is being parsed (and exposed) in the source once you build.
Related
I know this sounds as easy as using globals, but I'm not so sure.
Here's my case:
I have multiple files within /src directory of my React app, let's call them src/a.js, src/b.js,
every single of these files exports one object which I then use within my app:
./src/a.js:
export default {
filename: 'a',
foo: 'bar',
};
./src/b.js:
export default {
filename: 'b',
foo: 'bar',
blah: 'hah',
};
Now I have a command to check whether or not structure of objects within these files match (they are being changed by many developers multiple times a day), so when I do npm check in terminal it will return false for above input, because blah does not exist within two files.
My package.json looks like this:
"scripts": {
"check": "node check.js runCheck",
/.../
}
My question is: how the heck do I load these variables to compare them in package.json?
I have a file called:
./check.js:
function check(files) {
// checking files there
};
module.exports.check = check;
Approach #1 - imports
This is a build file, not part of the application itself, so when I try to do:
./check.js:
import a from './src/a';
import b from './src/b';
I'm getting:
SyntaxError: Cannot use import statement outside a module.
Approach #2 - require
This is going to cause trouble, because I'm using imports, not modules within my app, therefore doing:
./check.js:
const a = require('./src/a');
const b = require('./src/b');
Returns:
Error: Cannot find module './src/a'.
Of course I can't do module.exports within the a.js/b.js files, because they're part of my app and they should use exports, I've tried using both export and module.exports, but it does not work as well and looks shitty.
How do I tackle this? Should I load the files using some file loader, parse it as JSON an then compare? Or maybe there's an easier way?
You'll need to use something like esm (https://github.com/standard-things/esm) to run node with module support.
It should be as simple as:
npm install esm
Then update your package script to be:
"check": "node -r esm check.js runCheck",
Edit Btw, a very clear and well structured question.
I am coding a website with Next.js and I tried to add google Tag Manager.
I followed the tutorial on the Next.js Github example but for some reasons I can't access to my environment variables.
It says my variable is undefined.
I created a file .env.local on my project folder (at the same level as components, node_modules, pages, etc)
In this file I created a variable like this (test purpose) :
NEXT_PUBLIC_DB_HOST=localhost
And on my index page I tried this code :
console.log("test ", process.env.NEXT_PUBLIC_DB_HOST);
But in my console I get a "test undefined".
I tried to put my variable into an .env file instead, without success.
What I am doing wrong ?
This envs just works in Server Side. To access this envs in Client Side, you need declare in the next.config.js
This way:
module.exports = {
reactStrictMode: true,
env: {
BASE_URL: process.env.BASE_URL,
}
}
Create .env (all environments), .env.development (development environment), and .env.production (production environment).
Add the prefix NEXT_PUBLIC to all of your environment variables.
NEXT_PUBLIC_API_URL=http://localhost:3000/
Use with prefix process.env
process.env.NEXT_PUBLIC_API_URL
Stop the server and restart it:
npm run dev
I hope it works.
This solution for latest version of nextJs (above 9)
Restarting the server worked for me.
Edit & save .env.local
Stop the server and restart it, npm run dev
You should get an output on the next line like this:
> klout#0.1.0 dev
> next dev
Loaded env from [path]/.env.local
For those using NextJS +9 and looking for environment variables in the browser, you should use the NEXT_PUBLIC_ prefix. Example:
NEXT_PUBLIC_ANALYTICS_ID=123456789
See documentation for reference.
After spending countless hours on this, I found that there is a tiny little paragraph in both the pre and post nextjs 9.4 documentation:
(Pre-9.4) https://nextjs.org/docs/api-reference/next.config.js/environment-variables (same as this answer)
Next.js will replace process.env.customKey with 'my-value' at build time.
(^9.4) https://nextjs.org/docs/basic-features/environment-variables
In order to keep server-only secrets safe, Next.js replaces process.env.* with the correct values at build time.
Key words being BUILD TIME. This means you must have set these variables when running next build and not (just) at next start to be available for the client side to access these variables.
This is my next.config.js file.
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
env: {
BASE_URL: process.env.NEXT_PUBLIC_SITE_URL,
},
};
module.exports = nextConfig;
Restart the server and it worked fine. using Nextjs 12.1.0 with typescript
In my case, Im pasting REACT_APP_API_URL instead of NEXT_PUBLIC_API_URL.
Adding with the most recent version of the documentation on this, v12+.
Using the next.config.js file you can specify server and client variables:
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
You can still use an env.local file, and pass the variable in to the next.config.js file. For example:
publicRuntimeConfig: {
DB_URL: process.env.DB_URL
}
And then you can access the variable like this:
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
publicRuntimeConfig.DB_URL;
First time for me on Stack Overflow, please be kind ;) I'll try to do my best!
The context:
I am working on a Rails 6 app with webpacker. This is a program that will be shared by several companies and in order to apply the 'one code, multiple setups' paradigm, we decided to move all the company related configuration files to separate folders, and to put the company name as a variable in our .env file. We need to change some config variables as well as some geofencing data (so our customers can create a new delivery to some address). Basically that's what it looks like:
Project folder
| config
| companies
| a_first_company
| rails_config.rb
| geofencing.js
| a_second_company
| rails_config.rb
| geofencing.js
| ....
In the .env file:
COMPANY=a_first_company
And in the rails configuration (application.rb), we are using a simple:
require_relative "companies/#{ENV['COMPANY']}/rails_config"
But now, here comes the JS part! And I am running into trouble.
The problem:
I would like to include dynamically a JSON object in an existing script. A sample geofencing.js looks like that:
module.exports = {
"countries": ["be"],
"polygon": [
50.8917729, 4.3004608,
...
50.9162381, 4.3450928,
50.8917729, 4.3004608
]
}
And I am trying to import it as a geofencing variable in my existing address autocompletion script:
/app/javascript/plugins/places.js
// I know it doesn't work that way, but basically that what I would like to do:
const geofencing = require(`/config/companies/${process.env.COMPANY}/geofencing`);
...
const initPlaceAutocomplete = () => {
...
var placesAutocomplete = places(
{
// And use the variable here...
insidePolygon: [geofencing.polygon],
type: 'address',
// And there...
countries: geofencing.countries,
templates: {
value: (suggestion) => {
return suggestion.name;
}
},
container: addressInput
}
);
...
}
export { initPlaceAutocomplete };
This file is imported in the view with a <%= javascript_pack_tag 'delivery_new' %>:
/app/javascript/packs/delivery_new.js
import { initPlaceAutocomplete } from '../plugins/places';
initPlaceAutocomplete();
...
The solution (that I haven't found yet):
I have tried several things, like importing the file in the webpack config (/config/webpack/environment.js), just like in the ProvidePlugin documentation:
const {environment} = require('#rails/webpacker')
const path = require('path');
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
Popper: ['popper.js', 'default'],
geofencing: path.resolve(path.join(__dirname, '..', '..', 'config', 'companies', process.env['COMPANY'], 'geofencing'))
})
)
module.exports = environment
... But it didn't work.
I also tried various places to import 'geofencing' in several places, with always the same result in the Chrome console: Uncaught ReferenceError: geofencing is not defined.
I noticed, though, that I had access to the process.env variables in the places.js script: writing console.log(process.env['COMPANY']); in the file prompts me the company name in the dev console when I reload the page in Chrome.
Apart from this, I have to say that I am lost. I am basically a newbie to the Webpack 'magic' ;)
Please tell me if you need more info about my setup.
Thanks in advance for your help!
You should be able to require modules using interpolation in the require() expression similar to your example. Try using a relative path, e.g. (assuming parent directories are siblings):
require(`../config/${process.env.COMPANY}/geofencing`)
The above expression will add only the COMPANY geofencing module to the bundle.
If you wish this to be dynamic, webpack can resolve the interpolation at runtime assuming the require paths are scoped to a directory; you're already doing this here with "../config/". As a result, webpack will include all COMPANY geofencing modules in the bundle. As for usage, I might move the require statement inside a function:
function initPlaceAutocomplete(company) {
const geofencing = require(`../config/${company}/geofencing`)
// ...
}
// usage
import { initPlaceAutocomplete } from "../plugins/places"
initPlaceAutocomplete(process.env.COMPANY)
At this point, this could is a good use case for dynamic imports, i.e., webpack supports the TC39 proposal for dynamically loading modules at runtime.
Instead of require("../config..."), you can use the import() function syntax. Unlike require(), the import() function syntax resolves asynchronously.
Now webpack will bundle all the COMPANY geofencing modules, but as separate "chunks" to keep the size of your initial script down. webpack will insert code to resolve these "chunks" asynchronously at runtime. To support this, the import() expression returns a Promise so, I'm using the async/await syntax here as a result.
async function initPlaceAutocomplete(company) {
const geofencing = await import(`../companies/${company}/config`)
// ...
}
I'm trying to write tests for a react/redux app, and we have a bunch of webworkers which are currently imported via require('worker-loader?inline!downloadTrackWorker')
I've been going in circles trying to figure out how to separate out this code so I can run tests in node.js without having trouble with loading the webworker.
One solution I came up with was to expose the webworker globally in my webpack, which would mean I could define a stub or mock in my tests.
In my webpack config, I've added
module: {
loaders: [...],
rules: [{
test: require.resolve(APP_DIR + '/helpers/downloadTrackWorkerLoader'),
loader: 'expose-loader',
options: 'DownloadTrackWorker'
}]
}
my trackWorkerLoader is simply
const DownloadTrackWorker = require('worker-loader?inline!./downloadTrackWorker.js');
module.export = DownloadTrackWorker;
I've also tried the above without inline, but no luck.
I'm experiencing two problems.
when I look for DownloadTrackWorker in my console, it is undefined
with my updated webpack.config, I get an error that webpack can't parse may need appropriate loader at
ReactDOM.render(
<Provider store={store}>
<Player />
</Provider>,
document.getElementById('root')
);
Any suggestions on what I'm doing wrong? It appears to me the issues I'm seeing are related.
when I look for DownloadTrackWorker in my console, it is undefined
As the expose-loader notes in Readme - Usage, you need to import it in order to be included in the bundle and therefore exposed. The rules are not including anything but are applied to the imports in your app which satisfy the test. Besides that you're also not applying the loader to the correct file. You want to apply the expose-loader to trackWorkerLoader.js, so the correct rule would be:
{
test: require.resolve(APP_DIR + '/helpers/trackWorkerLoader'),
loader: 'expose-loader',
options: 'DownloadTrackWorker'
}
Now you need to import it somewhere in your app with:
require('./path/to/helpers/trackWorkerLoader');
This will correctly expose DownloadTrackWorker as a global variable, but you have a typo in trackWorkerLoader.js instead of module.exports you have module.export. Currently you're not actually exporting anything. It should be:
module.exports = DownloadTrackWorker;
Instead of inlining the worker-loader in the require (not talking about its option) you can also define it as a rule:
{
test: require.resolve(APP_DIR + '/helpers/downloadTrackWorker'),
loader: 'worker-loader',
options: {
inline: true
}
}
And now you can simply require it without needing to specify the loaders in trackWorkerLoader.js:
const DownloadTrackWorker = require('./downloadTrackWorker');
module.exports = DownloadTrackWorker;
with my updated webpack.config, I get an error that webpack can't parse may need appropriate loader
You're defining both module.loaders and module.rules at the same time. Although module.loaders still exists for compatibility reasons, it will be ignored completely if module.rules is present. Hence the loaders you configured before, are not being applied. Simply move all rules to module.rules.
I'm trying to pass some environment related variables into my React components using Webpack's DefinePlugin. Client side part works great, server side part returns 'MYVARIABLE is not defined'.
I'm using Webpack's Node.JS api. Important parts are below. Am I doing something wrong? Thanks
webpack.config.js
...
webpackConfig.plugins = [
new webpack.DefinePlugin({
MYVARIABLE: 'test-value'
})
]
...
server.js
...
import webpack from 'webpack'
import webpackConfig from '../config/webpack.config'
...
var compiler = webpack(webpackConfig)
...
component file
...
console.log(MYVARIABLE)
...
result
ReferenceError: MYVARIABLE is not defined
....
You have to JSON.stringify('your-value').
According https://webpack.js.org/plugins/define-plugin/ :
because the plugin does a direct text replacement, the value given to
it must include actual quotes inside of the string itself. Typically,
this is done either with either alternate quotes, such as
'"production"', or by using JSON.stringify('production').
so your webpack.config.js should be...
webpackConfig.plugins = [
new webpack.DefinePlugin({
MYVARIABLE: JSON.stringify('test-value')
})
]