Repository: VFK/gulp-html-replace
Branch: master
Commit: 4b99045040b3
Files: 15
Total size: 37.9 KB
Directory structure:
gitextract_3frrgmvh/
├── .gitignore
├── .travis.yml
├── LICENSE
├── appveyor.yml
├── lib/
│ ├── block.js
│ ├── common.js
│ ├── index.js
│ └── parser.js
├── package.json
├── readme.md
└── test/
├── buffer.js
├── expected.html
├── fixture.html
├── null.js
└── stream.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea
Thumbs.db
.DS_Store
node_modules
coverage
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- "0.10"
- "0.11"
- "0.12"
- "iojs"
after_script:
- npm run coveralls
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2014 Vladimir Kucherenko <kvsoftware@gmail.com>
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.
================================================
FILE: appveyor.yml
================================================
environment:
matrix:
# node.js
- nodejs_version: "0.10"
- nodejs_version: "0.11"
- nodejs_version: "0.12"
# io.js
- nodejs_version: "1.0"
install:
- ps: Install-Product node $env:nodejs_version
- npm install
test_script:
- node --version
- npm --version
- npm test
build: off
================================================
FILE: lib/block.js
================================================
'use strict';
var util = require('util');
var path = require('path');
var slash = require('slash');
var Block = function (config, file, match) {
this.replacement = match[0];
this.linefeed = match[1];
this.indent = match[2];
this.beginTag = match[3];
this.taskName = match[4];
this.originalContent = match[5];
this.endTag = match[6];
this.replacements = [];
this.config = config;
this.template = null;
this.file = file;
};
Block.prototype.build = function () {
if (!this.replacements.length) {
return this.config.keepUnassigned ? [this.indent + this.originalContent.trim()] : [];
}
// get the replacement strings and do replacements for extensions
if (this.uniqueExts) {
var extname = path.extname(this.file.path);
var basename = path.basename(this.file.path, extname);
if (this.uniqueExts['%f']) {
this.uniqueExts['%f'].value = basename;
}
if (this.uniqueExts['%e']) {
this.uniqueExts['%e'].value = extname;
}
Object.keys(this.uniqueExts).forEach(function (key) {
var unique = this.uniqueExts[key];
this.template = this.template.replace(unique.regex, unique.value);
}.bind(this));
}
if (this.srcIsNull) {
return [this.indent + this.template];
}
return this.replacements.map(function (replacement) {
if (this.template) {
if (Array.isArray(replacement)) {
replacement.unshift(this.template);
return this.indent + util.format.apply(util, replacement);
} else {
return this.indent + util.format(this.template, replacement);
}
}
if (this.config.resolvePaths) {
var replacementPath = path.resolve(this.file.cwd, replacement);
replacement = path.relative(path.dirname(this.file.path), replacementPath);
replacement = slash(replacement);
}
var ext = replacement.split('?')[0].toLowerCase().split('.').pop();
if (ext === 'js') {
return util.format('%s<script src="%s"></script>', this.indent, replacement);
} else if (ext === 'css') {
return util.format('%s<link rel="stylesheet" href="%s">', this.indent, replacement);
}
return this.indent + replacement;
}.bind(this));
};
Block.prototype.compile = function (tasks) {
var task = tasks[this.taskName];
if (task) {
this.replacements = task.src;
this.template = task.tpl;
this.uniqueExts = task.uni;
this.srcIsNull = task.srcIsNull;
}
var buildResult = this.build();
if (this.config.keepBlockTags) {
buildResult.unshift(this.indent + this.beginTag);
buildResult.push(this.indent + this.endTag);
}
buildResult.unshift(null);
buildResult.push(null);
return buildResult.join(this.linefeed);
};
module.exports = Block;
================================================
FILE: lib/common.js
================================================
'use strict';
var Promise = require('bluebird');
var buffer = require('vinyl-buffer');
function isStream(obj) {
return obj && typeof obj.pipe === 'function' && typeof obj.on === 'function';
}
/**
* Takes the src property of the task configuration and deeply "resolves" any vinyl file stream in it by turning
* it into a string.
*
* This function doesn't change the "arborescence" of the given value: all the forms with strings accepted
* work with vinyl file streams.
*
* @returns {Promise}
*/
function resolveSrcString(srcProperty) {
if (Array.isArray(srcProperty)) {
// handle multiple tag replacement
return Promise.all(srcProperty.map(function (item) {
return resolveSrcString(item);
}));
} else if (isStream(srcProperty)) {
return new Promise(function (resolve, reject) {
var strings = [];
srcProperty.pipe(buffer())
.on('data', function (file) {
strings.push(file.contents.toString());
})
.on('error', function(error) {
reject(error);
this.end();
})
.once('end', function () {
resolve(strings);
});
});
} else {
return Promise.resolve(srcProperty);
}
}
module.exports = {
/**
* tasks = {
* 'task-name': {
* 'src': [file1, file2],
* 'tpl': '<script src="%s"></script>'
* },
* ....
* }
**/
parseTasks: function (options) {
options = options || {};
var utilExtensions = /%f|%e/g;
var tasksByNames = {};
var tasksPromises = Object.keys(options).map(function (name) {
var task = {
src: [],
tpl: null,
uni: {},
srcIsNull: false
};
return Promise
.resolve()
.then(function () {
var item = options[name];
var src = typeof item.src !== 'undefined' ? item.src : item;
return resolveSrcString(src)
.then(function(srcStrings) {
task.srcIsNull = srcStrings === null;
task.src = task.src.concat(srcStrings);
task.tpl = item.tpl;
});
})
.then(function () {
var result;
while (result = utilExtensions.exec(task.tpl)) {
var type = result[0];
var unique = {};
if (task.uni[type]) {
continue;
}
unique.regex = new RegExp(result[0], "g");
unique.value = null;
task.uni[type] = unique;
}
})
.then(function () {
tasksByNames[name] = task;
});
});
return Promise.all(tasksPromises)
.then(function() {
return tasksByNames;
});
},
regexMatchAll: function (string, regexp) {
var matches = [];
string.replace(regexp, function () {
var arr = Array.prototype.slice.call(arguments);
matches.push(arr);
});
return matches;
}
};
================================================
FILE: lib/index.js
================================================
'use strict';
var clone = require('clone');
var objectAssign = require('object-assign');
var Transform = require('readable-stream/transform');
var common = require('./common');
var Parser = require('./parser');
module.exports = function (options, userConfig) {
var tasksPromise = common.parseTasks(options);
var config = {
keepUnassigned: false,
keepBlockTags: false,
resolvePaths: false
};
if (typeof userConfig === 'boolean') {
config.keepUnassigned = userConfig;
} else if (typeof userConfig === 'object') {
objectAssign(config, userConfig);
}
return new Transform({
objectMode: true,
transform: function (file, enc, callback) {
tasksPromise
.then(function (tasks) {
var parser = new Parser(clone(tasks), config, file);
if (file.isBuffer()) {
parser.write(file.contents);
parser.end();
var contents = new Buffer(0);
parser.on('data', function (data) {
contents = Buffer.concat([contents, data]);
});
parser.once('end', function () {
file.contents = contents;
callback(null, file);
});
return;
}
if (file.isStream()) {
file.contents = file.contents.pipe(parser);
}
callback(null, file);
})
.catch(callback);
}
});
};
================================================
FILE: lib/parser.js
================================================
'use strict';
var util = require('util');
var Transform = require('readable-stream/transform');
var Block = require('./block');
var common = require('./common');
/**
* 1 - newline
* 2 - indentation
* 3 - begin tag
* 4 - task name
* 5 - original content
* 6 - end tag
*
* @type {RegExp}
*/
var regex = /(\n?)([ \t]*)(<!--\s*build:(\w+(?:-\w+)*)\s*-->)\n?([\s\S]*?)\n?(<!--\s*endbuild\s*-->)\n?/ig;
function Parser(tasks, config, file) {
Transform.call(this);
this.tasks = tasks;
this.config = config;
this.file = file;
}
util.inherits(Parser, Transform);
Parser.prototype._transform = function (chunk, enc, done) {
var content = chunk.toString('utf8');
var matches = common.regexMatchAll(content, regex);
matches.forEach(function (match) {
var block = new Block(this.config, this.file, match);
content = content.replace(block.replacement, function () {
return block.compile(this.tasks)
}.bind(this));
}.bind(this));
done(null, content);
};
module.exports = Parser;
================================================
FILE: package.json
================================================
{
"name": "gulp-html-replace",
"version": "1.6.2",
"description": "Replace build blocks in HTML. Like useref but done right.",
"keywords": [
"gulpplugin",
"html",
"replace"
],
"files": [
"lib"
],
"repository": {
"type": "git",
"url": "https://github.com/VFK/gulp-html-replace.git"
},
"author": {
"name": "Vladimir Kucherenko",
"email": "kvsoftware@gmail.com"
},
"contributors": [
{
"name": "Bruce MacNaughton",
"email": "bmacnaughton@gmail.com"
}
],
"main": "./lib/index.js",
"scripts": {
"test": "mocha",
"coverage": "istanbul cover _mocha -- -R dot",
"coveralls": "istanbul cover _mocha && istanbul-coveralls"
},
"engines": {
"node": ">= 0.9"
},
"dependencies": {
"bluebird": "^3.1.1",
"clone": "^1.0.2",
"object-assign": "^4.0.1",
"readable-stream": "^2.0.4",
"slash": "^1.0.0",
"vinyl-buffer": "^1.0.0"
},
"devDependencies": {
"concat-stream": "^1.5.1",
"from2-string": "^1.1.0",
"gulp-util": "^3.0.7",
"istanbul": "^0.4.0",
"istanbul-coveralls": "^1.0.3",
"mocha": "^2.3.4",
"vinyl": "^1.1.0",
"vinyl-source-stream": "^1.1.0"
},
"license": "MIT"
}
================================================
FILE: readme.md
================================================
# gulp-html-replace [![NPM version][npm-image]][npm-url] [![Travis][travis-image]][travis-url] [![AppVeyor][appveyor-image]][appveyor-url] [![Coverage Status][coveralls-image]][coveralls-url]
> Replace build blocks in HTML. Like useref but done right.
### Table of Contents
- [Usage](#usage)
- [API](#api)
- [Example](#example)
- [Upgrade](#upgrade)
## Usage
Install:
```shell
npm install --save-dev gulp-html-replace
```
Put some blocks in your HTML file:
```html
<!-- build:<name> -->
Everything here will be replaced
<!-- endbuild -->
```
`name` is the name of the block. Could consist of letters, digits, underscore ( **_** ) and hyphen ( **-** ) symbols.
## API
### htmlreplace(tasks, options)
#### tasks
Type: `Object` `{task-name: replacement}`
* **task-name** - The name of the block in your HTML.
* **replacement** - `String|Array|stream.Readable|Object` The replacement. See examples below.
###### Simple example:
```javascript
// Options is a single string
htmlreplace({js: 'js/main.js'})
// Options is an array of strings
htmlreplace({js: ['js/monster.js', 'js/hero.js']})
```
>If your options strings ends with `.js` or `.css` they will be replaced by correct script/style tags, so you don't need to specify a template like in the example below.
###### Advanced example:
```javascript
// Options is an object
htmlreplace({
js: {
src: 'img/avatar.png',
tpl: '<img src="%s" align="left" />'
}
})
// Multiple tag replacement
htmlreplace({
js: {
src: [['data-main.js', 'require-src.js']],
tpl: '<script data-main="%s" src="%s"></script>'
}
})
```
* **src** - `String|Array|stream.Readable` Same thing as in simple example.
* **tpl** - `String` Template string. Uses [util.format()](http://nodejs.org/api/util.html#util_util_format_format) internally.
> In the first example `%s` will be replaced with `img/avatar.png` producing `<img src="img/avatar.png" align="left">` as the result.
> In the second example `data-main="%s"` and `src="%s"` will be replaced with `data-main.js` and `require-src.js` accordingly, producing `<script data-main="data-main.js" src="require-src.js"></script>` as the result
###### Extended replacements:
```javascript
// Replacement based on the file being processed
htmlreplace({
js: {
src: null,
tpl: '<script src="%f".js></script>'
}
})
// Extended replacement combined with standard replacement
htmlreplace({
js: {
src: 'dir',
tpl: '<script src="%s/%f".js"></script>'
}
})
```
* **src** - `null|String|Array|stream.Readable` Same as examples above but null if there are no standard replacements in the template.
* **tpl** - `String` Template string. Extended replacements do not use `util.format()` and are performed before standard replacements.
> In the first example `src` is null because there are no standard replacements. `%f` is replaced with the name (without extension) of the file currently being processed. If the file being processed is `xyzzy.html` the result is `<script src="xyzzy.js"></script>`.
> In the second example `src` has been set to the string `'dir'`. Extended replacements are processed first, replacing `%f` with `xyzzy`, then `%s` will be replaced with `dir` resulting in `<script src="dir/xyzzy.js"></script>`.
Valid extended replacements are:
* **%f** - this will be replaced with the filename, without an extension.
* **%e** - this will be replaced with the extension including the `.` character.
###### Stream replacements:
Everywhere a string replacement can be given, a stream of vinyl is also accepted. The content of each file will be treated as UTF-8 text and used for replacement. If the stream produces more than a file the behavior is the same as when an array is given.
```javascript
// Replacement is a stream
htmlreplace({
cssInline: {
src: gulp.src('style/main.scss').pipe(sass()),
tpl: '<style>%s</style>'
}
})
```
#### options
Type: `object`
All `false` by default.
- {Boolean} **keepUnassigned** - Whether to keep blocks with unused names or remove them.
- {Boolean} **keepBlockTags** - Whether to keep `<!-- build -->` and `<!-- endbuild -->` comments or remove them.
- {Boolean} **resolvePaths** - Try to resolve *relative* paths. For example if your `cwd` is ``/``, your html file is `/page/index.html` and you set replacement as `lib/file.js` the result path in that html will be `../lib/file.js`
###### Options example:
```javascript
htmlreplace({
js: {
src: null,
tpl: '<script src="%f".js></script>'
}
}, {
keepUnassigned: false,
keepBlockTags: false,
resolvePaths: false
})
```
## Example
index.html:
```html
<!DOCTYPE html>
<html>
<head>
<!-- build:css -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<!-- endbuild -->
</head>
<body>
<!-- build:js -->
<script src="js/player.js"></script>
<script src="js/monster.js"></script>
<script src="js/world.js"></script>
<!-- endbuild -->
```
gulpfile.js:
```javascript
var gulp = require('gulp');
var htmlreplace = require('gulp-html-replace');
gulp.task('default', function() {
gulp.src('index.html')
.pipe(htmlreplace({
'css': 'styles.min.css',
'js': 'js/bundle.min.js'
}))
.pipe(gulp.dest('build/'));
});
```
Result:
```html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.min.css">
</head>
<body>
<script src="js/bundle.min.js"></script>
```
## Upgrade
### From 0.x to 1.x
>This version introduces streaming support, less confusing API, new option *keepUnused* and full code overhaul.
* If you used single task like this: `htmlreplace('js', 'script.js')` just change it to `htmlreplace({js: 'script.js'})`
* If you used single task with template: `htmlreplace('js', 'script.js', '<script="%s">')` change it to `htmlreplace({js: {src: 'script.js', tpl: '<script="%s">'})`
* `files` renamed to `src`, see previous example. Rename if needed.
### From 1.1.x to 1.2.x
>This version switches to the new way of specifying options which is more future-proof. Before it was `htmlreplace(tasks, keepUnassigned = false)`, now it's `htmlreplace(tasks, {keepUnassigned: false})`.
No action required, old syntax will still work, but it is advisable to switch to the new syntax.
[npm-url]: https://npmjs.org/package/gulp-html-replace
[npm-image]: http://img.shields.io/npm/v/gulp-html-replace.svg
[travis-url]: https://travis-ci.org/VFK/gulp-html-replace
[travis-image]: https://travis-ci.org/VFK/gulp-html-replace.svg
[appveyor-url]: https://ci.appveyor.com/project/VFK/gulp-html-replace
[appveyor-image]: https://ci.appveyor.com/api/projects/status/66kwbnis5a1gwp6d?svg=true
[coveralls-url]: https://coveralls.io/github/VFK/gulp-html-replace?branch=master
[coveralls-image]: https://coveralls.io/repos/VFK/gulp-html-replace/badge.svg?branch=master&service=github
================================================
FILE: test/buffer.js
================================================
'use strict';
var plugin = require('..');
var fs = require('fs');
var path = require('path');
var File = require('vinyl');
var assert = require('assert');
var stringToStream = require('from2-string');
var source = require('vinyl-source-stream');
function compare(fixture, expected, stream, done) {
stream
.once('data', function (file) {
assert(file.isBuffer());
assert.strictEqual(String(file.contents), expected);
done();
})
.end(new File({
base: path.resolve('www'),
path: path.resolve('www', 'pages', 'index.html'),
contents: fixture
}));
}
describe('Buffer mode', function () {
it('should replace blocks', function (done) {
var fixture = fs.readFileSync(path.join('test', 'fixture.html'));
var expected = fs.readFileSync(path.join('test', 'expected.html'), 'utf8');
var stream = plugin({
css: 'css/combined.css',
js_files: ['js/one.js', 'js/two.js?ts=123', 'js/three.js?v=v1.5.3-1-g91cd575'],
js_files_tpl: {
src: 'js/with_tpl.js',
tpl: '<script src="%s"></script>'
},
js_files_tpl_multiple: {
src: ['js/with_tpl.js', 'js/with_tpl_2.js'],
tpl: '<script src="%s"></script>'
},
js_files_tpl_2vars: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js']],
tpl: '<script data-main="%s" src="%s"></script>'
},
js_files_tpl_2vars_multiple: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js'], ['js/with_tpl_2vars1_2.js', 'js/with_tpl_2vars2_2.js']],
tpl: '<script data-main="%s" src="%s"></script>'
},
js_files_x_tpl: {
src: null,
tpl: '<script src="js/%f.min.js"></script>'
},
js_files_x_tpl_src: {
src: 'js',
tpl: '<script src="%s/%f.min.js"></script>'
},
js_files_x_tpl_multiple: {
src: ['js/with_tpl.js', 'js/with_tpl_2.js'],
tpl: '<script data-src="%f.data" src="%s"></script>'
},
js_files_x_tpl_2vars: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js']],
tpl: '<script data-src="%f%e" data-main="%s" src="%s"></script>'
},
js_files_x_tpl_2vars_multiple: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js'], ['js/with_tpl_2vars1_2.js', 'js/with_tpl_2vars2_2.js']],
tpl: '<script data-src="%f.data" data-main="%s" src="%s"></script>'
},
'lorem-ipsum': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'stream-simple': stringToStream('Stream simple replacement').pipe(source('fake-vinyl.txt')),
'stream-advanced': {
src: stringToStream('Stream advanced replacement').pipe(source('fake-vinyl.txt'))
},
'stream-special': {
src: stringToStream('Stream $$ special replacement pattern').pipe(source('fake-vinyl.txt'))
}
});
compare(fixture, expected, stream, done);
});
it('should work with inline html', function (done) {
var fixture = '<!DOCTYPE html><head><!-- build:css --><link rel="stylesheet" href="_index.prefix.css"><!-- endbuild --></head>';
var expected = '<!DOCTYPE html><head><link rel="stylesheet" href="css/combined.css"></head>';
var stream = plugin({css: 'css/combined.css'});
compare(new Buffer(fixture), expected, stream, done);
});
it('should not fail if there are no build tags at all', function (done) {
var fixture = '<!DOCTYPE html><head><link rel="stylesheet" href="_index.prefix.css"></head>';
var stream = plugin({css: 'css/combined.css'});
compare(new Buffer(fixture), fixture, stream, done);
});
describe('Options', function () {
describe('keepUnassigned', function () {
it('Should keep empty blocks', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\nSome text\n</html>';
var stream = plugin({}, {keepUnassigned: true});
compare(new Buffer(fixture), expected, stream, done);
});
it('Should remove empty blocks', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n</html>';
var stream = plugin();
compare(new Buffer(fixture), expected, stream, done);
});
});
describe('keepBlockTags', function () {
it('Should keep placeholder tags without arguments', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n<!-- build:js -->\n<!-- endbuild -->\n</html>';
var stream = plugin({}, {keepBlockTags: true});
compare(new Buffer(fixture), expected, stream, done);
});
it('Should keep placeholder tags with arguments', function (done) {
var fixture = '<html>\n<!-- build:lorem -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n<!-- build:lorem -->\nipsum\n<!-- endbuild -->\n</html>';
var stream = plugin({lorem: 'ipsum'}, {keepBlockTags: true});
compare(new Buffer(fixture), expected, stream, done);
});
it('Should remove placeholder tags without arguments', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n</html>';
var stream = plugin();
compare(new Buffer(fixture), expected, stream, done);
});
it('Should remove placeholder tags with arguments', function (done) {
var fixture = '<html>\n<!-- build:lorem -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\nipsum\n</html>';
var stream = plugin({lorem: 'ipsum'});
compare(new Buffer(fixture), expected, stream, done);
});
it('Should keep indentation', function (done) {
var fixture = '<html>\n <!-- build:js -->\n Some text\n <!-- endbuild -->\n</html>';
var expected = '<html>\n <!-- build:js -->\n <!-- endbuild -->\n</html>';
var stream = plugin({}, {keepBlockTags: true});
compare(new Buffer(fixture), expected, stream, done);
});
});
describe('resolvePaths', function () {
it('Should resolve relative paths', function (done) {
var fixture = '<html>\n<!-- build:js -->\n<script src="file.js"></script>\n<!-- endbuild -->\n</html>';
var expected = '<html>\n<script src="../../lib/script.js"></script>\n</html>';
var stream = plugin({js: 'lib/script.js'}, {resolvePaths: true});
compare(new Buffer(fixture), expected, stream, done);
});
});
});
describe('Legacy versions', function () {
it('[version <1.2] should keep empty blocks (keepUnassigned = true)', function (done) {
var fixture = '<html>\n<!-- build:js -->\nThis should be removed if "keepUnassigned" is false\n<!-- endbuild -->\n</html>';
var expected = '<html>\nThis should be removed if "keepUnassigned" is false\n</html>';
var stream = plugin({}, true);
compare(new Buffer(fixture), expected, stream, done);
});
it('[version <1.2] should remove empty blocks (keepUnassigned = false)', function (done) {
var fixture = '<html>\n<!-- build:js -->\nThis should be removed if "keepUnassigned" is false\n<!-- endbuild -->\n</html>';
var expected = '<html>\n</html>';
var stream = plugin();
compare(new Buffer(fixture), expected, stream, done);
});
});
});
================================================
FILE: test/expected.html
================================================
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Test file</title>
<link rel="stylesheet" href="css/combined.css">
</head>
<body>
<script src="js/one.js"></script>
<script src="js/two.js?ts=123"></script>
<script src="js/three.js?v=v1.5.3-1-g91cd575"></script>
<script src="js/with_tpl.js"></script>
<script src="js/with_tpl.js"></script>
<script src="js/with_tpl_2.js"></script>
<script data-main="js/with_tpl_2vars1.js" src="js/with_tpl_2vars2.js"></script>
<script data-main="js/with_tpl_2vars1.js" src="js/with_tpl_2vars2.js"></script>
<script data-main="js/with_tpl_2vars1_2.js" src="js/with_tpl_2vars2_2.js"></script>
<script src="js/index.min.js"></script>
<script src="js/index.min.js"></script>
<script data-src="index.data" src="js/with_tpl.js"></script>
<script data-src="index.data" src="js/with_tpl_2.js"></script>
<script data-src="index.html" data-main="js/with_tpl_2vars1.js" src="js/with_tpl_2vars2.js"></script>
<script data-src="index.data" data-main="js/with_tpl_2vars1.js" src="js/with_tpl_2vars2.js"></script>
<script data-src="index.data" data-main="js/with_tpl_2vars1_2.js" src="js/with_tpl_2vars2_2.js"></script>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Stream simple replacement
Stream advanced replacement
Stream $$ special replacement pattern
</body>
</html>
================================================
FILE: test/fixture.html
================================================
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Test file</title>
<!-- build:css -->
<link rel="stylesheet" href="one.css">
<link rel="stylesheet" href="two.css">
<!-- endbuild -->
</head>
<body>
<!-- build:js_files -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_tpl -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_tpl_multiple -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_tpl_2vars -->
<script data-main="main.js" src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_tpl_2vars_multiple -->
<script data-main="main.js" src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_x_tpl -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_x_tpl_src -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_x_tpl_multiple -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_x_tpl_2vars -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:js_files_x_tpl_2vars_multiple -->
<script src="uncompressed.js"></script>
<!-- endbuild -->
<!-- build:lorem-ipsum -->
<!-- endbuild -->
<!-- build:stream-simple -->
<!-- endbuild -->
<!-- build:stream-advanced -->
<!-- endbuild -->
<!-- build:stream-special -->
<!-- endbuild -->
</body>
</html>
================================================
FILE: test/null.js
================================================
'use strict';
var plugin = require('..');
var File = require('vinyl');
var assert = require('assert');
describe('null files', function () {
it('should be passed through', function (done) {
plugin()
.once('data', function (file) {
assert(file.isNull());
done();
})
.end(new File({contents: null}));
});
});
================================================
FILE: test/stream.js
================================================
'use strict';
var plugin = require('..');
var fs = require('fs');
var path = require('path');
var File = require('vinyl');
var assert = require('assert');
var concatStream = require('concat-stream');
var stringToStream = require('from2-string');
var source = require('vinyl-source-stream');
function compare(fixture, expected, stream, done) {
var fakeFile = new File({
base: path.resolve('www'),
path: path.resolve('www', 'pages', 'index.html'),
contents: fixture
});
stream.write(fakeFile);
stream.once('data', function (file) {
assert(file.isStream());
file.contents.pipe(concatStream({encoding: 'string'}, function (data) {
assert.equal(data, expected);
done();
}));
});
}
describe('Stream mode', function () {
it('should replace blocks', function (done) {
var fixture = fs.createReadStream(path.join('test', 'fixture.html'));
var expected = fs.readFileSync(path.join('test', 'expected.html'), 'utf8');
var stream = plugin({
css: 'css/combined.css',
js_files: ['js/one.js', 'js/two.js?ts=123', 'js/three.js?v=v1.5.3-1-g91cd575'],
js_files_tpl: {
src: 'js/with_tpl.js',
tpl: '<script src="%s"></script>'
},
js_files_tpl_multiple: {
src: ['js/with_tpl.js', 'js/with_tpl_2.js'],
tpl: '<script src="%s"></script>'
},
js_files_tpl_2vars: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js']],
tpl: '<script data-main="%s" src="%s"></script>'
},
js_files_tpl_2vars_multiple: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js'], ['js/with_tpl_2vars1_2.js', 'js/with_tpl_2vars2_2.js']],
tpl: '<script data-main="%s" src="%s"></script>'
},
js_files_x_tpl: {
src: null,
tpl: '<script src="js/%f.min.js"></script>'
},
js_files_x_tpl_src: {
src: 'js',
tpl: '<script src="%s/%f.min.js"></script>'
},
js_files_x_tpl_multiple: {
src: ['js/with_tpl.js', 'js/with_tpl_2.js'],
tpl: '<script data-src="%f.data" src="%s"></script>'
},
js_files_x_tpl_2vars: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js']],
tpl: '<script data-src="%f%e" data-main="%s" src="%s"></script>'
},
js_files_x_tpl_2vars_multiple: {
src: [['js/with_tpl_2vars1.js', 'js/with_tpl_2vars2.js'], ['js/with_tpl_2vars1_2.js', 'js/with_tpl_2vars2_2.js']],
tpl: '<script data-src="%f.data" data-main="%s" src="%s"></script>'
},
'lorem-ipsum': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'stream-simple': stringToStream('Stream simple replacement').pipe(source('fake-vinyl.txt')),
'stream-advanced': {
src: stringToStream('Stream advanced replacement').pipe(source('fake-vinyl.txt'))
},
'stream-special': {
src: stringToStream('Stream $$ special replacement pattern').pipe(source('fake-vinyl.txt'))
}
});
compare(fixture, expected, stream, done);
});
it('should work with inline html', function (done) {
var fixture = '<!DOCTYPE html><head><!-- build:css --><link rel="stylesheet" href="_index.prefix.css"><!-- endbuild --></head>';
var expected = '<!DOCTYPE html><head><link rel="stylesheet" href="css/combined.css"></head>';
var stream = plugin({css: 'css/combined.css'});
compare(stringToStream(fixture), expected, stream, done);
});
it('should not fail if there are no build tags at all', function (done) {
var fixture = '<!DOCTYPE html><head><link rel="stylesheet" href="_index.prefix.css"></head>';
var stream = plugin({css: 'css/combined.css'});
compare(stringToStream(fixture), fixture, stream, done);
});
describe('Options', function () {
describe('keepUnassigned', function () {
it('Should keep empty blocks', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\nSome text\n</html>';
var stream = plugin({}, {keepUnassigned: true});
compare(stringToStream(fixture), expected, stream, done);
});
it('Should remove empty blocks', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n</html>';
var stream = plugin();
compare(stringToStream(fixture), expected, stream, done);
});
});
describe('keepBlockTags', function () {
it('Should keep placeholder tags without arguments', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n<!-- build:js -->\n<!-- endbuild -->\n</html>';
var stream = plugin({}, {keepBlockTags: true});
compare(stringToStream(fixture), expected, stream, done);
});
it('Should keep placeholder tags with arguments', function (done) {
var fixture = '<html>\n<!-- build:lorem -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n<!-- build:lorem -->\nipsum\n<!-- endbuild -->\n</html>';
var stream = plugin({lorem: 'ipsum'}, {keepBlockTags: true});
compare(stringToStream(fixture), expected, stream, done);
});
it('Should remove placeholder tags without arguments', function (done) {
var fixture = '<html>\n<!-- build:js -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\n</html>';
var stream = plugin();
compare(stringToStream(fixture), expected, stream, done);
});
it('Should remove placeholder tags with arguments', function (done) {
var fixture = '<html>\n<!-- build:lorem -->\nSome text\n<!-- endbuild -->\n</html>';
var expected = '<html>\nipsum\n</html>';
var stream = plugin({lorem: 'ipsum'});
compare(stringToStream(fixture), expected, stream, done);
});
it('Should keep indentation', function (done) {
var fixture = '<html>\n <!-- build:js -->\n Some text\n <!-- endbuild -->\n</html>';
var expected = '<html>\n <!-- build:js -->\n <!-- endbuild -->\n</html>';
var stream = plugin({}, {keepBlockTags: true});
compare(stringToStream(fixture), expected, stream, done);
});
});
describe('resolvePaths', function () {
it('Should resolve relative paths', function (done) {
var fixture = '<html>\n<!-- build:js -->\n<script src="file.js"></script>\n<!-- endbuild -->\n</html>';
var expected = '<html>\n<script src="../../lib/script.js"></script>\n</html>';
var stream = plugin({js: 'lib/script.js'}, {resolvePaths: true});
compare(stringToStream(fixture), expected, stream, done);
});
});
});
describe('Legacy versions', function () {
it('[version <1.2] should keep empty blocks (keepUnused = true)', function (done) {
var fixture = '<html>\n<!-- build:js -->\nThis should not be removed\n<!-- endbuild -->\n</html>';
var expected = '<html>\nThis should not be removed\n</html>';
var stream = plugin({}, true);
compare(stringToStream(fixture), expected, stream, done);
});
it('[version <1.2] should remove empty blocks (keepUnused = false)', function (done) {
var fixture = '<html>\n<!-- build:js -->\nThis should be removed\n<!-- endbuild -->\n</html>';
var expected = '<html>\n</html>';
var stream = plugin();
compare(stringToStream(fixture), expected, stream, done);
});
});
});
gitextract_3frrgmvh/
├── .gitignore
├── .travis.yml
├── LICENSE
├── appveyor.yml
├── lib/
│ ├── block.js
│ ├── common.js
│ ├── index.js
│ └── parser.js
├── package.json
├── readme.md
└── test/
├── buffer.js
├── expected.html
├── fixture.html
├── null.js
└── stream.js
SYMBOL INDEX (5 symbols across 4 files)
FILE: lib/common.js
function isStream (line 6) | function isStream(obj) {
function resolveSrcString (line 19) | function resolveSrcString(srcProperty) {
FILE: lib/parser.js
function Parser (line 20) | function Parser(tasks, config, file) {
FILE: test/buffer.js
function compare (line 11) | function compare(fixture, expected, stream, done) {
FILE: test/stream.js
function compare (line 12) | function compare(fixture, expected, stream, done) {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (41K chars).
[
{
"path": ".gitignore",
"chars": 48,
"preview": ".idea\nThumbs.db\n.DS_Store\nnode_modules\ncoverage\n"
},
{
"path": ".travis.yml",
"chars": 107,
"preview": "language: node_js\nnode_js:\n - \"0.10\"\n - \"0.11\"\n - \"0.12\"\n - \"iojs\"\nafter_script:\n - npm run coveralls\n"
},
{
"path": "LICENSE",
"chars": 1108,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Vladimir Kucherenko <kvsoftware@gmail.com>\n\nPermission is hereby granted, free"
},
{
"path": "appveyor.yml",
"chars": 303,
"preview": "environment:\n matrix:\n # node.js\n - nodejs_version: \"0.10\"\n - nodejs_version: \"0.11\"\n - nodejs_version: \"0.12\"\n # "
},
{
"path": "lib/block.js",
"chars": 2985,
"preview": "'use strict';\n\nvar util = require('util');\nvar path = require('path');\nvar slash = require('slash');\n\nvar Block = functi"
},
{
"path": "lib/common.js",
"chars": 3503,
"preview": "'use strict';\n\nvar Promise = require('bluebird');\nvar buffer = require('vinyl-buffer');\n\nfunction isStream(obj) {\n re"
},
{
"path": "lib/index.js",
"chars": 1701,
"preview": "'use strict';\n\nvar clone = require('clone');\nvar objectAssign = require('object-assign');\nvar Transform = require('reada"
},
{
"path": "lib/parser.js",
"chars": 1053,
"preview": "'use strict';\n\nvar util = require('util');\nvar Transform = require('readable-stream/transform');\nvar Block = require('./"
},
{
"path": "package.json",
"chars": 1223,
"preview": "{\n \"name\": \"gulp-html-replace\",\n \"version\": \"1.6.2\",\n \"description\": \"Replace build blocks in HTML. Like useref but d"
},
{
"path": "readme.md",
"chars": 6884,
"preview": "# gulp-html-replace [![NPM version][npm-image]][npm-url] [![Travis][travis-image]][travis-url] [![AppVeyor][appveyor-ima"
},
{
"path": "test/buffer.js",
"chars": 8303,
"preview": "'use strict';\n\nvar plugin = require('..');\nvar fs = require('fs');\nvar path = require('path');\nvar File = require('vinyl"
},
{
"path": "test/expected.html",
"chars": 1362,
"preview": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n <meta charset=\"UTF-8\">\n <title>Test file</title>\n\n <link rel=\"styleshe"
},
{
"path": "test/fixture.html",
"chars": 1443,
"preview": "<!DOCTYPE html>\n<html>\n<head lang=\"en\">\n <meta charset=\"UTF-8\">\n <title>Test file</title>\n\n <!-- build:css -->\n"
},
{
"path": "test/null.js",
"chars": 372,
"preview": "'use strict';\n\nvar plugin = require('..');\nvar File = require('vinyl');\nvar assert = require('assert');\n\ndescribe('null "
},
{
"path": "test/stream.js",
"chars": 8427,
"preview": "'use strict';\n\nvar plugin = require('..');\nvar fs = require('fs');\nvar path = require('path');\nvar File = require('vinyl"
}
]
About this extraction
This page contains the full source code of the VFK/gulp-html-replace GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (37.9 KB), approximately 9.7k tokens, and a symbol index with 5 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.