htmlprocessor-prepend
Version:
Process html file using special comments
163 lines (130 loc) • 4.62 kB
JavaScript
/*
* node-htmlprocessor
* https://github.com/dciccale/node-htmlprocessor
*
* Copyright (c) 2013-2014 Denis Ciccale (@tdecs)
* Licensed under the MIT license.
* https://github.com/dciccale/node-htmlprocessor/blob/master/LICENSE-MIT
*/
'use strict';
var fs = require('fs');
var path = require('path');
var utils = require('./utils');
var blockTypes = require('./blocktypes');
var getBlocks = function (content, marker) {
/*
* <!-- build:<type>[:target] [value] -->
* - type (required) js, css, attr, remove, template, include
* - target|attribute (optional) i.e. dev, prod, release or attributes like [href] [src]
* - value (optional) i.e. script.min.js
*/
var regStart = new RegExp('<!--\\s*' + marker + ':(\\[?[\\w-]+\\]?)(?::([\\w,]+))?(?:\\s*([^\\s]+)\\s*-->)*');
// <!-- /build -->
var regEnd = new RegExp('(?:<!--\\s*)*\\/' + marker + '\\s*-->');
// Normalize line endings and split in lines
var lines = content.replace(/\r\n/g, '\n').split(/\n/);
var inside = false;
var sections = [];
var block;
lines.forEach(function (line) {
var build = line.match(regStart);
var endbuild = regEnd.test(line);
var attr;
if (build) {
inside = true;
attr = build[1].match(/(?:\[([\w\-]+)\])*/)[1];
block = {
type: attr ? 'attr': build[1],
attr: attr,
targets: !!build[2] ? build[2].split(',') : null,
asset: build[3],
indent: /^\s*/.exec(line)[0],
raw: []
};
}
if (inside && block) {
block.raw.push(line);
}
if (inside && endbuild) {
inside = false;
sections.push(block);
}
});
return sections;
};
// The processor
var HTMLProcessor = function (options) {
var defaults = {
data: {},
environment: '',
templateSettings: null,
includeBase: null,
commentMarker: 'build',
strip: false,
recursive: false,
customBlockTypes: []
};
this.options = utils._.extend({}, defaults, options);
this.data = utils._.extend({}, this.options.data, {environment: this.options.environment});
// Register custom block types
if (this.options.customBlockTypes.length) {
this.options.customBlockTypes.forEach(function (processor) {
require(path.resolve(processor)).call(this, this);
}, this);
}
};
HTMLProcessor.prototype._blockTypes = blockTypes;
HTMLProcessor.prototype.registerBlockType = function (name, fn) {
this._blockTypes[name] = fn;
};
// Returns a single line of the current block comment
HTMLProcessor.prototype._getBlockLine = function (block) {
return block.raw.join(this.linefeed);
};
// Returns the block content (not including the build comments)
HTMLProcessor.prototype._getBlockContent = function (block) {
return block.raw.slice(1, -1).join(this.linefeed);
};
// Replace passed block with the processed content
HTMLProcessor.prototype._replace = function (block, content, filepath) {
var blockLine = this._getBlockLine(block);
var blockContent = this._getBlockContent(block);
var result = this._blockTypes[block.type].call(this, content, block, blockLine, blockContent,
filepath);
return result;
};
// Strips blocks not matched for the current environment
HTMLProcessor.prototype._strip = function (block, content) {
var blockLine = this._getBlockLine(block);
var blockContent = this._getBlockContent(block);
var blockRegExp = utils.blockToRegExp(blockLine);
var result = content.replace(blockRegExp, '\n\n' + blockContent);
return result;
};
HTMLProcessor.prototype._replaceBlocks = function (blocks, filepath) {
var result = this.content;
// Replace found blocks
utils._.each(blocks, function (block) {
// Parse through correct block type checking the build environment
if (this._blockTypes[block.type] && (!block.targets ||
utils._.indexOf(block.targets, this.options.environment) >= 0)) {
result = this._replace(block, result, filepath);
} else if (this.options.strip) {
result = this._strip(block, result, filepath);
}
}, this);
return result;
};
// Process the file content
HTMLProcessor.prototype.process = function (filepath) {
this.content = fs.readFileSync(filepath).toString();
this.linefeed = /\r\n/g.test(this.content) ? '\r\n' : '\n';
// Parse the file content to look for build comment blocks
var blocks = getBlocks(this.content, this.options.commentMarker);
// Replace found blocks
var content = this._replaceBlocks(blocks, filepath);
return content;
};
HTMLProcessor.prototype.template = utils._.template;
// Export the processor
module.exports = HTMLProcessor;