From 92fa812f34cf5204ce6fbee0403c694e8cf7e287 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Mon, 14 Mar 2022 03:27:25 -0500 Subject: [PATCH 1/7] feat: Automatic 'PREACT_APP_' env var injection & .env pickup --- README.md | 32 +++++++++++++------ packages/cli/lib/commands/build.js | 1 + packages/cli/lib/commands/watch.js | 4 +++ .../lib/lib/webpack/webpack-base-config.js | 20 +++++++++--- packages/cli/package.json | 1 + yarn.lock | 5 +++ 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9b547a613..ff23dacb7 100644 --- a/README.md +++ b/README.md @@ -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 also set 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 diff --git a/packages/cli/lib/commands/build.js b/packages/cli/lib/commands/build.js index 0f7059b43..22927b158 100644 --- a/packages/cli/lib/commands/build.js +++ b/packages/cli/lib/commands/build.js @@ -92,6 +92,7 @@ async function command(src, argv) { argv.production = toBool(argv.production); let cwd = resolve(argv.cwd); + require('dotenv').config({ path: resolve(cwd, '.env') }); if (argv.clean === void 0) { let dest = resolve(cwd, argv.dest); await promisify(rimraf)(dest); diff --git a/packages/cli/lib/commands/watch.js b/packages/cli/lib/commands/watch.js index 50e67119d..168b63459 100644 --- a/packages/cli/lib/commands/watch.js +++ b/packages/cli/lib/commands/watch.js @@ -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 = [ { @@ -105,6 +106,9 @@ async function command(src, argv) { argv.sw = toBool(argv.sw); } + let cwd = resolve(argv.cwd); + require('dotenv').config({ path: resolve(cwd, '.env') }); + argv.port = await determinePort(argv.port); if (argv.https || process.env.HTTPS) { diff --git a/packages/cli/lib/lib/webpack/webpack-base-config.js b/packages/cli/lib/lib/webpack/webpack-base-config.js index b56c30aee..67063aa3e 100644 --- a/packages/cli/lib/lib/webpack/webpack-base-config.js +++ b/packages/cli/lib/lib/webpack/webpack-base-config.js @@ -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'], diff --git a/packages/cli/package.json b/packages/cli/package.json index 9288bbf27..dd208fffe 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index 9f10b0738..41b6c2a87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From 646824c818467345476703c29a6abbb22e88e80e Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Mon, 14 Mar 2022 03:31:56 -0500 Subject: [PATCH 2/7] docs: Adding changeset --- .changeset/stupid-suns-crash.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/stupid-suns-crash.md diff --git a/.changeset/stupid-suns-crash.md b/.changeset/stupid-suns-crash.md new file mode 100644 index 000000000..1454c3b0e --- /dev/null +++ b/.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. From c581a41c077787640c05c57b27986806b669d07f Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Mon, 14 Mar 2022 03:52:38 -0500 Subject: [PATCH 3/7] test: Adding build & watch tests --- packages/cli/tests/build.test.js | 15 ++++++++++++ .../cli/tests/subjects/custom-dotenv/.env | 1 + .../cli/tests/subjects/custom-dotenv/index.js | 1 + .../tests/subjects/custom-dotenv/package.json | 4 ++++ packages/cli/tests/watch.test.js | 23 +++++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 packages/cli/tests/subjects/custom-dotenv/.env create mode 100644 packages/cli/tests/subjects/custom-dotenv/index.js create mode 100644 packages/cli/tests/subjects/custom-dotenv/package.json diff --git a/packages/cli/tests/build.test.js b/packages/cli/tests/build.test.js index 14e258ddc..7405ce16e 100644 --- a/packages/cli/tests/build.test.js +++ b/packages/cli/tests/build.test.js @@ -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); + }); }); diff --git a/packages/cli/tests/subjects/custom-dotenv/.env b/packages/cli/tests/subjects/custom-dotenv/.env new file mode 100644 index 000000000..d7f22463c --- /dev/null +++ b/packages/cli/tests/subjects/custom-dotenv/.env @@ -0,0 +1 @@ +PREACT_APP_MY_VARIABLE="Hello World!" diff --git a/packages/cli/tests/subjects/custom-dotenv/index.js b/packages/cli/tests/subjects/custom-dotenv/index.js new file mode 100644 index 000000000..9e532391e --- /dev/null +++ b/packages/cli/tests/subjects/custom-dotenv/index.js @@ -0,0 +1 @@ +console.log(process.env.PREACT_APP_MY_VARIABLE); diff --git a/packages/cli/tests/subjects/custom-dotenv/package.json b/packages/cli/tests/subjects/custom-dotenv/package.json new file mode 100644 index 000000000..cdfa8eb1a --- /dev/null +++ b/packages/cli/tests/subjects/custom-dotenv/package.json @@ -0,0 +1,4 @@ +{ + "private": true, + "name": "preact-custom-dotenv" +} diff --git a/packages/cli/tests/watch.test.js b/packages/cli/tests/watch.test.js index bff7f6945..ac51beb45 100644 --- a/packages/cli/tests/watch.test.js +++ b/packages/cli/tests/watch.test.js @@ -34,6 +34,29 @@ describe('preact', () => { server.close(); }); + + it('should use a custom `.env` with prefixed environment variables', async () => { + let app = await create('default'); + server = await watch(app, 8084); + + let page = await loadPage(chrome, 'http://127.0.0.1:8084/'); + + let header = resolve(app, './src/components/header/index.js'); + let original = await readFile(header, 'utf8'); + let update = original.replace( + '

Preact App

', + '

{process.env.PREACT_APP_MY_VARIABLE}

' + ); + await writeFile(header, update); + + // "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', () => { From 7168c5db74db912b58bf1efd9380b88c3ed498cf Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Mon, 14 Mar 2022 04:35:05 -0500 Subject: [PATCH 4/7] test: Avoid port collision? --- packages/cli/tests/watch.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/tests/watch.test.js b/packages/cli/tests/watch.test.js index ac51beb45..714dbe14a 100644 --- a/packages/cli/tests/watch.test.js +++ b/packages/cli/tests/watch.test.js @@ -37,9 +37,9 @@ describe('preact', () => { it('should use a custom `.env` with prefixed environment variables', async () => { let app = await create('default'); - server = await watch(app, 8084); + server = await watch(app, 8085); - let page = await loadPage(chrome, 'http://127.0.0.1:8084/'); + let page = await loadPage(chrome, 'http://127.0.0.1:8085/'); let header = resolve(app, './src/components/header/index.js'); let original = await readFile(header, 'utf8'); From 766b2e77d362c6308033d9d86f787ab5e09f82cf Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Tue, 29 Mar 2022 15:11:40 -0500 Subject: [PATCH 5/7] docs: Adding code comments for dotenv usage --- packages/cli/lib/commands/build.js | 6 ++++++ packages/cli/lib/commands/watch.js | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/packages/cli/lib/commands/build.js b/packages/cli/lib/commands/build.js index 22927b158..2b965e552 100644 --- a/packages/cli/lib/commands/build.js +++ b/packages/cli/lib/commands/build.js @@ -92,7 +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); diff --git a/packages/cli/lib/commands/watch.js b/packages/cli/lib/commands/watch.js index 168b63459..20c78f7da 100644 --- a/packages/cli/lib/commands/watch.js +++ b/packages/cli/lib/commands/watch.js @@ -107,6 +107,11 @@ async function command(src, argv) { } 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); From e07aef54023376ebc3251207c5675960573deaad Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Tue, 29 Mar 2022 15:15:13 -0500 Subject: [PATCH 6/7] docs: Fixing repetative verbiage in env var section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff23dacb7..a1fac105c 100644 --- a/README.md +++ b/README.md @@ -338,7 +338,7 @@ console.log(process.env.PREACT_APP_MY_VARIABLE); 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 also set variables using a `.env` file in the root of your project: +You can set and store variables using a `.env` file in the root of your project: > `.env` From 4fe11a810a35a30de5cb06b552f183ef796c32c1 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Sat, 2 Apr 2022 02:58:05 -0500 Subject: [PATCH 7/7] test: Correcting bad .env watch test --- packages/cli/tests/watch.test.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/cli/tests/watch.test.js b/packages/cli/tests/watch.test.js index 714dbe14a..f7ac825fc 100644 --- a/packages/cli/tests/watch.test.js +++ b/packages/cli/tests/watch.test.js @@ -37,9 +37,6 @@ describe('preact', () => { it('should use a custom `.env` with prefixed environment variables', async () => { let app = await create('default'); - server = await watch(app, 8085); - - let page = await loadPage(chrome, 'http://127.0.0.1:8085/'); let header = resolve(app, './src/components/header/index.js'); let original = await readFile(header, 'utf8'); @@ -48,6 +45,14 @@ describe('preact', () => { '

{process.env.PREACT_APP_MY_VARIABLE}

' ); 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(