Skip to content

Commit d325cf0

Browse files
serhalpdanielroe
andauthored
chore: use getEnv in oauth module for platform agnosticity (#1148)
Co-authored-by: Daniel Roe <daniel@roe.dev>
1 parent 737eb13 commit d325cf0

File tree

4 files changed

+236
-24
lines changed

4 files changed

+236
-24
lines changed

config/env.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// TODO(serhalp): Extract most of this module to https://github.com/unjs/std-env.
2+
13
import Git from 'simple-git'
24
import * as process from 'node:process'
35

@@ -27,19 +29,63 @@ export const gitBranch = process.env.BRANCH || process.env.VERCEL_GIT_COMMIT_REF
2729

2830
/**
2931
* Environment variable `CONTEXT` provided by Netlify.
32+
* `dev`, `production`, `deploy-preview`, `branch-deploy`, `preview-server`, or a branch name
3033
* @see {@link https://docs.netlify.com/build/configure-builds/environment-variables/#build-metadata}
3134
*
3235
* Environment variable `VERCEL_ENV` provided by Vercel.
36+
* `production`, `preview`, or `development`
3337
* @see {@link https://vercel.com/docs/environment-variables/system-environment-variables#VERCEL_ENV}
3438
*
35-
* Whether triggered by PR, `deploy-preview` or `dev`.
39+
* Whether this is some sort of preview environment.
3640
*/
3741
export const isPreview =
3842
isPR ||
39-
process.env.CONTEXT === 'deploy-preview' ||
40-
process.env.CONTEXT === 'dev' ||
43+
(process.env.CONTEXT && process.env.CONTEXT !== 'production') ||
4144
process.env.VERCEL_ENV === 'preview' ||
4245
process.env.VERCEL_ENV === 'development'
46+
export const isProduction =
47+
process.env.CONTEXT === 'production' || process.env.VERCEL_ENV === 'production'
48+
49+
/**
50+
* Environment variable `URL` provided by Netlify.
51+
* This is always the current deploy URL, regardless of env.
52+
* @see {@link https://docs.netlify.com/build/functions/environment-variables/#functions}
53+
*
54+
* Environment variable `VERCEL_URL` provided by Vercel.
55+
* This is always the current deploy URL, regardless of env.
56+
* NOTE: Not a valid URL, as the protocol is omitted.
57+
* @see {@link https://vercel.com/docs/environment-variables/system-environment-variables#VERCEL_URL}
58+
*
59+
* Preview URL for the current deployment, only available in preview environments.
60+
*/
61+
export const getPreviewUrl = () =>
62+
isPreview
63+
? process.env.URL
64+
? process.env.URL
65+
: process.env.NUXT_ENV_VERCEL_URL
66+
? `https://${process.env.NUXT_ENV_VERCEL_URL}`
67+
: undefined
68+
: undefined
69+
70+
/**
71+
* Environment variable `URL` provided by Netlify.
72+
* This is always the current deploy URL, regardless of env.
73+
* @see {@link https://docs.netlify.com/build/functions/environment-variables/#functions}
74+
*
75+
* Environment variable `VERCEL_PROJECT_PRODUCTION_URL` provided by Vercel.
76+
* NOTE: Not a valid URL, as the protocol is omitted.
77+
* @see {@link https://vercel.com/docs/environment-variables/system-environment-variables#VERCEL_PROJECT_PRODUCTION_URL}
78+
*
79+
* Production URL for the current deployment, only available in production environments.
80+
*/
81+
export const getProductionUrl = () =>
82+
isProduction
83+
? process.env.URL
84+
? process.env.URL
85+
: process.env.NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL
86+
? `https://${process.env.NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL}`
87+
: undefined
88+
: undefined
4389

4490
const git = Git()
4591
export async function getGitInfo() {
@@ -92,5 +138,14 @@ export async function getEnv(isDevelopment: boolean) {
92138
: branch === 'main'
93139
? 'canary'
94140
: 'release'
95-
return { commit, shortCommit, branch, env } as const
141+
const previewUrl = getPreviewUrl()
142+
const productionUrl = getProductionUrl()
143+
return {
144+
commit,
145+
shortCommit,
146+
branch,
147+
env,
148+
previewUrl,
149+
productionUrl,
150+
} as const
96151
}

modules/oauth.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,17 @@ import process from 'node:process'
33
import { join } from 'node:path'
44
import { appendFileSync, existsSync, readFileSync } from 'node:fs'
55
import { randomUUID } from 'node:crypto'
6+
import { getEnv } from '../config/env.ts'
67

78
export default defineNuxtModule({
89
meta: {
910
name: 'oauth',
1011
},
11-
setup() {
12+
async setup() {
1213
const nuxt = useNuxt()
1314

14-
const env = process.env.NUXT_ENV_VERCEL_ENV
15-
const previewUrl = process.env.NUXT_ENV_VERCEL_URL
16-
const prodUrl = process.env.NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL
17-
18-
let clientUri: string
19-
if (env === 'preview' && previewUrl) {
20-
clientUri = `https://${previewUrl}`
21-
} else if (env === 'production' && prodUrl) {
22-
clientUri = `https://${prodUrl}`
23-
} else {
24-
clientUri = 'http://127.0.0.1:3000'
25-
}
15+
const { previewUrl, productionUrl } = await getEnv(nuxt.options.dev)
16+
const clientUri = productionUrl || previewUrl || 'http://127.0.0.1:3000'
2617

2718
// bake it into a virtual file
2819
addServerTemplate({

test/unit/config/env.spec.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
const ALL_ENV_VARS = [
4+
'CONTEXT',
5+
'VERCEL_ENV',
6+
'URL',
7+
'NUXT_ENV_VERCEL_URL',
8+
'NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL',
9+
]
10+
11+
describe('getPreviewUrl', () => {
12+
beforeEach(() => {
13+
// Reset consts evaluated at module init time
14+
vi.resetModules()
15+
})
16+
17+
beforeEach(() => {
18+
for (const envVar of ALL_ENV_VARS) {
19+
vi.stubEnv(envVar, undefined)
20+
}
21+
})
22+
23+
afterEach(() => {
24+
vi.unstubAllEnvs()
25+
})
26+
27+
it('returns `undefined` if no known preview env is detected', async () => {
28+
const { getPreviewUrl } = await import('../../../config/env')
29+
30+
expect(getPreviewUrl()).toBeUndefined()
31+
})
32+
33+
it.each([
34+
['Netlify production', { CONTEXT: 'production', URL: 'https://prod.example.com' }],
35+
['Vercel production', { VERCEL_ENV: 'production', NUXT_ENV_VERCEL_URL: 'prod.example.com' }],
36+
])('%s environment returns `undefined`', async (_name, envVars) => {
37+
for (const [key, value] of Object.entries(envVars)) {
38+
vi.stubEnv(key, value)
39+
}
40+
const { getPreviewUrl } = await import('../../../config/env')
41+
42+
expect(getPreviewUrl()).toBeUndefined()
43+
})
44+
45+
it.each([
46+
['Netlify dev', { CONTEXT: 'dev', URL: 'https://dev.example.com' }, 'https://dev.example.com'],
47+
[
48+
'Netlify deploy-preview',
49+
{
50+
CONTEXT: 'deploy-preview',
51+
URL: 'https://preview.example.com',
52+
},
53+
'https://preview.example.com',
54+
],
55+
[
56+
'Netlify branch-deploy',
57+
{ CONTEXT: 'branch-deploy', URL: 'https://beta.example.com' },
58+
'https://beta.example.com',
59+
],
60+
[
61+
'Netlify preview-server',
62+
{
63+
CONTEXT: 'preview-server',
64+
URL: 'https://my-feat--preview.example.com',
65+
},
66+
'https://my-feat--preview.example.com',
67+
],
68+
[
69+
'Vercel development',
70+
{ VERCEL_ENV: 'development', NUXT_ENV_VERCEL_URL: 'dev.example.com' },
71+
'https://dev.example.com',
72+
],
73+
[
74+
'Vercel preview',
75+
{ VERCEL_ENV: 'preview', NUXT_ENV_VERCEL_URL: 'preview.example.com' },
76+
'https://preview.example.com',
77+
],
78+
])('%s environment returns preview URL', async (_name, envVars, expectedUrl) => {
79+
for (const [key, value] of Object.entries(envVars)) {
80+
vi.stubEnv(key, value)
81+
}
82+
83+
const { getPreviewUrl } = await import('../../../config/env')
84+
85+
expect(getPreviewUrl()).toBe(expectedUrl)
86+
})
87+
})
88+
89+
describe('getProductionUrl', () => {
90+
beforeEach(() => {
91+
// Reset consts evaluated at module init time
92+
vi.resetModules()
93+
})
94+
95+
beforeEach(() => {
96+
for (const envVar of ALL_ENV_VARS) {
97+
vi.stubEnv(envVar, undefined)
98+
}
99+
})
100+
101+
afterEach(() => {
102+
vi.unstubAllEnvs()
103+
})
104+
105+
it('returns `undefined` if no known production env is detected', async () => {
106+
const { getProductionUrl } = await import('../../../config/env')
107+
108+
expect(getProductionUrl()).toBeUndefined()
109+
})
110+
111+
it.each([
112+
['Netlify dev', { CONTEXT: 'dev', URL: 'https://dev.example.com' }],
113+
[
114+
'Netlify deploy-preview',
115+
{
116+
CONTEXT: 'deploy-preview',
117+
URL: 'https://preview.example.com',
118+
},
119+
],
120+
['Netlify branch-deploy', { CONTEXT: 'branch-deploy', URL: 'https://beta.example.com' }],
121+
[
122+
'Netlify preview-server',
123+
{
124+
CONTEXT: 'preview-server',
125+
URL: 'https://my-feat--preview.example.com',
126+
},
127+
],
128+
[
129+
'Vercel development',
130+
{
131+
VERCEL_ENV: 'development',
132+
NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL: 'dev.example.com',
133+
},
134+
],
135+
[
136+
'Vercel preview',
137+
{
138+
VERCEL_ENV: 'preview',
139+
NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL: 'preview.example.com',
140+
},
141+
],
142+
])('%s environment returns `undefined`', async (_name, envVars) => {
143+
for (const [key, value] of Object.entries(envVars)) {
144+
vi.stubEnv(key, value)
145+
}
146+
const { getProductionUrl } = await import('../../../config/env')
147+
148+
expect(getProductionUrl()).toBeUndefined()
149+
})
150+
151+
it.each([
152+
[
153+
'Netlify production',
154+
{ CONTEXT: 'production', URL: 'https://prod.example.com' },
155+
'https://prod.example.com',
156+
],
157+
[
158+
'Vercel production',
159+
{
160+
VERCEL_ENV: 'production',
161+
NUXT_ENV_VERCEL_PROJECT_PRODUCTION_URL: 'prod.example.com',
162+
},
163+
'https://prod.example.com',
164+
],
165+
])('%s environment returns production URL', async (_name, envVars, expectedUrl) => {
166+
for (const [key, value] of Object.entries(envVars)) {
167+
vi.stubEnv(key, value)
168+
}
169+
const { getProductionUrl } = await import('../../../config/env')
170+
171+
expect(getProductionUrl()).toBe(expectedUrl)
172+
})
173+
})

test/unit/index.spec.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)