Skip to content

Commit 733a1de

Browse files
author
yuuangzhang
committed
chore: add esm.js named export
1 parent 8097fe2 commit 733a1de

File tree

3 files changed

+275
-2
lines changed

3 files changed

+275
-2
lines changed

esm.js

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/*
2+
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
3+
Copyrights licensed under the New BSD License.
4+
See the accompanying LICENSE file for terms.
5+
*/
6+
7+
'use strict';
8+
9+
var randomBytes = require('randombytes');
10+
11+
// Generate an internal UID to make the regexp pattern harder to guess.
12+
var UID_LENGTH = 16;
13+
var UID = generateUID();
14+
var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + '-(\\d+)__@"', 'g');
15+
16+
var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g;
17+
var IS_PURE_FUNCTION = /function.*?\(/;
18+
var IS_ARROW_FUNCTION = /.*?=>.*?/;
19+
var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g;
20+
21+
var RESERVED_SYMBOLS = ['*', 'async'];
22+
23+
// Mapping of unsafe HTML and invalid JavaScript line terminator chars to their
24+
// Unicode char counterparts which are safe to use in JavaScript strings.
25+
var ESCAPED_CHARS = {
26+
'<' : '\\u003C',
27+
'>' : '\\u003E',
28+
'/' : '\\u002F',
29+
'\u2028': '\\u2028',
30+
'\u2029': '\\u2029'
31+
};
32+
33+
function escapeUnsafeChars(unsafeChar) {
34+
return ESCAPED_CHARS[unsafeChar];
35+
}
36+
37+
function generateUID() {
38+
var bytes = randomBytes(UID_LENGTH);
39+
var result = '';
40+
for(var i=0; i<UID_LENGTH; ++i) {
41+
result += bytes[i].toString(16);
42+
}
43+
return result;
44+
}
45+
46+
function deleteFunctions(obj){
47+
var functionKeys = [];
48+
for (var key in obj) {
49+
if (typeof obj[key] === "function") {
50+
functionKeys.push(key);
51+
}
52+
}
53+
for (var i = 0; i < functionKeys.length; i++) {
54+
delete obj[functionKeys[i]];
55+
}
56+
}
57+
58+
function serialize(obj, options) {
59+
options || (options = {});
60+
61+
// Backwards-compatibility for `space` as the second argument.
62+
if (typeof options === 'number' || typeof options === 'string') {
63+
options = {space: options};
64+
}
65+
66+
var functions = [];
67+
var regexps = [];
68+
var dates = [];
69+
var maps = [];
70+
var sets = [];
71+
var arrays = [];
72+
var undefs = [];
73+
var infinities= [];
74+
var bigInts = [];
75+
var urls = [];
76+
77+
// Returns placeholders for functions and regexps (identified by index)
78+
// which are later replaced by their string representation.
79+
function replacer(key, value) {
80+
81+
// For nested function
82+
if(options.ignoreFunction){
83+
deleteFunctions(value);
84+
}
85+
86+
if (!value && value !== undefined) {
87+
return value;
88+
}
89+
90+
// If the value is an object w/ a toJSON method, toJSON is called before
91+
// the replacer runs, so we use this[key] to get the non-toJSONed value.
92+
var origValue = this[key];
93+
var type = typeof origValue;
94+
95+
if (type === 'object') {
96+
if(origValue instanceof RegExp) {
97+
return '@__R-' + UID + '-' + (regexps.push(origValue) - 1) + '__@';
98+
}
99+
100+
if(origValue instanceof Date) {
101+
return '@__D-' + UID + '-' + (dates.push(origValue) - 1) + '__@';
102+
}
103+
104+
if(origValue instanceof Map) {
105+
return '@__M-' + UID + '-' + (maps.push(origValue) - 1) + '__@';
106+
}
107+
108+
if(origValue instanceof Set) {
109+
return '@__S-' + UID + '-' + (sets.push(origValue) - 1) + '__@';
110+
}
111+
112+
if(origValue instanceof Array) {
113+
var isSparse = origValue.filter(function(){return true}).length !== origValue.length;
114+
if (isSparse) {
115+
return '@__A-' + UID + '-' + (arrays.push(origValue) - 1) + '__@';
116+
}
117+
}
118+
119+
if(origValue instanceof URL) {
120+
return '@__L-' + UID + '-' + (urls.push(origValue) - 1) + '__@';
121+
}
122+
}
123+
124+
if (type === 'function') {
125+
return '@__F-' + UID + '-' + (functions.push(origValue) - 1) + '__@';
126+
}
127+
128+
if (type === 'undefined') {
129+
return '@__U-' + UID + '-' + (undefs.push(origValue) - 1) + '__@';
130+
}
131+
132+
if (type === 'number' && !isNaN(origValue) && !isFinite(origValue)) {
133+
return '@__I-' + UID + '-' + (infinities.push(origValue) - 1) + '__@';
134+
}
135+
136+
if (type === 'bigint') {
137+
return '@__B-' + UID + '-' + (bigInts.push(origValue) - 1) + '__@';
138+
}
139+
140+
return value;
141+
}
142+
143+
function serializeFunc(fn) {
144+
var serializedFn = fn.toString();
145+
if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {
146+
throw new TypeError('Serializing native function: ' + fn.name);
147+
}
148+
149+
// pure functions, example: {key: function() {}}
150+
if(IS_PURE_FUNCTION.test(serializedFn)) {
151+
return serializedFn;
152+
}
153+
154+
// arrow functions, example: arg1 => arg1+5
155+
if(IS_ARROW_FUNCTION.test(serializedFn)) {
156+
return serializedFn;
157+
}
158+
159+
var argsStartsAt = serializedFn.indexOf('(');
160+
var def = serializedFn.substr(0, argsStartsAt)
161+
.trim()
162+
.split(' ')
163+
.filter(function(val) { return val.length > 0 });
164+
165+
var nonReservedSymbols = def.filter(function(val) {
166+
return RESERVED_SYMBOLS.indexOf(val) === -1
167+
});
168+
169+
// enhanced literal objects, example: {key() {}}
170+
if(nonReservedSymbols.length > 0) {
171+
return (def.indexOf('async') > -1 ? 'async ' : '') + 'function'
172+
+ (def.join('').indexOf('*') > -1 ? '*' : '')
173+
+ serializedFn.substr(argsStartsAt);
174+
}
175+
176+
// arrow functions
177+
return serializedFn;
178+
}
179+
180+
// Check if the parameter is function
181+
if (options.ignoreFunction && typeof obj === "function") {
182+
obj = undefined;
183+
}
184+
// Protects against `JSON.stringify()` returning `undefined`, by serializing
185+
// to the literal string: "undefined".
186+
if (obj === undefined) {
187+
return String(obj);
188+
}
189+
190+
var str;
191+
192+
// Creates a JSON string representation of the value.
193+
// NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args.
194+
if (options.isJSON && !options.space) {
195+
str = JSON.stringify(obj);
196+
} else {
197+
str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space);
198+
}
199+
200+
// Protects against `JSON.stringify()` returning `undefined`, by serializing
201+
// to the literal string: "undefined".
202+
if (typeof str !== 'string') {
203+
return String(str);
204+
}
205+
206+
// Replace unsafe HTML and invalid JavaScript line terminator chars with
207+
// their safe Unicode char counterpart. This _must_ happen before the
208+
// regexps and functions are serialized and added back to the string.
209+
if (options.unsafe !== true) {
210+
str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);
211+
}
212+
213+
if (functions.length === 0 && regexps.length === 0 && dates.length === 0 && maps.length === 0 && sets.length === 0 && arrays.length === 0 && undefs.length === 0 && infinities.length === 0 && bigInts.length === 0 && urls.length === 0) {
214+
return str;
215+
}
216+
217+
// Replaces all occurrences of function, regexp, date, map and set placeholders in the
218+
// JSON string with their string representations. If the original value can
219+
// not be found, then `undefined` is used.
220+
return str.replace(PLACE_HOLDER_REGEXP, function (match, backSlash, type, valueIndex) {
221+
// The placeholder may not be preceded by a backslash. This is to prevent
222+
// replacing things like `"a\"@__R-<UID>-0__@"` and thus outputting
223+
// invalid JS.
224+
if (backSlash) {
225+
return match;
226+
}
227+
228+
if (type === 'D') {
229+
return "new Date(\"" + dates[valueIndex].toISOString() + "\")";
230+
}
231+
232+
if (type === 'R') {
233+
return "new RegExp(" + serialize(regexps[valueIndex].source) + ", \"" + regexps[valueIndex].flags + "\")";
234+
}
235+
236+
if (type === 'M') {
237+
return "new Map(" + serialize(Array.from(maps[valueIndex].entries()), options) + ")";
238+
}
239+
240+
if (type === 'S') {
241+
return "new Set(" + serialize(Array.from(sets[valueIndex].values()), options) + ")";
242+
}
243+
244+
if (type === 'A') {
245+
return "Array.prototype.slice.call(" + serialize(Object.assign({ length: arrays[valueIndex].length }, arrays[valueIndex]), options) + ")";
246+
}
247+
248+
if (type === 'U') {
249+
return 'undefined'
250+
}
251+
252+
if (type === 'I') {
253+
return infinities[valueIndex];
254+
}
255+
256+
if (type === 'B') {
257+
return "BigInt(\"" + bigInts[valueIndex] + "\")";
258+
}
259+
260+
if (type === 'L') {
261+
return "new URL(\"" + urls[valueIndex].toString() + "\")";
262+
}
263+
264+
var fn = functions[valueIndex];
265+
266+
return serializeFunc(fn);
267+
});
268+
}
269+
270+
export {
271+
serialize
272+
}

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function deleteFunctions(obj){
5555
}
5656
}
5757

58-
module.exports = function serialize(obj, options) {
58+
exports.serialize = function serialize(obj, options) {
5959
options || (options = {});
6060

6161
// Backwards-compatibility for `space` as the second argument.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
2-
"name": "serialize-javascript",
2+
"name": "ssr-serialize-javascript",
33
"version": "6.0.0",
44
"description": "Serialize JavaScript to a superset of JSON that includes regular expressions and functions.",
55
"main": "index.js",
6+
"module": "esm.js",
67
"scripts": {
78
"benchmark": "node -v && node test/benchmark/serialize.js",
89
"test": "nyc --reporter=lcov mocha test/unit"

0 commit comments

Comments
 (0)