UNPKG

rollup-plugin-version-injector

Version:

A simple rollup.js plugin to inject your application's version number and/or today's date into your built js, html, and css files!

274 lines (265 loc) 10.7 kB
'use strict'; var lodash = require('lodash'); var fs = require('fs'); var path = require('path'); var dateformat = require('dateformat'); var chalk = require('chalk'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var dateformat__default = /*#__PURE__*/_interopDefaultLegacy(dateformat); var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk); const defaultConfig = { injectInComments: { fileRegexp: /\.(js|html|css)$/, tag: 'Version: {version} - {date}', dateFormat: 'mmmm d, yyyy HH:MM:ss' }, injectInTags: { fileRegexp: /\.(js|html|css)$/, tagId: 'VI', dateFormat: 'mmmm d, yyyy HH:MM:ss' }, packageJson: './package.json', logLevel: 'info', logger: console, exclude: [] }; class VIInjector { constructor(logger) { this.logger = logger; this.codeChanged = false; this.code = ''; } getVersion(packagePath) { const packageFile = JSON.parse(fs__default['default'].readFileSync(path__default['default'].resolve(packagePath), 'utf8')); this.logger.debug(`retrieved package.json path "${packagePath}" and version "${packageFile.version}"`); return packageFile.version; } setCode(code) { this.code = code; this.codeChanged = false; } isCodeChanged() { return this.codeChanged; } getCode() { return this.code; } getFileExtension(fileName) { let len = fileName.length; let ext = null; if (fileName.substring(len - 2) === 'js') { ext = 'js'; } if (fileName.substring(len - 4) === 'html') { ext = 'html'; } if (fileName.substring(len - 3) === 'css') { ext = 'css'; } this.logger.debug(`retrieved file extension "${ext}" for file "${fileName}"`); return ext; } injectIntoTags(config, fileName, version) { if (!this.code) { this.logger.error('code not set in VIInjector called from injectIntoTags()'); } if (config && config.fileRegexp.test(fileName)) { let results = this.replaceVersionInTags(config.tagId, config.dateFormat, version, this.code); if (results.replaceCount) { this.logger.info(`found and replaced [${results.replaceCount}] version tags in`, fileName); this.codeChanged = true; this.code = results.code; } else { this.logger.info(`no tags found in file: "${fileName}"`); } } else { this.logger.debug('injectInTages skipped because it was set to "false" or fileName did not match expression', config); } } injectIntoComments(config, fileName, version) { if (!this.code) { this.logger.error('code not set in VIInjector called from injectIntoComments()'); } if (config && config.fileRegexp.test(fileName)) { let fileExt = this.getFileExtension(fileName); if (fileExt) { this.code = this.replaceVersionInComments(config.tag, config.dateFormat, version, fileExt, this.code); this.codeChanged = true; this.logger.info('injected version as comment in', fileName); } else { this.logger.warn(`file extension not supported for injecting into comments "${fileExt}"`); } } else { this.logger.debug('injectIntoComments skipped because it was set to "false" or fileName did not match expression', config); } } replaceVersionInTags(tagId, dateFormat, version, code) { this.logger.debug(`starting injectIntoTags() with args: { tagId: "${tagId}", dateFormat: ${dateFormat}, version: ${version}, code: "${code ? code.substring(0, 12) + '...' : code}" }`); let replaceCount = 0; const pattern = this.buildTagRegexp(tagId); let match = pattern.exec(code); while (match) { replaceCount++; this.logger.debug(`match #${replaceCount}: "${match[0]}"`); let start = match.index; let end = start + match[0].length; let replacement = this.stripTags(match[0], tagId); replacement = this.replaceVersion(replacement, version); replacement = this.replaceDate(replacement, dateFormat); this.logger.debug(`match #${replaceCount} replacement: "${replacement}"`); code = code.substring(0, start) + replacement + code.substring(end); match = pattern.exec(code); } this.logger.debug(`finished injectIntoTags() and replaced ${replaceCount} tags`); return { code, replaceCount }; } replaceVersionInComments(tag, dateFormat, version, fileExtension, code) { this.logger.debug(`starting injectIntoComments() with args: { tag: "${tag}", dateFormat: "${dateFormat}", version: "${version}", fileExtension: "${fileExtension}", code: "${code ? code.substring(0, 12) + '...' : code}" }`); let injectValue = this.replaceVersion(tag, version); injectValue = this.replaceDate(injectValue, dateFormat); let versionTag = ''; switch (fileExtension) { case 'html': versionTag = `<!-- ${injectValue} -->`; break; case 'js': case 'css': versionTag = `/* ${injectValue} */`; break; } if (versionTag) { this.logger.debug(`injecting "${versionTag}" into the passed in code as a comment`); code = versionTag + '\n' + code; } else { this.logger.warn(`did not inject "${versionTag}" into the passed in code as a comment - unsupported file extension`); } this.logger.debug('finished injectIntoComments()'); return code; } stripTags(tag, tagId) { const regexp = new RegExp(`(\\[${tagId}]|\\[\\/${tagId}])`, 'g'); this.logger.debug(`tag before stripping tagId`, tag); const newTag = tag.replace(regexp, ''); this.logger.debug(`tag after stripping tagId`, newTag); return newTag; } buildTagRegexp(tagId) { let regexp = new RegExp(`(\\[${tagId}\\])(.*)(\\[\\/${tagId}\\])`, 'g'); this.logger.debug('generated RegExp', regexp); return regexp; } replaceVersion(tag, version) { this.logger.debug(`tag before replacing version: "${tag}"`); let newTag = tag.replace('{version}', version); if (newTag === tag) { this.logger.debug(`could not find "{version}" placeholder in tag: "${tag}"`); } this.logger.debug(`tag after replacing version: "${newTag}"`); return newTag; } replaceDate(tag, dateFormat) { this.logger.debug(`tag before replacing date: "${tag}"`); let newTag = tag.replace('{date}', dateformat__default['default'](dateFormat)); if (newTag === tag) { this.logger.debug(`could not find "{date}" placeholder in tag: "${tag}"`); } this.logger.debug(`tag after replacing date: "${newTag}"`); return newTag; } } class VILogger { constructor(logLevel, logger = console) { this.DEFAULT_LOG_LEVEL = ['warn', 3]; this.logger = logger; this.internalLog = []; this.logMap = this.buildMap(); let logLevelNum = this.logMap.get(logLevel); let configLogLevel = 'debug'; let configLogMessage = `setting logging level to "${logLevel}"`; if (!logLevelNum) { configLogMessage = `log level passed in was invalid ("${logLevel}"). setting log level to default ("${this.DEFAULT_LOG_LEVEL[0]}")`; configLogLevel = 'warn'; logLevel = this.DEFAULT_LOG_LEVEL[0]; logLevelNum = this.DEFAULT_LOG_LEVEL[1]; } this.logLevel = [logLevel, logLevelNum]; this[configLogLevel](configLogMessage); } log(...args) { this.logIt('log', ...args); } debug(...args) { this.logIt('debug', ...args); } info(...args) { this.logIt('info', ...args); } warn(...args) { this.logIt('warn', ...args); } error(...args) { this.logIt('error', ...args); } logIt(level, ...args) { let logLevel = this.logMap.get(level); if (!logLevel) { logLevel = this.logLevel[1]; level = this.logLevel[0]; } if (logLevel >= this.logLevel[1]) { this.logger[level](chalk__default['default'].magenta.bold(' [VI]'), chalk__default['default'].yellow(`[${level.padEnd(5)}]`), ...args); } this.internalLog.push({ level, log: args }); } buildMap() { const map = new Map(); map.set('debug', 1); map.set('info', 2); map.set('warn', 3); map.set('error', 4); map.set('log', 5); return map; } } function versionInjector(userConfig) { const pluginName = 'version-injector'; const config = lodash.merge({}, defaultConfig, userConfig); const logger = new VILogger(config.logLevel, config.logger); const injector = new VIInjector(logger); const version = injector.getVersion(config.packageJson); return { name: pluginName, renderChunk(code, chunk) { logger.debug('chunk', chunk); logger.info(`${pluginName} started with version "${version}"`); logger.debug('config', config); const fileName = chunk.fileName; if (config.exclude.includes(fileName)) { logger.info('file was in the exclude list - skipping', fileName); return; } if (chunk.type === 'asset') { logger.info('output bundle was an asset - skipping', fileName); return; } injector.setCode(code); injector.injectIntoTags(config.injectInTags, fileName, version); injector.injectIntoComments(config.injectInComments, fileName, version); if (injector.isCodeChanged()) { logger.info(`${pluginName} finished`); return { code: injector.getCode(), map: null }; } else { logger.info(`file was not changed. did not write to file "${fileName}"`); } } }; } module.exports = versionInjector;