From 7fa725be03a21c3aa053d1619212adc8a35c9bea Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 17 Apr 2018 16:33:37 +0200 Subject: [PATCH 1/7] dataToNamedExports helper --- README.md | 19 +++++++++++++++++++ package.json | 3 ++- rollup.config.js | 2 +- src/dataToNamedExports.js | 30 ++++++++++++++++++++++++++++++ src/index.js | 1 + test/test.js | 17 +++++++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/dataToNamedExports.js diff --git a/README.md b/README.md index 1a6126d..2c26e04 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,25 @@ makeLegalIdentifier( 'foo-bar' ); // 'foo_bar' makeLegalIdentifier( 'typeof' ); // '_typeof' ``` +### dataToNamedExports + +Helper for treeshakable data imports + +```js +import { dataToNamedExports } from 'rollup-pluginutils'; + +const esModuleSource = dataToNamedExports({ + custom: 'data', + to: ['treeshake'] +}); +/* +Outputs the string ES module source: + export const custom = 'data'; + export const to = ['treeshake']; + export default { custom, to }; +*/ +``` + ## License diff --git a/package.json b/package.json index edc5b09..75967fa 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ }, "dependencies": { "estree-walker": "^0.3.0", - "micromatch": "^2.3.11" + "micromatch": "^2.3.11", + "tosource": "^1.0.0" }, "repository": "rollup/rollup-pluginutils", "keywords": [ diff --git a/rollup.config.js b/rollup.config.js index a2685c7..be95a67 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,7 +4,7 @@ import buble from 'rollup-plugin-buble'; export default { entry: 'src/index.js', plugins: [ buble() ], - external: [ 'path', 'estree-walker', 'micromatch' ], + external: [ 'path', 'estree-walker', 'micromatch', 'tosource' ], targets: [ { diff --git a/src/dataToNamedExports.js b/src/dataToNamedExports.js new file mode 100644 index 0000000..380fc5a --- /dev/null +++ b/src/dataToNamedExports.js @@ -0,0 +1,30 @@ +import makeLegalIdentifier from './makeLegalIdentifier'; +import tosource from 'tosource'; + +// convert data object into separate named exports (and default) +export default function dataToNamedExports ( obj ) { + let output = ''; + let defaultExports = '\n'; + const usedLegalKeys = Object.create( null ); + let index = 0; + const keys = Object.keys( obj ); + for ( let i = 0; i < keys.length; i++ ) { + const key = keys[i]; + const legalKey = makeLegalIdentifier( key ); + output += `export const ${ legalKey } = ${tosource( obj[key] )};\n`; + while ( usedLegalKeys[legalKey + (index || '')] ) + index++; + index = 0; + usedLegalKeys[legalKey + ( index || '' )] = true; + if ( defaultExports.length > 1 ) + defaultExports += ',\n'; + if ( key === legalKey ) + defaultExports += ` ${ key }`; + else + defaultExports += ` ${ `'${ key }'` }: ${ legalKey }`; + } + if ( defaultExports.length > 3 ) + defaultExports += '\n'; + output += `export default {${ defaultExports }};`; + return output; +}; \ No newline at end of file diff --git a/src/index.js b/src/index.js index fdce1bb..3fa03b5 100644 --- a/src/index.js +++ b/src/index.js @@ -2,3 +2,4 @@ export { default as addExtension } from './addExtension'; export { default as attachScopes } from './attachScopes'; export { default as createFilter } from './createFilter'; export { default as makeLegalIdentifier } from './makeLegalIdentifier'; +export { default as dataToNamedExports } from './dataToNamedExports'; \ No newline at end of file diff --git a/test/test.js b/test/test.js index bebb113..30308b6 100644 --- a/test/test.js +++ b/test/test.js @@ -442,4 +442,21 @@ describe( 'rollup-pluginutils', function () { assert.equal( makeLegalIdentifier( 'arguments' ), '_arguments' ); }); }); + + describe( 'dataToNamedExports', function () { + var dataToNamedExports = utils.dataToNamedExports; + + it( 'outputs treeshakable data', function () { + assert.equal( dataToNamedExports({ some: 'data', another: 'data' }), `export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};` ); + }); + + it( 'handles illegal identifiers', function () { + assert.equal( dataToNamedExports({ '1': 'data', 'default': 'data' }), `export const _1 = "data";\nexport const _default = "data";\nexport default {\n '1': _1,\n 'default': _default\n};` ); + }); + + it( 'supports non-JSON data', function () { + const date = new Date(); + assert.equal( dataToNamedExports({ inf: Infinity, date }), `export const inf = Infinity;\nexport const date = new Date(${date.getTime()});\nexport default {\n inf,\n date\n};` ); + }); + }); }); From 8984870c523e48176827193b3cdad671a31ef912 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 17 Apr 2018 16:37:39 +0200 Subject: [PATCH 2/7] Node 0.12 support --- test/test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test.js b/test/test.js index 30308b6..f6f9c75 100644 --- a/test/test.js +++ b/test/test.js @@ -447,16 +447,16 @@ describe( 'rollup-pluginutils', function () { var dataToNamedExports = utils.dataToNamedExports; it( 'outputs treeshakable data', function () { - assert.equal( dataToNamedExports({ some: 'data', another: 'data' }), `export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};` ); + assert.equal( dataToNamedExports({ some: 'data', another: 'data' }), 'export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};' ); }); it( 'handles illegal identifiers', function () { - assert.equal( dataToNamedExports({ '1': 'data', 'default': 'data' }), `export const _1 = "data";\nexport const _default = "data";\nexport default {\n '1': _1,\n 'default': _default\n};` ); + assert.equal( dataToNamedExports({ '1': 'data', 'default': 'data' }), 'export const _1 = "data";\nexport const _default = "data";\nexport default {\n \'1\': _1,\n \'default\': _default\n};' ); }); it( 'supports non-JSON data', function () { const date = new Date(); - assert.equal( dataToNamedExports({ inf: Infinity, date }), `export const inf = Infinity;\nexport const date = new Date(${date.getTime()});\nexport default {\n inf,\n date\n};` ); + assert.equal( dataToNamedExports({ inf: Infinity, date: date }), 'export const inf = Infinity;\nexport const date = new Date(' + date.getTime() + ');\nexport default {\n inf,\n date\n};' ); }); }); }); From b669268a239441481145b8bae628543b3d0b4726 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 22 Apr 2018 11:40:08 +0200 Subject: [PATCH 3/7] dataToNamedExports -> dataToEsm --- README.md | 6 +++--- src/{dataToNamedExports.js => dataToEsm.js} | 0 src/index.js | 2 +- test/test.js | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/{dataToNamedExports.js => dataToEsm.js} (100%) diff --git a/README.md b/README.md index 2c26e04..fa32899 100644 --- a/README.md +++ b/README.md @@ -103,14 +103,14 @@ makeLegalIdentifier( 'foo-bar' ); // 'foo_bar' makeLegalIdentifier( 'typeof' ); // '_typeof' ``` -### dataToNamedExports +### dataToEsm Helper for treeshakable data imports ```js -import { dataToNamedExports } from 'rollup-pluginutils'; +import { dataToEsm } from 'rollup-pluginutils'; -const esModuleSource = dataToNamedExports({ +const esModuleSource = dataToEsm({ custom: 'data', to: ['treeshake'] }); diff --git a/src/dataToNamedExports.js b/src/dataToEsm.js similarity index 100% rename from src/dataToNamedExports.js rename to src/dataToEsm.js diff --git a/src/index.js b/src/index.js index 3fa03b5..b783da9 100644 --- a/src/index.js +++ b/src/index.js @@ -2,4 +2,4 @@ export { default as addExtension } from './addExtension'; export { default as attachScopes } from './attachScopes'; export { default as createFilter } from './createFilter'; export { default as makeLegalIdentifier } from './makeLegalIdentifier'; -export { default as dataToNamedExports } from './dataToNamedExports'; \ No newline at end of file +export { default as dataToEsm } from './dataToEsm'; \ No newline at end of file diff --git a/test/test.js b/test/test.js index f6f9c75..499aaba 100644 --- a/test/test.js +++ b/test/test.js @@ -443,20 +443,20 @@ describe( 'rollup-pluginutils', function () { }); }); - describe( 'dataToNamedExports', function () { - var dataToNamedExports = utils.dataToNamedExports; + describe( 'dataToEsm', function () { + var dataToEsm = utils.dataToEsm; it( 'outputs treeshakable data', function () { - assert.equal( dataToNamedExports({ some: 'data', another: 'data' }), 'export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};' ); + assert.equal( dataToEsm({ some: 'data', another: 'data' }), 'export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};' ); }); it( 'handles illegal identifiers', function () { - assert.equal( dataToNamedExports({ '1': 'data', 'default': 'data' }), 'export const _1 = "data";\nexport const _default = "data";\nexport default {\n \'1\': _1,\n \'default\': _default\n};' ); + assert.equal( dataToEsm({ '1': 'data', 'default': 'data' }), 'export const _1 = "data";\nexport const _default = "data";\nexport default {\n \'1\': _1,\n \'default\': _default\n};' ); }); it( 'supports non-JSON data', function () { const date = new Date(); - assert.equal( dataToNamedExports({ inf: Infinity, date: date }), 'export const inf = Infinity;\nexport const date = new Date(' + date.getTime() + ');\nexport default {\n inf,\n date\n};' ); + assert.equal( dataToEsm({ inf: Infinity, date: date }), 'export const inf = Infinity;\nexport const date = new Date(' + date.getTime() + ');\nexport default {\n inf,\n date\n};' ); }); }); }); From f0b801051abafb4e306c13244559e624faa203d5 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 22 Apr 2018 23:29:21 +0200 Subject: [PATCH 4/7] dataToEsm compact option --- src/dataToEsm.js | 19 +++++++++++-------- test/test.js | 11 ++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/dataToEsm.js b/src/dataToEsm.js index 380fc5a..0ec003b 100644 --- a/src/dataToEsm.js +++ b/src/dataToEsm.js @@ -2,29 +2,32 @@ import makeLegalIdentifier from './makeLegalIdentifier'; import tosource from 'tosource'; // convert data object into separate named exports (and default) -export default function dataToNamedExports ( obj ) { +export default function dataToNamedExports ( obj, compact ) { + const _ = compact ? '' : ' '; + const nl = compact ? '' : '\n'; let output = ''; - let defaultExports = '\n'; + let defaultExports = compact ? '' : nl; const usedLegalKeys = Object.create( null ); let index = 0; const keys = Object.keys( obj ); for ( let i = 0; i < keys.length; i++ ) { const key = keys[i]; const legalKey = makeLegalIdentifier( key ); - output += `export const ${ legalKey } = ${tosource( obj[key] )};\n`; + let serialized = tosource( obj[key], null, compact ? false : ' ' ); + output += `export const ${ legalKey }${ _ }=${ _ }${ serialized };${ nl }`; while ( usedLegalKeys[legalKey + (index || '')] ) index++; index = 0; usedLegalKeys[legalKey + ( index || '' )] = true; if ( defaultExports.length > 1 ) - defaultExports += ',\n'; + defaultExports += `,${ nl }`; if ( key === legalKey ) - defaultExports += ` ${ key }`; + defaultExports += `${ _ }${ _ }${ key }`; else - defaultExports += ` ${ `'${ key }'` }: ${ legalKey }`; + defaultExports += `${ _ }${ _ }${ `'${ key }'` }:${ _ }${ legalKey }`; } if ( defaultExports.length > 3 ) - defaultExports += '\n'; - output += `export default {${ defaultExports }};`; + defaultExports += nl; + output += `export default${_}{${ defaultExports }};`; return output; }; \ No newline at end of file diff --git a/test/test.js b/test/test.js index 499aaba..be6c5eb 100644 --- a/test/test.js +++ b/test/test.js @@ -447,16 +447,21 @@ describe( 'rollup-pluginutils', function () { var dataToEsm = utils.dataToEsm; it( 'outputs treeshakable data', function () { - assert.equal( dataToEsm({ some: 'data', another: 'data' }), 'export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};' ); + assert.equal( dataToEsm( { some: 'data', another: 'data' } ), 'export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};' ); }); it( 'handles illegal identifiers', function () { - assert.equal( dataToEsm({ '1': 'data', 'default': 'data' }), 'export const _1 = "data";\nexport const _default = "data";\nexport default {\n \'1\': _1,\n \'default\': _default\n};' ); + assert.equal( dataToEsm( { '1': 'data', 'default': 'data' } ), 'export const _1 = "data";\nexport const _default = "data";\nexport default {\n \'1\': _1,\n \'default\': _default\n};' ); }); it( 'supports non-JSON data', function () { const date = new Date(); - assert.equal( dataToEsm({ inf: Infinity, date: date }), 'export const inf = Infinity;\nexport const date = new Date(' + date.getTime() + ');\nexport default {\n inf,\n date\n};' ); + assert.equal( dataToEsm( { inf: Infinity, date: date } ), 'export const inf = Infinity;\nexport const date = new Date(' + date.getTime() + ');\nexport default {\n inf,\n date\n};' ); + }); + + it( 'supports a compact argument', function () { + assert.equal( dataToEsm( { some: 'data', another: 'data' }, true ), 'export const some="data";export const another="data";export default{some,another};' ); + assert.equal( dataToEsm( { some: { deep: { object: 'definition', here: 'here' } }, another: 'data' }, true ), 'export const some={deep:{object:"definition",here:"here"}};export const another="data";export default{some,another};' ); }); }); }); From fdd287d98dac766ee1310b3798ceb11dcb97ed6c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 6 May 2018 18:49:38 +0200 Subject: [PATCH 5/7] update dataToEsm implementation, provide more output options --- README.md | 5 +++++ src/dataToEsm.js | 48 ++++++++++++++++++++---------------------------- test/test.js | 12 ++++++------ 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index fa32899..9a3f9bc 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,11 @@ import { dataToEsm } from 'rollup-pluginutils'; const esModuleSource = dataToEsm({ custom: 'data', to: ['treeshake'] +}, options = { + compact: false, + indent: '\t', + preferConst: false, + objectShorthand: false }); /* Outputs the string ES module source: diff --git a/src/dataToEsm.js b/src/dataToEsm.js index 0ec003b..4707765 100644 --- a/src/dataToEsm.js +++ b/src/dataToEsm.js @@ -2,32 +2,24 @@ import makeLegalIdentifier from './makeLegalIdentifier'; import tosource from 'tosource'; // convert data object into separate named exports (and default) -export default function dataToNamedExports ( obj, compact ) { - const _ = compact ? '' : ' '; - const nl = compact ? '' : '\n'; - let output = ''; - let defaultExports = compact ? '' : nl; - const usedLegalKeys = Object.create( null ); - let index = 0; - const keys = Object.keys( obj ); - for ( let i = 0; i < keys.length; i++ ) { - const key = keys[i]; - const legalKey = makeLegalIdentifier( key ); - let serialized = tosource( obj[key], null, compact ? false : ' ' ); - output += `export const ${ legalKey }${ _ }=${ _ }${ serialized };${ nl }`; - while ( usedLegalKeys[legalKey + (index || '')] ) - index++; - index = 0; - usedLegalKeys[legalKey + ( index || '' )] = true; - if ( defaultExports.length > 1 ) - defaultExports += `,${ nl }`; - if ( key === legalKey ) - defaultExports += `${ _ }${ _ }${ key }`; - else - defaultExports += `${ _ }${ _ }${ `'${ key }'` }:${ _ }${ legalKey }`; - } - if ( defaultExports.length > 3 ) - defaultExports += nl; - output += `export default${_}{${ defaultExports }};`; - return output; +export default function dataToNamedExports ( data, options = {} ) { + const t = options.compact ? '' : 'indent' in options ? options.indent : '\t'; + const _ = options.compact ? '' : ' '; + const n = options.compact ? '' : '\n'; + const declarationType = options.preferConst ? 'const' : 'var'; + + let namedExportCode = ''; + const defaultExportRows = []; + Object.keys( data ).forEach(key => { + if (key === makeLegalIdentifier( key )) { + if ( options.objectShorthand ) + defaultExportRows.push(key); + else + defaultExportRows.push( `${ key }:${ _ }${ key }` ); + namedExportCode += `export ${declarationType} ${key}${ _ }=${ _ }${ tosource( data[key], null, options.compact ? false : t ) };${ n }`; + } else { + defaultExportRows.push( `${ JSON.stringify(key) }: ${ tosource( data[key], null, options.compact ? false : t )}` ); + } + }); + return namedExportCode + `export default${ _ }{${ n }${ t }${ defaultExportRows.join(`,${ n }${ t }`) }${ n }};${ n }`; }; \ No newline at end of file diff --git a/test/test.js b/test/test.js index be6c5eb..cde2e44 100644 --- a/test/test.js +++ b/test/test.js @@ -447,21 +447,21 @@ describe( 'rollup-pluginutils', function () { var dataToEsm = utils.dataToEsm; it( 'outputs treeshakable data', function () { - assert.equal( dataToEsm( { some: 'data', another: 'data' } ), 'export const some = "data";\nexport const another = "data";\nexport default {\n some,\n another\n};' ); + assert.equal( dataToEsm( { some: 'data', another: 'data' } ), 'export var some = "data";\nexport var another = "data";\nexport default {\n\tsome: some,\n\tanother: another\n};\n' ); }); - it( 'handles illegal identifiers', function () { - assert.equal( dataToEsm( { '1': 'data', 'default': 'data' } ), 'export const _1 = "data";\nexport const _default = "data";\nexport default {\n \'1\': _1,\n \'default\': _default\n};' ); + it( 'handles illegal identifiers, object shorthand, preferConst', function () { + assert.equal( dataToEsm( { '1': 'data', 'default': 'data' }, { objectShorthand: true, preferConst: true } ), 'export default {\n\t"1": "data",\n\t"default": "data"\n};\n' ); }); it( 'supports non-JSON data', function () { const date = new Date(); - assert.equal( dataToEsm( { inf: Infinity, date: date } ), 'export const inf = Infinity;\nexport const date = new Date(' + date.getTime() + ');\nexport default {\n inf,\n date\n};' ); + assert.equal( dataToEsm( { inf: Infinity, date: date } ), 'export var inf = Infinity;\nexport var date = new Date(' + date.getTime() + ');\nexport default {\n\tinf: inf,\n\tdate: date\n};\n' ); }); it( 'supports a compact argument', function () { - assert.equal( dataToEsm( { some: 'data', another: 'data' }, true ), 'export const some="data";export const another="data";export default{some,another};' ); - assert.equal( dataToEsm( { some: { deep: { object: 'definition', here: 'here' } }, another: 'data' }, true ), 'export const some={deep:{object:"definition",here:"here"}};export const another="data";export default{some,another};' ); + assert.equal( dataToEsm( { some: 'data', another: 'data' }, { compact: true, objectShorthand: true } ), 'export var some="data";export var another="data";export default{some,another};' ); + assert.equal( dataToEsm( { some: { deep: { object: 'definition', here: 'here' } }, another: 'data' }, { compact: true, objectShorthand: true } ), 'export var some={deep:{object:"definition",here:"here"}};export var another="data";export default{some,another};' ); }); }); }); From 3dedacdc8b434f605733c992b735b0e904c8e4c5 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 6 May 2018 21:40:21 +0200 Subject: [PATCH 6/7] add compact:true objectShorthand:false test --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index cde2e44..ce6f93a 100644 --- a/test/test.js +++ b/test/test.js @@ -461,7 +461,7 @@ describe( 'rollup-pluginutils', function () { it( 'supports a compact argument', function () { assert.equal( dataToEsm( { some: 'data', another: 'data' }, { compact: true, objectShorthand: true } ), 'export var some="data";export var another="data";export default{some,another};' ); - assert.equal( dataToEsm( { some: { deep: { object: 'definition', here: 'here' } }, another: 'data' }, { compact: true, objectShorthand: true } ), 'export var some={deep:{object:"definition",here:"here"}};export var another="data";export default{some,another};' ); + assert.equal( dataToEsm( { some: { deep: { object: 'definition', here: 'here' } }, another: 'data' }, { compact: true, objectShorthand: false } ), 'export var some={deep:{object:"definition",here:"here"}};export var another="data";export default{some:some,another:another};' ); }); }); }); From 5f409641c6512e95c5d1328f92074d6507476afd Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 6 May 2018 21:49:15 +0200 Subject: [PATCH 7/7] use for loop --- src/dataToEsm.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dataToEsm.js b/src/dataToEsm.js index 4707765..5ac1d25 100644 --- a/src/dataToEsm.js +++ b/src/dataToEsm.js @@ -10,7 +10,9 @@ export default function dataToNamedExports ( data, options = {} ) { let namedExportCode = ''; const defaultExportRows = []; - Object.keys( data ).forEach(key => { + const dataKeys = Object.keys( data ); + for (let i = 0; i < dataKeys.length; i++) { + const key = dataKeys[i]; if (key === makeLegalIdentifier( key )) { if ( options.objectShorthand ) defaultExportRows.push(key); @@ -20,6 +22,6 @@ export default function dataToNamedExports ( data, options = {} ) { } else { defaultExportRows.push( `${ JSON.stringify(key) }: ${ tosource( data[key], null, options.compact ? false : t )}` ); } - }); + } return namedExportCode + `export default${ _ }{${ n }${ t }${ defaultExportRows.join(`,${ n }${ t }`) }${ n }};${ n }`; }; \ No newline at end of file