Skip to content
This repository has been archived by the owner on Apr 20, 2018. It is now read-only.

Rebase on #306 and additions #518

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ It uses only one target: `html`, with a list of the concerned files. For example
By default, it will consider the directory where the looked-at file is located as the 'root' filesystem. Each relative path (for example to a javascript file) will be resolved from this path. Same goes for the absolute ones.
If you need to change the 'root' dir, use the `root` option (see below).

Note that `useminPrepare` and `usemin` tasks will both throw an error if a resource is not found.

```js
useminPrepare: {
html: 'index.html'
Expand Down Expand Up @@ -255,6 +257,84 @@ useminPrepare: {

The given steps or post-processors may be specified as strings (for the default steps and post-processors), or as an object (for the user-defined ones).

### resolveSource

Type: 'function'
Default: `undefined`

This is an optional hook allowing applications to override how source urls
are resolved to filesystem paths. This function is called with the signature
`resolveSource(sourceUrl, fileDir, fileName, blockTarget, searchPath)`, where:

* `sourceUrl` is the source reference being resolved,
* `fileDir` and `fileName` are respectively, the directory and name of the file, the reference was found in,
* `blockTarget` is the target path for the block the reference occurred within,
* `searchPath` is an array of the directories that would be searched by default.

The return value of this function should be :
* the path to the source file,
* `null` to indicate the default resolution rules should be used to resolve `sourceUrl`,
* `false` to indicate the file cannot be resolved, thus default resolution rules are not used.

The default resolution is to search the `sourceUrl` in all provided entries in `searchPath` array.

For example:

* To override a specific URL prefix, and use the default behavior for all others:

```js
'useminPrepare', {
options: {
resolveSource: function (sourceUrl, fileDir, fileName, blockTarget, searchPath) {
var fs = require('fs');
var path = require('path');

var match = sourceUrl.match(/^\/alt-static-url\/(.*)/);
if (match) {
var source = path.join("alt-static-location", match[1]);
// return the override source path if found on filesystem
// else will default to null which means that resource will be search using
// default behavior
if (fs.existsSync(source)) {
return source;
}
/*
// You might be interested in return false if source cannot be resolved using the
// following code instead:
// it will return the overriden source path if resource found on filesystem
// or false if not found, meaning default behavior will not be used
return fs.existsSync(source) ? source : false;
*/
}
// returning null will match the default behavior for any url not matching
// the prefix `^/alt-static-url/`
return null;
}
}
}
```

* To replicate the default behavior, use the following version. It should be noted that this is only for illustration purpose, as the default behavior will be used if no `resolveSource` hook is provided, or if `resolveSource` fails to resolve the sourceUrl, hence returning null.

```js
'useminPrepare', {
options: {
resolveSource: function (sourceUrl, fileDir, fileName, blockTarget, searchPath) {
var fs = require('fs');
var path = require('path');
for (var i = 0; i < searchPath.length; ++i) {
var source = path.join(searchPath[i], fname);
if (fs.existsSync(source)) {
return source;
}
}
// source not found, so returning false as it cannot be resolved
return false;
}
}
}
```

#### User-defined steps and post-processors

User-defined steps and post-processors must have 2 attributes:
Expand Down
20 changes: 1 addition & 19 deletions lib/config/concat.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';
var path = require('path');
var fs = require('fs');
var _ = require('lodash');

exports.name = 'concat';

Expand All @@ -21,23 +19,7 @@ exports.createConfig = function (context, block) {
// Depending whether or not we're the last of the step we're not going to output the same thing
var files = {};
files.dest = outfile;
files.src = [];

context.inFiles.forEach(function (f) {
if (_.isArray(context.inDir)) {
context.inDir.every(function (d) {
var joinedPath = path.join(d, f);
var joinedPathExists = fs.existsSync(joinedPath);
if (joinedPathExists) {
files.src.push(joinedPath);
}
return !joinedPathExists;
});
} else {
files.src.push(path.join(context.inDir, f));
}
});

files.src = context.inFiles.map(context.resolveInFile, context);
cfg.files.push(files);
context.outFiles = [block.dest];
return cfg;
Expand Down
5 changes: 1 addition & 4 deletions lib/config/cssmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ exports.createConfig = function (context, block) {
// Depending whether or not we're the last of the step we're not going to output the same thing
var files = {};
files.dest = outfile;
files.src = [];
context.inFiles.forEach(function (f) {
files.src.push(path.join(context.inDir, f));
});
files.src = context.inFiles.map(context.resolveInFile, context);
cfg.files.push(files);
context.outFiles = [block.dest];
return cfg;
Expand Down
8 changes: 3 additions & 5 deletions lib/config/uglify.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ exports.createConfig = function (context, block) {
var files = {};
var ofile = path.join(context.outDir, block.dest);
files.dest = ofile;
files.src = context.inFiles.map(function (fname) {
return path.join(context.inDir, fname);
});
// cfg[ofile] = context.inFiles.map(function (fname) { return path.join(context.inDir, fname);});
files.src = context.inFiles.map(context.resolveInFile, context);
// cfg[ofile] = context.inFiles.map(context.resolveInFile, context);
cfg.files.push(files);
context.outFiles.push(block.dest);
} else {
context.inFiles.forEach(function (fname) {
var file = path.join(context.inDir, fname);
var file = context.resolveInFile(fname);
var outfile = path.join(context.outDir, fname);
cfg.files.push({
src: [file],
Expand Down
77 changes: 64 additions & 13 deletions lib/configwriter.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
'use strict';
var path = require('path');
var fs = require('fs');
var grunt = require('grunt');
var File = require('./file');
var _ = require('lodash');

var deepMerge = function (origCfg, cfg) {
var outCfg = origCfg;

// If the newly generated part is already in the config file
// with the same destination update only the source part, instead of add the whole block again.
// This way the same targets wont be regenerated multiple times if usemin is called
// multiple times which can happen with grunt-watcher spawn:false mode (#307)
// If the newly generated part is already in the config file
// with the same destination update only the source part, instead of add the whole block again.
// This way the same targets wont be regenerated multiple times if usemin is called
// multiple times which can happen with grunt-watcher spawn:false mode (#307)

if (origCfg.files && cfg.files) {
var done = false;
Expand Down Expand Up @@ -54,13 +56,17 @@ var deepMerge = function (origCfg, cfg) {
// - Deliver for each block the requested file under dist directory
//
//
var ConfigWriter = module.exports = function (flow, dirs) {
var ConfigWriter = module.exports = function (flow, dirs, options) {
var self = this;
this.flow = flow;
// FIXME: check dest and staging are furnished
this.root = dirs.root;
this.dest = dirs.dest;
this.staging = dirs.staging;

// optional hook to resolve source url
this.resolveSource = options && options.resolveSource;

this.steps = {};
this.postprocessors = [];
this.destinations = {};
Expand Down Expand Up @@ -152,6 +158,8 @@ ConfigWriter.prototype.forEachStep = function (blockType, cb) {
ConfigWriter.prototype.process = function (file, config) {
var self = this;
var lfile = file;
var rootPath = self.root || [];
if (!Array.isArray(rootPath)) { rootPath = [rootPath]; }

config = config || {};

Expand All @@ -160,16 +168,60 @@ ConfigWriter.prototype.process = function (file, config) {
}

lfile.blocks.forEach(function (block) {
// FIXME: support several searchPath...
// NOTE: initial context.inDir is only used by ./config/requirejs,
// everything else uses context.resolveInFile(),
// which checks entire searchPath.
var searchPath = block.searchPath.concat(rootPath, lfile.searchPath);
var context = {
inDir: self.root || lfile.searchPath[0],
inFiles: block.src,
outFiles: []
};

if (block.searchPath.length > 0) {
// FIXME: we must use all the furnished directories
context.inDir = block.searchPath[0];
function resolveInFile (fname) {
var source = null;
// if a resolveSource hook function is provided use it for file source
if (self.resolveSource) {
source = self.resolveSource(fname, lfile.dir, lfile.name, block.dest, searchPath);
if (source) {
if (!fs.existsSync(source)) {
grunt.fail.warn(_.template(
'usemin: resolveSource() returned non-existent path "<%= source %>"' +
' for source "<%= fname %>".')(
{
source: source,
fname: fname
}));
}
return source;
}
}
// default behavior to search file in provided search paths.
// * source being null meaning that no resolveSource hook provided
// * source being false meaning that
if (source !== false) {
for (var i = 0; i < searchPath.length; ++i) {
source = path.join(searchPath[i], fname);
if (fs.existsSync(source)) {
return source;
}
}
}
grunt.fail.warn(_.template(
'usemin: can\'t resolve source reference "<%= fname %>" ' +
'(file "<%= filepath %>", block "<%= block.dest %>").')(
{
fname: fname,
block: block,
filepath: path.join(lfile.dir, lfile.name)
}));
// we know it's not there, but return placeholder to match old behavior.
return path.join(searchPath[0], fname);
}
context.resolveInFile = resolveInFile;

function resolveTempFile(fname) {
return path.join(context.inDir, fname);
}

self.forEachStep(block.type, function (writer, last) {
Expand Down Expand Up @@ -207,15 +259,14 @@ ConfigWriter.prototype.process = function (file, config) {
}
context.inDir = context.outDir;
context.inFiles = context.outFiles;
context.resolveInFile = resolveTempFile;
context.outFiles = [];
context.options = null;
});

context.inDir = lfile.searchPath[0];
if (block.searchPath.length > 0) {
context.inDir = block.searchPath[0];
}
context.inDir = searchPath[0];
context.inFiles = block.src;
context.resolveInFile = resolveInFile;

if (self.postprocessors.hasOwnProperty(block.type)) {
self.postprocessors[block.type].forEach(function (pp) {
Expand Down
12 changes: 11 additions & 1 deletion tasks/usemin.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ module.exports = function (grunt) {
var blockReplacements = options.blockReplacements || {};

debug('Looking at %s target', this.target);

var patterns = [];
var type = this.target;

Expand All @@ -132,6 +133,11 @@ module.exports = function (grunt) {
nonull: true,
filter: 'isFile'
}, fileObj.src);

if (!files.length) {
grunt.fail.warn('No files found for target ' + type);
}

files.forEach(function (filename) {
debug('looking at file %s', filename);

Expand All @@ -158,6 +164,10 @@ module.exports = function (grunt) {
var staging = options.staging || '.tmp';
var root = options.root;

if (!this.filesSrc.length) {
grunt.fail.warn('No source file found.');
}

grunt.verbose
.writeln('Going through ' + grunt.log.wordlist(this.filesSrc) + ' to update the config')
.writeln('Looking for build script HTML comment blocks');
Expand All @@ -168,7 +178,7 @@ module.exports = function (grunt) {
root: root,
dest: dest,
staging: staging
});
}, options);

var cfgNames = [];
c.stepWriters().forEach(function (i) {
Expand Down
12 changes: 0 additions & 12 deletions test/fixtures/usemin.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,6 @@ <h3>Enjoy coding! - Yeoman</h3>
<!-- build:js /scripts/plugins.js -->
<script src="scripts/vendor/bootstrap/bootstrap-affix.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-alert.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-dropdown.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-tooltip.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-modal.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-transition.js"></script>

<script src="scripts/vendor/bootstrap/bootstrap-button.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-popover.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-typeahead.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-carousel.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-scrollspy.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-collapse.js"></script>
<script src="scripts/vendor/bootstrap/bootstrap-tab.js"></script>
<!-- endbuild -->

<a href="<?php echo URL::to('admin/create') ?>"><i class='icon-plus icon-white'></i>&nbsp;&nbsp;New</a>
Expand Down
16 changes: 16 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var rimraf = require('rimraf');
var mkdirp = require('mkdirp');
var _ = require('lodash');
var helpers = module.exports;
var grunt = require('grunt');

helpers.directory = function directory(dir) {
return function directory(done) {
Expand Down Expand Up @@ -139,3 +140,18 @@ helpers.normalize = function (object) {

return object;
};

var warn = grunt.fail.warn;

// mock grunt.fail.warn to avoid breaking test
helpers.mockGruntFailWarn = function (ctx, name) {
name = name || 'warnMessage';
grunt.fail.warn = function (message) {
ctx[name] = message;
};
};

// mock grunt.fail.warn to avoid breaking test
helpers.restoreGruntFailWarn = function () {
grunt.fail.warn = warn;
};
Loading