Skip to content

minor: better type safety and best practice to escape function #16579

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

Merged
merged 3 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 1 addition & 74 deletions integrations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { platform, tmpdir } from 'node:os'
import path from 'node:path'
import { stripVTControlCharacters } from 'node:util'
import { test as defaultTest, type ExpectStatic } from 'vitest'
import { escape } from '../packages/tailwindcss/src/utils/escape'

const REPO_ROOT = path.join(__dirname, '..')
const PUBLIC_PACKAGES = (await fs.readdir(path.join(REPO_ROOT, 'dist'))).map((name) =>
Expand Down Expand Up @@ -521,80 +522,6 @@ export function candidate(strings: TemplateStringsArray, ...values: any[]) {
return `.${escape(output.join('').trim())}`
}

// https://drafts.csswg.org/cssom/#serialize-an-identifier
export function escape(value: string) {
if (arguments.length == 0) {
throw new TypeError('`CSS.escape` requires an argument.')
}
var string = String(value)
var length = string.length
var index = -1
var codeUnit
var result = ''
var firstCodeUnit = string.charCodeAt(0)

if (
// If the character is the first character and is a `-` (U+002D), and
// there is no second character, […]
length == 1 &&
firstCodeUnit == 0x002d
) {
return '\\' + string
}

while (++index < length) {
codeUnit = string.charCodeAt(index)
// Note: there’s no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.

// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
// (U+FFFD).
if (codeUnit == 0x0000) {
result += '\uFFFD'
continue
}

if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001f) ||
codeUnit == 0x007f ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(index == 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit == 0x002d)
) {
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' '
continue
}

// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit == 0x002d ||
codeUnit == 0x005f ||
(codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
(codeUnit >= 0x0041 && codeUnit <= 0x005a) ||
(codeUnit >= 0x0061 && codeUnit <= 0x007a)
) {
// the character itself
result += string.charAt(index)
continue
}

// Otherwise, the escaped character.
// https://drafts.csswg.org/cssom/#escape-a-character
result += '\\' + string.charAt(index)
}
return result
}

export async function retryAssertion<T>(
fn: () => Promise<T>,
{ timeout = ASSERTION_TIMEOUT, delay = 5 }: { timeout?: number; delay?: number } = {},
Expand Down
30 changes: 15 additions & 15 deletions packages/tailwindcss/src/utils/escape.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// https://drafts.csswg.org/cssom/#serialize-an-identifier
export function escape(value: string) {
if (arguments.length == 0) {
if (arguments.length === 0) {
throw new TypeError('`CSS.escape` requires an argument.')
}
var string = String(value)
var length = string.length
var index = -1
var codeUnit
var result = ''
var firstCodeUnit = string.charCodeAt(0)
let string = String(value)
let length = string.length
let index = -1
let codeUnit: number
let result = ''
let firstCodeUnit = string.charCodeAt(0)

if (
// If the character is the first character and is a `-` (U+002D), and
// there is no second character, […]
length == 1 &&
firstCodeUnit == 0x002d
length === 1 &&
firstCodeUnit === 0x002d
) {
return '\\' + string
}
Expand All @@ -26,7 +26,7 @@ export function escape(value: string) {

// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER
// (U+FFFD).
if (codeUnit == 0x0000) {
if (codeUnit === 0x0000) {
result += '\uFFFD'
continue
}
Expand All @@ -35,13 +35,13 @@ export function escape(value: string) {
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001f) ||
codeUnit == 0x007f ||
codeUnit === 0x007f ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
(index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(index == 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit == 0x002d)
(index === 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit === 0x002d)
) {
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' '
Expand All @@ -54,8 +54,8 @@ export function escape(value: string) {
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit == 0x002d ||
codeUnit == 0x005f ||
codeUnit === 0x002d ||
codeUnit === 0x005f ||
(codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
(codeUnit >= 0x0041 && codeUnit <= 0x005a) ||
(codeUnit >= 0x0061 && codeUnit <= 0x007a)
Expand Down