UNPKG

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
# jsesc-es <div align="center"> [![npm version](https://img.shields.io/npm/v/jsesc-es.svg)](https://www.npmjs.com/package/jsesc-es) [![npm downloads](https://img.shields.io/npm/dm/jsesc-es.svg)](https://www.npmjs.com/package/jsesc-es) [![jsdelivr](https://data.jsdelivr.com/v1/package/npm/jsesc-es/badge)](https://www.jsdelivr.com/package/npm/jsesc-es) [![unpkg](https://img.shields.io/badge/unpkg-jsesc--es-blue.svg)](https://unpkg.com/jsesc-es/) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/) [![ES Modules](https://img.shields.io/badge/ES-Modules-brightgreen.svg)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 字符串转义方法。🚀