Skip to content

Commit

Permalink
Wrap a try-catch around the replacer, so that if a cross-origin error…
Browse files Browse the repository at this point in the history
… occurs, we catch it and can proceed

storybookjs/storybook#7215
  • Loading branch information
ndelangen committed Nov 25, 2019
1 parent 81b7ed0 commit 0cbd656
Showing 1 changed file with 100 additions and 96 deletions.
196 changes: 100 additions & 96 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,125 +103,129 @@ export const replacer = function replacer(options: Options) {
let keys: string[];

return function replace(this: any, key: string, value: any) {
// very first iteration
if (key === '') {
keys = ['root'];
objects = [{ keys: 'root', value }];
stack = [];
return value;
}

// From the JSON.stringify's doc:
// "The object in which the key was found is provided as the replacer's this parameter." thus one can control the depth
while (stack.length && this !== stack[0]) {
stack.shift();
keys.pop();
}

if (isRegExp(value)) {
if (!options.allowRegExp) {
return undefined;
try {
// very first iteration
if (key === '') {
keys = ['root'];
objects = [{ keys: 'root', value }];
stack = [];
return value;
}
return `_regexp_${value.flags}|${value.source}`;
}

if (isFunction(value)) {
if (!options.allowFunction) {
return undefined;
}
const { name } = value;
const stringified = value.toString();

if (
!stringified.match(
/(\[native code\]|WEBPACK_IMPORTED_MODULE|__webpack_exports__|__webpack_require__)/
)
) {
return `_function_${name}|${cleanCode(convertShorthandMethods(key, stringified))}`;
// From the JSON.stringify's doc:
// "The object in which the key was found is provided as the replacer's this parameter." thus one can control the depth
while (stack.length && this !== stack[0]) {
stack.shift();
keys.pop();
}
return `_function_${name}|${(() => {}).toString()}`;
}

if (isSymbol(value)) {
if (!options.allowSymbol) {
return undefined;
if (isRegExp(value)) {
if (!options.allowRegExp) {
return undefined;
}
return `_regexp_${value.flags}|${value.source}`;
}
return `_symbol_${value.toString().slice(7, -1)}`;
}

if (typeof value === 'string' && dateFormat.test(value)) {
if (!options.allowDate) {
return undefined;
}
return `_date_${value}`;
}
if (isFunction(value)) {
if (!options.allowFunction) {
return undefined;
}
const { name } = value;
const stringified = value.toString();

if (value === undefined) {
if (!options.allowUndefined) {
return undefined;
if (
!stringified.match(
/(\[native code\]|WEBPACK_IMPORTED_MODULE|__webpack_exports__|__webpack_require__)/
)
) {
return `_function_${name}|${cleanCode(convertShorthandMethods(key, stringified))}`;
}
return `_function_${name}|${(() => {}).toString()}`;
}
return '_undefined_';
}

if (typeof value === 'number') {
if (value === -Infinity) {
return '_-Infinity_';
if (isSymbol(value)) {
if (!options.allowSymbol) {
return undefined;
}
return `_symbol_${value.toString().slice(7, -1)}`;
}
if (value === Infinity) {
return '_Infinity_';

if (typeof value === 'string' && dateFormat.test(value)) {
if (!options.allowDate) {
return undefined;
}
return `_date_${value}`;
}
if (Number.isNaN(value)) {
return '_NaN_';

if (value === undefined) {
if (!options.allowUndefined) {
return undefined;
}
return '_undefined_';
}

return value;
}
if (typeof value === 'number') {
if (value === -Infinity) {
return '_-Infinity_';
}
if (value === Infinity) {
return '_Infinity_';
}
if (Number.isNaN(value)) {
return '_NaN_';
}

if (typeof value === 'string') {
return value;
}
return value;
}

if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'string') {
return value;
}

if (stack.length >= options.maxDepth) {
if (Array.isArray(value)) {
return `[Array(${value.length})]`;
if (typeof value === 'boolean') {
return value;
}
return '[Object]';
}

const found = objects.find(o => o.value === value);
if (!found) {
if (
value &&
isObject(value) &&
value.constructor &&
value.constructor.name &&
value.constructor.name !== 'Object'
) {
if (!options.allowClass) {
return undefined;
if (stack.length >= options.maxDepth) {
if (Array.isArray(value)) {
return `[Array(${value.length})]`;
}
return '[Object]';
}

try {
Object.assign(value, { '_constructor-name_': value.constructor.name });
} catch (e) {
// immutable objects can't be written to and throw
// we could make a deep copy but if the user values the correct instance name,
// the user should make the deep copy themselves.
const found = objects.find(o => o.value === value);
if (!found) {
if (
value &&
isObject(value) &&
value.constructor &&
value.constructor.name &&
value.constructor.name !== 'Object'
) {
if (!options.allowClass) {
return undefined;
}

try {
Object.assign(value, { '_constructor-name_': value.constructor.name });
} catch (e) {
// immutable objects can't be written to and throw
// we could make a deep copy but if the user values the correct instance name,
// the user should make the deep copy themselves.
}
}

keys.push(key);
stack.unshift(value);
objects.push({ keys: keys.join('.'), value });
return value;
}

keys.push(key);
stack.unshift(value);
objects.push({ keys: keys.join('.'), value });
return value;
// actually, here's the only place where the keys keeping is useful
return `_duplicate_${found.keys}`;
} catch (e) {
return undefined;
}

// actually, here's the only place where the keys keeping is useful
return `_duplicate_${found.keys}`;
};
};

Expand Down Expand Up @@ -372,7 +376,7 @@ const mutator = () => {
};

export const parse = (data: string, options: Partial<Options> = {}) => {
const mergedOptions: Options = Object.assign({}, defaultOptions, options);
const mergedOptions: Options = { ...defaultOptions, ...options};
const result = JSON.parse(data, reviver(mergedOptions));

mutator()(result);
Expand Down

0 comments on commit 0cbd656

Please sign in to comment.