diff --git a/src/index-rollup-legacy.js b/src/index-rollup-legacy.js new file mode 100644 index 00000000..13db4371 --- /dev/null +++ b/src/index-rollup-legacy.js @@ -0,0 +1,99 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 Mickael Jeanroy + * + * 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. + */ + +'use strict'; + +const _ = require('lodash'); +const LicensePlugin = require('./license-plugin.js'); + +module.exports = (options = {}) => { + const plugin = new LicensePlugin(options); + + return { + /** + * Name of the plugin, used automatically by rollup. + * @type {string} + */ + name: plugin.name, + + /** + * Function called by rollup when a JS file is loaded: it is used to scan + * third-party dependencies. + * + * @param {string} id JS file path. + * @return {void} + */ + load(id) { + plugin.scanDependency(id); + }, + + /** + * Function called by rollup to read global options: if source map parameter + * is truthy, enable it on the plugin. + * + * @param {object} opts Rollup options. + * @return {void} + */ + options(opts) { + if (!opts) { + return; + } + + if (_.has(options, 'sourceMap') || _.has(options, 'sourcemap')) { + // SourceMap has been set on the plugin itself. + return; + } + + // Rollup >= 0.48 replace `sourceMap` with `sourcemap`. + // If `sourcemap` is disabled globally, disable it on the plugin. + if (opts.sourceMap === false || opts.sourcemap === false) { + plugin.disableSourceMap(); + } + }, + + /** + * Function called by rollup when the final bundle is generated: it is used + * to prepend the banner file on the generated bundle. + * + * @param {string} code Bundle content. + * @param {Object} outputOptions The options for this output. + * @return {void} + */ + transformBundle(code, outputOptions = {}) { + const sourcemap = outputOptions.sourcemap !== false || outputOptions.sourceMap !== false; + return plugin.prependBanner(code, sourcemap); + }, + + /** + * Function called by rollup when the final bundle will be written on disk: it + * is used to generate a file containing a summary of all third-party dependencies + * with license information. + * + * @return {void} + */ + ongenerate() { + plugin.exportThirdParties(); + }, + }; +}; diff --git a/src/index-rollup-stable.js b/src/index-rollup-stable.js new file mode 100644 index 00000000..77415cf8 --- /dev/null +++ b/src/index-rollup-stable.js @@ -0,0 +1,74 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 Mickael Jeanroy + * + * 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. + */ + +'use strict'; + +const LicensePlugin = require('./license-plugin.js'); + +module.exports = (options = {}) => { + const plugin = new LicensePlugin(options); + + return { + /** + * Name of the plugin, used automatically by rollup. + * @type {string} + */ + name: plugin.name, + + /** + * Function called by rollup when a JS file is loaded: it is used to scan + * third-party dependencies. + * + * @param {string} id JS file path. + * @return {void} + */ + load(id) { + plugin.scanDependency(id); + }, + + /** + * Function called by rollup when the final bundle is generated: it is used + * to prepend the banner file on the generated bundle. + * + * @param {string} code Bundle content. + * @param {Object} chunk The chunk being generated. + * @param {Object} outputOptions The options for the generated output. + * @return {void} + */ + renderChunk(code, chunk, outputOptions = {}) { + return plugin.prependBanner(code, outputOptions.sourcemap !== false); + }, + + /** + * Function called by rollup when the final bundle will be written on disk: it + * is used to generate a file containing a summary of all third-party dependencies + * with license information. + * + * @return {void} + */ + generateBundle() { + plugin.exportThirdParties(); + }, + }; +}; diff --git a/src/index.js b/src/index.js index dc48bbaa..48d25595 100644 --- a/src/index.js +++ b/src/index.js @@ -24,74 +24,9 @@ 'use strict'; -const _ = require('lodash'); -const LicensePlugin = require('./license-plugin.js'); +const rollup = require('rollup'); +const VERSION = rollup.VERSION; +const MAJOR_VERSION = VERSION ? Number(VERSION.split('.')[0]) : 0; +const IS_ROLLUP_LEGACY = MAJOR_VERSION === 0; -module.exports = (options = {}) => { - const plugin = new LicensePlugin(options); - - return { - /** - * Name of the plugin, used automatically by rollup. - * @type {string} - */ - name: plugin.name, - - /** - * Function called by rollup when a JS file is loaded: it is used to scan - * third-party dependencies. - * - * @param {string} id JS file path. - * @return {void} - */ - load(id) { - plugin.scanDependency(id); - }, - - /** - * Function called by rollup to read global options: if source map parameter - * is truthy, enable it on the plugin. - * - * @param {object} opts Rollup options. - * @return {void} - */ - options(opts) { - if (!opts) { - return; - } - - if (_.has(options, 'sourceMap') || _.has(options, 'sourcemap')) { - // SourceMap has been set on the plugin itself. - return; - } - - // Rollup >= 0.48 replace `sourceMap` with `sourcemap`. - // If `sourcemap` is disabled globally, disable it on the plugin. - if (opts.sourceMap === false || opts.sourcemap === false) { - plugin.disableSourceMap(); - } - }, - - /** - * Function called by rollup when the final bundle is generated: it is used - * to prepend the banner file on the generated bundle. - * - * @param {string} code Bundle content. - * @return {void} - */ - transformBundle(code) { - return plugin.prependBanner(code); - }, - - /** - * Function called by rollup when the final bundle will be written on disk: it - * is used to generate a file containing a summary of all third-party dependencies - * with license information. - * - * @return {void} - */ - ongenerate() { - plugin.exportThirdParties(); - }, - }; -}; +module.exports = IS_ROLLUP_LEGACY ? require('./index-rollup-legacy') : require('./index-rollup-stable'); diff --git a/src/license-plugin.js b/src/license-plugin.js index 456eb174..31944302 100644 --- a/src/license-plugin.js +++ b/src/license-plugin.js @@ -54,7 +54,7 @@ class LicensePlugin { this._debug = options.debug || false; // SourceMap can now be disable/enable on the plugin. - this._sourceMap = options.sourceMap !== false && options.sourcemap !== false; + this._sourcemap = options.sourceMap !== false && options.sourcemap !== false; // This is a cache storing a directory path to associated package. // This is an improvement to avoid looking for package information for @@ -68,7 +68,7 @@ class LicensePlugin { * @return {void} */ disableSourceMap() { - this._sourceMap = false; + this._sourcemap = false; } /** @@ -137,10 +137,11 @@ class LicensePlugin { * This hook is used here to prepend the license banner to the final bundle. * * @param {string} code The bundle content. + * @param {boolean} sourcemap If sourcemap must be generated. * @return {Object} The result containing the code and, optionnally, the source map * if it has been enabled (using `enableSourceMap` method). */ - prependBanner(code) { + prependBanner(code, sourcemap) { // Create a magicString: do not manipulate the string directly since it // will be used to generate the sourcemap. const magicString = new MagicString(code); @@ -192,7 +193,7 @@ class LicensePlugin { code: magicString.toString(), }; - if (this._sourceMap) { + if (this._sourcemap !== false && sourcemap !== false) { result.map = magicString.generateMap({ hires: true, }); diff --git a/test/dependency.spec.js b/test/dependency.spec.js index 013c10a0..9fc8a335 100644 --- a/test/dependency.spec.js +++ b/test/dependency.spec.js @@ -22,6 +22,7 @@ * SOFTWARE. */ +const join = require('./utils/join.js'); const Dependency = require('../dist/dependency.js'); describe('Dependency', () => { @@ -125,12 +126,12 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + ])); }); it('should format dependency with optional description fied', () => { @@ -143,13 +144,13 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false\n` + - `Description: ${pkg.description}` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + `Description: ${pkg.description}`, + ])); }); it('should format dependency with optional author fied', () => { @@ -165,13 +166,13 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false\n` + - `Author: ${pkg.author.name} <${pkg.author.email}>` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + `Author: ${pkg.author.name} <${pkg.author.email}>`, + ])); }); it('should format dependency with optional repository field', () => { @@ -187,13 +188,13 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false\n` + - `Repository: ${pkg.repository.url}` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + `Repository: ${pkg.repository.url}`, + ])); }); it('should format dependency with optional homepage field', () => { @@ -206,13 +207,13 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false\n` + - `Homepage: ${pkg.homepage}` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + `Homepage: ${pkg.homepage}`, + ])); }); it('should format dependency with optional contributors field', () => { @@ -228,15 +229,15 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false\n` + - `Contributors:\n` + - ` ${pkg.contributors[0].name} <${pkg.contributors[0].email}>\n` + - ` ${pkg.contributors[1].name}` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + `Contributors:`, + ` ${pkg.contributors[0].name} <${pkg.contributors[0].email}>`, + ` ${pkg.contributors[1].name}`, + ])); }); it('should format dependency with all optional fields', () => { @@ -256,19 +257,19 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text()).toEqual( - `Name: ${pkg.name}\n` + - `Version: ${pkg.version}\n` + - `License: ${pkg.license}\n` + - `Private: false\n` + - `Description: ${pkg.description}\n` + - `Repository: ${pkg.repository.url}\n` + - `Homepage: ${pkg.homepage}\n` + - `Author: ${pkg.author.name} <${pkg.author.email}>\n` + - `Contributors:\n` + - ` ${pkg.contributors[0].name} <${pkg.contributors[0].email}>\n` + - ` ${pkg.contributors[1].name}` - ); + expect(dependency.text()).toEqual(join([ + `Name: ${pkg.name}`, + `Version: ${pkg.version}`, + `License: ${pkg.license}`, + `Private: false`, + `Description: ${pkg.description}`, + `Repository: ${pkg.repository.url}`, + `Homepage: ${pkg.homepage}`, + `Author: ${pkg.author.name} <${pkg.author.email}>`, + `Contributors:`, + ` ${pkg.contributors[0].name} <${pkg.contributors[0].email}>`, + ` ${pkg.contributors[1].name}`, + ])); }); it('should format dependency with prefix and suffix', () => { @@ -288,18 +289,18 @@ describe('Dependency', () => { const dependency = new Dependency(pkg); - expect(dependency.text(' * ', ' --')).toEqual( - ` * Name: ${pkg.name} --\n` + - ` * Version: ${pkg.version} --\n` + - ` * License: ${pkg.license} --\n` + - ` * Private: false --\n` + - ` * Description: ${pkg.description} --\n` + - ` * Repository: ${pkg.repository.url} --\n` + - ` * Homepage: ${pkg.homepage} --\n` + - ` * Author: ${pkg.author.name} <${pkg.author.email}> --\n` + - ` * Contributors: --\n` + - ` * ${pkg.contributors[0].name} <${pkg.contributors[0].email}> --\n` + - ` * ${pkg.contributors[1].name} --` - ); + expect(dependency.text(' * ', ' --')).toEqual(join([ + ` * Name: ${pkg.name} --`, + ` * Version: ${pkg.version} --`, + ` * License: ${pkg.license} --`, + ` * Private: false --`, + ` * Description: ${pkg.description} --`, + ` * Repository: ${pkg.repository.url} --`, + ` * Homepage: ${pkg.homepage} --`, + ` * Author: ${pkg.author.name} <${pkg.author.email}> --`, + ` * Contributors: --`, + ` * ${pkg.contributors[0].name} <${pkg.contributors[0].email}> --`, + ` * ${pkg.contributors[1].name} --`, + ])); }); }); diff --git a/test/fixtures/fake-package/package.json b/test/fixtures/fake-package/package.json index 6ac5c5d7..9e08f39b 100644 --- a/test/fixtures/fake-package/package.json +++ b/test/fixtures/fake-package/package.json @@ -8,5 +8,8 @@ }, "author": "Mickael Jeanroy ", "license": "MIT", - "private": true + "private": true, + "dependencies": { + "lodash": "*" + } } diff --git a/test/generate-block-comment.spec.js b/test/generate-block-comment.spec.js index 1b02a844..054dd4c9 100644 --- a/test/generate-block-comment.spec.js +++ b/test/generate-block-comment.spec.js @@ -22,23 +22,26 @@ * SOFTWARE. */ +const join = require('./utils/join.js'); const generateBlockComment = require('../dist/generate-block-comment.js'); describe('generateBlockComment', () => { it('should generate block comment from given text', () => { - const text = - `First Line\n` + - `\n` + - `Second Line`; + const text = join([ + 'First Line', + '', + 'Second Line', + ]); const comment = generateBlockComment(text); - expect(comment).toEqual( - `/**\n` + - ` * First Line\n` + - ` *\n` + - ` * Second Line\n` + - ` */\n` - ); + expect(comment).toEqual(join([ + '/**', + ' * First Line', + ' *', + ' * Second Line', + ' */', + '', + ])); }); }); diff --git a/test/index-rollup-legacy.spec.js b/test/index-rollup-legacy.spec.js new file mode 100644 index 00000000..e564445f --- /dev/null +++ b/test/index-rollup-legacy.spec.js @@ -0,0 +1,222 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 Mickael Jeanroy + * + * 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. + */ + +const path = require('path'); +const fs = require('fs'); +const tmp = require('tmp'); +const join = require('./utils/join.js'); +const plugin = require('../dist/index-rollup-legacy.js'); + +describe('rollup-plugin-license [rollup legacy]', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = tmp.dirSync({ + unsafeCleanup: true, + }); + }); + + afterEach(() => { + tmpDir.removeCallback(); + }); + + it('should return new plugin instance', () => { + const instance = plugin(); + expect(instance.name).toBe('rollup-plugin-license'); + }); + + it('should enable sourceMap if `options.sourceMap` (camelcase) is true', () => { + const instance = plugin(); + const sourceMap = true; + instance.options({sourceMap}); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should enable sourceMap if `options.sourcemap` (lowercase) is true', () => { + const instance = plugin(); + const sourcemap = true; + instance.options({sourcemap}); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should not enable sourceMap if `options.sourceMap` (camelcase) is false', () => { + const instance = plugin(); + const sourceMap = false; + instance.options({sourceMap}); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).not.toBeDefined(); + }); + + it('should not enable sourceMap if options.sourcemap (lowercase) is false', () => { + const instance = plugin(); + const sourcemap = false; + instance.options({sourcemap}); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).not.toBeDefined(); + }); + + it('should not try to disable sourceMap if `sourceMap` (camelcase) is set in plugin options', () => { + const instance = plugin({ + sourcemap: true, + }); + + instance.options({ + sourceMap: false, + }); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should not try to disable sourceMap if sourcemap (lowercase) is set in plugin options', () => { + const instance = plugin({ + sourcemap: true, + }); + + instance.options({ + sourcemap: false, + }); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should enable sourceMap by default', () => { + const instance = plugin(); + + instance.options({}); + + const code = 'var foo = 0;'; + const outputOptions = {}; + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should scan dependency on load', (done) => { + const thirdPartyOutput = path.join(tmpDir.name, 'dependencies.txt'); + const instance = plugin({ + thirdParty: { + includePrivate: true, + output: thirdPartyOutput, + }, + }); + + const id = path.join(__dirname, 'fixtures', 'fake-package', 'src', 'index.js'); + + instance.load(id); + instance.ongenerate(); + + fs.readFile(thirdPartyOutput, 'utf8', (err, data) => { + if (err) { + done.fail(err); + } + + const content = data.toString(); + expect(content).toBeDefined(); + expect(content).toContain('fake-package'); + done(); + }); + }); + + it('should prepend banner when bundle is transformed', () => { + const banner = 'test banner'; + const instance = plugin({banner}); + + const code = 'var foo = 0;'; + const outputOptions = {}; + + const result = instance.transformBundle(code, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.code).toBe(join([ + '/**', + ' * test banner', + ' */', + '', + 'var foo = 0;', + ])); + }); + + it('should create third-parties file when bundle is generated', (done) => { + const thirdPartyOutput = path.join(tmpDir.name, 'dependencies.txt'); + const instance = plugin({ + thirdParty: { + output: thirdPartyOutput, + }, + }); + + instance.ongenerate(); + + fs.readFile(thirdPartyOutput, 'utf8', (err, data) => { + if (err) { + done.fail(err); + } + + const content = data.toString(); + expect(content).toBeDefined(); + expect(content.length).not.toBe(0); + done(); + }); + }); +}); diff --git a/test/index-rollup-stable.spec.js b/test/index-rollup-stable.spec.js new file mode 100644 index 00000000..5f88c2d8 --- /dev/null +++ b/test/index-rollup-stable.spec.js @@ -0,0 +1,185 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 Mickael Jeanroy + * + * 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. + */ + +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const tmp = require('tmp'); +const join = require('./utils/join.js'); +const plugin = require('../dist/index-rollup-stable.js'); + +describe('rollup-plugin-license [rollup stable]', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = tmp.dirSync({ + unsafeCleanup: true, + }); + }); + + afterEach(() => { + tmpDir.removeCallback(); + }); + + it('should return new plugin instance', () => { + const instance = plugin(); + expect(instance.name).toBe('rollup-plugin-license'); + }); + + it('should scan dependency on load', (done) => { + const thirdPartyOutput = path.join(tmpDir.name, 'dependencies.txt'); + const instance = plugin({ + thirdParty: { + includePrivate: true, + output: thirdPartyOutput, + }, + }); + + const id = path.join(__dirname, 'fixtures', 'fake-package', 'src', 'index.js'); + + instance.load(id); + instance.generateBundle(); + + fs.readFile(thirdPartyOutput, 'utf8', (err, data) => { + if (err) { + done.fail(err); + } + + const content = data.toString(); + expect(content).toBeDefined(); + expect(content).toContain('fake-package'); + done(); + }); + }); + + it('should prepend banner when bundle is transformed', () => { + const banner = 'test banner'; + const instance = plugin({ + banner, + }); + + const code = 'var foo = 0;'; + const chunk = {}; + const outputOptions = {}; + + const result = instance.renderChunk(code, chunk, outputOptions); + + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.code).toBe(join([ + '/**', + ' * test banner', + ' */', + '', + 'var foo = 0;', + ])); + }); + + it('should create third-parties file when bundle is generated', (done) => { + const thirdPartyOutput = path.join(tmpDir.name, 'dependencies.txt'); + const instance = plugin({ + thirdParty: { + output: thirdPartyOutput, + }, + }); + + instance.load('lodash'); + instance.generateBundle(); + + fs.readFile(thirdPartyOutput, 'utf8', (err, data) => { + if (err) { + done.fail(err); + } + + const content = data.toString(); + expect(content).toBeDefined(); + expect(content.length).not.toBe(0); + done(); + }); + }); + + it('should initialize plugin with sourcemap option', () => { + const instance = plugin({ + sourcemap: true, + }); + + const code = 'var foo = 0;'; + const chunk = {}; + const outputOptions = {}; + const result = instance.renderChunk(code, chunk, outputOptions); + + expect(instance).toBeDefined(); + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should enable sourcemap by default', () => { + const instance = plugin(); + + const code = 'var foo = 0;'; + const chunk = {}; + const outputOptions = {}; + const result = instance.renderChunk(code, chunk, outputOptions); + + expect(instance).toBeDefined(); + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).toBeDefined(); + }); + + it('should disbable sourcemap', () => { + const instance = plugin({ + sourcemap: false, + }); + + const code = 'var foo = 0;'; + const chunk = {}; + const outputOptions = {}; + const result = instance.renderChunk(code, chunk, outputOptions); + + expect(instance).toBeDefined(); + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).not.toBeDefined(); + }); + + it('should disable sourcemap using output options', () => { + const instance = plugin(); + + const code = 'var foo = 0;'; + const chunk = {}; + const outputOptions = { + sourcemap: false, + }; + + const result = instance.renderChunk(code, chunk, outputOptions); + + expect(instance).toBeDefined(); + expect(result).toBeDefined(); + expect(result.code).toBeDefined(); + expect(result.map).not.toBeDefined(); + }); +}); diff --git a/test/index.spec.js b/test/index.spec.js deleted file mode 100644 index a9cb5870..00000000 --- a/test/index.spec.js +++ /dev/null @@ -1,161 +0,0 @@ -/** - * The MIT License (MIT) - * - * Copyright (c) 2016-2018 Mickael Jeanroy - * - * 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. - */ - -const path = require('path'); -const LicensePlugin = require('../dist/license-plugin.js'); -const plugin = require('../dist/index.js'); - -describe('rollup-plugin-license', () => { - it('should return new plugin instance', () => { - const instance = plugin(); - expect(instance.name).toBe('rollup-plugin-license'); - }); - - it('should scan dependency on load', () => { - spyOn(LicensePlugin.prototype, 'scanDependency').and.callThrough(); - - const instance = plugin(); - const id = path.join(__dirname, 'fixtures', 'fake-package', 'src', 'index.js'); - - const result = instance.load(id); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.scanDependency).toHaveBeenCalledWith(id); - }); - - it('should enable sourceMap if options.sourceMap is true', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin(); - const result = instance.options({ - sourceMap: true, - }); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).not.toHaveBeenCalled(); - }); - - it('should enable sourceMap if options.sourcemap (lowercase) is true', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin(); - const result = instance.options({ - sourcemap: true, - }); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).not.toHaveBeenCalled(); - }); - - it('should not enable sourceMap if options.sourceMap is false', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin(); - const result = instance.options({ - sourceMap: false, - }); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).toHaveBeenCalledWith(); - }); - - it('should not enable sourceMap if options.sourcemap (lowercase) is false', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin(); - const result = instance.options({ - sourcemap: false, - }); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).toHaveBeenCalledWith(); - }); - - it('should not try to disable sourceMap if sourceMap is set in plugin options', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin({sourceMap: true}); - const result = instance.options({ - sourceMap: false, - }); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).not.toHaveBeenCalledWith(); - }); - - it('should not try to disable sourceMap if sourcemap (lowercase) is set in plugin options', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin({sourcemap: true}); - const result = instance.options({ - sourceMap: false, - }); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).not.toHaveBeenCalledWith(); - }); - - it('should enable sourceMap by default', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin(); - const result = instance.options({}); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).not.toHaveBeenCalled(); - }); - - it('should enable sourceMap if options is not set', () => { - spyOn(LicensePlugin.prototype, 'disableSourceMap').and.callThrough(); - - const instance = plugin(); - const result = instance.options(); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.disableSourceMap).not.toHaveBeenCalled(); - }); - - it('should prepend banner when bundle is transformed', () => { - spyOn(LicensePlugin.prototype, 'prependBanner').and.callThrough(); - - const instance = plugin(); - const code = 'var foo = 0;'; - - const result = instance.transformBundle(code); - - expect(result).toBeDefined(); - expect(LicensePlugin.prototype.prependBanner).toHaveBeenCalledWith(code); - }); - - it('should create third-parties file when bundle is generated', () => { - spyOn(LicensePlugin.prototype, 'exportThirdParties').and.callThrough(); - - const instance = plugin(); - - const result = instance.ongenerate(); - - expect(result).not.toBeDefined(); - expect(LicensePlugin.prototype.exportThirdParties).toHaveBeenCalledWith(); - }); -}); diff --git a/test/license-plugin.spec.js b/test/license-plugin.spec.js index 7a333d1b..75c68662 100644 --- a/test/license-plugin.spec.js +++ b/test/license-plugin.spec.js @@ -22,15 +22,17 @@ * SOFTWARE. */ +'use strict'; + const fs = require('fs'); const path = require('path'); const tmp = require('tmp'); const moment = require('moment'); +const join = require('./utils/join.js'); const LicensePlugin = require('../dist/license-plugin.js'); describe('LicensePlugin', () => { - // eslint-disable-next-line - var tmpDir; + let tmpDir; beforeEach(() => { tmpDir = tmp.dirSync({ @@ -38,11 +40,15 @@ describe('LicensePlugin', () => { }); }); + afterEach(() => { + tmpDir.removeCallback(); + }); + it('should initialize instance', () => { const plugin = new LicensePlugin(); expect(plugin._cwd).toBeDefined(); expect(plugin._pkg).toBeDefined(); - expect(plugin._sourceMap).toBe(true); + expect(plugin._sourcemap).toBe(true); expect(plugin._dependencies).toEqual({}); }); @@ -63,7 +69,7 @@ describe('LicensePlugin', () => { expect(plugin._cwd).toBeDefined(); expect(plugin._pkg).toBeDefined(); - expect(plugin._sourceMap).toBe(false); + expect(plugin._sourcemap).toBe(false); expect(plugin._dependencies).toEqual({}); }); @@ -74,16 +80,16 @@ describe('LicensePlugin', () => { expect(plugin._cwd).toBeDefined(); expect(plugin._pkg).toBeDefined(); - expect(plugin._sourceMap).toBe(false); + expect(plugin._sourcemap).toBe(false); expect(plugin._dependencies).toEqual({}); }); it('should disable source map', () => { const plugin = new LicensePlugin(); - expect(plugin._sourceMap).toBe(true); + expect(plugin._sourcemap).toBe(true); plugin.disableSourceMap(); - expect(plugin._sourceMap).toBe(false); + expect(plugin._sourcemap).toBe(false); }); it('should load pkg', () => { @@ -374,15 +380,15 @@ describe('LicensePlugin', () => { expect(result).toBeDefined(); expect(result.map).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Test banner.\n` + - ` *\n` + - ` * With a second line.\n` + - ` */\n` + - `\n` + - `${code}` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Test banner.', + ' *', + ' * With a second line.', + ' */', + '', + code, + ])); }); it('should prepend banner to bundle with template', () => { @@ -398,15 +404,15 @@ describe('LicensePlugin', () => { expect(result).toBeDefined(); expect(result.map).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Test banner.\n` + - ` *\n` + - ` * With a second line.\n` + - ` */\n` + - `\n` + - `${code}` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Test banner.', + ' *', + ' * With a second line.', + ' */', + '', + code, + ])); }); it('should prepend banner to bundle with custom encoding', () => { @@ -427,15 +433,15 @@ describe('LicensePlugin', () => { expect(fs.readFileSync).toHaveBeenCalledWith(jasmine.any(String), encoding); expect(result).toBeDefined(); expect(result.map).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Test banner.\n` + - ` *\n` + - ` * With a second line.\n` + - ` */\n` + - `\n` + - `${code}` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Test banner.', + ' *', + ' * With a second line.', + ' */', + '', + code, + ])); }); it('should prepend banner to bundle without source map', () => { @@ -452,15 +458,15 @@ describe('LicensePlugin', () => { expect(result).toBeDefined(); expect(result.map).not.toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Test banner.\n` + - ` *\n` + - ` * With a second line.\n` + - ` */\n` + - `\n` + - `${code}` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Test banner.', + ' *', + ' * With a second line.', + ' */', + '', + code, + ])); }); it('should not prepend default banner to bundle', () => { @@ -503,15 +509,15 @@ describe('LicensePlugin', () => { const result = instance.prependBanner(code); expect(result).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Test banner.\n` + - ` *\n` + - ` * With a second line.\n` + - ` */\n` + - `\n` + - `${code}` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Test banner.', + ' *', + ' * With a second line.', + ' */', + '', + code, + ])); }); it('should prepend banner to bundle and create sourceMap', () => { @@ -541,13 +547,13 @@ describe('LicensePlugin', () => { const result = instance.prependBanner(code); expect(result).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Date: ${moment().format('YYYY-MM-DD')}\n` + - ` */\n` + - `\n` + - `var foo = 0;` - ); + expect(result.code).toEqual(join([ + '/**', + ` * Date: ${moment().format('YYYY-MM-DD')}`, + ' */', + '', + 'var foo = 0;', + ])); }); it('should prepend banner and replace custom data object', () => { @@ -566,14 +572,14 @@ describe('LicensePlugin', () => { const result = instance.prependBanner(code); expect(result).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Foo: bar\n` + - ` * Bar: baz\n` + - ` */\n` + - `\n` + - `var foo = 0;` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Foo: bar', + ' * Bar: baz', + ' */', + '', + 'var foo = 0;', + ])); }); it('should prepend banner and replace custom data function', () => { @@ -594,14 +600,14 @@ describe('LicensePlugin', () => { const result = instance.prependBanner(code); expect(result).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Foo: bar\n` + - ` * Bar: baz\n` + - ` */\n` + - `\n` + - `var foo = 0;` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Foo: bar', + ' * Bar: baz', + ' */', + '', + 'var foo = 0;', + ])); }); it('should prepend banner and replace package variables', () => { @@ -616,13 +622,13 @@ describe('LicensePlugin', () => { const result = instance.prependBanner(code); expect(result).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Name: rollup-plugin-license\n` + - ` */\n` + - `\n` + - `var foo = 0;` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Name: rollup-plugin-license', + ' */', + '', + 'var foo = 0;', + ])); }); it('should prepend banner and replace dependencies placeholders', () => { @@ -639,18 +645,18 @@ describe('LicensePlugin', () => { const result = instance.prependBanner(code); expect(result).toBeDefined(); - expect(result.code).toEqual( - `/**\n` + - ` * Name: rollup-plugin-license\n` + - ` *\n` + - ` * Dependencies:\n` + - ` * \n` + - ` * fake-package -- MIT\n` + - ` * \n` + - ` */\n` + - `\n` + - `var foo = 0;` - ); + expect(result.code).toEqual(join([ + '/**', + ' * Name: rollup-plugin-license', + ' *', + ' * Dependencies:', + ' * ', + ' * fake-package -- MIT', + ' * ', + ' */', + '', + 'var foo = 0;', + ])); }); it('should display single dependency', (done) => { @@ -685,14 +691,14 @@ describe('LicensePlugin', () => { const txt = content.toString(); expect(txt).toBeDefined(); - expect(txt).toEqual( - `Name: foo\n` + - `Version: 1.0.0\n` + - `License: MIT\n` + - `Private: false\n` + - `Description: Foo Package\n` + - `Author: Mickael Jeanroy ` - ); + expect(txt).toEqual(join([ + 'Name: foo', + 'Version: 1.0.0', + 'License: MIT', + 'Private: false', + 'Description: Foo Package', + 'Author: Mickael Jeanroy ', + ])); done(); }); @@ -719,7 +725,6 @@ describe('LicensePlugin', () => { }); const result = instance.exportThirdParties(); - expect(result).not.toBeDefined(); fs.readFile(file, 'utf-8', (err, content) => { @@ -730,14 +735,14 @@ describe('LicensePlugin', () => { const txt = content.toString(); expect(txt).toBeDefined(); - expect(txt).toEqual( - `Name: foo\n` + - `Version: 1.0.0\n` + - `License: MIT\n` + - `Private: false\n` + - `Description: Foo Package\n` + - `Author: Mickael Jeanroy ` - ); + expect(txt).toEqual(join([ + 'Name: foo', + 'Version: 1.0.0', + 'License: MIT', + 'Private: false', + 'Description: Foo Package', + 'Author: Mickael Jeanroy ', + ])); done(); }); @@ -783,22 +788,22 @@ describe('LicensePlugin', () => { const txt = content.toString(); expect(txt).toBeDefined(); - expect(txt).toEqual( - `Name: foo\n` + - `Version: 1.0.0\n` + - `License: MIT\n` + - `Private: false\n` + - `Description: Foo Package\n` + - `Author: Mickael Jeanroy \n` + - `\n` + - `---\n` + - `\n` + - `Name: bar\n` + - `Version: 2.0.0\n` + - `License: Apache 2.0\n` + - `Private: false\n` + - `Description: Bar Package` - ); + expect(txt).toEqual(join([ + 'Name: foo', + 'Version: 1.0.0', + 'License: MIT', + 'Private: false', + 'Description: Foo Package', + 'Author: Mickael Jeanroy ', + '', + '---', + '', + 'Name: bar', + 'Version: 2.0.0', + 'License: Apache 2.0', + 'Private: false', + 'Description: Bar Package', + ])); done(); }); @@ -851,22 +856,22 @@ describe('LicensePlugin', () => { const txt = content.toString(); expect(txt).toBeDefined(); - expect(txt).toEqual( - `Name: foo\n` + - `Version: 1.0.0\n` + - `License: MIT\n` + - `Private: false\n` + - `Description: Foo Package\n` + - `Author: Mickael Jeanroy \n` + - `\n` + - `---\n` + - `\n` + - `Name: bar\n` + - `Version: 2.0.0\n` + - `License: Apache 2.0\n` + - `Private: false\n` + - `Description: Bar Package` - ); + expect(txt).toEqual(join([ + 'Name: foo', + 'Version: 1.0.0', + 'License: MIT', + 'Private: false', + 'Description: Foo Package', + 'Author: Mickael Jeanroy ', + '', + '---', + '', + 'Name: bar', + 'Version: 2.0.0', + 'License: Apache 2.0', + 'Private: false', + 'Description: Bar Package', + ])); done(); }); @@ -939,14 +944,14 @@ describe('LicensePlugin', () => { const txt = content.toString(); expect(txt).toBeDefined(); - expect(txt).toEqual( - `Name: foo\n` + - `Version: 1.0.0\n` + - `License: MIT\n` + - `Private: false\n` + - `Description: Foo Package\n` + - `Author: Mickael Jeanroy ` - ); + expect(txt).toEqual(join([ + 'Name: foo', + 'Version: 1.0.0', + 'License: MIT', + 'Private: false', + 'Description: Foo Package', + 'Author: Mickael Jeanroy ', + ])); done(); }); @@ -993,22 +998,22 @@ describe('LicensePlugin', () => { const txt = content.toString(); expect(txt).toBeDefined(); - expect(txt).toEqual( - `Name: foo\n` + - `Version: 1.0.0\n` + - `License: MIT\n` + - `Private: false\n` + - `Description: Foo Package\n` + - `Author: Mickael Jeanroy \n` + - `\n` + - `---\n` + - `\n` + - `Name: bar\n` + - `Version: 2.0.0\n` + - `License: Apache 2.0\n` + - `Private: true\n` + - `Description: Bar Package` - ); + expect(txt).toEqual(join([ + 'Name: foo', + 'Version: 1.0.0', + 'License: MIT', + 'Private: false', + 'Description: Foo Package', + 'Author: Mickael Jeanroy ', + '', + '---', + '', + 'Name: bar', + 'Version: 2.0.0', + 'License: Apache 2.0', + 'Private: true', + 'Description: Bar Package', + ])); done(); }); diff --git a/test/utils/join.js b/test/utils/join.js new file mode 100644 index 00000000..30938625 --- /dev/null +++ b/test/utils/join.js @@ -0,0 +1,34 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016-2018 Mickael Jeanroy + * + * 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. + */ + +/** + * Join given lines into a single line. + * + * @param {Array} lines The lines to join. + * @param {string} separator The separator, defaults to '\n'. + * @return {string} The full text. + */ +module.exports = function join(lines, separator = '\n') { + return lines.join(separator); +};