From 19ad91709e72b6e8d57af5b4cf702036ad09d223 Mon Sep 17 00:00:00 2001
From: "Blitz.js Bot" <79382586+blitzjs-bot@users.noreply.github.com>
Date: Fri, 3 Dec 2021 08:23:32 -0500
Subject: [PATCH 1/3] (meta) added @divpreet as contributor
---
.all-contributorsrc | 9 +++++++++
README.md | 5 ++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 779d7a9c83..04f12499db 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -3499,6 +3499,15 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "divpreet",
+ "name": "Div",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2805650?v=4",
+ "profile": "https://github.com/divpreet",
+ "contributions": [
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index f31a112a24..90d355d140 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-
+
@@ -732,6 +732,9 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
David Christie 📖 |
Ajanth 📖 |
+
+ Div 📖 |
+
From 4aba0d31f6cf990810f34ac56820ca7dd2942e8f Mon Sep 17 00:00:00 2001
From: Aleksandra Sikora
Date: Fri, 3 Dec 2021 16:05:37 +0100
Subject: [PATCH 2/3] Load env variables based on `APP_ENV` or `-e` flag
(#2878)
(minor)
---
.github/workflows/compressed.yml | 3 +
.github/workflows/main.yml | 37 ++--
CONTRIBUTING.md | 6 +
examples/cypress/cypress/plugins/index.ts | 4 +-
examples/fauna/test/setup.ts | 4 +-
nextjs/packages/next-env/README.md | 2 +-
nextjs/packages/next-env/index.ts | 9 +-
nextjs/packages/next-env/package.json | 7 +-
nextjs/packages/next/bin/next.ts | 7 +
nextjs/packages/next/build/entries.ts | 2 +-
nextjs/packages/next/build/index.ts | 2 +-
.../loaders/next-serverless-loader/index.ts | 2 +-
nextjs/packages/next/export/index.ts | 2 +-
nextjs/packages/next/package.json | 2 +-
.../next/server/config-utils-worker.ts | 2 +-
nextjs/packages/next/server/config.ts | 2 +-
nextjs/packages/next/server/next-server.ts | 2 +-
nextjs/packages/next/shared/lib/utils.ts | 2 +-
.../integration/chunking/test/index.test.js | 2 +-
.../integration/env-config-app-env/app/.env | 5 +
.../env-config-app-env/app/.env.production | 1 +
.../env-config-app-env/app/.env.staging | 4 +
.../env-config-app-env/app/.env.staging.local | 1 +
.../env-config-app-env/app/next.config.js | 17 ++
.../env-config-app-env/app/package.json | 4 +
.../env-config-app-env/app/pages/api/all.js | 24 +++
.../env-config-app-env/app/pages/global.js | 1 +
.../env-config-app-env/app/pages/index.js | 35 ++++
.../env-config-app-env/app/pages/some-ssg.js | 35 ++++
.../env-config-app-env/app/pages/some-ssp.js | 34 ++++
.../env-config-app-env/test/index.test.js | 163 ++++++++++++++++++
.../package-managers/pnpm/app/package.json | 2 +-
.../package-managers/pnpm/test/index.test.js | 2 +-
package.json | 8 +-
packages/blitz/jest-preset/global-setup.js | 4 +-
packages/blitz/src/cli.ts | 4 +
packages/cli/package.json | 1 -
packages/cli/src/commands/build.ts | 4 +
packages/cli/src/commands/codegen.ts | 4 +
packages/cli/src/commands/console.ts | 4 +
packages/cli/src/commands/db.ts | 4 +
packages/cli/src/commands/dev.ts | 4 +
packages/cli/src/commands/export.ts | 4 +
packages/cli/src/commands/generate.ts | 4 +
packages/cli/src/commands/install.ts | 4 +
packages/cli/src/commands/new.ts | 4 +-
packages/cli/src/commands/prisma.ts | 9 +-
packages/cli/src/commands/start.ts | 4 +
packages/cli/src/index.ts | 8 +-
yarn.lock | 32 ++--
50 files changed, 476 insertions(+), 57 deletions(-)
create mode 100644 nextjs/test/integration/env-config-app-env/app/.env
create mode 100644 nextjs/test/integration/env-config-app-env/app/.env.production
create mode 100644 nextjs/test/integration/env-config-app-env/app/.env.staging
create mode 100644 nextjs/test/integration/env-config-app-env/app/.env.staging.local
create mode 100644 nextjs/test/integration/env-config-app-env/app/next.config.js
create mode 100644 nextjs/test/integration/env-config-app-env/app/package.json
create mode 100644 nextjs/test/integration/env-config-app-env/app/pages/api/all.js
create mode 100644 nextjs/test/integration/env-config-app-env/app/pages/global.js
create mode 100644 nextjs/test/integration/env-config-app-env/app/pages/index.js
create mode 100644 nextjs/test/integration/env-config-app-env/app/pages/some-ssg.js
create mode 100644 nextjs/test/integration/env-config-app-env/app/pages/some-ssp.js
create mode 100644 nextjs/test/integration/env-config-app-env/test/index.test.js
diff --git a/.github/workflows/compressed.yml b/.github/workflows/compressed.yml
index ce7fc6f009..175069598a 100644
--- a/.github/workflows/compressed.yml
+++ b/.github/workflows/compressed.yml
@@ -15,6 +15,9 @@ jobs:
steps:
- uses: actions/checkout@v2
+ - uses: actions/setup-node@v2
+ with:
+ node-version: "14"
- name: Count size
uses: preactjs/compressed-size-action@v2
with:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2ff8fa5ac1..17483f8fee 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -30,9 +30,9 @@ jobs:
path: |
${{ steps.yarn-cache-dir-path.outputs.dir }}
**/node_modules
- key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
+ key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
restore-keys: |
- ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
+ ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
- name: Install dependencies
run: yarn install --frozen-lockfile --silent
env:
@@ -74,9 +74,9 @@ jobs:
path: |
${{ steps.yarn-cache-dir-path.outputs.dir }}
**/node_modules
- key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
+ key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
restore-keys: |
- ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
+ ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
- run: yarn install --frozen-lockfile --check-files
- name: Build Packages
run: yarn build
@@ -98,6 +98,10 @@ jobs:
with:
path: ./*
key: ${{ runner.os }}-${{ github.sha }}
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: "14"
- name: Setup kernel to increase watchers
if: runner.os == 'Linux'
run: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
@@ -120,6 +124,10 @@ jobs:
with:
path: ./*
key: ${{ runner.os }}-${{ github.sha }}
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: "14"
# Needed to get cypress binary
- run: yarn cypress install
- name: Install sass
@@ -157,9 +165,9 @@ jobs:
# path: |
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
# **/node_modules
- # key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
+ # key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
# restore-keys: |
- # ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
+ # ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
- run: yarn install --frozen-lockfile --check-files
- name: Build Packages
run: yarn build
@@ -222,6 +230,10 @@ jobs:
with:
path: ./*
key: ${{ runner.os }}-${{ github.sha }}
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: "14"
# TODO: remove after we fix watchpack watching too much
- name: Setup kernel to increase watchers
@@ -256,9 +268,9 @@ jobs:
# path: |
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
# **/node_modules
- # key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
+ # key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
# restore-keys: |
- # ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
+ # ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
- run: yarn install --frozen-lockfile --check-files
- name: Build Packages
run: yarn build
@@ -285,7 +297,10 @@ jobs:
with:
path: ./*
key: ${{ runner.os }}-${{ github.sha }}
-
+ - name: Use Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: "14"
# TODO: remove after we fix watchpack watching too much
- name: Setup kernel to increase watchers
if: runner.os == 'Linux'
@@ -318,9 +333,9 @@ jobs:
# path: |
# ${{ steps.yarn-cache-dir-path.outputs.dir }}
# **/node_modules
- # key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-${{ hashFiles('yarn.lock') }}
+ # key: ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-${{ hashFiles('yarn.lock') }}
# restore-keys: |
- # ${{ runner.os }}-${{ runner.node_version}}-yarn-v13-
+ # ${{ runner.os }}-${{ runner.node_version}}-yarn-v14-
- run: yarn install --frozen-lockfile --check-files
- name: Build Packages
run: yarn build
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 48672c9edb..3b5ed7fcf3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,6 +4,12 @@
## Notes For Core Team
+### To Publish a new NPM Package under `@blitzjs/` namespace
+
+1. cd into the package directory
+2. Run `npm publish --tag danger --access public`
+ - `--access public` is required because scoped packages are set to private by default
+
### Syncing Next.js Fork
1. Run `yarn push-nextjs`
diff --git a/examples/cypress/cypress/plugins/index.ts b/examples/cypress/cypress/plugins/index.ts
index 5e198bd0b8..b7967503bf 100644
--- a/examples/cypress/cypress/plugins/index.ts
+++ b/examples/cypress/cypress/plugins/index.ts
@@ -1,5 +1,7 @@
process.env.NODE_ENV = "test"
-require("dotenv-flow").config({ silent: true })
+
+import { loadEnvConfig } from "@blitzjs/env"
+loadEnvConfig()
import "./register-ts-paths"
import db from "db"
diff --git a/examples/fauna/test/setup.ts b/examples/fauna/test/setup.ts
index 8f777a3189..ecbde24ef3 100644
--- a/examples/fauna/test/setup.ts
+++ b/examples/fauna/test/setup.ts
@@ -3,4 +3,6 @@
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom/extend-expect"
-require("dotenv-flow").config({ silent: true })
+import { loadEnvConfig } from "@blitzjs/env"
+
+loadEnvConfig()
diff --git a/nextjs/packages/next-env/README.md b/nextjs/packages/next-env/README.md
index cc08c112ee..af60128ea7 100644
--- a/nextjs/packages/next-env/README.md
+++ b/nextjs/packages/next-env/README.md
@@ -1,3 +1,3 @@
-# `@next/env`
+# `@blitzjs/env`
Next.js' util for loading dotenv files in with the proper priorities
diff --git a/nextjs/packages/next-env/index.ts b/nextjs/packages/next-env/index.ts
index a33636c702..2ab59f2288 100644
--- a/nextjs/packages/next-env/index.ts
+++ b/nextjs/packages/next-env/index.ts
@@ -66,7 +66,7 @@ export function processEnv(
}
export function loadEnvConfig(
- dir: string,
+ dir: string = process.cwd(),
dev?: boolean,
log: Log = console
): {
@@ -79,8 +79,11 @@ export function loadEnvConfig(
const isTest = process.env.NODE_ENV === 'test'
const mode = isTest ? 'test' : dev ? 'development' : 'production'
- const dotenvFiles = [
+ const appEnv = process.env.APP_ENV
+ let dotenvFiles = [
`.env.${mode}.local`,
+ appEnv && `.env.${appEnv}.local`,
+ appEnv && `.env.${appEnv}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
@@ -106,7 +109,7 @@ export function loadEnvConfig(
path: envFile,
contents,
})
- } catch (err) {
+ } catch (err: any) {
if (err.code !== 'ENOENT') {
log.error(`Failed to load env from ${envFile}`, err)
}
diff --git a/nextjs/packages/next-env/package.json b/nextjs/packages/next-env/package.json
index b2f6a2b635..b71c45e0e6 100644
--- a/nextjs/packages/next-env/package.json
+++ b/nextjs/packages/next-env/package.json
@@ -1,7 +1,6 @@
{
- "private": true,
- "name": "@next/env",
- "version": "11.1.0",
+ "name": "@blitzjs/env",
+ "version": "0.43.0",
"keywords": [
"react",
"next",
@@ -14,7 +13,7 @@
"url": "https://github.com/vercel/next.js",
"directory": "packages/next-env"
},
- "author": "Next.js Team ",
+ "author": "Blitz.js",
"license": "MIT",
"main": "dist/index.js",
"types": "types/index.d.ts",
diff --git a/nextjs/packages/next/bin/next.ts b/nextjs/packages/next/bin/next.ts
index 9e34173180..3bf38eb68c 100755
--- a/nextjs/packages/next/bin/next.ts
+++ b/nextjs/packages/next/bin/next.ts
@@ -30,10 +30,12 @@ const args = arg(
'--version': Boolean,
'--help': Boolean,
'--inspect': Boolean,
+ '--env': String,
// Aliases
'-v': '--version',
'-h': '--help',
+ '-e': '--env'
},
{
permissive: true,
@@ -61,6 +63,7 @@ if (!foundCommand && args['--help']) {
${Object.keys(commands).join(', ')}
Options
+ --env, -e App environment name
--version, -v Version number
--help, -h Displays this message
@@ -83,6 +86,10 @@ if (args['--help']) {
forwardedArgs.push('--help')
}
+if (args['--env']) {
+ process.env.APP_ENV = args['--env']
+}
+
const defaultEnv = command === 'dev' ? 'development' : 'production'
const standardEnv = ['production', 'development', 'test']
diff --git a/nextjs/packages/next/build/entries.ts b/nextjs/packages/next/build/entries.ts
index 59a3bcc7c7..2798c14360 100644
--- a/nextjs/packages/next/build/entries.ts
+++ b/nextjs/packages/next/build/entries.ts
@@ -8,7 +8,7 @@ import { normalizePagePath } from '../server/normalize-page-path'
import { warn } from './output/log'
import { ClientPagesLoaderOptions } from './webpack/loaders/next-client-pages-loader'
import { ServerlessLoaderQuery } from './webpack/loaders/next-serverless-loader'
-import { LoadedEnvFiles } from '@next/env'
+import { LoadedEnvFiles } from '@blitzjs/env'
import { convertPageFilePathToRoutePath } from './utils'
import { NextConfigComplete } from '../server/config-shared'
diff --git a/nextjs/packages/next/build/index.ts b/nextjs/packages/next/build/index.ts
index cce32b9926..cf678d9477 100644
--- a/nextjs/packages/next/build/index.ts
+++ b/nextjs/packages/next/build/index.ts
@@ -1,4 +1,4 @@
-import { loadEnvConfig } from '@next/env'
+import { loadEnvConfig } from '@blitzjs/env'
import chalk from 'chalk'
import crypto from 'crypto'
import { promises, writeFileSync } from 'fs'
diff --git a/nextjs/packages/next/build/webpack/loaders/next-serverless-loader/index.ts b/nextjs/packages/next/build/webpack/loaders/next-serverless-loader/index.ts
index 7db9b8832d..6196232978 100644
--- a/nextjs/packages/next/build/webpack/loaders/next-serverless-loader/index.ts
+++ b/nextjs/packages/next/build/webpack/loaders/next-serverless-loader/index.ts
@@ -90,7 +90,7 @@ const nextServerlessLoader: webpack.loader.Loader = function () {
)
const envLoading = `
- const { processEnv } = require('@next/env')
+ const { processEnv } = require('@blitzjs/env')
processEnv(${Buffer.from(loadedEnvFiles, 'base64').toString()})
`
diff --git a/nextjs/packages/next/export/index.ts b/nextjs/packages/next/export/index.ts
index 646e2eaf23..294bae2665 100644
--- a/nextjs/packages/next/export/index.ts
+++ b/nextjs/packages/next/export/index.ts
@@ -37,7 +37,7 @@ import {
normalizePagePath,
denormalizePagePath,
} from '../server/normalize-page-path'
-import { loadEnvConfig } from '@next/env'
+import { loadEnvConfig } from '@blitzjs/env'
import { PrerenderManifest } from '../build'
import { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
import { getPagePath } from '../server/require'
diff --git a/nextjs/packages/next/package.json b/nextjs/packages/next/package.json
index 88fe41bbd3..476b93ddcf 100644
--- a/nextjs/packages/next/package.json
+++ b/nextjs/packages/next/package.json
@@ -73,8 +73,8 @@
"dependencies": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/runtime": "7.12.5",
+ "@blitzjs/env": "0.43.0",
"@hapi/accept": "5.0.2",
- "@next/env": "11.1.0",
"@next/polyfill-module": "11.1.0",
"@next/react-dev-overlay": "11.1.0",
"@next/react-refresh-utils": "11.1.0",
diff --git a/nextjs/packages/next/server/config-utils-worker.ts b/nextjs/packages/next/server/config-utils-worker.ts
index 3fa34c399f..957c38584f 100644
--- a/nextjs/packages/next/server/config-utils-worker.ts
+++ b/nextjs/packages/next/server/config-utils-worker.ts
@@ -1,4 +1,4 @@
-import { loadEnvConfig } from '@next/env'
+import { loadEnvConfig } from '@blitzjs/env'
import findUp from 'next/dist/compiled/find-up'
import { init as initWebpack } from 'next/dist/compiled/webpack/webpack'
import { CONFIG_FILE, PHASE_DEVELOPMENT_SERVER } from '../shared/lib/constants'
diff --git a/nextjs/packages/next/server/config.ts b/nextjs/packages/next/server/config.ts
index ecf7e3230c..49ecb170f3 100644
--- a/nextjs/packages/next/server/config.ts
+++ b/nextjs/packages/next/server/config.ts
@@ -14,7 +14,7 @@ import {
} from './config-shared'
import { loadWebpackHook } from './config-utils'
import { ImageConfig, imageConfigDefault, VALID_LOADERS } from './image-config'
-import { loadEnvConfig } from '@next/env'
+import { loadEnvConfig } from '@blitzjs/env'
import { hasNextSupport } from '../telemetry/ci-info'
const debug = require('debug')('blitz:config')
diff --git a/nextjs/packages/next/server/next-server.ts b/nextjs/packages/next/server/next-server.ts
index 3580e16a8d..8d991f63f3 100644
--- a/nextjs/packages/next/server/next-server.ts
+++ b/nextjs/packages/next/server/next-server.ts
@@ -82,7 +82,7 @@ import {
resultFromChunks,
resultToChunks,
} from './utils'
-import { loadEnvConfig } from '@next/env'
+import { loadEnvConfig } from '@blitzjs/env'
import './node-polyfill-fetch'
import { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
import { removePathTrailingSlash } from '../client/normalize-trailing-slash'
diff --git a/nextjs/packages/next/shared/lib/utils.ts b/nextjs/packages/next/shared/lib/utils.ts
index ea031e843d..c9f5f5d6ba 100644
--- a/nextjs/packages/next/shared/lib/utils.ts
+++ b/nextjs/packages/next/shared/lib/utils.ts
@@ -2,7 +2,7 @@ import { formatUrl } from './router/utils/format-url'
import type { BuildManifest } from '../../server/get-page-files'
import type { ComponentType } from 'react'
import type { DomainLocale } from '../../server/config'
-import type { Env } from '@next/env'
+import type { Env } from '@blitzjs/env'
import type { IncomingMessage, ServerResponse } from 'http'
import type { NextRouter } from './router/router'
import type { ParsedUrlQuery } from 'querystring'
diff --git a/nextjs/test/integration/chunking/test/index.test.js b/nextjs/test/integration/chunking/test/index.test.js
index 24bcfd92d2..5b0d74a807 100644
--- a/nextjs/test/integration/chunking/test/index.test.js
+++ b/nextjs/test/integration/chunking/test/index.test.js
@@ -12,7 +12,7 @@ import {
import webdriver from 'next-webdriver'
import { join } from 'path'
-jest.setTimeout(1000 * 60 * 1)
+jest.setTimeout(1000 * 60 * 3)
const appDir = join(__dirname, '../')
diff --git a/nextjs/test/integration/env-config-app-env/app/.env b/nextjs/test/integration/env-config-app-env/app/.env
new file mode 100644
index 0000000000..3ae85099f0
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/.env
@@ -0,0 +1,5 @@
+PROCESS_ENV_KEY="env"
+ENV_FILE_KEY=env
+NEXT_PUBLIC_TEST_DEST=another
+ENV_KEY_IN_NEXT_CONFIG="hello from next.config.js"
+NEXT_PUBLIC_ENV_KEY_IN_NEXT_CONFIG="hello again from next.config.js"
diff --git a/nextjs/test/integration/env-config-app-env/app/.env.production b/nextjs/test/integration/env-config-app-env/app/.env.production
new file mode 100644
index 0000000000..04bfde25bb
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/.env.production
@@ -0,0 +1 @@
+PRODUCTION_ENV_FILE_KEY=production
diff --git a/nextjs/test/integration/env-config-app-env/app/.env.staging b/nextjs/test/integration/env-config-app-env/app/.env.staging
new file mode 100644
index 0000000000..1d105ed833
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/.env.staging
@@ -0,0 +1,4 @@
+NEXT_PUBLIC_TEST_DEST=staging-route
+ENV_KEY_IN_NEXT_CONFIG="hello from staging app"
+NEXT_PUBLIC_ENV_KEY_IN_NEXT_CONFIG="hello again from staging app"
+ENV_FILE_DEVELOPMENT_OVERRIDE_TEST=staging
diff --git a/nextjs/test/integration/env-config-app-env/app/.env.staging.local b/nextjs/test/integration/env-config-app-env/app/.env.staging.local
new file mode 100644
index 0000000000..bf2d4e5ae4
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/.env.staging.local
@@ -0,0 +1 @@
+ENV_FILE_DEVELOPMENT_OVERRIDE_TEST=staginglocal
diff --git a/nextjs/test/integration/env-config-app-env/app/next.config.js b/nextjs/test/integration/env-config-app-env/app/next.config.js
new file mode 100644
index 0000000000..df889543a5
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/next.config.js
@@ -0,0 +1,17 @@
+module.exports = {
+ cleanDistDir: false,
+ // update me
+ env: {
+ nextConfigEnv: process.env.ENV_KEY_IN_NEXT_CONFIG,
+ nextConfigPublicEnv: process.env.NEXT_PUBLIC_ENV_KEY_IN_NEXT_CONFIG,
+ },
+ async redirects() {
+ return [
+ {
+ source: '/hello',
+ permanent: false,
+ destination: `/${process.env.NEXT_PUBLIC_TEST_DEST}`,
+ },
+ ]
+ },
+}
diff --git a/nextjs/test/integration/env-config-app-env/app/package.json b/nextjs/test/integration/env-config-app-env/app/package.json
new file mode 100644
index 0000000000..2904b4924b
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "env-config-app-env",
+ "dependencies": {}
+}
diff --git a/nextjs/test/integration/env-config-app-env/app/pages/api/all.js b/nextjs/test/integration/env-config-app-env/app/pages/api/all.js
new file mode 100644
index 0000000000..4099fa68fd
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/pages/api/all.js
@@ -0,0 +1,24 @@
+const variables = [
+ 'PROCESS_ENV_KEY',
+ 'ENV_FILE_KEY',
+ 'ENV_FILE_EMPTY_FIRST',
+ 'PRODUCTION_ENV_FILE_KEY',
+ 'LOCAL_PRODUCTION_ENV_FILE_KEY',
+ 'ENV_FILE_DEVELOPMENT_OVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_OVERRIDEOVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_LOCAL_OVERRIDEOVERRIDE_TEST',
+]
+
+const items = {
+ nextConfigEnv: process.env.nextConfigEnv,
+ nextConfigPublicEnv: process.env.nextConfigPublicEnv,
+}
+
+variables.forEach((variable) => {
+ items[variable] = process.env[variable]
+})
+
+export default async (req, res) => {
+ // Only for testing, don't do this...
+ res.json(items)
+}
diff --git a/nextjs/test/integration/env-config-app-env/app/pages/global.js b/nextjs/test/integration/env-config-app-env/app/pages/global.js
new file mode 100644
index 0000000000..ded4746e31
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/pages/global.js
@@ -0,0 +1 @@
+export default () => {process.env.NEXT_PUBLIC_TEST_DEST}
diff --git a/nextjs/test/integration/env-config-app-env/app/pages/index.js b/nextjs/test/integration/env-config-app-env/app/pages/index.js
new file mode 100644
index 0000000000..36d51b372e
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/pages/index.js
@@ -0,0 +1,35 @@
+const variables = [
+ 'PROCESS_ENV_KEY',
+ 'ENV_FILE_KEY',
+ 'ENV_FILE_EMPTY_FIRST',
+ 'PRODUCTION_ENV_FILE_KEY',
+ 'LOCAL_PRODUCTION_ENV_FILE_KEY',
+ 'ENV_FILE_DEVELOPMENT_OVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_OVERRIDEOVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_LOCAL_OVERRIDEOVERRIDE_TEST',
+]
+
+export async function getStaticProps() {
+ const items = {}
+
+ variables.forEach((variable) => {
+ if (process.env[variable]) {
+ items[variable] = process.env[variable]
+ }
+ })
+
+ return {
+ // Do not pass any sensitive values here as they will
+ // be made PUBLICLY available in `pageProps`
+ props: { env: items },
+ revalidate: 1,
+ }
+}
+
+export default ({ env }) => (
+ <>
+ {JSON.stringify(env)}
+ {process.env.nextConfigEnv}
+ {process.env.nextConfigPublicEnv}
+ >
+)
diff --git a/nextjs/test/integration/env-config-app-env/app/pages/some-ssg.js b/nextjs/test/integration/env-config-app-env/app/pages/some-ssg.js
new file mode 100644
index 0000000000..0fc7a3b6dc
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/pages/some-ssg.js
@@ -0,0 +1,35 @@
+const variables = [
+ 'PROCESS_ENV_KEY',
+ 'ENV_FILE_KEY',
+ 'ENV_FILE_EMPTY_FIRST',
+ 'PRODUCTION_ENV_FILE_KEY',
+ 'LOCAL_PRODUCTION_ENV_FILE_KEY',
+ 'ENV_FILE_DEVELOPMENT_OVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_OVERRIDEOVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_LOCAL_OVERRIDEOVERRIDE_TEST',
+]
+
+export async function getStaticProps() {
+ const items = {}
+
+ variables.forEach((variable) => {
+ if (typeof process.env[variable] !== 'undefined') {
+ items[variable] = process.env[variable]
+ }
+ })
+
+ return {
+ // Do not pass any sensitive values here as they will
+ // be made PUBLICLY available in `pageProps`
+ props: { env: items },
+ revalidate: 1,
+ }
+}
+
+export default ({ env }) => (
+ <>
+ {JSON.stringify(env)}
+ {process.env.nextConfigEnv}
+ {process.env.nextConfigPublicEnv}
+ >
+)
diff --git a/nextjs/test/integration/env-config-app-env/app/pages/some-ssp.js b/nextjs/test/integration/env-config-app-env/app/pages/some-ssp.js
new file mode 100644
index 0000000000..e9a908e955
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/app/pages/some-ssp.js
@@ -0,0 +1,34 @@
+const variables = [
+ 'PROCESS_ENV_KEY',
+ 'ENV_FILE_KEY',
+ 'ENV_FILE_EMPTY_FIRST',
+ 'PRODUCTION_ENV_FILE_KEY',
+ 'LOCAL_PRODUCTION_ENV_FILE_KEY',
+ 'ENV_FILE_DEVELOPMENT_OVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_OVERRIDEOVERRIDE_TEST',
+ 'ENV_FILE_PRODUCTION_LOCAL_OVERRIDEOVERRIDE_TEST',
+]
+
+export async function getServerSideProps() {
+ const items = {}
+
+ variables.forEach((variable) => {
+ if (typeof process.env[variable] !== 'undefined') {
+ items[variable] = process.env[variable]
+ }
+ })
+
+ return {
+ // Do not pass any sensitive values here as they will
+ // be made PUBLICLY available in `pageProps`
+ props: { env: items },
+ }
+}
+
+export default ({ env }) => (
+ <>
+ {JSON.stringify(env)}
+ {process.env.nextConfigEnv}
+ {process.env.nextConfigPublicEnv}
+ >
+)
diff --git a/nextjs/test/integration/env-config-app-env/test/index.test.js b/nextjs/test/integration/env-config-app-env/test/index.test.js
new file mode 100644
index 0000000000..653e17a5f2
--- /dev/null
+++ b/nextjs/test/integration/env-config-app-env/test/index.test.js
@@ -0,0 +1,163 @@
+/* eslint-env jest */
+
+import url from 'url'
+import fs from 'fs-extra'
+import { join } from 'path'
+import cheerio from 'cheerio'
+import {
+ nextBuild,
+ nextStart,
+ renderViaHTTP,
+ findPort,
+ launchApp,
+ killApp,
+ fetchViaHTTP,
+} from 'next-test-utils'
+
+jest.setTimeout(1000 * 60 * 2)
+
+let app
+let appPort
+const appDir = join(__dirname, '../app')
+
+const getEnvFromHtml = async (path) => {
+ const html = await renderViaHTTP(appPort, path)
+ const $ = cheerio.load(html)
+ const env = JSON.parse($('p').text())
+ env.nextConfigEnv = $('#nextConfigEnv').text()
+ env.nextConfigPublicEnv = $('#nextConfigPublicEnv').text()
+ return env
+}
+
+const runTests = (mode = 'dev') => {
+ const isDevOnly = mode === 'dev'
+
+ const checkEnvData = (data) => {
+ expect(data.ENV_FILE_KEY).toBe('env')
+ expect(data.PRODUCTION_ENV_FILE_KEY).toBe(
+ isDevOnly ? undefined : 'production'
+ )
+ expect(data.ENV_FILE_DEVELOPMENT_OVERRIDE_TEST).toEqual('staginglocal')
+
+ expect(data.nextConfigEnv).toBe('hello from staging app')
+ expect(data.nextConfigPublicEnv).toBe('hello again from staging app')
+ }
+
+ it('should pass staging env to next.config.js', async () => {
+ const res = await fetchViaHTTP(appPort, '/hello', undefined, {
+ redirect: 'manual',
+ })
+ const { pathname } = url.parse(res.headers.get('location'))
+
+ expect(res.status).toBe(307)
+ expect(pathname).toBe('/staging-route')
+ })
+
+ it('should provide env for SSG', async () => {
+ const data = await getEnvFromHtml('/some-ssg')
+ checkEnvData(data)
+ })
+
+ it('should provide env correctly for SSR', async () => {
+ const data = await getEnvFromHtml('/some-ssp')
+ checkEnvData(data)
+ })
+
+ it('should provide env correctly for API routes', async () => {
+ const data = JSON.parse(await renderViaHTTP(appPort, '/api/all'))
+ checkEnvData(data)
+ })
+
+ it('should load env from .env', async () => {
+ const data = await getEnvFromHtml('/')
+ expect(data.ENV_FILE_KEY).toEqual('env')
+ })
+}
+
+describe('Env Config', () => {
+ describe('dev mode', () => {
+ beforeAll(async () => {
+ appPort = await findPort()
+ app = await launchApp(appDir, appPort, {
+ env: {
+ APP_ENV: 'staging',
+ },
+ })
+ })
+ afterAll(() => killApp(app))
+
+ runTests('dev')
+ })
+
+ describe('server mode', () => {
+ beforeAll(async () => {
+ const { code } = await nextBuild(appDir, [], {
+ env: {
+ APP_ENV: 'staging',
+ },
+ })
+ if (code !== 0) throw new Error(`Build failed with exit code ${code}`)
+
+ appPort = await findPort()
+ app = await nextStart(appDir, appPort, {
+ env: {
+ APP_ENV: 'staging',
+ },
+ })
+ })
+ afterAll(() => killApp(app))
+
+ runTests('server')
+ })
+
+ describe('serverless mode', () => {
+ let nextConfigContent = ''
+ const nextConfigPath = join(appDir, 'next.config.js')
+ const envFiles = [
+ '.env',
+ '.env.staging',
+ '.env.staging.local',
+ '.env.production',
+ ].map((file) => join(appDir, file))
+
+ beforeAll(async () => {
+ nextConfigContent = await fs.readFile(nextConfigPath, 'utf8')
+ await fs.writeFile(
+ nextConfigPath,
+ nextConfigContent.replace(
+ '// update me',
+ `target: 'experimental-serverless-trace',`
+ )
+ )
+ const { code } = await nextBuild(appDir, [], {
+ env: {
+ APP_ENV: 'staging',
+ },
+ })
+
+ if (code !== 0) throw new Error(`Build failed with exit code ${code}`)
+ appPort = await findPort()
+
+ // rename the files so they aren't loaded by `next start`
+ // to test that they were bundled into the serverless files
+ for (const file of envFiles) {
+ await fs.rename(file, `${file}.bak`)
+ }
+
+ app = await nextStart(appDir, appPort, {
+ env: {
+ APP_ENV: 'staging',
+ },
+ })
+ })
+ afterAll(async () => {
+ for (const file of envFiles) {
+ await fs.rename(`${file}.bak`, file)
+ }
+ await fs.writeFile(nextConfigPath, nextConfigContent)
+ await killApp(app)
+ })
+
+ runTests('serverless')
+ })
+})
diff --git a/nextjs/test/package-managers/pnpm/app/package.json b/nextjs/test/package-managers/pnpm/app/package.json
index 2c323a6a50..14bc0660a6 100644
--- a/nextjs/test/package-managers/pnpm/app/package.json
+++ b/nextjs/test/package-managers/pnpm/app/package.json
@@ -10,7 +10,7 @@
"dependencies": {
"react": "^16.7.0",
"react-dom": "^16.7.0",
- "@next/env": "^10.0.9",
+ "@blitzjs/env": "0.43.0",
"@next/polyfill-module": "^10.0.9",
"@next/react-dev-overlay": "^10.0.9",
"@next/react-refresh-utils": "^10.0.9"
diff --git a/nextjs/test/package-managers/pnpm/test/index.test.js b/nextjs/test/package-managers/pnpm/test/index.test.js
index 8e9a4a1d40..9198e5da34 100644
--- a/nextjs/test/package-managers/pnpm/test/index.test.js
+++ b/nextjs/test/package-managers/pnpm/test/index.test.js
@@ -79,7 +79,7 @@ describe('pnpm support', () => {
await usingTempDir(async (tempDir) => {
const nextTarballPath = await pack(tempDir, 'next')
const dependencyTarballPaths = {
- // '@next/env': await pack(tempDir, 'next-env'),
+ '@blitzjs/env': await pack(tempDir, 'next-env'),
// '@next/polyfill-module': await pack(tempDir, 'next-polyfill-module'),
// '@next/react-dev-overlay': await pack(tempDir, 'react-dev-overlay'),
// '@next/react-refresh-utils': await pack(tempDir, 'react-refresh-utils'),
diff --git a/package.json b/package.json
index 03bf164dc5..2de6efb052 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"nextjs/packages/next-mdx",
"nextjs/packages/eslint-config-next",
"nextjs/packages/eslint-plugin-next",
+ "nextjs/packages/next-env",
"examples/*",
"recipes/*"
],
@@ -28,14 +29,16 @@
"wait:nextjs": "wait-on -d 1000 nextjs/packages/next/dist/build/index.js",
"wait:nextjs-types": "wait-on -d 1000 nextjs/packages/next/dist/build/index.d.ts",
"dev:nextjs": "yarn workspace next dev",
+ "dev:next-env": "yarn workspace @blitzjs/env dev",
"dev:nextjs-types": "yarn wait:nextjs && yarn workspace next types && echo 'Finished building nextjs types'",
"dev:blitz": "cross-env BLITZ_PROD_BUILD=true preconstruct watch",
"dev:tsc": "yarn dev:nextjs-types && tsc --watch --pretty --preserveWatchOutput",
"dev:cli": "yarn wait:nextjs-types && yarn workspace @blitzjs/cli dev",
"dev:templates": "yarn workspace @blitzjs/generator dev",
- "dev": "concurrently --names \"nextjs,blitz,typecheck,cli,templates\" -c \"magenta,cyan,green,yellow,black\" -p \"{name}\" \"npm:dev:nextjs\" \"npm:dev:blitz\" \"npm:dev:tsc\" \"npm:dev:cli\" \"npm:dev:templates\"",
+ "dev": "concurrently --names \"nextjs,blitz,typecheck,cli,templates,next-env\" -c \"magenta,cyan,green,yellow,black,blue\" -p \"{name}\" \"npm:dev:nextjs\" \"npm:dev:blitz\" \"npm:dev:tsc\" \"npm:dev:cli\" \"npm:dev:templates\" \"npm:dev:next-env\"",
"build:nextjs": "yarn workspace next prepublish",
- "build": "yarn build:nextjs && cross-env BLITZ_PROD_BUILD=true preconstruct build && ultra -r --filter \"packages/*\" buildpkg && tsc",
+ "build:next-env": "yarn workspace @blitzjs/env prepublish",
+ "build": "yarn build:nextjs && yarn build:next-env && cross-env BLITZ_PROD_BUILD=true preconstruct build && ultra -r --filter \"packages/*\" buildpkg && tsc",
"lint": "eslint --ext \".js,.ts,.tsx\" .",
"link-cli": "yarn workspace blitz link",
"unlink-cli": "yarn workspace blitz unlink",
@@ -103,7 +106,6 @@
"@types/debug": "4.1.5",
"@types/detect-port": "1.3.0",
"@types/diff": "5.0.0",
- "@types/dotenv-flow": "3.1.0",
"@types/flush-write-stream": "1.0.0",
"@types/from2": "2.3.0",
"@types/fs-extra": "8.1.0",
diff --git a/packages/blitz/jest-preset/global-setup.js b/packages/blitz/jest-preset/global-setup.js
index 66196efa38..9b0b882711 100644
--- a/packages/blitz/jest-preset/global-setup.js
+++ b/packages/blitz/jest-preset/global-setup.js
@@ -1,4 +1,6 @@
// eslint-disable-next-line import/no-default-export
+import {loadEnvConfig} from "@blitzjs/env"
module.exports = function globalSetup(globalConfig) {
- require("dotenv-flow").config({silent: true})
+ const projectDir = process.cwd()
+ loadEnvConfig(projectDir)
}
diff --git a/packages/blitz/src/cli.ts b/packages/blitz/src/cli.ts
index efef22dcaf..86fbf72080 100755
--- a/packages/blitz/src/cli.ts
+++ b/packages/blitz/src/cli.ts
@@ -33,6 +33,10 @@ async function main() {
const cli = require(cliPkgPath)
+ if (options.e || options.env) {
+ process.env.APP_ENV = options.e || options.env
+ }
+
const hasVersionFlag = options._.length === 0 && (options.v || options.version)
const hasVerboseFlag = options._.length === 0 && (options.V || options.verbose)
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 9ccccfe087..e63b7ad478 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -37,7 +37,6 @@
"chalk": "^4.1.0",
"cross-spawn": "7.0.3",
"dotenv-expand": "^5.1.0",
- "dotenv-flow": "^3.2.0",
"enquirer": "2.3.6",
"esm": "3.2.25",
"fs-extra": "^9.1.0",
diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts
index 2fc4e3e8a5..39f701a724 100644
--- a/packages/cli/src/commands/build.ts
+++ b/packages/cli/src/commands/build.ts
@@ -7,6 +7,10 @@ export class Build extends Command {
static flags = {
help: flags.help({char: "h"}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
async run() {
diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts
index 6072c0ac97..c509e53546 100644
--- a/packages/cli/src/commands/codegen.ts
+++ b/packages/cli/src/commands/codegen.ts
@@ -9,6 +9,10 @@ export class CodeGen extends Command {
static flags = {
help: flags.help({char: "h"}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
async run() {
diff --git a/packages/cli/src/commands/console.ts b/packages/cli/src/commands/console.ts
index 10b322de6d..d9549e167f 100644
--- a/packages/cli/src/commands/console.ts
+++ b/packages/cli/src/commands/console.ts
@@ -14,6 +14,10 @@ export class Console extends Command {
static flags = {
help: flags.help({char: "h"}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
async run() {
diff --git a/packages/cli/src/commands/db.ts b/packages/cli/src/commands/db.ts
index e02da6ccf3..2e1fcc6b0d 100644
--- a/packages/cli/src/commands/db.ts
+++ b/packages/cli/src/commands/db.ts
@@ -69,6 +69,10 @@ ${require("chalk").bold("seed")} Generates seeded data in database via Prisma.
char: "f",
description: `Path to the seeds file, relative to the project root folder. Examples: db/seeds, db/seeds.ts, db/seeds/index.ts, db/my-seeds`,
}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
static strict = false
diff --git a/packages/cli/src/commands/dev.ts b/packages/cli/src/commands/dev.ts
index 8ce762db67..96833586d2 100644
--- a/packages/cli/src/commands/dev.ts
+++ b/packages/cli/src/commands/dev.ts
@@ -21,6 +21,10 @@ export class Dev extends Command {
"no-incremental-build": flags.boolean({
description: "Disable incremental build and start from a fresh cache",
}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
async run() {
diff --git a/packages/cli/src/commands/export.ts b/packages/cli/src/commands/export.ts
index da8590dc3f..57c9780e11 100644
--- a/packages/cli/src/commands/export.ts
+++ b/packages/cli/src/commands/export.ts
@@ -11,6 +11,10 @@ export class Export extends Command {
char: "o",
description: "set the output dir (defaults to 'out')",
}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
async run() {
diff --git a/packages/cli/src/commands/generate.ts b/packages/cli/src/commands/generate.ts
index 9f04f5416f..e766e7169d 100644
--- a/packages/cli/src/commands/generate.ts
+++ b/packages/cli/src/commands/generate.ts
@@ -116,6 +116,10 @@ export class Generate extends Command {
char: "d",
description: "Show what files will be created without writing them to disk",
}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
static examples = [
diff --git a/packages/cli/src/commands/install.ts b/packages/cli/src/commands/install.ts
index 09aa9c178a..82c3af0066 100644
--- a/packages/cli/src/commands/install.ts
+++ b/packages/cli/src/commands/install.ts
@@ -94,6 +94,10 @@ export class Install extends Command {
default: false,
description: "Install the recipe automatically without user confirmation",
}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
static args = [
diff --git a/packages/cli/src/commands/new.ts b/packages/cli/src/commands/new.ts
index 43620d2b97..652fa45807 100644
--- a/packages/cli/src/commands/new.ts
+++ b/packages/cli/src/commands/new.ts
@@ -1,4 +1,5 @@
import {log} from "@blitzjs/display"
+import {loadEnvConfig} from "@blitzjs/env"
import type {AppGeneratorOptions} from "@blitzjs/generator"
import {getLatestVersion} from "@blitzjs/generator"
import {flags} from "@oclif/command"
@@ -167,7 +168,8 @@ export class New extends Command {
const spinner = log.spinner(log.withBrand("Initializing SQLite database")).start()
try {
// Required in order for DATABASE_URL to be available
- require("dotenv-expand")(require("dotenv-flow").config({silent: true}))
+ const projectDir = process.cwd()
+ loadEnvConfig(projectDir)
const result = await runPrisma(["migrate", "dev", "--name", "Initial migration"], true)
if (!result.success) throw new Error()
diff --git a/packages/cli/src/commands/prisma.ts b/packages/cli/src/commands/prisma.ts
index 4a42a2e26c..dd59668a55 100644
--- a/packages/cli/src/commands/prisma.ts
+++ b/packages/cli/src/commands/prisma.ts
@@ -1,4 +1,4 @@
-import {Command} from "@oclif/command"
+import {Command, flags} from "@oclif/command"
import {Readable} from "stream"
const getPrismaBin = async () => {
@@ -55,6 +55,13 @@ export class PrismaCommand extends Command {
static description = "Loads env variables then proxies all args to Prisma CLI"
static aliases = ["p"]
+ static flags = {
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
+ }
+
static strict = false
async run() {
diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts
index 1626968b2e..97dffebf1e 100644
--- a/packages/cli/src/commands/start.ts
+++ b/packages/cli/src/commands/start.ts
@@ -18,6 +18,10 @@ export class Start extends Command {
inspect: flags.boolean({
description: "Enable the Node.js inspector",
}),
+ env: flags.string({
+ char: "e",
+ description: "Set app environment name",
+ }),
}
async run() {
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index 9b528da2d7..8cdc8b0708 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -2,12 +2,18 @@ require("v8-compile-cache")
const cacheFile = require("path").join(__dirname, ".blitzjs-cli-cache")
const lazyLoad = require("@salesforce/lazy-require").default.create(cacheFile)
lazyLoad.start()
+import {loadEnvConfig} from "@blitzjs/env"
import {run as oclifRun} from "@oclif/command"
import {compileConfig} from "next/dist/server/config-shared"
import {getProjectRoot} from "next/dist/server/lib/utils"
+const options = require("minimist")(process.argv.slice(2))
+if (options.e || options.env) {
+ process.env.APP_ENV = options.e || options.env
+}
+
// Load the .env environment variable so it's available for all commands
-require("dotenv-expand")(require("dotenv-flow").config({silent: true}))
+loadEnvConfig()
async function buildConfigIfNeeded() {
if (["help", "-h", "autocomplete", "new", "s", "start"].includes(process.argv[2])) {
diff --git a/yarn.lock b/yarn.lock
index 21ad809cd8..5d32ce6970 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3197,11 +3197,6 @@
dependencies:
webpack-bundle-analyzer "4.3.0"
-"@next/env@11.1.0":
- version "11.1.0"
- resolved "https://registry.yarnpkg.com/@next/env/-/env-11.1.0.tgz#cae83d8e0a65aa9f2af3368f8269ffd9d911746a"
- integrity sha512-zPJkMFRenSf7BLlVee8987G0qQXAhxy7k+Lb/5hLAGkPVHAHm+oFFeL+2ipbI2KTEFlazdmGY0M+AlLQn7pWaw==
-
"@next/polyfill-module@11.1.0":
version "11.1.0"
resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-11.1.0.tgz#ee6b9117a1f9bb137479dfa51d5a9e38e066a62f"
@@ -4629,10 +4624,12 @@
resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.0.tgz#eb71e94feae62548282c4889308a3dfb57e36020"
integrity sha512-jrm2K65CokCCX4NmowtA+MfXyuprZC13jbRuwprs6/04z/EcFg/MCwYdsHn+zgV4CQBiATiI7AEq7y1sZCtWKA==
-"@types/dotenv-flow@3.1.0":
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/@types/dotenv-flow/-/dotenv-flow-3.1.0.tgz#e9ba53f95f3d40bbc0df99b206f649b2acfca0c6"
- integrity sha512-qaWT42KDePdAGZFryYoV7EZnuuYZAO4KPVDWUV9OBOyJx7xCgKKERtVB7jBCM2mtKVI+OMMDK2ef11PWcHJz3g==
+"@types/dotenv@8.2.0":
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-8.2.0.tgz#5cd64710c3c98e82d9d15844375a33bf1b45d053"
+ integrity sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==
+ dependencies:
+ dotenv "*"
"@types/duplexify@*":
version "3.6.0"
@@ -5866,6 +5863,11 @@
async-retry "1.2.3"
lru-cache "5.1.1"
+"@zeit/ncc@0.20.4":
+ version "0.20.4"
+ resolved "https://registry.yarnpkg.com/@zeit/ncc/-/ncc-0.20.4.tgz#00f0a25a88cac3712af4ba66561d9e281c6f05c9"
+ integrity sha512-fmq+F/QxPec+k/zvT7HiVpk7oiGFseS6brfT/AYqmCUp6QFRK7vZf2Ref46MImsg/g2W3g5X6SRvGRmOAvEfdA==
+
"@zeit/next-css@1.0.2-canary.2":
version "1.0.2-canary.2"
resolved "https://registry.yarnpkg.com/@zeit/next-css/-/next-css-1.0.2-canary.2.tgz#0eeb877e7469892b65471c1ec7c14346b8f240df"
@@ -9924,14 +9926,12 @@ dotenv-expand@^5.1.0:
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
-dotenv-flow@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/dotenv-flow/-/dotenv-flow-3.2.0.tgz#a5d79dd60ddb6843d457a4874aaf122cf659a8b7"
- integrity sha512-GEB6RrR4AbqDJvNSFrYHqZ33IKKbzkvLYiD5eo4+9aFXr4Y4G+QaFrB/fNp0y6McWBmvaPn3ZNjIufnj8irCtg==
- dependencies:
- dotenv "^8.0.0"
+dotenv@*:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
+ integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
-dotenv@^8.0.0, dotenv@^8.2.0:
+dotenv@8.2.0, dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
From ad71e15290b4133a5431236c3d5ce0666486a61f Mon Sep 17 00:00:00 2001
From: Aleksandra Sikora
Date: Fri, 3 Dec 2021 20:54:40 +0100
Subject: [PATCH 3/3] Make prefetching work with `usePaginatedQuery` and
`useInfiniteQuery` (#3014)
(patch)
---
.github/workflows/compressed.yml | 3 +-
.../build/babel/plugins/rewrite-imports.ts | 1 +
nextjs/packages/next/data-client/index.ts | 1 +
.../next/data-client/react-query-utils.ts | 17 +++++
.../packages/next/data-client/react-query.tsx | 54 ++++++++++++----
.../queries/getIncrementedWithPagination.ts | 33 ++++++++++
.../dehydrated-state-use-infinite-query.tsx | 47 ++++++++++++++
.../dehydrated-state-use-paginated-query.tsx | 32 ++++++++++
...ate.tsx => dehydrated-state-use-query.tsx} | 7 +--
.../pages/invalidate-use-infinite-query.tsx | 34 ++++++++++
...nvalidate.tsx => invalidate-use-query.tsx} | 0
test/integration/queries/test/index.test.ts | 62 ++++++++++++++++++-
12 files changed, 269 insertions(+), 22 deletions(-)
create mode 100644 test/integration/queries/app/queries/getIncrementedWithPagination.ts
create mode 100644 test/integration/queries/pages/dehydrated-state-use-infinite-query.tsx
create mode 100644 test/integration/queries/pages/dehydrated-state-use-paginated-query.tsx
rename test/integration/queries/pages/{dehydrated-state.tsx => dehydrated-state-use-query.tsx} (85%)
create mode 100644 test/integration/queries/pages/invalidate-use-infinite-query.tsx
rename test/integration/queries/pages/{invalidate.tsx => invalidate-use-query.tsx} (100%)
diff --git a/.github/workflows/compressed.yml b/.github/workflows/compressed.yml
index 175069598a..18a78bafb9 100644
--- a/.github/workflows/compressed.yml
+++ b/.github/workflows/compressed.yml
@@ -15,7 +15,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- - uses: actions/setup-node@v2
+ - name: Use Node.js
+ uses: actions/setup-node@v2
with:
node-version: "14"
- name: Count size
diff --git a/nextjs/packages/next/build/babel/plugins/rewrite-imports.ts b/nextjs/packages/next/build/babel/plugins/rewrite-imports.ts
index fdffcf84de..8953ec89f3 100644
--- a/nextjs/packages/next/build/babel/plugins/rewrite-imports.ts
+++ b/nextjs/packages/next/build/babel/plugins/rewrite-imports.ts
@@ -56,6 +56,7 @@ const specialImports: Record = {
useMutation: 'next/data-client',
queryClient: 'next/data-client',
getQueryKey: 'next/data-client',
+ getInfiniteQueryKey: 'next/data-client',
invalidateQuery: 'next/data-client',
setQueryData: 'next/data-client',
useQueryErrorResetBoundary: 'next/data-client',
diff --git a/nextjs/packages/next/data-client/index.ts b/nextjs/packages/next/data-client/index.ts
index 2b3e688513..c6c19c9754 100644
--- a/nextjs/packages/next/data-client/index.ts
+++ b/nextjs/packages/next/data-client/index.ts
@@ -11,6 +11,7 @@ export {
export {
queryClient,
getQueryKey,
+ getInfiniteQueryKey,
invalidateQuery,
setQueryData,
} from './react-query-utils'
diff --git a/nextjs/packages/next/data-client/react-query-utils.ts b/nextjs/packages/next/data-client/react-query-utils.ts
index 2b4be6192b..d1fab760c8 100644
--- a/nextjs/packages/next/data-client/react-query-utils.ts
+++ b/nextjs/packages/next/data-client/react-query-utils.ts
@@ -125,6 +125,23 @@ export function getQueryKey(
return getQueryKeyFromUrlAndParams(sanitizeQuery(resolver)._routePath, params)
}
+export function getInfiniteQueryKey(
+ resolver: T | Resolver | RpcClient,
+ params?: TInput
+) {
+ if (typeof resolver === 'undefined') {
+ throw new Error(
+ 'getInfiniteQueryKey is missing the first argument - it must be a resolver function'
+ )
+ }
+
+ const queryKey = getQueryKeyFromUrlAndParams(
+ sanitizeQuery(resolver)._routePath,
+ params
+ )
+ return [...queryKey, 'infinite']
+}
+
export function invalidateQuery(
resolver: T | Resolver | RpcClient,
params?: TInput
diff --git a/nextjs/packages/next/data-client/react-query.tsx b/nextjs/packages/next/data-client/react-query.tsx
index 50651143ad..5a738af000 100644
--- a/nextjs/packages/next/data-client/react-query.tsx
+++ b/nextjs/packages/next/data-client/react-query.tsx
@@ -20,6 +20,7 @@ import {
QueryCacheFunctions,
sanitizeQuery,
sanitizeMutation,
+ getInfiniteQueryKey,
} from './react-query-utils'
import { useRouter } from '../client/router'
@@ -166,16 +167,19 @@ export function usePaginatedQuery<
)
}
- const suspense =
- options?.enabled === false || options?.enabled === null
+ const suspenseEnabled = Boolean(process.env.__BLITZ_SUSPENSE_ENABLED)
+ let enabled =
+ isServer && suspenseEnabled
? false
- : options?.suspense
+ : options?.enabled ?? options?.enabled !== null
+ const suspense = enabled === false ? false : options?.suspense
+
const session = useSession({ suspense })
if (session.isLoading) {
- options.enabled = false
+ enabled = false
}
- const routerIsReady = useRouter().isReady
+ const routerIsReady = useRouter().isReady || (isServer && suspenseEnabled)
const enhancedResolverRpcClient = sanitizeQuery(queryFn)
const queryKey = getQueryKey(queryFn, params)
@@ -186,8 +190,20 @@ export function usePaginatedQuery<
: (emptyQueryFn as any),
...options,
keepPreviousData: true,
+ enabled,
})
+ if (
+ queryRest.isIdle &&
+ isServer &&
+ suspenseEnabled !== false &&
+ !data &&
+ (!options || !('suspense' in options) || options.suspense) &&
+ (!options || !('enabled' in options) || options.enabled)
+ ) {
+ throw new Promise(() => {})
+ }
+
const rest = {
...queryRest,
...getQueryCacheFunctions, TResult, T>(queryFn, params),
@@ -250,24 +266,26 @@ export function useInfiniteQuery<
)
}
- const suspense =
- options?.enabled === false || options?.enabled === null
+ const suspenseEnabled = Boolean(process.env.__BLITZ_SUSPENSE_ENABLED)
+ let enabled =
+ isServer && suspenseEnabled
? false
- : options?.suspense
+ : options?.enabled ?? options?.enabled !== null
+ const suspense = enabled === false ? false : options?.suspense
const session = useSession({ suspense })
if (session.isLoading) {
- options.enabled = false
+ enabled = false
}
- const routerIsReady = useRouter().isReady
+ const routerIsReady = useRouter().isReady || (isServer && suspenseEnabled)
const enhancedResolverRpcClient = sanitizeQuery(queryFn)
- const queryKey = getQueryKey(queryFn, getQueryParams)
+ const queryKey = getInfiniteQueryKey(queryFn, getQueryParams)
const { data, ...queryRest } = useInfiniteReactQuery({
// we need an extra cache key for infinite loading so that the cache for
// for this query is stored separately since the hook result is an array of results.
// Without this cache for usePaginatedQuery and this will conflict and break.
- queryKey: routerIsReady ? [...queryKey, 'infinite'] : ['_routerNotReady_'],
+ queryKey: routerIsReady ? queryKey : ['_routerNotReady_'],
queryFn: routerIsReady
? ({ pageParam }) =>
enhancedResolverRpcClient(getQueryParams(pageParam), {
@@ -275,8 +293,20 @@ export function useInfiniteQuery<
})
: (emptyQueryFn as any),
...options,
+ enabled,
})
+ if (
+ queryRest.isIdle &&
+ isServer &&
+ suspenseEnabled !== false &&
+ !data &&
+ (!options || !('suspense' in options) || options.suspense) &&
+ (!options || !('enabled' in options) || options.enabled)
+ ) {
+ throw new Promise(() => {})
+ }
+
const rest = {
...queryRest,
...getQueryCacheFunctions, TResult, T>(
diff --git a/test/integration/queries/app/queries/getIncrementedWithPagination.ts b/test/integration/queries/app/queries/getIncrementedWithPagination.ts
new file mode 100644
index 0000000000..f852bf0393
--- /dev/null
+++ b/test/integration/queries/app/queries/getIncrementedWithPagination.ts
@@ -0,0 +1,33 @@
+import {paginate, resolver} from "blitz"
+
+const dataset = Array.from(Array(100).keys())
+
+type Args = {
+ skip: number
+ take: number
+ where?: {value: {gte: number}}
+}
+
+let counter = 0
+export default resolver.pipe(async ({skip = 0, take = 100, where}: Args) => {
+ counter++
+ const {items, hasMore, nextPage, count} = await paginate({
+ skip,
+ take,
+ count: async () => dataset.length,
+ query: async (paginateArgs) =>
+ dataset
+ .filter((i) => {
+ if (!where) return true
+ return i >= where.value.gte
+ })
+ .slice(paginateArgs.skip, paginateArgs.skip + paginateArgs.take),
+ })
+ return {
+ counter,
+ items,
+ hasMore,
+ nextPage,
+ count,
+ }
+})
diff --git a/test/integration/queries/pages/dehydrated-state-use-infinite-query.tsx b/test/integration/queries/pages/dehydrated-state-use-infinite-query.tsx
new file mode 100644
index 0000000000..e75d508da1
--- /dev/null
+++ b/test/integration/queries/pages/dehydrated-state-use-infinite-query.tsx
@@ -0,0 +1,47 @@
+import getPaginated from "app/queries/getPaginated"
+import {
+ dehydrate,
+ getInfiniteQueryKey,
+ GetServerSideProps,
+ invokeWithMiddleware,
+ QueryClient,
+ useInfiniteQuery,
+} from "blitz"
+import {useState} from "react"
+
+export const getServerSideProps: GetServerSideProps = async (ctx) => {
+ const queryClient = new QueryClient()
+ const queryKey = getInfiniteQueryKey(getPaginated, {where: {value: {gte: 10}}, take: 5, skip: 0})
+
+ await queryClient.prefetchInfiniteQuery(queryKey, () =>
+ invokeWithMiddleware(getPaginated, {where: {value: {gte: 10}}, take: 5, skip: 0}, ctx),
+ )
+
+ return {
+ props: {
+ dehydratedState: dehydrate(queryClient),
+ },
+ }
+}
+
+function Content() {
+ const [value] = useState(10)
+
+ const [groups] = useInfiniteQuery(
+ getPaginated,
+ (page = {take: 5, skip: 0}) => ({
+ where: {value: {gte: value}},
+ ...page,
+ }),
+ {
+ getNextPageParam: (lastGroup) => lastGroup.nextPage,
+ },
+ )
+ return {JSON.stringify(groups)}
+}
+
+function InfiniteQueryDehydratedState() {
+ return
+}
+
+export default InfiniteQueryDehydratedState
diff --git a/test/integration/queries/pages/dehydrated-state-use-paginated-query.tsx b/test/integration/queries/pages/dehydrated-state-use-paginated-query.tsx
new file mode 100644
index 0000000000..80c3197b20
--- /dev/null
+++ b/test/integration/queries/pages/dehydrated-state-use-paginated-query.tsx
@@ -0,0 +1,32 @@
+import getMap from "app/queries/getMap"
+import {
+ dehydrate,
+ getQueryKey,
+ GetServerSideProps,
+ invokeWithMiddleware,
+ QueryClient,
+ usePaginatedQuery,
+} from "blitz"
+
+export const getServerSideProps: GetServerSideProps = async (ctx) => {
+ const queryClient = new QueryClient()
+ const queryKey = getQueryKey(getMap, undefined)
+ await queryClient.prefetchQuery(queryKey, () => invokeWithMiddleware(getMap, undefined, ctx))
+
+ return {
+ props: {
+ dehydratedState: dehydrate(queryClient),
+ },
+ }
+}
+
+function Content() {
+ const [map] = usePaginatedQuery(getMap, undefined)
+ return map is Map: {"" + (map instanceof Map)}
+}
+
+function DehydratedStateWithPagination() {
+ return
+}
+
+export default DehydratedStateWithPagination
diff --git a/test/integration/queries/pages/dehydrated-state.tsx b/test/integration/queries/pages/dehydrated-state-use-query.tsx
similarity index 85%
rename from test/integration/queries/pages/dehydrated-state.tsx
rename to test/integration/queries/pages/dehydrated-state-use-query.tsx
index df6ed362d3..b4d8a40f9b 100644
--- a/test/integration/queries/pages/dehydrated-state.tsx
+++ b/test/integration/queries/pages/dehydrated-state-use-query.tsx
@@ -7,7 +7,6 @@ import {
QueryClient,
useQuery,
} from "blitz"
-import {Suspense} from "react"
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const queryClient = new QueryClient()
@@ -27,11 +26,7 @@ function Content() {
}
function DehydratedState() {
- return (
-
-
-
- )
+ return
}
export default DehydratedState
diff --git a/test/integration/queries/pages/invalidate-use-infinite-query.tsx b/test/integration/queries/pages/invalidate-use-infinite-query.tsx
new file mode 100644
index 0000000000..15b5b88b9b
--- /dev/null
+++ b/test/integration/queries/pages/invalidate-use-infinite-query.tsx
@@ -0,0 +1,34 @@
+import getIncremented from "app/queries/getIncrementedWithPagination"
+import {invalidateQuery, useInfiniteQuery} from "blitz"
+import {Suspense} from "react"
+
+function Content() {
+ const [groups] = useInfiniteQuery(
+ getIncremented,
+ (page = {take: 5, skip: 0}) => ({
+ where: {value: {gte: 10}},
+ ...page,
+ }),
+ {
+ getNextPageParam: (lastGroup) => lastGroup.nextPage,
+ },
+ )
+ return (
+ <>
+
+ {JSON.stringify(groups)}
+ >
+ )
+}
+
+function InvalidateInfiniteQuery() {
+ return (
+
+
+
+
+
+ )
+}
+
+export default InvalidateInfiniteQuery
diff --git a/test/integration/queries/pages/invalidate.tsx b/test/integration/queries/pages/invalidate-use-query.tsx
similarity index 100%
rename from test/integration/queries/pages/invalidate.tsx
rename to test/integration/queries/pages/invalidate-use-query.tsx
diff --git a/test/integration/queries/test/index.test.ts b/test/integration/queries/test/index.test.ts
index ffcc3a2df3..b694bb7191 100644
--- a/test/integration/queries/test/index.test.ts
+++ b/test/integration/queries/test/index.test.ts
@@ -13,7 +13,7 @@ describe("Queries", () => {
env: {__NEXT_TEST_WITH_DEVTOOL: 1},
})
- const prerender = ["/use-query", "/invalidate"]
+ const prerender = ["/use-query", "/invalidate-use-query", "/invalidate-use-infinite-query"]
await Promise.all(prerender.map((route) => renderViaHTTP(context.appPort, route)))
})
afterAll(() => killApp(context.server))
@@ -32,7 +32,7 @@ describe("Queries", () => {
describe("invalidateQuery", () => {
it("should invalidate the query", async () => {
- const browser = await webdriver(context.appPort, "/invalidate")
+ const browser = await webdriver(context.appPort, "/invalidate-use-query")
await browser.waitForElementByCss("#content")
let text = await browser.elementByCss("#content").text()
expect(text).toMatch(/0/)
@@ -75,10 +75,66 @@ describe("Queries", () => {
describe("DehydratedState", () => {
it("should work", async () => {
- const browser = await webdriver(context.appPort, "/dehydrated-state")
+ const browser = await webdriver(context.appPort, "/dehydrated-state-use-query")
let text = await browser.elementByCss("#content").text()
expect(text).toMatch(/map is Map: true/)
if (browser) await browser.close()
})
})
+
+ describe("DehydratedState with usePaginatedQuery", () => {
+ it("should work", async () => {
+ const browser = await webdriver(context.appPort, "/dehydrated-state-use-paginated-query")
+ let text = await browser.elementByCss("#content").text()
+ expect(text).toMatch(/map is Map: true/)
+ if (browser) await browser.close()
+ })
+ })
+
+ describe("DehydratedState with useInfiniteQuery", () => {
+ it("should work", async () => {
+ const browser = await webdriver(context.appPort, "/dehydrated-state-use-infinite-query")
+ await browser.waitForElementByCss("#content")
+ let text = await browser.elementByCss("#content").text()
+ expect(JSON.parse(text)).toEqual([
+ {
+ items: [10, 11, 12, 13, 14],
+ hasMore: true,
+ nextPage: {take: 5, skip: 5},
+ count: 100,
+ },
+ ])
+ })
+ })
+
+ describe("invalidateQuery with useInfiniteQuery", () => {
+ it("should invalidate the query", async () => {
+ const browser = await webdriver(context.appPort, "/invalidate-use-infinite-query")
+ await browser.waitForElementByCss("#content")
+ let text = await browser.elementByCss("#content").text()
+ expect(JSON.parse(text)).toEqual([
+ {
+ counter: 1,
+ items: [10, 11, 12, 13, 14],
+ hasMore: true,
+ nextPage: {take: 5, skip: 5},
+ count: 100,
+ },
+ ])
+ await browser.elementByCss("button").click()
+ waitFor(500)
+ text = await browser.elementByCss("#content").text()
+ expect(JSON.parse(text)).toEqual([
+ {
+ counter: 2,
+ items: [10, 11, 12, 13, 14],
+ hasMore: true,
+ nextPage: {take: 5, skip: 5},
+ count: 100,
+ },
+ ])
+
+ if (browser) await browser.close()
+ })
+ })
})