Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add 'PREACT_APP_' prefixed env vars automatically & pickup .env file #1671

Merged
merged 7 commits into from Apr 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/stupid-suns-crash.md
@@ -0,0 +1,7 @@
---
'preact-cli': minor
---

Any environment variables prefixed with 'PREACT_APP_' will automatically be available for reference and use in your application without having to configure `DefinePlugin` any more. Furthermore, if a `.env` file exists in the root of your application, any variables it defines will automatically be available for use.

Huge shout out to [robinvdvleuten](https://github.com/robinvdvleuten) who provided this functionality through the [`preact-cli-plugin-env-vars`](https://github.com/robinvdvleuten/preact-cli-plugin-env-vars) package in the past.
32 changes: 23 additions & 9 deletions README.md
Expand Up @@ -328,19 +328,33 @@ The default templates comes with a `.css` file for each component. You can start

### Using Environment Variables

You can reference and use environment variables in your `preact.config.js` by using `process.env`:
You can reference and use any environment variable in your application that has been prefixed with `PREACT_APP_` automatically:

> `src/index.js`

```js
export default {
webpack(config, env, helpers, options) {
if (process.env.MY_VARIABLE) {
/** You can add a config here that will only used when your variable is truthy **/
}
},
};
console.log(process.env.PREACT_APP_MY_VARIABLE);
```

If you'd like to use these variables in your application, you can use the [DefinePlugin] config from our recipes wiki.
If your variable is not prefixed, you can still add it manually by using your `preact.config.js` (see [DefinePlugin] config in the recipes wiki).

You can set and store variables using a `.env` file in the root of your project:

> `.env`

```
PREACT_APP_MY_VARIABLE="my-value"
```

You can also reference environment variables in your `preact.config.js`:

```js
export default (config, env, helpers, options) => {
if (process.env.MY_VARIABLE) {
/** You can add a config here that will only used when your variable is truthy **/
}
};
```

### Route-Based Code Splitting

Expand Down
7 changes: 7 additions & 0 deletions packages/cli/lib/commands/build.js
Expand Up @@ -92,6 +92,13 @@ async function command(src, argv) {
argv.production = toBool(argv.production);

let cwd = resolve(argv.cwd);

// we explicitly set the path as `dotenv` otherwise uses
// `process.cwd()` -- this would cause issues in environments
// like mono-repos or our test suite subjects where project root
// and the current directory differ.
require('dotenv').config({ path: resolve(cwd, '.env') });

if (argv.clean === void 0) {
let dest = resolve(cwd, argv.dest);
await promisify(rimraf)(dest);
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/lib/commands/watch.js
Expand Up @@ -2,6 +2,7 @@ const runWebpack = require('../lib/webpack/run-webpack');
const { isPortFree, toBool, warn } = require('../util');
const { validateArgs } = require('./validate-args');
const getPort = require('get-port');
const { resolve } = require('path');

const options = [
{
Expand Down Expand Up @@ -105,6 +106,14 @@ async function command(src, argv) {
argv.sw = toBool(argv.sw);
}

let cwd = resolve(argv.cwd);

// we explicitly set the path as `dotenv` otherwise uses
// `process.cwd()` -- this would cause issues in environments
// like mono-repos or our test suite subjects where project root
// and the current directory differ.
require('dotenv').config({ path: resolve(cwd, '.env') });

argv.port = await determinePort(argv.port);

if (argv.https || process.env.HTTPS) {
Expand Down
20 changes: 15 additions & 5 deletions packages/cli/lib/lib/webpack/webpack-base-config.js
Expand Up @@ -306,11 +306,21 @@ module.exports = function createBaseConfig(env) {

plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(
isProd ? 'production' : 'development'
),
}),
new webpack.DefinePlugin(
Object.keys(process.env)
.filter(key => /^PREACT_APP_/.test(key))
.reduce(
(env, key) => {
env[`process.env.${key}`] = JSON.stringify(process.env[key]);
return env;
},
{
'process.env.NODE_ENV': JSON.stringify(
isProd ? 'production' : 'development'
),
}
)
),
new webpack.ProvidePlugin({
h: ['preact', 'h'],
Fragment: ['preact', 'Fragment'],
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Expand Up @@ -97,6 +97,7 @@
"critters-webpack-plugin": "^2.5.0",
"cross-spawn-promise": "^0.10.1",
"css-loader": "^5.2.4",
"dotenv": "^16.0.0",
"ejs-loader": "^0.5.0",
"envinfo": "^7.8.1",
"esm": "^3.2.25",
Expand Down
15 changes: 15 additions & 0 deletions packages/cli/tests/build.test.js
Expand Up @@ -309,4 +309,19 @@ describe('preact build', () => {
);
});
});

it('should use a custom `.env` with prefixed environment variables', async () => {
let dir = await subject('custom-dotenv');
await build(dir);

const bundleFile = (await readdir(`${dir}/build`)).find(file =>
/bundle\.\w{5}\.js$/.test(file)
);
const transpiledChunk = await readFile(
`${dir}/build/${bundleFile}`,
'utf8'
);
// "Hello World!" should replace 'process.env.PREACT_APP_MY_VARIABLE'
expect(transpiledChunk.includes('console.log("Hello World!")')).toBe(true);
});
});
1 change: 1 addition & 0 deletions packages/cli/tests/subjects/custom-dotenv/.env
@@ -0,0 +1 @@
PREACT_APP_MY_VARIABLE="Hello World!"
1 change: 1 addition & 0 deletions packages/cli/tests/subjects/custom-dotenv/index.js
@@ -0,0 +1 @@
console.log(process.env.PREACT_APP_MY_VARIABLE);
4 changes: 4 additions & 0 deletions packages/cli/tests/subjects/custom-dotenv/package.json
@@ -0,0 +1,4 @@
{
"private": true,
"name": "preact-custom-dotenv"
}
28 changes: 28 additions & 0 deletions packages/cli/tests/watch.test.js
Expand Up @@ -34,6 +34,34 @@ describe('preact', () => {

server.close();
});

it('should use a custom `.env` with prefixed environment variables', async () => {
let app = await create('default');

let header = resolve(app, './src/components/header/index.js');
let original = await readFile(header, 'utf8');
let update = original.replace(
'<h1>Preact App</h1>',
'<h1>{process.env.PREACT_APP_MY_VARIABLE}</h1>'
);
await writeFile(header, update);
await writeFile(
resolve(app, '.env'),
'PREACT_APP_MY_VARIABLE="Hello World!"'
);

server = await watch(app, 8085);

let page = await loadPage(chrome, 'http://127.0.0.1:8085/');

// "Hello World!" should replace 'process.env.PREACT_APP_MY_VARIABLE'
await waitUntilExpression(
page,
`document.querySelector('header > h1').innerText === 'Hello World!'`
);

server.close();
});
});

describe('should determine the correct port', () => {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Expand Up @@ -5668,6 +5668,11 @@ dot-prop@^6.0.1:
dependencies:
is-obj "^2.0.0"

dotenv@^16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411"
integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==

dotenv@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef"
Expand Down