Repository: node-modules/urlencode
Branch: master
Commit: e7a237f1f34f
Files: 13
Total size: 29.3 KB
Directory structure:
gitextract_z9mdxkfg/
├── .eslintrc
├── .github/
│ └── workflows/
│ ├── nodejs.yml
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── benchmark/
│ ├── urlencode.cjs
│ └── urlencode.decode.cjs
├── package.json
├── src/
│ └── index.ts
├── test/
│ └── urlencode.test.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc
================================================
{
"extends": [
"eslint-config-egg/typescript",
"eslint-config-egg/lib/rules/enforce-node-prefix"
]
}
================================================
FILE: .github/workflows/nodejs.yml
================================================
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
Job:
name: Node.js
uses: node-modules/github-actions/.github/workflows/node-test.yml@master
with:
os: 'ubuntu-latest'
version: '16, 18, 20, 21'
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
branches: [ master ]
jobs:
release:
name: Node.js
uses: node-modules/github-actions/.github/workflows/node-release.yml@master
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
================================================
FILE: .gitignore
================================================
lib-cov
coverage/
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
.tshy*
dist
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## [2.0.0](https://github.com/node-modules/urlencode/compare/v1.1.0...v2.0.0) (2023-10-28)
### ⚠ BREAKING CHANGES
* Drop Node.js < 16 support
closes https://github.com/node-modules/urlencode/issues/22
### Features
* support esm and cjs both ([#23](https://github.com/node-modules/urlencode/issues/23)) ([8f9308f](https://github.com/node-modules/urlencode/commit/8f9308fc830a2ba380343c8bce154aae3f3551f0))
1.1.0 / 2015-08-14
==================
* fix typo
* feat: Support IE8
1.0.1 / 2015-07-06
==================
* refactor: add \n to benchmark
* fix '\n' encoding
1.0.0 / 2015-04-04
==================
* deps: upgrade iconv-lite to 0.4.7
0.2.0 / 2014-04-25
==================
* urlencode.stringify done (@alsotang)
0.1.2 / 2014-04-09
==================
* remove unused variable QueryString (@azbykov)
0.1.1 / 2014-02-25
==================
* improve parse() performance 10x
0.1.0 / 2014-02-24
==================
* decode with charset
* add npm image
* remove 0.6 for travis
* update to support coveralls
* use jscover instead of jscoverage
* update gitignore
* Merge pull request #1 from aleafs/master
* Add http entries test case
0.0.1 / 2012-10-31
==================
* encode() done. add benchmark and tests
* Initial commit
================================================
FILE: LICENSE.txt
================================================
This software is licensed under the MIT License.
Copyright (C) 2012 - 2014 fengmk2 <fengmk2@gmail.com>
Copyright (C) 2015 node-modules
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# urlencode
[![NPM version][npm-image]][npm-url]
[](https://github.com/node-modules/urllib/actions/workflows/nodejs.yml)
[![Test coverage][codecov-image]][codecov-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]
[npm-image]: https://img.shields.io/npm/v/urlencode.svg?style=flat-square
[npm-url]: https://npmjs.org/package/urlencode
[codecov-image]: https://codecov.io/gh/node-modules/urlencode/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/node-modules/urlencode
[snyk-image]: https://snyk.io/test/npm/urlencode/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/urlencode
[download-image]: https://img.shields.io/npm/dm/urlencode.svg?style=flat-square
[download-url]: https://npmjs.org/package/urlencode
encodeURIComponent with charset, e.g.: `gbk`
## Install
```bash
npm install urlencode
```
## Usage
```ts
import { encode, decode, parse, stringify } from 'urlencode';
console.log(encode('苏千')); // default is utf8
console.log(encode('苏千', 'gbk')); // '%CB%D5%C7%A7'
// decode gbk
decode('%CB%D5%C7%A7', 'gbk'); // '苏千'
// parse gbk querystring
parse('nick=%CB%D5%C7%A7', { charset: 'gbk' }); // {nick: '苏千'}
// stringify obj with gbk encoding
var str = 'x[y][0][v][w]=' + encode('雾空', 'gbk'); // x[y][0][v][w]=%CE%ED%BF%D5
var obj = {'x' : {'y' : [{'v' : {'w' : '雾空'}}]}};
assert.equal(urlencode.stringify(obj, { charset: 'gbk' }, str);
```
## Benchmark
### encode(str, encoding)
```bash
$ node benchmark/urlencode.cjs
node version: v21.1.0
"苏千测试\n, 哈哈, haha"
urlencode Benchmark
node version: v21.1.0, date: Sat Oct 28 2023 21:01:00 GMT+0800 (中国标准时间)
Starting...
4 tests completed.
urlencode(str) x 4,617,242 ops/sec ±2.60% (95 runs sampled)
urlencode(str, "gbk") x 1,122,430 ops/sec ±2.20% (95 runs sampled)
encodeURIComponent(str) x 4,608,523 ops/sec ±2.94% (93 runs sampled)
encodeUTF8(str) x 833,170 ops/sec ±1.37% (96 runs sampled)
node version: v20.9.0
"苏千测试\n, 哈哈, haha"
urlencode Benchmark
node version: v20.9.0, date: Sat Oct 28 2023 21:01:37 GMT+0800 (中国标准时间)
Starting...
4 tests completed.
urlencode(str) x 4,304,468 ops/sec ±2.83% (89 runs sampled)
urlencode(str, "gbk") x 1,005,759 ops/sec ±2.10% (90 runs sampled)
encodeURIComponent(str) x 4,289,880 ops/sec ±2.99% (92 runs sampled)
encodeUTF8(str) x 827,841 ops/sec ±1.06% (96 runs sampled)
node version: v18.18.0
"苏千测试\n, 哈哈, haha"
urlencode Benchmark
node version: v18.18.0, date: Sat Oct 28 2023 19:34:06 GMT+0800 (中国标准时间)
Starting...
4 tests completed.
urlencode(str) x 4,597,865 ops/sec ±0.22% (96 runs sampled)
urlencode(str, "gbk") x 633,620 ops/sec ±15.31% (71 runs sampled)
encodeURIComponent(str) x 3,902,229 ops/sec ±2.49% (87 runs sampled)
encodeUTF8(str) x 510,456 ops/sec ±26.76% (88 runs sampled)
node version: v16.20.2
"苏千测试\n, 哈哈, haha"
urlencode Benchmark
node version: v16.20.2, date: Sat Oct 28 2023 21:02:11 GMT+0800 (中国标准时间)
Starting...
4 tests completed.
urlencode(str) x 4,438,372 ops/sec ±1.80% (93 runs sampled)
urlencode(str, "gbk") x 1,175,761 ops/sec ±0.68% (95 runs sampled)
encodeURIComponent(str) x 4,374,525 ops/sec ±1.96% (97 runs sampled)
encodeUTF8(str) x 751,616 ops/sec ±2.49% (86 runs sampled)
```
### decode(str, encoding)
```bash
$ node benchmark/urlencode.decode.cjs
node version: v21.1.0, date: "2023-10-28T12:51:20.191Z"
urlencode.decode Benchmark
node version: v21.1.0, date: Sat Oct 28 2023 20:51:20 GMT+0800 (中国标准时间)
Starting...
7 tests completed.
urlencode.decode(str) x 515,410 ops/sec ±1.95% (91 runs sampled)
urlencode.decode(str, "gbk") x 54,018 ops/sec ±3.17% (78 runs sampled)
decodeURIComponent(str) x 313,204 ops/sec ±2.93% (78 runs sampled)
urlencode.parse(qs, {charset: "gbk"}) x 311,613 ops/sec ±1.26% (95 runs sampled)
urlencode.stringify(data, {charset: "gbk"}) x 316,558 ops/sec ±1.55% (93 runs sampled)
urlencode.parse(qs, {charset: "utf8"}) x 490,744 ops/sec ±1.25% (94 runs sampled)
urlencode.stringify(data, {charset: "utf8"}) x 357,206 ops/sec ±0.46% (97 runs sampled)
node version: v20.9.0, date: "2023-10-28T12:49:57.236Z"
urlencode.decode Benchmark
node version: v20.9.0, date: Sat Oct 28 2023 20:49:57 GMT+0800 (中国标准时间)
Starting...
7 tests completed.
urlencode.decode(str) x 573,899 ops/sec ±0.62% (95 runs sampled)
urlencode.decode(str, "gbk") x 83,184 ops/sec ±0.13% (100 runs sampled)
decodeURIComponent(str) x 573,371 ops/sec ±1.67% (93 runs sampled)
urlencode.parse(qs, {charset: "gbk"}) x 303,202 ops/sec ±0.70% (100 runs sampled)
urlencode.stringify(data, {charset: "gbk"}) x 319,546 ops/sec ±0.29% (99 runs sampled)
urlencode.parse(qs, {charset: "utf8"}) x 462,578 ops/sec ±0.25% (98 runs sampled)
urlencode.stringify(data, {charset: "utf8"}) x 343,487 ops/sec ±0.17% (100 runs sampled)
node version: v18.18.0, date: "2023-10-28T12:44:56.355Z"
urlencode.decode Benchmark
node version: v18.18.0, date: Sat Oct 28 2023 20:44:56 GMT+0800 (中国标准时间)
Starting...
7 tests completed.
urlencode.decode(str) x 550,451 ops/sec ±1.74% (98 runs sampled)
urlencode.decode(str, "gbk") x 67,311 ops/sec ±1.16% (96 runs sampled)
decodeURIComponent(str) x 569,461 ops/sec ±0.30% (93 runs sampled)
urlencode.parse(qs, {charset: "gbk"}) x 293,407 ops/sec ±0.90% (97 runs sampled)
urlencode.stringify(data, {charset: "gbk"}) x 234,162 ops/sec ±4.55% (75 runs sampled)
urlencode.parse(qs, {charset: "utf8"}) x 316,697 ops/sec ±4.37% (78 runs sampled)
urlencode.stringify(data, {charset: "utf8"}) x 192,787 ops/sec ±4.58% (80 runs sampled)
node version: v16.20.2, date: "2023-10-28T12:47:38.431Z"
urlencode.decode Benchmark
node version: v16.20.2, date: Sat Oct 28 2023 20:47:38 GMT+0800 (中国标准时间)
Starting...
7 tests completed.
urlencode.decode(str) x 537,995 ops/sec ±2.07% (96 runs sampled)
urlencode.decode(str, "gbk") x 78,073 ops/sec ±0.17% (99 runs sampled)
decodeURIComponent(str) x 558,509 ops/sec ±0.48% (96 runs sampled)
urlencode.parse(qs, {charset: "gbk"}) x 252,590 ops/sec ±2.87% (90 runs sampled)
urlencode.stringify(data, {charset: "gbk"}) x 287,978 ops/sec ±2.47% (92 runs sampled)
urlencode.parse(qs, {charset: "utf8"}) x 416,600 ops/sec ±0.72% (93 runs sampled)
urlencode.stringify(data, {charset: "utf8"}) x 281,319 ops/sec ±2.43% (85 runs sampled)
```
## License
[MIT](LICENSE.txt)
================================================
FILE: benchmark/urlencode.cjs
================================================
const Benchmark = require('benchmark');
const benchmarks = require('beautify-benchmark');
const { encode } = require('../');
console.log('node version: %s', process.version);
function encodeUTF8(str) {
let encodeStr = '';
const buf = Buffer.from(str);
let ch = '';
for (let i = 0; i < buf.length; i++) {
ch = buf[i].toString('16');
if (ch.length === 1) {
ch = '0' + ch;
}
encodeStr += '%' + ch;
}
return encodeStr.toUpperCase();
}
console.log('%j', decodeURIComponent(encodeUTF8('苏千测试\n, 哈哈, haha')));
const suite = new Benchmark.Suite();
suite
.add('urlencode(str)', function () {
// urlencode('苏千');
encode('苏千写的\nurlencode,应该有用');
// urlencode('suqian want to sleep early tonight.');
// urlencode('你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢');
})
.add('urlencode(str, "gbk")', function () {
// urlencode('苏千', 'gbk');
encode('苏千写的\nurlencode,应该有用', 'gbk');
// urlencode('suqian want to sleep early tonight.', 'gbk');
// urlencode('你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢', 'gbk');
})
.add('encodeURIComponent(str)', function () {
// encodeURIComponent('苏千');
encodeURIComponent('苏千写的\nurlencode,应该有用');
// encodeURIComponent('suqian want to sleep early tonight.');
// encodeURIComponent('你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢');
})
.add('encodeUTF8(str)', function () {
// encodeUTF8('苏千');
encodeUTF8('苏千写的\nurlencode,应该有用');
// encodeUTF8('suqian want to sleep early tonight.');
// encodeUTF8('你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢');
})
.on('cycle', function(event) {
benchmarks.add(event.target);
})
.on('start', function(event) {
console.log('\n urlencode Benchmark\n node version: %s, date: %s\n Starting...',
process.version, Date());
})
.on('complete', function done() {
benchmarks.log();
})
.run({ 'async': false });
================================================
FILE: benchmark/urlencode.decode.cjs
================================================
const Benchmark = require('benchmark');
const benchmarks = require('beautify-benchmark');
const { parse, encode, decode, stringify } = require('../');
console.log('node version: %s, date: %j', process.version, new Date());
const suite = new Benchmark.Suite();
const utf8DecodeItems = [
encode('苏千'),
encode('苏千写的urlencode,应该有用'),
encode('suqian want to sleep early tonight.'),
encode('你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢'),
];
const gbkDecodeItems = [
encode('苏千', 'gbk'),
encode('苏千写的urlencode,应该有用', 'gbk'),
encode('suqian want to sleep early tonight.', 'gbk'),
encode('你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢,你让同一个项目中写两份一样代码的人情何以堪呢', 'gbk'),
];
// console.log(decode(gbkDecodeItems[3], 'gbk'))
const gbkEncodeString = 'umidtoken=Tc230acc03a564530aee31d22701e9b95&usertag4=0&usertag3=512&usertag2=0&status=0&userid=665377421&out_user=suqian.yf%40taobao.com&promotedtype=0&account_no=20885028063394350156&loginstatus=true&usertag=0&nick=%CB%D5%C7%A7&tairlastupdatetime=1319008872&strid=a68f6ee38f44d2b89ca508444c1ccaf9';
const data = parse(gbkEncodeString, {charset: 'gbk'});
// console.log(stringify(data, {charset: 'gbk'}) === gbkEncodeString);
suite
.add('urlencode.decode(str)', function () {
decode(utf8DecodeItems[0]);
decode(utf8DecodeItems[1]);
decode(utf8DecodeItems[2]);
decode(utf8DecodeItems[3]);
})
.add('urlencode.decode(str, "gbk")', function () {
decode(gbkDecodeItems[0], 'gbk');
decode(gbkDecodeItems[1], 'gbk');
decode(gbkDecodeItems[2], 'gbk');
decode(gbkDecodeItems[3], 'gbk');
})
.add('decodeURIComponent(str)', function () {
decodeURIComponent(utf8DecodeItems[0]);
decodeURIComponent(utf8DecodeItems[1]);
decodeURIComponent(utf8DecodeItems[2]);
decodeURIComponent(utf8DecodeItems[3]);
})
.add('urlencode.parse(qs, {charset: "gbk"})', function () {
parse(gbkEncodeString, {charset: 'gbk'});
})
.add('urlencode.stringify(data, {charset: "gbk"})', function () {
stringify(data, {charset: 'gbk'});
})
.add('urlencode.parse(qs, {charset: "utf8"})', function () {
parse('umidtoken=Tc230acc03a564530aee31d22701e9b95&usertag4=0&usertag3=512&usertag2=0&status=0&userid=665377421&out_user=suqian.yf%40taobao.com&promotedtype=0&account_no=20885028063394350156&loginstatus=true&usertag=0&nick=%E8%8B%8F%E5%8D%83&tairlastupdatetime=1319008872&strid=a68f6ee38f44d2b89ca508444c1ccaf9',
{charset: 'utf8'});
})
.add('urlencode.stringify(data, {charset: "utf8"})', function () {
stringify(data, {charset: 'utf8'});
})
.on('cycle', function(event) {
benchmarks.add(event.target);
})
.on('start', function(event) {
console.log('\n urlencode.decode Benchmark\n node version: %s, date: %s\n Starting...',
process.version, Date());
})
.on('complete', function done() {
benchmarks.log();
})
.run({ 'async': false });
================================================
FILE: package.json
================================================
{
"name": "urlencode",
"version": "2.0.0",
"description": "encodeURIComponent with charset",
"scripts": {
"test": "egg-bin test",
"ci": "npm run lint && egg-bin cov && npm run prepublishOnly && npm run benchmark",
"lint": "eslint . --ext ts",
"benchmark": "node benchmark/urlencode.cjs && node benchmark/urlencode.decode.cjs",
"prepublishOnly": "tshy && tshy-after"
},
"dependencies": {
"iconv-lite": "~0.6.3"
},
"devDependencies": {
"@eggjs/tsconfig": "^1.3.3",
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.7",
"beautify-benchmark": "^0.2.4",
"benchmark": "^2.1.4",
"egg-bin": "^6.5.2",
"eslint": "^8.51.0",
"eslint-config-egg": "^13.0.0",
"git-contributor": "^2.1.5",
"tshy": "^1.5.0",
"tshy-after": "^1.0.0",
"typescript": "^5.2.2"
},
"homepage": "https://github.com/node-modules/urlencode",
"repository": {
"type": "git",
"url": "git://github.com/node-modules/urlencode.git"
},
"keywords": [
"urlencode",
"urldecode",
"encodeURIComponent",
"decodeURIComponent",
"querystring",
"parse"
],
"author": "fengmk2 <fengmk2@gmail.com>",
"license": "MIT",
"files": [
"dist",
"src"
],
"tshy": {
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts"
}
},
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
}
},
"main": "./dist/commonjs/index.js",
"types": "./dist/commonjs/index.d.ts",
"type": "module"
}
================================================
FILE: src/index.ts
================================================
import iconv from 'iconv-lite';
export type SupportEncodeValue = string | number | boolean | undefined | null;
export type SupportEncodeObject = Record<string, object | SupportEncodeValue>;
export interface Options {
charset?: string;
maxKeys?: number;
}
function isUTF8(charset?: string) {
if (!charset) {
return true;
}
charset = charset.toLowerCase();
return charset === 'utf8' || charset === 'utf-8';
}
export function encode(str: string, charset?: string | null) {
if (!charset || isUTF8(charset)) {
return encodeURIComponent(str);
}
const buf = iconv.encode(str, charset);
let encodeStr = '';
let ch = '';
for (let i = 0; i < buf.length; i++) {
ch = buf[i].toString(16);
if (ch.length === 1) {
ch = '0' + ch;
}
encodeStr += '%' + ch;
}
encodeStr = encodeStr.toUpperCase();
return encodeStr;
}
export default encode;
export function decode(str: string, charset?: string | null) {
if (!charset || isUTF8(charset)) {
return decodeURIComponent(str);
}
const bytes = [];
for (let i = 0; i < str.length;) {
if (str[i] === '%') {
i++;
bytes.push(parseInt(str.substring(i, i + 2), 16));
i += 2;
} else {
bytes.push(str.charCodeAt(i));
i++;
}
}
const buf = Buffer.from(bytes);
return iconv.decode(buf, charset);
}
export function parse(qs: string, options?: Options): SupportEncodeObject;
export function parse(qs: string, sep?: string, eq?: string, options?: Options): SupportEncodeObject;
export function parse(qs: string, sepOrOptions?: string | Options, eq?: string, options?: Options): SupportEncodeObject {
let sep: string | undefined;
if (typeof sepOrOptions === 'object') {
// parse(qs, options)
options = sepOrOptions;
} else {
// parse(qs, sep, eq, options)
sep = sepOrOptions;
}
sep = sep || '&';
eq = eq || '=';
const obj: SupportEncodeObject = {};
if (typeof qs !== 'string' || qs.length === 0) {
return obj;
}
const regexp = /\+/g;
const splits = qs.split(sep);
let maxKeys = 1000;
let charset = '';
if (options) {
if (typeof options.maxKeys === 'number') {
maxKeys = options.maxKeys;
}
if (typeof options.charset === 'string') {
charset = options.charset;
}
}
let len = splits.length;
// maxKeys <= 0 means that we should not limit keys count
if (maxKeys > 0 && len > maxKeys) {
len = maxKeys;
}
for (let i = 0; i < len; ++i) {
const x = splits[i].replace(regexp, '%20');
const idx = x.indexOf(eq);
let keyString: string;
let valueString: string;
let k: string;
let v: string;
if (idx >= 0) {
keyString = x.substring(0, idx);
valueString = x.substring(idx + 1);
} else {
keyString = x;
valueString = '';
}
if (keyString && keyString.includes('%')) {
try {
k = decode(keyString, charset);
} catch (e) {
k = keyString;
}
} else {
k = keyString;
}
if (valueString && valueString.includes('%')) {
try {
v = decode(valueString, charset);
} catch (e) {
v = valueString;
}
} else {
v = valueString;
}
if (!has(obj, k)) {
obj[k] = v;
} else if (Array.isArray(obj[k])) {
(obj[k] as any).push(v);
} else {
obj[k] = [ obj[k], v ];
}
}
return obj;
}
function has(obj: object, prop: string) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function isASCII(str: string) {
// eslint-disable-next-line no-control-regex
return /^[\x00-\x7F]*$/.test(str);
}
function encodeComponent(item: string, charset?: string) {
item = String(item);
if (isASCII(item)) {
item = encodeURIComponent(item);
} else {
item = encode(item, charset);
}
return item;
}
function stringifyArray(values: (SupportEncodeValue | SupportEncodeObject)[], prefix: string, options: Options) {
const items = [];
for (const [ index, value ] of values.entries()) {
items.push(stringify(value, `${prefix}[${index}]`, options));
}
return items.join('&');
}
function stringifyObject(obj: SupportEncodeObject, prefix: string, options: Options) {
const items = [];
const charset = options.charset;
for (const key in obj) {
if (key === '') {
continue;
}
const value = obj[key];
if (value === null || value === undefined) {
items.push(encode(key, charset) + '=');
} else {
const keyPrefix = prefix ? prefix + '[' + encodeComponent(key, charset) + ']' : encodeComponent(key, charset);
items.push(stringify(value, keyPrefix, options));
}
}
return items.join('&');
}
export function stringify(obj: object | SupportEncodeValue, prefix?: string): string;
export function stringify(obj: object | SupportEncodeValue, options?: Options): string;
export function stringify(obj: object | SupportEncodeValue, prefix?: string, options?: Options): string;
export function stringify(obj: object | SupportEncodeValue, prefixOrOptions?: string | Options, options?: Options): string {
let prefix: string | undefined;
if (typeof prefixOrOptions !== 'string') {
options = prefixOrOptions || {};
} else {
prefix = prefixOrOptions;
}
options = options ?? {};
if (Array.isArray(obj)) {
if (!prefix) {
throw new TypeError('stringify expects an object');
}
return stringifyArray(obj, prefix, options);
}
const objValue = String(obj);
if (obj && typeof obj === 'object' && objValue === '[object Object]') {
return stringifyObject(obj as SupportEncodeObject, prefix ?? '', options);
}
if (!prefix) {
throw new TypeError('stringify expects an object');
}
const charset = options?.charset ?? 'utf-8';
return `${prefix}=${encodeComponent(objValue, charset)}`;
}
================================================
FILE: test/urlencode.test.ts
================================================
import { strict as assert } from 'node:assert';
import { encode, decode, stringify, parse } from '../src/index.js';
import urlencode from '../src/index.js';
import type { SupportEncodeObject } from '../src/index.js';
describe('urlencode.test.js', () => {
describe('encode() and decode()', () => {
const items = [
[ '苏千', null, encodeURIComponent('苏千') ],
[ '苏千', undefined, encodeURIComponent('苏千') ],
[ '苏千', '', encodeURIComponent('苏千') ],
[ '苏千', 'utf8', encodeURIComponent('苏千') ],
[ '苏千', 'utf-8', encodeURIComponent('苏千') ],
[ 'nodeJS', 'gbk', '%6E%6F%64%65%4A%53' ],
[ '苏千', 'gbk', '%CB%D5%C7%A7' ],
[ '苏千,nodejs。!@#¥%……&**(&*)&)}{|~~!@+——?、》《。,“‘:;|、】【}{~·中文', 'gbk',
'%CB%D5%C7%A7%A3%AC%6E%6F%64%65%6A%73%A1%A3%A3%A1%40%23%A3%A4%25%A1%AD%A1%AD%26%2A%2A%A3%A8%26%2A%A3%A9%26%A3%A9%7D%7B%7C%7E%7E%A3%A1%40%2B%A1%AA%A1%AA%A3%BF%A1%A2%A1%B7%A1%B6%A1%A3%A3%AC%A1%B0%A1%AE%A3%BA%A3%BB%7C%A1%A2%A1%BF%A1%BE%7D%7B%7E%A1%A4%D6%D0%CE%C4' ],
[ '\\诚%http://github.com/aleafs?a=b&c[1]= &c2#', 'gbk', '%5C%B3%CF%25%68%74%74%70%3A%2F%2F%67%69%74%68%75%62%2E%63%6F%6D%2F%61%6C%65%61%66%73%3F%61%3D%62%26%63%5B%31%5D%3D%20%26%63%32%23' ],
[ '\n\r\n', 'gbk', '%0A%0D%0A' ],
];
items.forEach(item => {
const str = item[0] as string;
const charset = item[1];
const expect = item[2] as string;
it('should encode ' + str.substring(0, 20) + ' with ' + charset + ' to ' + expect.substring(0, 30), () => {
assert.equal(encode(str, charset), expect);
assert.equal(urlencode(str, charset), expect);
});
});
const decodeItems = [
[ '%CB%D5%C7%A7a', 'gbk', '苏千a' ],
[ '%CB%D5%C7%A7a%0A%C7%A7a%a', 'gbk', '苏千a\n千a\n' ],
[
'%CB%D5%C7%A7%A3%ACnodejs%A1%A3%A3%A1%40%23%A3%A4%25%A1%AD%A1%AD%26**%A3%A8%26*%A3%A9%26%A3%A9%7D%7B%7C%7E%7E%A3%A1%40%2B%A1%AA%A1%AA%A3%BF%A1%A2%A1%B7%A1%B6%A1%A3%A3%AC%A1%B0%A1%AE%A3%BA%A3%BB%7C%A1%A2%A1%BF%A1%BE%7D%7B%7E%A1%A4%D6%D0%CE%C4',
'gbk',
'苏千,nodejs。!@#¥%……&**(&*)&)}{|~~!@+——?、》《。,“‘:;|、】【}{~·中文',
],
];
decodeItems.forEach(item => {
const str = item[0];
const charset = item[1];
const expect = item[2];
it('should decode ' + str.substring(0, 20) + ' with ' + charset + ' to ' + expect.substring(0, 30), () => {
assert.equal(decode(str, charset), expect);
});
});
items.forEach(item => {
const str = item[2] as string;
const charset = item[1];
const expect = item[0] as string;
it('should decode ' + str.substring(0, 20) + ' with ' + charset + ' to ' + expect.substring(0, 30), () => {
assert.equal(decode(str, charset), expect);
});
});
});
describe('parse()', () => {
it('should work with gbk encoding', () => {
const qs = 'umidtoken=Tc230acc03a564530aee31d22701e9b95&usertag4=0&usertag3=512&usertag2=0&status=0&userid=665377421&out_user=suqian.yf%40taobao.com&promotedtype=0&account_no=20885028063394350156&loginstatus=true&usertag=0&nick=%CB%D5%C7%A7&tairlastupdatetime=1319008872&strid=a68f6ee38f44d2b89ca508444c1ccaf9';
const obj = parse(qs, { charset: 'gbk' });
assert.deepEqual(obj, {
umidtoken: 'Tc230acc03a564530aee31d22701e9b95',
usertag4: '0',
usertag3: '512',
usertag2: '0',
status: '0',
userid: '665377421',
out_user: 'suqian.yf@taobao.com',
promotedtype: '0',
account_no: '20885028063394350156',
loginstatus: 'true',
usertag: '0',
nick: '苏千',
tairlastupdatetime: '1319008872',
strid: 'a68f6ee38f44d2b89ca508444c1ccaf9',
});
});
// TODO
// var qs = 'x[y][0][v][w]=%CE%ED%BF%D5';
// var obj = {'x' : {'y' : [{'v' : {'w' : '雾空'}}]}};
// urlencode.parse(qs, {charset: 'gbk'})
// .should.eql(obj);
});
describe('stringify()', () => {
it('should work with gbk encoding', () => {
let obj: SupportEncodeObject = { xm: '苏千', xb: 1, xh: 1111 };
assert.equal(stringify(obj, { charset: 'gbk' }), 'xm=%CB%D5%C7%A7&xb=1&xh=1111');
// `qs` and `obj` is copy from `describe('parse()', ->)`
const qs = 'umidtoken=Tc230acc03a564530aee31d22701e9b95&usertag4=0&usertag3=512&usertag2=0&status=0&userid=665377421&out_user=suqian.yf%40taobao.com&promotedtype=0&account_no=20885028063394350156&loginstatus=true&usertag=0&nick=%CB%D5%C7%A7&tairlastupdatetime=1319008872&strid=a68f6ee38f44d2b89ca508444c1ccaf9';
obj = {
umidtoken: 'Tc230acc03a564530aee31d22701e9b95',
usertag4: '0',
usertag3: '512',
usertag2: '0',
status: '0',
userid: '665377421',
out_user: 'suqian.yf@taobao.com',
promotedtype: '0',
account_no: '20885028063394350156',
loginstatus: 'true',
usertag: '0',
nick: '苏千',
tairlastupdatetime: '1319008872',
strid: 'a68f6ee38f44d2b89ca508444c1ccaf9',
};
assert.equal(stringify(obj, { charset: 'gbk' }), qs);
// str: x[y][0][v][w]=%CE%ED%BF%D5
let str = 'x[y][0][v][w]=' + encode('雾空', 'gbk');
obj = { x: { y: [{ v: { w: '雾空' } }] } };
assert.equal(stringify(obj, { charset: 'gbk' }), str);
// str : xh=23123&%CE%ED%BF%D5=%CE%ED%BF%D5
// 这里是 chrome 在 gbk 编码网页的行为
str = 'xh=13241234' +
'&xb=1' +
'&' + encode('雾空', 'gbk') + '=' + encode('雾空', 'gbk');
obj = { xh: 13241234, xb: 1, 雾空: '雾空' };
assert.equal(stringify(obj, { charset: 'gbk' }), str);
});
it('should work with utf-8 encoding', () => {
let obj: SupportEncodeObject = { h: 1, j: 2, k: '3' };
assert.equal(stringify(obj, { charset: 'utf-8' }), 'h=1&j=2&k=3');
assert.equal(stringify(obj), 'h=1&j=2&k=3');
let str = 'x[y][0][v][w]=1';
obj = { x: { y: [{ v: { w: '1' } }] } };
assert.equal(stringify(obj), str);
str = 'x[y][0][v][w]=' + encodeURIComponent('雾空');
obj = { x: { y: [{ v: { w: '雾空' } }] } };
assert.equal(stringify(obj), str);
str = 'x[y][0][v][w]=' + encodeURIComponent('雾空');
obj = { x: { y: [{ v: { w: '雾空' } }] } };
assert.equal(stringify(obj, { charset: 'utf-8' }), str);
});
it('should work with big5 encoding', () => {
const str = 'x[y][0][v][w]=' + encode('雾空', 'big5');
const obj = { x: { y: [{ v: { w: '雾空' } }] } };
assert.equal(stringify(obj, { charset: 'big5' }), str);
});
it('should support nest obj and array', () => {
const encoding = 'gbk';
const obj: SupportEncodeObject = {
edp: {
name: [ '阿里', '巴巴', '数据产品' ],
hello: 100,
nihao: '100',
},
good: '好',
};
// qs : edp[name][0]=%B0%A2%C0%EF
// &edp[name][1]=%B0%CD%B0%CD
// &edp[name][2]=%CA%FD%BE%DD%B2%FA%C6%B7
// &edp[hello]=100
// &edp[nihao]=100
// &good=%BA%C3
const qs = 'edp[name][0]=' + encode('阿里', encoding) +
'&edp[name][1]=' + encode('巴巴', encoding) +
'&edp[name][2]=' + encode('数据产品', encoding) +
'&edp[hello]=100' +
'&edp[nihao]=100' +
'&good=' + encode('好', encoding);
assert.equal(stringify(obj, { charset: 'gbk' }), qs);
});
});
});
================================================
FILE: tsconfig.json
================================================
{
"extends": "@eggjs/tsconfig",
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}
gitextract_z9mdxkfg/ ├── .eslintrc ├── .github/ │ └── workflows/ │ ├── nodejs.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── benchmark/ │ ├── urlencode.cjs │ └── urlencode.decode.cjs ├── package.json ├── src/ │ └── index.ts ├── test/ │ └── urlencode.test.ts └── tsconfig.json
SYMBOL INDEX (14 symbols across 2 files)
FILE: benchmark/urlencode.cjs
function encodeUTF8 (line 7) | function encodeUTF8(str) {
FILE: src/index.ts
type SupportEncodeValue (line 3) | type SupportEncodeValue = string | number | boolean | undefined | null;
type SupportEncodeObject (line 4) | type SupportEncodeObject = Record<string, object | SupportEncodeValue>;
type Options (line 5) | interface Options {
function isUTF8 (line 10) | function isUTF8(charset?: string) {
function encode (line 18) | function encode(str: string, charset?: string | null) {
function decode (line 39) | function decode(str: string, charset?: string | null) {
function parse (line 61) | function parse(qs: string, sepOrOptions?: string | Options, eq?: string,...
function has (line 147) | function has(obj: object, prop: string) {
function isASCII (line 151) | function isASCII(str: string) {
function encodeComponent (line 156) | function encodeComponent(item: string, charset?: string) {
function stringifyArray (line 166) | function stringifyArray(values: (SupportEncodeValue | SupportEncodeObjec...
function stringifyObject (line 174) | function stringifyObject(obj: SupportEncodeObject, prefix: string, optio...
function stringify (line 195) | function stringify(obj: object | SupportEncodeValue, prefixOrOptions?: s...
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
{
"path": ".eslintrc",
"chars": 113,
"preview": "{\n \"extends\": [\n \"eslint-config-egg/typescript\",\n \"eslint-config-egg/lib/rules/enforce-node-prefix\"\n ]\n}\n"
},
{
"path": ".github/workflows/nodejs.yml",
"chars": 266,
"preview": "name: CI\n\non:\n push:\n branches: [ master ]\n\n pull_request:\n branches: [ master ]\n\njobs:\n Job:\n name: Node.js"
},
{
"path": ".github/workflows/release.yml",
"chars": 265,
"preview": "name: Release\n\non:\n push:\n branches: [ master ]\n\njobs:\n release:\n name: Node.js\n uses: node-modules/github-ac"
},
{
"path": ".gitignore",
"chars": 119,
"preview": "lib-cov\ncoverage/\n*.seed\n*.log\n*.csv\n*.dat\n*.out\n*.pid\n*.gz\n\npids\nlogs\nresults\n\nnode_modules\nnpm-debug.log\n.tshy*\ndist\n"
},
{
"path": "CHANGELOG.md",
"chars": 1293,
"preview": "# Changelog\n\n## [2.0.0](https://github.com/node-modules/urlencode/compare/v1.1.0...v2.0.0) (2023-10-28)\n\n\n### ⚠ BREAKING"
},
{
"path": "LICENSE.txt",
"chars": 1160,
"preview": "This software is licensed under the MIT License.\n\nCopyright (C) 2012 - 2014 fengmk2 <fengmk2@gmail.com>\nCopyright (C) 20"
},
{
"path": "README.md",
"chars": 6895,
"preview": "# urlencode\n\n[![NPM version][npm-image]][npm-url]\n[;\nconst benchmarks = require('beautify-benchmark');\nconst { encode } = require('.."
},
{
"path": "benchmark/urlencode.decode.cjs",
"chars": 2883,
"preview": "const Benchmark = require('benchmark');\nconst benchmarks = require('beautify-benchmark');\nconst { parse, encode, decode,"
},
{
"path": "package.json",
"chars": 1748,
"preview": "{\n \"name\": \"urlencode\",\n \"version\": \"2.0.0\",\n \"description\": \"encodeURIComponent with charset\",\n \"scripts\": {\n \"t"
},
{
"path": "src/index.ts",
"chars": 5795,
"preview": "import iconv from 'iconv-lite';\n\nexport type SupportEncodeValue = string | number | boolean | undefined | null;\nexport t"
},
{
"path": "test/urlencode.test.ts",
"chars": 7285,
"preview": "import { strict as assert } from 'node:assert';\nimport { encode, decode, stringify, parse } from '../src/index.js';\nimpo"
},
{
"path": "tsconfig.json",
"chars": 195,
"preview": "{\n \"extends\": \"@eggjs/tsconfig\",\n \"compilerOptions\": {\n \"strict\": true,\n \"noImplicitAny\": true,\n \"target\": \"E"
}
]
About this extraction
This page contains the full source code of the node-modules/urlencode GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (29.3 KB), approximately 10.3k tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.