jsesc-es
Version:
Given some data, jsesc returns the shortest possible stringified & ASCII-safe representation of that data.
600 lines (433 loc) • 16.1 kB
Markdown
# jsesc-es
<div align="center">
[](https://www.npmjs.com/package/jsesc-es) [](https://www.npmjs.com/package/jsesc-es) [](https://www.jsdelivr.com/package/npm/jsesc-es) [](https://unpkg.com/jsesc-es/) [](https://www.typescriptlang.org/) [](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) [](https://opensource.org/licenses/MIT)
**[English](README.md)** | **简体中文**
</div>
> 基于流行的 [jsesc](https://github.com/mathiasbynens/jsesc) 库,使用现代 TypeScript + ESM 重构,100% API 兼容
给定一些数据,_jsesc-es_ 返回该数据的字符串化表示。jsesc-es 类似于 `JSON.stringify()`,但有以下不同:
1. 默认情况下输出 JavaScript 而不是 [JSON](#json),支持 ES6 map 和 set 等数据结构;
2. 提供[多种选项](#api)来自定义输出;
3. 默认情况下输出 [ASCII 安全](#minimal),在需要时使用[转义序列](https://mathiasbynens.be/notes/javascript-escapes);
4. **新特性**:完整的 TypeScript 支持和全面的类型定义;
5. **新特性**:原生 ES 模块,支持 tree-shaking;
6. **新特性**:现代开发工具链和改进的性能。
对于任何输入,jsesc-es 都会生成最短的有效可打印 ASCII 输出。[这里有一个在线演示。](https://mothereff.in/js-escapes)
jsesc-es 的输出可以用来替代 `JSON.stringify` 的输出,以避免[乱码](https://en.wikipedia.org/wiki/Mojibake)和其他编码问题,甚至可以[避免错误](https://twitter.com/annevk/status/380000829643571200),当向 JavaScript 解析器或 UTF-8 编码器传递 JSON 格式数据(可能包含 U+2028 行分隔符、U+2029 段落分隔符或[孤立代理](https://esdiscuss.org/topic/code-points-vs-unicode-scalar-values#content-14))时。
## ✨ jsesc-es 的新特性
### 🔥 TypeScript 优先
- **内置类型定义** - 无需 `@types/jsesc`
- **完整类型安全** 和全面的 `JsescOptions` 接口
- **更好的 IDE 支持** 包括 IntelliSense 和自动补全
- **编译时类型安全选项验证**
### 📦 现代模块系统
- **原生 ES 模块** 支持 tree-shaking
- **多种导入样式** - 默认导入、命名导入或混合导入
- **CommonJS 兼容性** 用于传统项目
- **优化的包大小** 配合现代构建工具
### ⚡ 增强性能
- **改进的函数处理** 更好的类型检测
- **优化的转义逻辑** 适用于常见用例
- **现代 JavaScript 特性** 提供更好的性能
- **相比原版减少的包开销**
### 🛠️ 开发体验
- **现代工具链** 使用 Vitest、tsdown 和 pnpm
- **更好的错误消息** 集成 TypeScript
- **全面的测试覆盖** 包含类型检查
- **积极维护** 定期更新
## 安装
```bash
# pnpm(推荐)- https://pnpm.io/
pnpm add jsesc-es
# yarn - https://yarnpkg.com/
yarn add jsesc-es
# npm - https://www.npmjs.com/
npm install jsesc-es
# 或其他运行时如...
# bun - https://bun.sh/
bun add jsesc-es
# deno - https://deno.land/
deno add jsesc-es
```
## 使用方法
### ES 模块(推荐)
```typescript
// 默认导入(最常见)
import jsesc from 'jsesc-es'
// 命名导入
import { jsesc } from 'jsesc-es'
// 带类型导入
import type { JsescOptions } from 'jsesc-es'
import jsesc from 'jsesc-es'
// 导入版本信息
import { version } from 'jsesc-es'
// 混合导入(不推荐)
import jsesc, { version } from 'jsesc-es'
```
### TypeScript 使用
```typescript
import type { JsescOptions } from 'jsesc-es'
import jsesc from 'jsesc-es'
// 类型安全的选项
const options: JsescOptions = {
quotes: 'single',
wrap: true,
es6: true,
minimal: false
}
// 类型安全的函数使用
function escapeForHTML(input: string): string {
return jsesc(input, {
quotes: 'double',
wrap: true,
isScriptContext: true
})
}
// 类型安全的配置对象
const configs = {
json: { json: true } as JsescOptions,
minimal: { minimal: true } as JsescOptions,
es6: { es6: true, quotes: 'backtick' } as JsescOptions
}
```
### CommonJS(传统支持)
```javascript
// CommonJS require(仍然支持)
const jsesc = require('jsesc-es')
// 动态导入(现代替代方案)
const { jsesc } = await import('jsesc-es')
```
### Node.js ESM
确保您的 `package.json` 包含:
```json
{
"type": "module"
}
```
或使用 `.mjs` 文件扩展名:
```javascript
// app.mjs
import jsesc from 'jsesc-es'
```
## API
### `jsesc(value, options?)`
此函数接受一个值并返回该值的转义版本,其中任何非可打印 ASCII 符号的字符都使用最短可能(但有效的)[JavaScript 字符串转义序列](https://mathiasbynens.be/notes/javascript-escapes)进行转义。第一个支持的值类型是字符串:
```js
jsesc('Ich ♥ Bücher')
// → 'Ich \\u2665 B\\xFCcher'
jsesc('foo 𝌆 bar')
// → 'foo \\uD834\\uDF06 bar'
```
除了字符串,`value` 也可以是数组、对象、map、set、数字、BigInt 或 buffer。在这种情况下,`jsesc` 返回值的字符串化版本,其中任何非可打印 ASCII 符号的字符都以相同方式转义。
```js
// 转义数组
jsesc([
'Ich ♥ Bücher',
'foo 𝌆 bar'
])
// → '[\'Ich \\u2665 B\\xFCcher\',\'foo \\uD834\\uDF06 bar\']'
// 转义对象
jsesc({
'Ich ♥ Bücher': 'foo 𝌆 bar'
})
// → '{\'Ich \\u2665 B\\xFCcher\':\'foo \\uD834\\uDF06 bar\'}'
```
### 配置选项
可选的 `options` 参数接受一个具有以下选项的对象。在 TypeScript 中,使用 `JsescOptions` 类型获得完整的类型安全:
```typescript
import type { JsescOptions } from 'jsesc-es'
const options: JsescOptions = {
// 您的选项在这里,享有完整的 IntelliSense 支持
}
```
#### `quotes`
`quotes` 选项的默认值是 `'single'`。这意味着输入字符串中出现的任何 `'` 都被转义为 `\'`,以便输出可以在用单引号包裹的字符串字面量中使用。
```js
jsesc('`Lorem` ipsum "dolor" sit \'amet\' etc.')
// → 'Lorem ipsum "dolor" sit \\\'amet\\\' etc.'
jsesc('`Lorem` ipsum "dolor" sit \'amet\' etc.', {
quotes: 'single'
})
// → '`Lorem` ipsum "dolor" sit \\\'amet\\\' etc.'
```
如果您想将输出用作用双引号包裹的字符串字面量的一部分,请将 `quotes` 选项设置为 `'double'`。
```js
jsesc('`Lorem` ipsum "dolor" sit \'amet\' etc.', {
quotes: 'double'
})
// → '`Lorem` ipsum \\"dolor\\" sit \'amet\' etc.'
```
如果您想将输出用作模板字面量(即用反引号包裹)的一部分,请将 `quotes` 选项设置为 `'backtick'`。
```js
jsesc('`Lorem` ipsum "dolor" sit \'amet\' etc.', {
quotes: 'backtick'
})
// → '\\`Lorem\\` ipsum "dolor" sit \'amet\' etc.'
```
#### `numbers`
`numbers` 选项的默认值是 `'decimal'`。这意味着任何数值都使用十进制整数字面量表示。其他有效选项是 `binary`、`octal` 和 `hexadecimal`。
```js
jsesc(42, { numbers: 'binary' })
// → '0b101010'
jsesc(42, { numbers: 'octal' })
// → '0o52'
jsesc(42, { numbers: 'decimal' })
// → '42'
jsesc(42, { numbers: 'hexadecimal' })
// → '0x2A'
```
#### `wrap`
`wrap` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,输出是一个有效的 JavaScript 字符串字面量,用引号包裹。
```js
jsesc('Lorem ipsum "dolor" sit \'amet\' etc.', {
quotes: 'single',
wrap: true
})
// → '\'Lorem ipsum "dolor" sit \\\'amet\\\' etc.\''
jsesc('Lorem ipsum "dolor" sit \'amet\' etc.', {
quotes: 'double',
wrap: true
})
// → '"Lorem ipsum \\"dolor\\" sit \'amet\' etc."'
```
#### `es6`
`es6` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,输入中的任何星界 Unicode 符号都使用 [ECMAScript 6 Unicode 代码点转义序列](https://mathiasbynens.be/notes/javascript-escapes#unicode-code-point)进行转义。
```js
// 默认情况下,`es6` 选项是禁用的:
jsesc('foo 𝌆 bar 💩 baz')
// → 'foo \\uD834\\uDF06 bar \\uD83D\\uDCA9 baz'
// 启用它:
jsesc('foo 𝌆 bar 💩 baz', {
es6: true
})
// → 'foo \\u{1D306} bar \\u{1F4A9} baz'
```
#### `escapeEverything`
`escapeEverything` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,输出中的所有符号都被转义——甚至是可打印的 ASCII 符号。
```js
jsesc('lolwat"foo\'bar', {
escapeEverything: true
})
// → '\\x6C\\x6F\\x6C\\x77\\x61\\x74\\"\\x66\\x6F\\x6F\\\'\\x62\\x61\\x72'
```
#### `minimal`
`minimal` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,输出中只有有限的符号集合被转义。
**注意:** 启用此选项后,jsesc-es 的输出不再保证是 ASCII 安全的。
```js
jsesc('foo\u2029bar\nbaz©qux𝌆flops', {
minimal: true
})
// → 'foo\\u2029bar\\nbaz©qux𝌆flops'
```
#### `isScriptContext`
`isScriptContext` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,输出中出现的 [`</script` 和 `</style`](https://mathiasbynens.be/notes/etago) 被转义。
```js
jsesc('foo</script>bar', {
isScriptContext: true
})
// → 'foo<\\/script>bar'
```
#### `compact`
`compact` 选项接受布尔值(`true` 或 `false`),默认为 `true`(启用)。启用时,数组和对象的输出尽可能紧凑。
```js
jsesc({ 'Ich ♥ Bücher': 'foo 𝌆 bar' }, {
compact: true // 这是默认值
})
// → '{\'Ich \u2665 B\xFCcher\':\'foo \uD834\uDF06 bar\'}'
jsesc({ 'Ich ♥ Bücher': 'foo 𝌆 bar' }, {
compact: false
})
// → '{\n\t\'Ich \u2665 B\xFCcher\': \'foo \uD834\uDF06 bar\'\n}'
```
#### `indent`
`indent` 选项接受字符串值,默认为 `'\t'`。当 `compact` 设置禁用(`false`)时,`indent` 选项的值用于格式化输出。
```js
jsesc({ 'Ich ♥ Bücher': 'foo 𝌆 bar' }, {
compact: false,
indent: ' '
})
// → '{\n \'Ich \u2665 B\xFCcher\': \'foo \uD834\uDF06 bar\'\n}'
```
#### `indentLevel`
`indentLevel` 选项接受数值,默认为 `0`。它表示当前的缩进级别。
```js
jsesc(['a', 'b', 'c'], {
compact: false,
indentLevel: 1
})
// → '[\n\t\t\'a\',\n\t\t\'b\',\n\t\t\'c\'\n\t]'
```
#### `json`
`json` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,输出是有效的 JSON。
```js
jsesc('foo\x00bar\xFF\uFFFDbaz', {
json: true
})
// → '"foo\\u0000bar\\u00FF\\uFFFDbaz"'
jsesc({ 'foo\x00bar\xFF\uFFFDbaz': 'foo\x00bar\xFF\uFFFDbaz' }, {
json: true
})
// → '{"foo\\u0000bar\\u00FF\\uFFFDbaz":"foo\\u0000bar\\u00FF\\uFFFDbaz"}'
```
#### `lowercaseHex`
`lowercaseHex` 选项接受布尔值(`true` 或 `false`),默认为 `false`(禁用)。启用时,转义序列中的任何字母十六进制数字都是小写的。
```js
jsesc('Ich ♥ Bücher', {
lowercaseHex: true
})
// → 'Ich \\u2665 B\\xfccher'
// ^^
jsesc(42, {
numbers: 'hexadecimal',
lowercaseHex: true
})
// → '0x2a'
// ^^
```
### 版本信息
```typescript
// 访问版本信息
import jsesc, { version } from 'jsesc-es'
console.log(jsesc.version) // 例如:"0.0.3"
console.log(version) // 例如:"0.0.3"
```
## 从 jsesc 迁移
从原始 `jsesc` 库迁移很简单:
### 1. 更新依赖
```bash
# 移除旧包
npm uninstall jsesc
# 安装新包
npm install jsesc-es
```
### 2. 更新导入
```javascript
// 之前(CommonJS)
const jsesc = require('jsesc')
// 之后(ES 模块)
import jsesc from 'jsesc-es'
// 或带类型(TypeScript)
import jsesc, { type JsescOptions } from 'jsesc-es'
```
### 3. 享受增强功能
- **完整的 TypeScript 支持** 无需额外设置
- **更好的 IDE 体验** 包括 IntelliSense
- **现代模块系统** 支持 tree-shaking
- **100% API 兼容** - 无需代码更改
详细的迁移说明,请参见 [MIGRATION.md](./MIGRATION.md)。
## TypeScript 示例
### 创建工具函数
```typescript
import type { JsescOptions } from 'jsesc-es'
import jsesc from 'jsesc-es'
// 创建类型安全的工具函数
function createEscaper(defaultOptions: JsescOptions) {
return (input: string, options?: Partial<JsescOptions>): string => {
return jsesc(input, { ...defaultOptions, ...options })
}
}
// 预配置的转义器
const escapeForHTML = createEscaper({
quotes: 'double',
wrap: true,
isScriptContext: true
})
const escapeForJSON = createEscaper({
json: true
})
const escapeMinimal = createEscaper({
minimal: true
})
// 具有完整类型安全的使用
const htmlSafe = escapeForHTML('Hello "World"')
const jsonSafe = escapeForJSON({ message: 'Hello 世界' })
const minimalEscape = escapeMinimal('简单文本')
```
### 高级配置
```typescript
import type { JsescOptions } from 'jsesc-es'
// 定义配置预设
const PRESETS: Record<string, JsescOptions> = {
html: {
quotes: 'double',
wrap: true,
isScriptContext: true,
escapeEverything: false
},
json: {
json: true,
compact: true
},
es6: {
es6: true,
quotes: 'backtick',
wrap: true
},
debug: {
escapeEverything: true,
compact: false,
indent: ' '
}
} as const
// 类型安全的预设使用
function escapeWithPreset(
input: any,
preset: keyof typeof PRESETS,
overrides?: Partial<JsescOptions>
): string {
const config = { ...PRESETS[preset], ...overrides }
return jsesc(input, config)
}
```
## 性能
jsesc-es 相比原版包含几项性能改进:
- **优化的类型检查** 提供更好的运行时性能
- **改进的函数处理** 增强的检测逻辑
- **现代 JavaScript 特性** 更好的引擎优化
- **常见转义场景中的开销减少**
- **Tree-shaking 支持** 更小的包大小
## 浏览器支持
jsesc-es 通过其双构建系统提供广泛的兼容性:
### 运行时要求
**对于 ES 模块(推荐):**
- **Node.js**:14+(原生 ESM 支持)
- **浏览器**:Chrome 61+、Firefox 60+、Safari 10.1+、Edge 16+
- **ES 模块**:需要原生支持
**对于 CommonJS(传统):**
- **Node.js**:6+(转译输出)
- **浏览器**:Chrome 27+、Firefox 3+、Safari 4+、Opera 10+(使用打包器)
### 构建目标 vs. 模块系统
虽然 jsesc-es 通过其转译的 CommonJS 构建保持与传统环境的兼容性(目标 node6、chrome27 等),**ES 模块需要原生支持 `import`/`export` 语法的现代环境**。构建目标确保 _JavaScript 语法和 API_ 在较旧环境中工作,但 _模块系统本身_ 有最低版本要求。
**要点:**
- 📦 **CommonJS 构建**:在非常旧的环境中工作(Node 6+、Chrome 27+)
- 🚀 **ESM 构建**:需要具有原生 ESM 支持的现代环境
- 🔧 **传统项目**:使用 CommonJS 或使用 Babel/TypeScript 转译 ESM
- ⚡ **现代项目**:使用 ESM 获得更好的 tree-shaking 和性能
### TypeScript 支持
- **TypeScript**:4.5+
- **内置类型**:无需 `@types/` 包
## 开发
```bash
# 克隆仓库
git clone https://github.com/Drswith/jsesc-es.git
cd jsesc-es
# 安装依赖
pnpm install
# 运行测试
pnpm test
# 构建项目
pnpm build
# 类型检查
pnpm typecheck
# 代码检查
pnpm lint
```
## 贡献
欢迎贡献!请阅读我们的[贡献指南](CONTRIBUTING.md)了解行为准则和提交拉取请求的流程详情。
## 许可证
本项目采用 MIT 许可证 - 详情请参见 [LICENSE](LICENSE) 文件。
## 致谢
- **[Mathias Bynens](https://github.com/mathiasbynens)** - 原始 [jsesc](https://github.com/mathiasbynens/jsesc) 库的创建者
- **TypeScript 团队** - 提供出色的工具和类型系统
- **开源社区** - 持续的反馈和贡献
---
**jsesc-es** - 现代的、TypeScript 优先的 JavaScript 字符串转义方法。🚀