pax_global_header00006660000000000000000000000064130526663510014521gustar00rootroot0000000000000052 comment=d39ac35e5272411171ae5ac89a8e416eb7a674eb tacks-1.2.6/000077500000000000000000000000001305266635100126345ustar00rootroot00000000000000tacks-1.2.6/.gitignore000066400000000000000000000000401305266635100146160ustar00rootroot00000000000000*~ .#* node_modules .nyc_output tacks-1.2.6/CHANGES.md000066400000000000000000000016121305266635100142260ustar00rootroot00000000000000# tacks ## v1.2.6 2017-02-20 * Sometimes links are created as symlinks, sometimes as junctions. Make the result on read be consistent regardless of source. ## v1.2.5 2017-02-20 * MORE consistently use unix-style path separators * Create symlinks if possible, use junctions as fallback only ## v1.2.4 2017-02-17 * Don't consider the final slash meaningful when comparing path names for equality. ## v1.2.3 2017-02-17 * Strip off Windows drive letter absolutes when creating symlinks just as we do with `/`. * Consistently use unix-style path separators on Windows. ## v1.2.2 2016-09-19 * Added repo info to package.json. Thank you @watilde! ## v1.2.1 2016-04-22 * Lots of internal-only refactoring/rewriting to simplify future additions. ## v1.2.0 2016-04-20 * Add tap comparer for tacks objects ## v1.1.0 2016-04-19 * Add new Symlink entry type ## v1.0.0 2016-01-26 * Initial release tacks-1.2.6/README.md000066400000000000000000000123651305266635100141220ustar00rootroot00000000000000## tacks Generate fixture modules from folders ### USAGE Generate a fixture from a folder on disk: ``` tacks /path/to/fixture/example > example.js ``` Create and destroy the fixture from your tests: ``` var Tacks = require('tacks') var Dir = Tacks.Dir var File = Tacks.File var Symlink = Tacks.Symlink // I like my fixture paths to match my test filename: var fixturepath = path.join(__dirname, path.basename(__filename, '.js')) var example = require('./example.js') example.create(fixturepath) … example.remove(fixturepath) ``` Or create your own fixture inline: ``` var example = new Tacks(Dir({ 'package.json': File({ name: 'example', version: '1.0.0' }) })) example.create(fixturepath) … example.remove(fixturepath) ``` ### STATUS This is very much a "release early" type release. Still very much in progress, but being used. ### CLASSES These are used in the generated code. It's totally legit to write them directly though. #### Consturctor ``` var fixture = new Tacks(Dir({ 'package.json': File({ name: 'example', version: '1.0.0' }) })) ``` Create a new fixture object based on a `Dir` object, see below. #### Create Fixture On Disk ``` fixture.create('/path/to/fixture') ``` Take the directory and files described by the fixture and create it in `/path/to/fixture` #### Remove Fixture From Disk ``` fixture.remove('/path/to/fixture') ``` Cleanup a fixture we installed in `/path/to/fixture`. #### Add Directory ``` var Dir = Tacks.Dir var mydir = Tacks.Dir(dirspec) ``` Creates a new `Dir` object for consumption by `new Tacks`. `dirspec` is a object whose properties are the names of files in a directory and whose values are either `File` objects, `Dir` objects or `Symlink` objects. #### Add File ``` var File = Tacks.File var myfile = Tacks.File(filespec) ``` Creates a new `File` object for use in `Dir` objects. `filespec` can be either a `String`, a `Buffer` or an `Object`. In the last case, it will be stringified with `JSON.stringify` before writing it to disk #### Add Symlink ``` var Symlink = Tacks.Symlink var mysymlink = Tacks.Symlink(destination) ``` Creates a new `Symlink` object for use in `Dir` objects. `destination` should either be relative to where the symlink is being created, or absolute relative to the root of the fixture. That is, `Tacks.Symlink('/')` will create a symlink pointing at the fixture root. #### Generate Fixture Object From Directory ``` var loadFromDir = require('tacks/load-from-dir') var onDisk = loadFromDir('tests/example') ``` The value returned is a `Tacks` object that you can call `create` or `remove` on. It's also handy for using in tests use compare an in memory tacks fixture to whatever ended up on disk. #### Assert Two Fixtures The Same With node-tap ``` var test = require('tap').test var tacksAreTheSame = require('tacks/tap').areTheSame test('example', function (t) { return tacksAreTheSame(t, actual, expected, 'got the expected results') }) ``` The `tacks/tap` submodule is the start of tap assertions for comparing fixtures. `areTheSame` creates a subtest, and inside that subtest runs a bunch of assertions comparing the contents of the two models. It's smart enough to consider `tacks` equivalent things equal, eg strings & buffers with the same content. Because it creates a subtest, it's async, it returns the subtest (which is also a promise) so you can either return it yourself and your test will complete when it does, or do something like: ``` tacksAreTheSame(t, actual, expected, 'got the expected results').then(t.done) ``` or ``` tacksAreTheSame(t, actual, expected, 'got the expected results').then(function () { … more tests … t.done() }) ``` #### Geneate JavaScript From Directory ``` var generateFromDir = require('tacks/generate-from-dir') var fixturestr = Tacks.generateFromDir(dir) ``` This is what's used by the commandline– it generates javascript as a string from a directory on disk. It works hard to produce something that looks like it might have been typed by a human– It translates JSON on disk into object literals. And it doesn't quote property names in object literals unless it has to. It uses single quotes when it can. It double quotes when it has to, and escapes when it has no other choice. It includs plain text as strings concatenated one per line. For everything else it makes Buffer objects using hex encoded strings as input. ### WANT TO HAVES These are things I'll do sooner or late myself. * Include adding a `.mockFs('/tmp/fixture/path/')` function which returns a patched version of `fs` that, for attempts to read from `/tmp/fixture/path` returns data from the in memory fixture instead of looking at the filesystem. For injection into tested modules with something like `require-inject`. ### NICE TO HAVES I'd love to see these, but I may never get time to do them myself. If someone else did them though… * Having some way to control the formatting of the generated output would be nice for folks who don't use `standard`… eg, semicolons, indentation, default quoting. The right answer might be to generate AST objects for use by an existing formatter. Relatedly, it'd be nice to have some standard extension method for the generated sourcecode. Right now I make use of it just by concattenating source code. tacks-1.2.6/as-literal.js000066400000000000000000000031301305266635100152240ustar00rootroot00000000000000'use strict' module.exports = asLiteral var asObjectKey = require('./as-object-key.js') function asLiteral (thing) { if (thing === null) return 'null' if (thing == null) return 'undefined' if (typeof thing === 'boolean' || typeof thing === 'number') { return thing.toString() } else if (typeof thing === 'string') { return asStringLiteral(thing) } else if (thing instanceof Array) { return asArrayLiteral(thing) } else { return asObjectLiteral(thing) } } function asStringLiteral (thing) { var str = thing.toString() .replace(/\\/g, '\\\\') .replace(/[\0]/g, '\\0') .replace(/[\b]/g, '\\b') .replace(/[\f]/g, '\\f') .replace(/[\n]/g, '\\n') .replace(/[\r]/g, '\\r') .replace(/[\t]/g, '\\t') .replace(/[\v]/g, '\\v') if (/'/.test(str) && !/"/.test(str)) { return '"' + str + '"' } else { return "'" + str.replace(/'/g, "\\'") + "'" } } function asArrayLiteral (thing) { if (!thing.length) return '[ ]' var arr = '[\n' function arrayItem (item) { return asLiteral(item).replace(/\n(.*)(?=\n)/g, '\n $1') } arr += arrayItem(thing.shift()) thing.forEach(function (item) { arr += ',\n' + arrayItem(item) }) arr += '\n]' return arr } function asObjectLiteral (thing) { var keys = Object.keys(thing) if (!keys.length) return '{ }' var obj = '{\n' function objectValue (key) { return asObjectKey(key) + ': ' + asLiteral(thing[key]).replace(/\n(.*)(?=\n)/g, '\n $1') } obj += objectValue(keys.shift()) keys.forEach(function (key) { obj += ',\n' + objectValue(key) }) obj += '\n}' return obj } tacks-1.2.6/as-object-key.js000066400000000000000000000003251305266635100156270ustar00rootroot00000000000000'use strict' module.exports = asObjectKey var asLiteral = require('./as-literal.js') function asObjectKey (key) { var isIdent = /^[a-zA-Z$_][a-zA-Z$_0-9]+$/.test(key) return isIdent ? key : asLiteral(key) } tacks-1.2.6/cmdline.js000077500000000000000000000003651305266635100146140ustar00rootroot00000000000000#!/usr/bin/env node 'use strict' var argv = require('yargs') .usage('Usage: $0 fixturedir') .demand(1, 1) .argv var generateFromDir = require('./generate-from-dir.js') var fixturedata = generateFromDir(argv._[0]) console.log(fixturedata) tacks-1.2.6/dir.js000066400000000000000000000027361305266635100137600ustar00rootroot00000000000000'use strict' var path = require('path') var mkdirp = require('mkdirp') var inherits = require('util').inherits var Entry = require('./entry') var asObjectKey = require('./as-object-key.js') module.exports = Dir function Dir (contents) { if (this == null) return new Dir(contents) Entry.call(this, 'dir', contents || {}) } inherits(Dir, Entry) Dir.prototype.forContents = function (cb) { var contentNames = Object.keys(this.contents) for (var ii in contentNames) { var name = contentNames[ii] cb.call(this, this.contents[name], name, ii, contentNames) } } Dir.prototype.computePath = function (entitypath) { Entry.prototype.computePath.call(this, entitypath) this.forContents(function (content, name) { content.computePath(path.join(entitypath, name)) }) } Dir.prototype.create = function (where) { var subdirpath = path.resolve(where, this.path) mkdirp.sync(subdirpath) this.forContents(function (content) { content.create(where) }) } Dir.prototype.remove = function (where) { this.forContents(function (content) { content.remove(where) }) Entry.prototype.remove.call(this, where) } Dir.prototype.toSource = function () { var output = 'Dir({\n' this.forContents(function (content, filename, ii, keys) { var key = asObjectKey(filename) var value = content.toSource() output += ' ' + key + ': ' + value.replace(/(\n)(.)/mg, '$1 $2') if (ii < keys.length - 1) output += ',' output += '\n' }) return output + '})' } tacks-1.2.6/entry.js000066400000000000000000000011321305266635100143300ustar00rootroot00000000000000'use strict' var path = require('path') var rimraf = require('rimraf') module.exports = Entry function Entry (type, contents) { this.type = type this.contents = contents this.path = null } Entry.prototype = {} Entry.prototype.forContents = function (cb) { cb.call(this, this.contents) } Entry.prototype.computePath = function (entitypath) { this.path = path.relative('/', entitypath) } Entry.prototype.create = function (where) { throw new Error("Don't know how to create " + this.constructor.name + " at " + where) } Entry.prototype.remove = function (where) { rimraf.sync(where) } tacks-1.2.6/file.js000066400000000000000000000037341305266635100141200ustar00rootroot00000000000000'use strict' var path = require('path') var fs = require('fs') var inherits = require('util').inherits var Entry = require('./entry') var asLiteral = require('./as-literal.js') module.exports = File function File (contents) { if (this == null) return new File(contents) this.type = 'file' if (typeof contents === 'object' && !Buffer.isBuffer(contents)) { contents = JSON.stringify(contents) } Entry.call(this, 'file', contents) } inherits(File, Entry) File.prototype.create = function (where) { fs.writeFileSync(path.resolve(where, this.path), this.contents) } function tryJSON (str) { try { return JSON.parse(str) } catch (ex) { return } } File.prototype.toSource = function () { if (this.contents.length === 0) return "File('')" var output = 'File(' var fromJson = tryJSON(this.contents) if (fromJson != null) { var jsonStr = asLiteral(fromJson) if (/^[\[{]/.test(jsonStr)) { output += jsonStr.replace(/\n/g, '\n ') .replace(/[ ]{2}([}\]])$/, '$1)') } else { output += jsonStr + '\n )' } } else if (/[^\-\w\s~`!@#$%^&*()_=+[\]{}|\\;:'",./<>?]/.test(this.contents.toString())) { output += outputAsBuffer(this.contents) .replace(/[)]$/, '\n))') } else { output += outputAsText(this.contents) + '\n)' } return output } function outputAsText (content) { content = content.toString('utf8') var endsInNewLine = /\n$/.test(content) var lines = content.split(/\n/).map(function (line) { return line + '\n' }) if (endsInNewLine) lines.pop() var output = '\n ' + asLiteral(lines.shift()) lines.forEach(function (line) { output += ' +\n ' + asLiteral(line) }) return output } function outputAsBuffer (content) { var chunks = content.toString('hex').match(/.{1,60}/g) var output = 'new Buffer(\n' output += " '" + chunks.shift() + "'" chunks.forEach(function (chunk) { output += ' +\n ' + "'" + chunk + "'" }) output += ",\n 'hex')" return output } tacks-1.2.6/generate-from-dir.js000066400000000000000000000005071305266635100165030ustar00rootroot00000000000000'use strict' var loadFromDir = require('./load-from-dir.js') module.exports = function generateFromDir (dir) { return "var Tacks = require('tacks')\n" + 'var File = Tacks.File\n' + 'var Symlink = Tacks.Symlink\n' + 'var Dir = Tacks.Dir\n' + 'module.exports = ' + loadFromDir(dir).toSource() + '\n' } tacks-1.2.6/index.js000066400000000000000000000001671305266635100143050ustar00rootroot00000000000000'use strict' module.exports = require('./tacks.js') module.exports.generateFromDir = require('./generate-from-dir.js') tacks-1.2.6/load-from-dir.js000066400000000000000000000032731305266635100156330ustar00rootroot00000000000000'use strict' var path = require('path') var fs = require('graceful-fs') var Tacks = require('./tacks.js') var File = Tacks.File var Dir = Tacks.Dir var Symlink = Tacks.Symlink module.exports = function (dir) { return new Tacks(loadFromDir(dir)) } function fromJSON (str) { try { return JSON.parse(str) } catch (ex) { return } } function loadFromDir (dir, top) { if (!top) top = dir var dirInfo = {} fs.readdirSync(dir).forEach(function (filename) { if (filename === '.git') return var filepath = path.join(dir, filename) var fileinfo = fs.lstatSync(filepath) if (fileinfo.isSymbolicLink()) { var dest = fs.readlinkSync(filepath).replace(/[\\/]$/, '') var absDest = path.resolve(path.dirname(filepath), dest) var relativeDest = path.relative(path.dirname(filepath), absDest) // if we're two or more levels up, plot relative to the top level instead of // the link point if (/^\.\.[/\\]\.\.[/\\]/.test(relativeDest.slice(0,6))) { dest = '/' + path.relative(top, absDest) } else { dest = relativeDest } dirInfo[filename] = Symlink(dest.replace(/\\/g, '/')) } else if (fileinfo.isDirectory()) { dirInfo[filename] = loadFromDir(filepath, top) } else { var content = fs.readFileSync(filepath) var contentStr = content.toString('utf8') var contentJSON = fromJSON(contentStr) if (contentJSON !== undefined) { dirInfo[filename] = File(contentJSON) } else if (/[^\-\w\s~`!@#$%^&*()_=+[\]{}|\\;:'",./<>?]/.test(contentStr)) { dirInfo[filename] = File(content) } else { dirInfo[filename] = File(contentStr) } } }) return Dir(dirInfo) } tacks-1.2.6/package.json000066400000000000000000000012721305266635100151240ustar00rootroot00000000000000{ "name": "tacks", "version": "1.2.6", "description": "Generate fixture modules from folders", "main": "index.js", "bin": { "tacks": "cmdline.js" }, "scripts": { "test": "tap test/*.js" }, "repository": { "type": "git", "url": "https://github.com/iarna/tacks" }, "keywords": [], "author": "Rebecca Turner (http://re-becca.org/)", "license": "ISC", "bugs": { "url": "https://github.com/iarna/tacks/issues" }, "homepage": "https://github.com/iarna/tacks", "dependencies": { "graceful-fs": "^4.1.2", "mkdirp": "^0.5.1", "rimraf": "^2.5.1", "yargs": "^3.32.0" }, "devDependencies": { "tap": "^5.7.1" } } tacks-1.2.6/symlink.js000066400000000000000000000016431305266635100146640ustar00rootroot00000000000000'use strict' var path = require('path') var fs = require('fs') var inherits = require('util').inherits var Entry = require('./entry') var asLiteral = require('./as-literal.js') module.exports = Symlink function Symlink (dest) { if (this == null) return new Symlink(dest) if (dest == null || dest === '') throw new Error('Symlinks must have a destination') Entry.call(this, 'symlink', dest) } inherits(Symlink, Entry) Symlink.prototype.create = function (where) { var filepath = path.resolve(where, this.path) var dest = this.contents if (dest[0] === '/') { dest = path.resolve(where, dest.slice(1)) } else if (/^\w:[\\/]/.test(dest)) { dest = path.resolve(where, dest.slice(3)) } try { fs.symlinkSync(dest, filepath, 'directory') } catch (_) { fs.symlinkSync(dest, filepath, 'junction') } } Symlink.prototype.toSource = function () { return 'Symlink(' + asLiteral(this.contents) + ')' } tacks-1.2.6/tacks.js000066400000000000000000000010671305266635100143030ustar00rootroot00000000000000'use strict' var fs = require('fs') module.exports = Tacks Tacks.File = require('./file.js') Tacks.Dir = require('./dir.js') Tacks.Symlink = require('./symlink.js') function Tacks (fixture) { this.fixture = fixture fixture.computePath('/') } Tacks.prototype = {} Tacks.prototype.create = function (location) { this.fixture.create(location) } Tacks.prototype.remove = function (location) { this.fixture.remove(location) } Tacks.prototype.toSource = function () { return 'new Tacks(\n' + this.fixture.toSource().replace(/(^|\n)/g, '$1 ') + '\n)' } tacks-1.2.6/tap.js000066400000000000000000000031361305266635100137610ustar00rootroot00000000000000'use strict' exports.areTheSame = function (tap, actual, expected, msg) { return tap.test(msg, function (t) { compare(t, '/', actual.fixture, expected.fixture) t.done() }) } function join (p1, p2) { if (p1 === '/') return p1 + p2 return p1 + '/' + p2 } function compare (t, path, actual, expected) { t.is(actual.type, expected.type, path + ': type') if (actual.type !== expected.type) return if (expected.type === 'dir') { return compareDir(t, path, actual, expected) } else if (expected.type === 'file') { return compareFile(t, path, actual, expected) } else if (expected.type === 'symlink') { return compareSymlink(t, path, actual, expected) } else { throw new Error('what? ' + expected.type) } } function compareDir (t, path, actual, expected) { expected.forContents(function (expectedContent, filename) { if (!actual.contents[filename]) { t.fail(join(path, filename) + ' missing file') return } compare(t, join(path, filename), actual.contents[filename], expectedContent) }) actual.forContents(function (_, filename) { if (!expected.contents[filename]) { t.fail(join(path, filename) + ' extraneous file') } }) } function compareFile (t, path, actual, expected) { if (Buffer.isBuffer(expected.contents)) { t.same(Buffer(actual.contents), expected.contents, path + ': file buffer content') } else { t.is(actual.contents.toString(), expected.contents, path + ': file string content') } } function compareSymlink(t, path, actual, expected) { t.is(actual.contents, expected.contents, path + ': symlink destination') } tacks-1.2.6/test/000077500000000000000000000000001305266635100136135ustar00rootroot00000000000000tacks-1.2.6/test/functional.js000066400000000000000000000042041305266635100163130ustar00rootroot00000000000000var fs = require('fs') var path = require('path') var tap = require('tap') var test = require('tap').test var rimraf = require('rimraf') var loadFromDir = require('../load-from-dir.js') var generateFromDir = require('../generate-from-dir.js') var tacksAreTheSame = require('../tap.js').areTheSame var Tacks = require('../index.js') var File = Tacks.File var Dir = Tacks.Dir var Symlink = Tacks.Symlink var testroot = path.join(__dirname, path.basename(__filename, '.js')) var testdir = path.join(testroot, 'example') var testmodule = path.join(testroot, 'example.js') var fixture = new Tacks( Dir({ 'a': Dir({ 'b': Dir({ 'c': Dir({ 'foo.txt': File(''), 'bar.txt': Symlink('foo.txt'), 'ascii.txt': Symlink('/ascii.txt') }) }) }), 'ascii.txt': File( 'abc\n' ), 'foo': Dir({ 'foo.txt': Symlink('/a/b/c/foo.txt') }), 'binary.gz': File(new Buffer( '1f8b0800d063115700034b4c4ae602004e81884704000000', 'hex' )), 'empty.txt': File(''), 'example.json': File({ 'a': true, 'b': 23, 'c': 'xyzzy', 'd': [], 'e': {}, 'f': null, 'complex': { 'xyz': [1, 2, 3, { abc: 'def' }], '123': false } }), 'x': Dir({ 'y': Dir({ 'z': Dir({ }) }) }) }) ) function setup () { fixture.create(testdir) } function cleanup () { fixture.remove(testdir) } test('setup', function (t) { rimraf.sync(testroot) setup() t.done() }) test('loadFromDir', function (t) { var model = loadFromDir(testdir) return tacksAreTheSame(t, model, fixture, 'loadFromDir') }) test('generateFromDir', function (t) { var js = generateFromDir(testdir) fs.writeFileSync(testmodule, js.replace(/'tacks'/g, "'../../index.js'")) var modelFromModule = require(testmodule) return tacksAreTheSame(t, modelFromModule, fixture, 'generateFromDir') }).catch(test.throws) test('cleanup', function (t) { cleanup() try { fs.statSync(testdir) t.fail(testdir + ' should not exist') } catch (ex) { t.pass(testdir + ' should not exist') } rimraf.sync(testroot) t.done() })