From 7dc426e30f38a91daae31f1e9513d07337f74068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E9=81=93?= Date: Tue, 28 May 2019 15:21:14 +0800 Subject: [PATCH 1/5] add support for edge cases * U+2028 U+2029 * -Infinity -0 * export default Date/RegExp * export default + space (even when compact) + value (startsWith letter/number) --- src/dataToEsm.ts | 23 +++++++++++++++++------ src/makeLegalIdentifier.ts | 2 +- test/dataToEsm.test.ts | 16 +++++++++++----- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/dataToEsm.ts b/src/dataToEsm.ts index 710f173..d8be3e7 100644 --- a/src/dataToEsm.ts +++ b/src/dataToEsm.ts @@ -3,6 +3,10 @@ import { DataToEsm } from './pluginutils'; export type Indent = string | null | undefined; +function stringify (obj :any) :string { + return (JSON.stringify(obj) || 'undefined').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029'); +} + function serializeArray(arr: Array, indent: Indent, baseIndent: string): string { let output = '['; const separator = indent ? '\n' + baseIndent + indent : ''; @@ -19,7 +23,7 @@ function serializeObject(obj: { [key: string]: T }, indent: Indent, baseInden const keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { const key = keys[i]; - const stringKey = makeLegalIdentifier(key) === key ? key : JSON.stringify(key); + const stringKey = makeLegalIdentifier(key) === key ? key : stringify(key); output += `${i > 0 ? ',' : ''}${separator}${stringKey}:${indent ? ' ' : ''}${serialize( obj[key], indent, @@ -31,13 +35,15 @@ function serializeObject(obj: { [key: string]: T }, indent: Indent, baseInden function serialize(obj: any, indent: Indent, baseIndent: string): string { if (obj === Infinity) return 'Infinity'; + if (obj === -Infinity) return '-Infinity'; + if (obj === 0 && 1/obj === -Infinity) return '-0'; if (obj instanceof Date) return 'new Date(' + obj.getTime() + ')'; if (obj instanceof RegExp) return obj.toString(); - if (typeof obj === 'number' && isNaN(obj)) return 'NaN'; + if (obj !== obj) return 'NaN'; if (Array.isArray(obj)) return serializeArray(obj, indent, baseIndent); if (obj === null) return 'null'; if (typeof obj === 'object') return serializeObject(obj, indent, baseIndent); - return JSON.stringify(obj); + return stringify(obj); } const dataToEsm: DataToEsm = function dataToEsm(data, options = {}) { @@ -50,9 +56,14 @@ const dataToEsm: DataToEsm = function dataToEsm(data, options = {}) { options.namedExports === false || typeof data !== 'object' || Array.isArray(data) || + data instanceof Date || + data instanceof RegExp || data === null - ) - return `export default${_}${serialize(data, options.compact ? null : t, '')};`; + ) { + const code = serialize(data, options.compact ? null : t, ''); + const __ = _ || (/^[{[\-\/]/.test(code) ? '' : ' '); + return `export default${__}${code};`; + } let namedExportCode = ''; const defaultExportRows = []; @@ -69,7 +80,7 @@ const dataToEsm: DataToEsm = function dataToEsm(data, options = {}) { )};${n}`; } else { defaultExportRows.push( - `${JSON.stringify(key)}: ${serialize(data[key], options.compact ? null : t, '')}` + `${stringify(key)}:${_}${serialize(data[key], options.compact ? null : t, '')}` ); } } diff --git a/src/makeLegalIdentifier.ts b/src/makeLegalIdentifier.ts index f2d8334..04df298 100644 --- a/src/makeLegalIdentifier.ts +++ b/src/makeLegalIdentifier.ts @@ -15,7 +15,7 @@ export const makeLegalIdentifier: MakeLegalIdentifier = function makeLegalIdenti str = `_${str}`; } - return str; + return str || '_'; }; export { makeLegalIdentifier as default }; diff --git a/test/dataToEsm.test.ts b/test/dataToEsm.test.ts index 1bd56fa..ed56574 100644 --- a/test/dataToEsm.test.ts +++ b/test/dataToEsm.test.ts @@ -15,8 +15,8 @@ describe('dataToEsm', function() { it('supports non-JSON data', function() { const date = new Date(); - expect(dataToEsm({ inf: Infinity, date, number: NaN, regexp: /.*/ })).toEqual( - 'export var inf = Infinity;\nexport var date = new Date(' + + expect(dataToEsm({ inf: -Infinity, date, number: NaN, regexp: /.*/ })).toEqual( + 'export var inf = -Infinity;\nexport var date = new Date(' + date.getTime() + ');\nexport var number = NaN;\nexport var regexp = /.*/;\nexport default {\n\tinf: inf,\n\tdate: date,\n\tnumber: number,\n\tregexp: regexp\n};\n' ); @@ -28,11 +28,11 @@ describe('dataToEsm', function() { ).toEqual('export var some="data";export var another="data";export default{some,another};'); expect( dataToEsm( - { some: { deep: { object: 'definition', here: 'here' } }, another: 'data' }, + { some: { deep: { object: 'definition', here: 'here' } }, else: { deep: { object: 'definition', here: 'here' } } }, { compact: true, objectShorthand: false } ) ).toEqual( - 'export var some={deep:{object:"definition",here:"here"}};export var another="data";export default{some:some,another:another};' + 'export var some={deep:{object:"definition",here:"here"}};export default{some:some,else:{deep:{object:"definition",here:"here"}}};' ); }); @@ -67,7 +67,7 @@ describe('dataToEsm', function() { }); it('exports default only for null', function() { - expect(dataToEsm(null)).toEqual('export default null;'); + expect(dataToEsm(null, { compact: true })).toEqual('export default null;'); }); it('exports default only for primitive values', function() { @@ -79,4 +79,10 @@ describe('dataToEsm', function() { 'export var a = "x";\nexport default {\n\ta: a,\n' + '\t"": "y"\n};\n' ); }); + + it('avoid U+2029 U+2029 -0 be ignored by JSON.stringify', function() { + expect(dataToEsm([-0, '\u2028\u2029'], { compact: true })).toEqual( + 'export default[-0,"\\u2028\\u2029"];' + ); + }); }); From 9284d1bbe9141b8c5ac33148abfcfd71bd2d1e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E9=81=93?= Date: Tue, 28 May 2019 15:31:15 +0800 Subject: [PATCH 2/5] fix new bug --- test/dataToEsm.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dataToEsm.test.ts b/test/dataToEsm.test.ts index ed56574..a7fc745 100644 --- a/test/dataToEsm.test.ts +++ b/test/dataToEsm.test.ts @@ -32,7 +32,7 @@ describe('dataToEsm', function() { { compact: true, objectShorthand: false } ) ).toEqual( - 'export var some={deep:{object:"definition",here:"here"}};export default{some:some,else:{deep:{object:"definition",here:"here"}}};' + 'export var some={deep:{object:"definition",here:"here"}};export default{some:some,"else":{deep:{object:"definition",here:"here"}}};' ); }); From 42c7af3ec06ffccb5a0eaf29caf807184920cdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E9=81=93?= Date: Tue, 28 May 2019 15:32:39 +0800 Subject: [PATCH 3/5] add iternal test for makeLegalIdentifier --- test/makeLegalIdentifier.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/makeLegalIdentifier.test.ts b/test/makeLegalIdentifier.test.ts index b8002b9..be741cb 100644 --- a/test/makeLegalIdentifier.test.ts +++ b/test/makeLegalIdentifier.test.ts @@ -12,4 +12,8 @@ describe('makeLegalIdentifier', function() { it('blacklists arguments (https://github.com/rollup/rollup/issues/871)', function() { expect(makeLegalIdentifier('arguments')).toEqual('_arguments'); }); + + it('empty', function() { + expect(makeLegalIdentifier('')).toEqual('_'); + }); }); From 5e227ecd2b5179e5e8c35f3d3863ea4ada3246dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E9=81=93?= Date: Sat, 1 Jun 2019 23:52:38 +0800 Subject: [PATCH 4/5] add test for voiding JSON.stringify return non-string --- src/dataToEsm.ts | 4 ++-- test/dataToEsm.test.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dataToEsm.ts b/src/dataToEsm.ts index d8be3e7..f8b6427 100644 --- a/src/dataToEsm.ts +++ b/src/dataToEsm.ts @@ -3,8 +3,8 @@ import { DataToEsm } from './pluginutils'; export type Indent = string | null | undefined; -function stringify (obj :any) :string { - return (JSON.stringify(obj) || 'undefined').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029'); +function stringify(obj: any): string { + return (JSON.stringify(obj) || 'undefined').replace(/[\u2028\u2029]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`); } function serializeArray(arr: Array, indent: Indent, baseIndent: string): string { diff --git a/test/dataToEsm.test.ts b/test/dataToEsm.test.ts index a7fc745..ff74cbf 100644 --- a/test/dataToEsm.test.ts +++ b/test/dataToEsm.test.ts @@ -80,9 +80,9 @@ describe('dataToEsm', function() { ); }); - it('avoid U+2029 U+2029 -0 be ignored by JSON.stringify', function() { - expect(dataToEsm([-0, '\u2028\u2029'], { compact: true })).toEqual( - 'export default[-0,"\\u2028\\u2029"];' + it('avoid U+2029 U+2029 -0 be ignored by JSON.stringify, and avoid it return non-string (undefined) before replacing', function() { + expect(dataToEsm([-0, '\u2028\u2029', undefined, function() {}], { compact: true })).toEqual( + 'export default[-0,"\\u2028\\u2029",undefined,undefined];' ); }); }); From 279d399145761ba4b1dd9bf3ecd26e95004f1cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E8=85=BE=E9=81=93?= Date: Sun, 2 Jun 2019 00:14:37 +0800 Subject: [PATCH 5/5] change padStart to support Node.js 6 check --- src/dataToEsm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dataToEsm.ts b/src/dataToEsm.ts index f8b6427..01cd4a3 100644 --- a/src/dataToEsm.ts +++ b/src/dataToEsm.ts @@ -4,7 +4,7 @@ import { DataToEsm } from './pluginutils'; export type Indent = string | null | undefined; function stringify(obj: any): string { - return (JSON.stringify(obj) || 'undefined').replace(/[\u2028\u2029]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`); + return (JSON.stringify(obj) || 'undefined').replace(/[\u2028\u2029]/g, char => `\\u${('000' + char.charCodeAt(0).toString(16)).slice(-4)}`); } function serializeArray(arr: Array, indent: Indent, baseIndent: string): string {