Comparing version 0.0.3 to 0.1.0
History | ||
======= | ||
## 0.1.0 | ||
* Add `_templatesFilter` default derived special variable support. | ||
[#4](https://github.com/FormidableLabs/denim/issues/4) | ||
* Change internal `.gitignore` default filtering to use the resolved path name | ||
(e.g., `"foo/bar.txt"`) instead of unexpanded template path (e.g., | ||
`"{{varForFoo}}/bar.txt"`). | ||
## 0.0.3 | ||
@@ -5,0 +13,0 @@ |
@@ -33,2 +33,15 @@ "use strict"; | ||
// Filter function for inclusion of resolved template files. | ||
// | ||
// Should call back with a function of the signature `(filePath, isIncluded)` | ||
// where the parameters are: | ||
// - `filePath`: resolved filepath of a template file | ||
// - `isIncluded`: boolean indicating if denim default logic would include | ||
// - return value: a boolean w/ `true` for "keep", `false` for "exclude" | ||
_templatesFilter: function (data, cb) { | ||
cb(null, function (filePath, isIncluded) { | ||
return isIncluded; // Simple proxy default included value. | ||
}); | ||
}, | ||
// `.npmignore` and `.gitignore` need to be proxied as a template to avoid | ||
@@ -35,0 +48,0 @@ // NPM losing dev files in `init/` when uploading and executing `npm pack` |
@@ -282,2 +282,5 @@ "use strict"; | ||
filterTemplates: ["templatesDir", "walkTemplates", "loadIgnore", function (results, cb) { | ||
// Get filter function. | ||
var templatesFilter = self.data._templatesFilter; | ||
// Get ignore filter (if any). | ||
@@ -292,4 +295,10 @@ var ignoreSrc = (results.loadIgnore || "").toString(); | ||
var filtered = results.walkTemplates.filter(function (stat) { | ||
var relPath = path.relative(results.templatesDir, stat.path); | ||
return gitignore.accepts(relPath); | ||
// Get relative, resolved path. | ||
var relPath = path.relative(results.templatesDir, stat.resolvedPath); | ||
// Default include algorithm. | ||
var isIncluded = gitignore.accepts(relPath); | ||
// Push to overridable filter function. | ||
return templatesFilter(relPath, isIncluded); | ||
}); | ||
@@ -296,0 +305,0 @@ |
{ | ||
"name": "denim", | ||
"version": "0.0.3", | ||
"version": "0.1.0", | ||
"description": "A lightweight, npm-based template engine.", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -20,2 +20,3 @@ [![Travis Status][trav_img]][trav_site] | ||
- [Templates Module Data](#templates-module-data) | ||
- [Special Variables](#special-variables) | ||
- [Imports and Dependencies](#imports-and-dependencies) | ||
@@ -173,2 +174,23 @@ - [User Prompts](#user-prompts) | ||
#### Special Variables | ||
There are several default data fields provided by denim that can be overridden | ||
in `denim.js` configuration files. A brief list: | ||
* _Control_ | ||
* `_templatesDir` (`"templates"`): The directory root of the templates to | ||
use during inflation. | ||
* `_templatesFilter` (_a noop function_): A function with the signature | ||
`(filePath, isIncluded)` where `filePath` is the resolved path to a file | ||
(relative to templates dir), and `isIncluded` is a boolean indicating | ||
whether or not denim would ordinarily include it (e.g., it is not excluded | ||
by the `.gitignore`). An overriding function should return `true` or | ||
`false` based on custom logic and can optionally use the `isIncluded` | ||
parameter from denim's default logic. | ||
* _File naming helpers_ | ||
* `_gitignore` (`".gitignore"`) | ||
* `_npmignore` (`".npmignore"`) | ||
* `_npmrc` (`".npmrc"`): | ||
* `_eslintrc` (`".eslintrc"`) | ||
#### Imports and Dependencies | ||
@@ -175,0 +197,0 @@ |
@@ -122,2 +122,77 @@ "use strict"; | ||
it("allows overriding templates filter", stdioWrap(function (done) { | ||
// Manually hack together a denim.js with functions. | ||
var denimJs = "module.exports = " + JSON.stringify({ | ||
prompts: { | ||
textVar: { message: "a text template path variable" } | ||
}, | ||
derived: { | ||
_templatesFilter: "TOKEN_TEMPLATES_FILTER", | ||
oneVar: "TOKEN_ONE_VAR" | ||
} | ||
}) | ||
.replace("\"TOKEN_TEMPLATES_FILTER\"", function (data, cb) { | ||
cb(null, function (filePath, isIncluded) { | ||
// Start with excludes... | ||
return filePath.indexOf("one/") !== 0 && // Remove anything starting with "one/" | ||
filePath !== "two/nuke.txt" && // Exact file path exclusion | ||
isIncluded // Default | ||
|| | ||
// Then, unconditional includes that override... | ||
filePath === "two/alsoignored.txt"; // Override gitignore to include | ||
}); | ||
}.toString()) | ||
.replace("\"TOKEN_ONE_VAR\"", function (data, cb) { | ||
cb(null, "one"); | ||
}.toString()); | ||
var stubs = mockFlow({ | ||
"denim.js": denimJs, | ||
"templates": { | ||
".gitignore": "*/gitignored.txt\n*/alsoignored.txt", | ||
"one": { | ||
// Normal path. Excluded because of "one". | ||
"foo.js": "module.exports = { foo: 42 };" | ||
}, | ||
"{{oneVar}}": { | ||
// Path that is resolved through template variable. Excluded because of "one". | ||
"bar.js": "module.exports = { bar: 'bar' };" | ||
}, | ||
"two": { | ||
// Should be kept. | ||
"baz.js": "module.exports = { baz: 'baz' };", | ||
"{{textVar}}.txt": "texty mctextface", | ||
// Git ignored, but custom filter overrides with include. | ||
"alsoignored.txt": "kept with custom fn", | ||
// Should be excluded by "two/nuke.txt" match. | ||
"nuke.txt": "nuke me", | ||
// Should be excluded by .gitignore. | ||
"gitignored.txt": "git hates me" | ||
} | ||
} | ||
}); | ||
// Note: These have to match prompt fields + `destination` in order. | ||
stubs.prompt | ||
.reset() | ||
.onCall(0).yields("text") | ||
.onCall(1).yields("dest"); | ||
init({ argv: ["node", SCRIPT, "mock-module"] }, function (err) { | ||
if (err) { return void done(err); } | ||
expect(base.fileRead("dest/.gitignore")).to.contain("gitignored.txt"); | ||
expect(base.fileRead("dest/two/baz.js")).to.contain("module.exports = { baz: 'baz' };"); | ||
expect(base.fileRead("dest/two/text.txt")).to.contain("texty mctextface"); | ||
expect(base.fileRead("dest/two/alsoignored.txt")).to.contain("kept with custom fn"); | ||
expect(base.fileExists("dest/one/foo.js")).to.be.false; | ||
expect(base.fileExists("dest/one/bar.js")).to.be.false; | ||
expect(base.fileExists("dest/two/nuke.txt")).to.be.false; | ||
expect(base.fileExists("dest/two/gitignored.txt")).to.be.false; | ||
done(); | ||
}); | ||
})); | ||
it("initializes a basic project", stdioWrap(function (done) { | ||
@@ -124,0 +199,0 @@ var stubs = mockFlow({ |
@@ -8,3 +8,2 @@ "use strict"; | ||
var base = require("../base.spec"); | ||
var addDefaults = base.addPromptDefaults.bind(base); | ||
@@ -53,3 +52,20 @@ // Helpers | ||
/** | ||
* We clone functions in mocking configs which makes deep comparisons fail. | ||
* This method mutates the final data object to be more "comparable" by | ||
* doing things like stringifying functions. | ||
* | ||
* @param {Object} data data to compared | ||
* @returns {Object} normalized data | ||
*/ | ||
var norm = function (data) { | ||
return _.mapValues(data, function (val) { | ||
return _.isFunction(val) ? val.toString() : val; | ||
}); | ||
}; | ||
var addDefaults = function (data) { | ||
return norm(base.addPromptDefaults(data)); | ||
}; | ||
describe("lib/prompts", function () { | ||
@@ -115,9 +131,9 @@ var runStub; | ||
promptsWithData({}, function (data) { | ||
expect(data).to.deep.equal(addDefaults()); | ||
expect(norm(data)).to.deep.equal(addDefaults()); | ||
}), | ||
promptsWithData({ prompts: [] }, function (data) { | ||
expect(data).to.deep.equal(addDefaults()); | ||
expect(norm(data)).to.deep.equal(addDefaults()); | ||
}), | ||
promptsWithData({ prompts: {}, derived: {} }, function (data) { | ||
expect(data).to.deep.equal(addDefaults()); | ||
expect(norm(data)).to.deep.equal(addDefaults()); | ||
}) | ||
@@ -136,3 +152,3 @@ ], done); | ||
expect(runStub).to.not.be.called; | ||
expect(data).to.deep.equal(addDefaults({ name: "Bob" })); | ||
expect(norm(data)).to.deep.equal(addDefaults({ name: "Bob" })); | ||
})(done); | ||
@@ -145,2 +161,3 @@ }); | ||
async.series([ | ||
// The basics. | ||
promptsWithData({ | ||
@@ -152,6 +169,17 @@ derived: { | ||
}, function (data) { | ||
expect(data).to.deep.equal(addDefaults({ foo: "foo", bar: "bar" })); | ||
expect(norm(data)).to.deep.equal(addDefaults({ foo: "foo", bar: "bar" })); | ||
}), | ||
// Override special function-based variables. | ||
promptsWithData({ | ||
derived: { | ||
_templatesFilter: function (data, cb) { cb(null, function () { return true; }); } | ||
} | ||
}, function (data) { | ||
expect(norm(data)).to.deep.equal(addDefaults({ | ||
_templatesFilter: function () { return true; } | ||
})); | ||
}), | ||
// Deferred resolutions. | ||
promptsWithData({ | ||
derived: { | ||
deferred: function (data, cb) { | ||
@@ -164,3 +192,3 @@ // Defer, then advance faked time. | ||
}, function (data) { | ||
expect(data).to.deep.equal(addDefaults({ deferred: "foo" })); | ||
expect(norm(data)).to.deep.equal(addDefaults({ deferred: "foo" })); | ||
}) | ||
@@ -214,3 +242,3 @@ ], done); | ||
}, function (data) { | ||
expect(data).to.deep.equal(addDefaults({ licenseDate: "2016" })); | ||
expect(norm(data)).to.deep.equal(addDefaults({ licenseDate: "2016" })); | ||
}), | ||
@@ -230,3 +258,3 @@ | ||
}, function (data) { | ||
expect(data).to.deep.equal(addDefaults({ | ||
expect(norm(data)).to.deep.equal(addDefaults({ | ||
packageName: "whiz-bang", | ||
@@ -256,3 +284,3 @@ packageDescription: "The Whiz Bang" | ||
}, function (data) { | ||
expect(data).to.deep.equal(addDefaults({ | ||
expect(norm(data)).to.deep.equal(addDefaults({ | ||
year: "2016", | ||
@@ -279,3 +307,3 @@ reverseYear: "6102", | ||
}, function (data) { | ||
expect(data).to.deep.equal(addDefaults({ | ||
expect(norm(data)).to.deep.equal(addDefaults({ | ||
foo: "prompts" | ||
@@ -282,0 +310,0 @@ })); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
122539
2398
497