UNPKG

rollup-plugin-license

Version:

Rollup plugin to add license banner to the final bundle and output third party licenses

293 lines (241 loc) 9.07 kB
/** * The MIT License (MIT) * * Copyright (c) 2016 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'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var fs = require('fs'); var path = require('path'); var mkdirp = require('mkdirp'); var _ = require('lodash'); var moment = require('moment'); var MagicString = require('magic-string'); var Dependency = require('./dependency.js'); var generateBlockComment = require('./generate-block-comment.js'); var EOL = require('./eol.js'); /** * Rollup Plugin. */ var LicensePlugin = function () { /** * Initialize plugin. * * @param {Object} options Plugin options. */ function LicensePlugin() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, LicensePlugin); // Plugin name, used by rollup. this.name = 'rollup-plugin-license'; this._options = options; this._sourceMap = false; this._cwd = process.cwd(); this._dependencies = {}; this._pkg = require(path.join(this._cwd, 'package.json')); this._debug = options.debug || false; // This is a cache storing a directory path to associated package. // This is an improvement to avoid looking for package information for // already scanned directory. this._cache = {}; } /** * Enable source map. * * @return {void} */ _createClass(LicensePlugin, [{ key: 'enableSourceMap', value: function enableSourceMap() { this._sourceMap = true; } /** * Hook triggered by `rollup` to load code from given path file. * * This hook is used here to analyze a JavaScript file to extract * associated `package.json` file and store the main information about * it (license, author, etc.). * * This method is used to analyse all the files added to the final bundle * to extract license informations. * * @param {string} id Module identifier. * @return {void} */ }, { key: 'scanDependency', value: function scanDependency(id) { var _this = this; this.debug('scanning ' + id); // Look for the `package.json` file var dir = path.parse(id).dir; var pkg = null; var scannedDirs = []; while (dir && dir !== this._cwd) { // Try the cache. if (_.has(this._cache, dir)) { pkg = this._cache[dir]; if (pkg) { this.debug('found package.json in cache (package: ' + pkg.name + ')'); this.addDependency(pkg); } break; } scannedDirs.push(dir); var pkgPath = path.join(dir, 'package.json'); var exists = fs.existsSync(pkgPath); if (exists) { this.debug('found package.json at: ' + pkgPath + ', read it'); // Read `package.json` file pkg = require(pkgPath); // Add the new dependency to the set of third-party dependencies. this.addDependency(pkg); // We can stop now. break; } // Go up in the directory tree. dir = path.normalize(path.join(dir, '..')); } // Update the cache _.forEach(scannedDirs, function (scannedDir) { _this._cache[scannedDir] = pkg; }); } /** * Hook triggered by `rollup` to transform the final generated bundle. * This hook is used here to prepend the license banner to the final bundle. * * @param {string} code The bundle content. * @return {Object} The result containing the code and, optionnally, the source map * if it has been enabled (using `enableSourceMap` method). */ }, { key: 'prependBanner', value: function prependBanner(code) { // Create a magicString: do not manipulate the string directly since it // will be used to generate the sourcemap. var magicString = new MagicString(code); var banner = this._options.banner; var content = void 0; if (_.isString(banner)) { this.debug('prepend banner from template'); content = banner; } else if (banner) { var file = banner.file; this.debug('prepend banner from file: ' + file); var filePath = path.resolve(file); var exists = fs.existsSync(filePath); if (exists) { var encoding = banner.encoding || 'utf-8'; this.debug('use encoding: ' + encoding); content = fs.readFileSync(filePath, encoding); } else { this.debug('template file does not exist, skip.'); } } if (content) { // Create the template function with lodash. var tmpl = _.template(content); // Generate the banner. var pkg = this._pkg; var dependencies = _.values(this._dependencies); var text = tmpl({ _: _, moment: moment, pkg: pkg, dependencies: dependencies }); // Make a block comment if needed var trimmedBanner = text.trim(); var start = trimmedBanner.slice(0, 3); if (start !== '/**' && start !== '/*!') { text = generateBlockComment(text); } // Prepend the banner. magicString.prepend('' + text + EOL); } var result = { code: magicString.toString() }; if (this._sourceMap) { result.map = magicString.generateMap({ hires: true }); } return result; } /** * Add new dependency to the bundle descriptor. * * @param {Object} pkg Dependency package information. * @return {void} */ }, { key: 'addDependency', value: function addDependency(pkg) { var name = pkg.name; if (!_.has(this._dependencies, name)) { this._dependencies[name] = new Dependency(pkg); } } /** * Generate third-party dependencies summary. * * @param {boolean} includePrivate Flag that can be used to include / exclude private dependencies. * @return {void} */ }, { key: 'exportThirdParties', value: function exportThirdParties() { var thirdParty = this._options.thirdParty; if (!thirdParty) { return; } var output = thirdParty.output; if (output) { this.debug('exporting third-party summary to ' + output); // Create directory if it does not already exist. mkdirp(path.parse(output).dir); var includePrivate = thirdParty.includePrivate; var text = _.chain(this._dependencies).values().filter(function (dependency) { return includePrivate || !dependency.private; }).map(function (dependency) { return dependency.text(); }).join('' + EOL + EOL + '---' + EOL + EOL).trim().value(); var encoding = thirdParty.encoding || 'utf-8'; this.debug('use encoding: ' + encoding); fs.writeFileSync(output, text || 'No third parties dependencies', { encoding: encoding }); } } /** * Log debug message if debug mode is enabled. * * @param {string} msg Log message. */ }, { key: 'debug', value: function debug(msg) { if (this._debug) { console.log('[' + this.name + '] -- ' + msg); } } }]); return LicensePlugin; }(); module.exports = LicensePlugin;