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: allow any JS identifier in define, not ASCII-only #5972

Merged
merged 12 commits into from May 5, 2022
2 changes: 1 addition & 1 deletion docs/config/index.md
Expand Up @@ -158,7 +158,7 @@ export default defineConfig(({ command, mode }) => {

- To be consistent with [esbuild behavior](https://esbuild.github.io/api/#define), expressions must either be a JSON object (null, boolean, number, string, array, or object) or a single identifier.

- Replacements are performed only when the match is surrounded by word boundaries (`\b`).
- Replacements are performed only when the match isn't surrounded by other letters, numbers, `_` or `$`.

::: warning
Because it's implemented as straightforward text replacements without any syntax analysis, we recommend using `define` for CONSTANTS only.
Expand Down
8 changes: 8 additions & 0 deletions packages/playground/define/__tests__/define.spec.ts
Expand Up @@ -20,6 +20,14 @@ test('string', async () => {
expect(await page.textContent('.spread-array')).toBe(
JSON.stringify([...defines.__STRING__])
)
expect(await page.textContent('.dollar-identifier')).toBe(
String(defines.$DOLLAR)
)
expect(await page.textContent('.unicode-identifier')).toBe(
String(defines.ÖUNICODE_LETTERɵ)
)
expect(await page.textContent('.no-identifier-substring')).toBe(String(true))
expect(await page.textContent('.no-property')).toBe(String(true))
// html would't need to define replacement
expect(await page.textContent('.exp-define')).toBe('__EXP__')
expect(await page.textContent('.import-json')).toBe('__EXP__')
Expand Down
13 changes: 13 additions & 0 deletions packages/playground/define/index.html
Expand Up @@ -9,6 +9,10 @@ <h1>Define</h1>
<p>process as property: <code class="process-as-property"></code></p>
<p>spread object: <code class="spread-object"></code></p>
<p>spread array: <code class="spread-array"></code></p>
<p>dollar identifier: <code class="dollar-identifier"></code></p>
<p>unicode identifier: <code class="unicode-identifier"></code></p>
<p>no property: <code class="no-property"></code></p>
<p>no identifier substring: <span class="no-identifier-substring"></span></p>
<p>define variable in html: <code class="exp-define">__EXP__</code></p>
<p>import json: <code class="import-json"></code></p>

Expand All @@ -28,6 +32,15 @@ <h1>Define</h1>
})
)
text('.spread-array', JSON.stringify([...`"${__STRING__}"`]))
text('.dollar-identifier', $DOLLAR)
text('.unicode-identifier', ÖUNICODE_LETTERɵ)

// make sure these kinds of use are NOT replaced:
const obj = { [`${'_'}_EXP__`]: true }
text('.no-property', obj.__EXP__)

window[`${'_'}_EXP__SUBSTR__`] = true
text('.no-identifier-substring', __EXP__SUBSTR__)

import dataJson from './data.json'
text('.import-json', dataJson.foo)
Expand Down
6 changes: 4 additions & 2 deletions packages/playground/define/vite.config.js
Expand Up @@ -15,7 +15,9 @@ module.exports = {
}
}
},
__VAR_NAME__: false,
'process.env.SOMEVAR': '"SOMEVAR"'
'process.env.SOMEVAR': '"SOMEVAR"',
$DOLLAR: 456,
ÖUNICODE_LETTERɵ: 789,
__VAR_NAME__: false
}
}
12 changes: 7 additions & 5 deletions packages/vite/src/node/plugins/define.ts
Expand Up @@ -68,16 +68,18 @@ export function definePlugin(config: ResolvedConfig): Plugin {
const replacementsKeys = Object.keys(replacements)
const pattern = replacementsKeys.length
? new RegExp(
// Do not allow preceding '.', but do allow preceding '...' for spread operations
'(?<!(?<!\\.\\.)\\.)\\b(' +
// Mustn't be preceded by a char that can be part of an identifier
// or a '.' that isn't part of a spread operator
'(?<![\\p{L}\\p{N}_$]|(?<!\\.\\.)\\.)(' +
replacementsKeys
.map((str) => {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')
})
.join('|') +
// prevent trailing assignments
')\\b(?!\\s*?=[^=])',
'g'
// Mustn't be followed by a char that can be part of an identifier
// or an assignment (but allow equality operators)
')(?![\\p{L}\\p{N}_$]|\\s*?=[^=])',
'gu'
)
: null

Expand Down