UNPKG

ember-introjs

Version:
200 lines (154 loc) 6.09 kB
var Filter = require('broccoli-filter'); var path = require('path'); var Cache = require('broccoli-filter/lib/cache'); function normalize(str) { return str.replace(/[\\\/]+/g, '/'); } function relative(a, b) { if (/\./.test(path.basename(a))) { a = path.dirname(a); } var relativePath = path.relative(a, b); // path.relative might have added back \-s on windows relativePath = normalize(relativePath); return relativePath.charAt(0) !== '.' ? './' + relativePath : relativePath; } function AssetRewrite(inputNode, options) { if (!(this instanceof AssetRewrite)) { return new AssetRewrite(inputNode, options); } options = options || {}; Filter.call(this, inputNode, { extensions: options.replaceExtensions || ['html', 'css'], // We should drop support for `description` in the next major release annotation: options.description || options.annotation }); this.assetMap = options.assetMap || {}; this.prepend = options.prepend || ''; this.ignore = options.ignore || []; // files to ignore this.assetMapKeys = null; } AssetRewrite.prototype = Object.create(Filter.prototype); AssetRewrite.prototype.constructor = AssetRewrite; AssetRewrite.prototype.processAndCacheFile = function (srcDir, destDir, relativePath) { this._cache = new Cache(); return Filter.prototype.processAndCacheFile.apply(this, arguments); } /** * Checks that file is not being ignored and destination doesn't already have a file * @param relativePath * @returns {boolean} */ AssetRewrite.prototype.canProcessFile = function(relativePath) { if (!this.assetMapKeys) { this.generateAssetMapKeys(); } if (!this.inverseAssetMap) { var inverseAssetMap = {}; var assetMap = this.assetMap; Object.keys(assetMap).forEach(function(key) { var value = assetMap[key]; inverseAssetMap[value] = key; }, this); this.inverseAssetMap = inverseAssetMap; } /* * relativePath can be fingerprinted or not. * Check that neither of these variations are being ignored */ if (this.ignore.indexOf(relativePath) !== -1 || this.ignore.indexOf(this.inverseAssetMap[relativePath]) !== -1) { return false; } return Filter.prototype.canProcessFile.apply(this, arguments); } AssetRewrite.prototype.rewriteAssetPath = function (string, assetPath, replacementPath) { // Early exit: does the file contain the asset path? if (string.indexOf(assetPath) === -1) return string; var newString = string; /* * Replace all of the assets with their new fingerprint name * * Uses a regular expression to find assets in html tags, css backgrounds, handlebars pre-compiled templates, etc. * * ["\'(=] - Match one of "'(= exactly one time * \\s* - Any amount of white space * ( - Starts the first capture group * [^"\'()=]* - Do not match any of ^"'()= 0 or more times * [^"\'()\\>=]* - Do not match any of ^"'()\>= 0 or more times - Explicitly add \ here because of handlebars compilation * ) - End first capture group * (\\?[^"\')> ]*)? - Allow for query parameters to be present after the URL of an asset * \\s* - Any amount of white space * \\\\* - Allow any amount of \ - For handlebars compilation (includes \\\) * \\s* - Any amount of white space * ["\')> ] - Match one of "'( > exactly one time */ var re = new RegExp('["\'(=]\\s*([^"\'()=]*' + escapeRegExp(assetPath) + '[^"\'()\\>=]*)(\\?[^"\')> ]*)?\\s*\\\\*\\s*["\')> ]', 'g'); var match = null; /* * This is to ignore matches that should not be changed * Any URL encoded match that would be ignored above will be ignored by this: "'()=\ */ var ignoreLibraryCode = new RegExp('%(22|27|5C|28|29|3D)[^"\'()=]*' + escapeRegExp(assetPath)); while (match = re.exec(newString)) { var replaceString = ''; if (ignoreLibraryCode.exec(match[1])) { continue; } replaceString = match[1].replace(assetPath, replacementPath); if (this.prepend && replaceString.indexOf(this.prepend) !== 0) { var removeLeadingRelativeOrSlashRegex = new RegExp('^(\\.*/)*(.*)$'); replaceString = this.prepend + removeLeadingRelativeOrSlashRegex.exec(replaceString)[2]; } newString = newString.replace(new RegExp(escapeRegExp(match[1]), 'g'), replaceString); } var self = this; return newString.replace(new RegExp('sourceMappingURL=' + escapeRegExp(assetPath)), function(wholeMatch) { var replaceString = replacementPath; if (self.prepend && (!/^sourceMappingURL=(http|https|\/\/)/.test(wholeMatch))) { replaceString = self.prepend + replacementPath; } return wholeMatch.replace(assetPath, replaceString); }); }; AssetRewrite.prototype.processString = function (string, relativePath) { var newString = string; for (var i = 0, keyLength = this.assetMapKeys.length; i < keyLength; i++) { var key = this.assetMapKeys[i]; if (this.assetMap.hasOwnProperty(key)) { /* * Rewrite absolute URLs */ newString = this.rewriteAssetPath(newString, key, this.assetMap[key]); /* * Rewrite relative URLs. If there is a prepend, use the full absolute path. */ var pathDiff = relative(relativePath, key).replace(/^\.\//, ""); var replacementDiff = relative(relativePath, this.assetMap[key]).replace(/^\.\//, ""); if (this.prepend && this.prepend !== '') { replacementDiff = this.assetMap[key]; } newString = this.rewriteAssetPath(newString, pathDiff, replacementDiff); } } return newString; }; AssetRewrite.prototype.generateAssetMapKeys = function () { var keys = Object.keys(this.assetMap); keys.sort(function (a, b) { if (a.length < b.length) { return 1; } if (a.length > b.length) { return -1; } return 0; }); this.assetMapKeys = keys; }; /* * /([.*+?^=!:${}()|\[\]\/\\])/g - Replace .*+?^=!:${}()|[]/\ in filenames with an escaped version for an exact name match */ function escapeRegExp(string) { return string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1"); } module.exports = AssetRewrite;