Skip to content

Commit

Permalink
feat(html): support env replacement (#12202)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy committed Mar 7, 2023
1 parent 108aadf commit 4f2c49f
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 0 deletions.
11 changes: 11 additions & 0 deletions docs/guide/env-and-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@ If your code relies on types from browser environments such as [DOM](https://git
}
```
## HTML Env Replacement
Vite also supports replacing env variables in HTML files. Any properties in `import.meta.env` can be used in HTML files with a special `%ENV_NAME%` syntax:
```html
<h1>Vite is running in %MODE%</h1>
<p>Using data from %VITE_API_URL%</p>
```
If the env doesn't exist in `import.meta.env`, e.g. `%NON_EXISTENT%`, it will be ignored and not replaced, unlike `import.meta.env.NON_EXISTENT` in JS where it's replaced as `undefined`.
## Modes
By default, the dev server (`dev` command) runs in `development` mode and the `build` command runs in `production` mode.
Expand Down
41 changes: 41 additions & 0 deletions packages/vite/src/node/plugins/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '../utils'
import type { ResolvedConfig } from '../config'
import { toOutputFilePathInHtml } from '../build'
import { resolveEnvPrefix } from '../env'
import {
assetUrlRE,
checkPublicFile,
Expand Down Expand Up @@ -285,6 +286,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
config.plugins,
)
preHooks.unshift(preImportMapHook(config))
normalHooks.unshift(htmlEnvHook(config))
postHooks.push(postImportMapHook())
const processedHtml = new Map<string, string>()
const isExcludedUrl = (url: string) =>
Expand Down Expand Up @@ -942,6 +944,45 @@ export function postImportMapHook(): IndexHtmlTransformHook {
}
}

/**
* Support `%ENV_NAME%` syntax in html files
*/
export function htmlEnvHook(config: ResolvedConfig): IndexHtmlTransformHook {
const pattern = /%(\S+?)%/g
const envPrefix = resolveEnvPrefix({ envPrefix: config.envPrefix })
const env: Record<string, any> = { ...config.env }
// account for user env defines
for (const key in config.define) {
if (key.startsWith(`import.meta.env.`)) {
const val = config.define[key]
env[key.slice(16)] = typeof val === 'string' ? val : JSON.stringify(val)
}
}
return (html, ctx) => {
return html.replace(pattern, (text, key) => {
if (key in env) {
return env[key]
} else {
if (envPrefix.some((prefix) => key.startsWith(prefix))) {
const relativeHtml = normalizePath(
path.relative(config.root, ctx.filename),
)
config.logger.warn(
colors.yellow(
colors.bold(
`(!) ${text} is not defined in env variables found in /${relativeHtml}. ` +
`Is the variable mistyped?`,
),
),
)
}

return text
}
})
}
}

export function resolveHtmlTransforms(
plugins: readonly Plugin[],
): [
Expand Down
2 changes: 2 additions & 0 deletions packages/vite/src/node/server/middlewares/indexHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
assetAttrsConfig,
getAttrKey,
getScriptInfo,
htmlEnvHook,
nodeIsElement,
overwriteAttrValue,
postImportMapHook,
Expand Down Expand Up @@ -51,6 +52,7 @@ export function createDevHtmlTransformFn(
[
preImportMapHook(server.config),
...preHooks,
htmlEnvHook(server.config),
devHtmlHook,
...normalHooks,
...postHooks,
Expand Down
1 change: 1 addition & 0 deletions playground/html/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_FOO=bar
14 changes: 14 additions & 0 deletions playground/html/__tests__/html.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,20 @@ describe('Valid HTML', () => {
})
})

describe('env', () => {
beforeAll(async () => {
await page.goto(viteTestUrl + '/env.html')
})

test('env works', async () => {
expect(await page.textContent('.env')).toBe('bar')
expect(await page.textContent('.env-define')).toBe('5173')
expect(await page.textContent('.env-bar')).toBeTruthy()
expect(await page.textContent('.env-prod')).toBe(isBuild + '')
expect(await page.textContent('.env-dev')).toBe(isServe + '')
})
})

describe('importmap', () => {
beforeAll(async () => {
await page.goto(viteTestUrl + '/importmapOrder.html')
Expand Down
5 changes: 5 additions & 0 deletions playground/html/env.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<p class="env">%VITE_FOO%</p>
<p class="env-define">%VITE_NUMBER%</p>
<p class="env-%VITE_FOO%">class name should be env-bar</p>
<p class="env-prod">%PROD%</p>
<p class="env-dev">%DEV%</p>
5 changes: 5 additions & 0 deletions playground/html/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ export default defineConfig({
linkProps: resolve(__dirname, 'link-props/index.html'),
valid: resolve(__dirname, 'valid.html'),
importmapOrder: resolve(__dirname, 'importmapOrder.html'),
env: resolve(__dirname, 'env.html'),
},
},
},

define: {
'import.meta.env.VITE_NUMBER': 5173,
},

plugins: [
{
name: 'pre-transform',
Expand Down

0 comments on commit 4f2c49f

Please sign in to comment.