pax_global_header00006660000000000000000000000064132761300310014506gustar00rootroot0000000000000052 comment=7e223749ee2ada1abd3b2fb326178d8ad8f39f2c vinyl-fs-3.0.3/000077500000000000000000000000001327613003100132605ustar00rootroot00000000000000vinyl-fs-3.0.3/.editorconfig000066400000000000000000000003261327613003100157360ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 2 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf [*.md] trim_trailing_whitespace = false vinyl-fs-3.0.3/.eslintrc000066400000000000000000000000301327613003100150750ustar00rootroot00000000000000{ "extends": "gulp" } vinyl-fs-3.0.3/.gitattributes000066400000000000000000000000171327613003100161510ustar00rootroot00000000000000* text eol=lf vinyl-fs-3.0.3/.gitignore000066400000000000000000000013011327613003100152430ustar00rootroot00000000000000# Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory # Commenting this out is preferred by some people, see # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- node_modules # Users Environment Variables .lock-wscript # Garbage files .DS_Store # VSCode configuration files .vscode # Generated by integration tests test/out-fixtures/ vinyl-fs-3.0.3/.jscsrc000066400000000000000000000000271327613003100145470ustar00rootroot00000000000000{ "preset": "gulp" } vinyl-fs-3.0.3/.travis.yml000066400000000000000000000004721327613003100153740ustar00rootroot00000000000000sudo: required language: node_js node_js: - '9' - '8' - '6' - '5' - '4' - '0.12' - '0.10' before_script: - find test -type d -exec chmod g+s {} \; - sudo chown root test/fixtures/not-owned/not-owned.txt - sudo chmod 666 test/fixtures/not-owned/not-owned.txt after_script: - npm run coveralls vinyl-fs-3.0.3/LICENSE000066400000000000000000000022211327613003100142620ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2013-2017 Blaine Bublitz , Eric Schoffstall and other contributors 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. vinyl-fs-3.0.3/README.md000066400000000000000000000304541327613003100145450ustar00rootroot00000000000000

# vinyl-fs [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![AppVeyor Build Status][appveyor-image]][appveyor-url] [![Coveralls Status][coveralls-image]][coveralls-url] [![Gitter chat][gitter-image]][gitter-url] [Vinyl][vinyl] adapter for the file system. ## What is Vinyl? [Vinyl][vinyl] is a very simple metadata object that describes a file. When you think of a file, two attributes come to mind: `path` and `contents`. These are the main attributes on a [Vinyl][vinyl] object. A file does not necessarily represent something on your computer’s file system. You have files on S3, FTP, Dropbox, Box, CloudThingly.io and other services. [Vinyl][vinyl] can be used to describe files from all of these sources. ## What is a Vinyl Adapter? While Vinyl provides a clean way to describe a file, we now need a way to access these files. Each file source needs what we call a "Vinyl adapter". A Vinyl adapter simply exposes a `src(globs)` and a `dest(folder)` method. Each return a stream. The `src` stream produces Vinyl objects, and the `dest` stream consumes Vinyl objects. Vinyl adapters can expose extra methods that might be specific to their input/output medium, such as the `symlink` method `vinyl-fs` provides. ## Usage ```javascript var map = require('map-stream'); var vfs = require('vinyl-fs'); var log = function(file, cb) { console.log(file.path); cb(null, file); }; vfs.src(['./js/**/*.js', '!./js/vendor/*.js']) .pipe(map(log)) .pipe(vfs.dest('./output')); ``` ## API ### `src(globs[, options])` Takes a glob string or an array of glob strings as the first argument and an options object as the second. Returns a stream of [vinyl] `File` objects. __Note: UTF-8 BOM will be removed from all UTF-8 files read with `.src` unless disabled in the options.__ #### Globs Globs are executed in order, so negations should follow positive globs. For example: ```js fs.src(['!b*', '*']) ``` would not exclude any files, but the following would exclude all files starting with "b": ```js fs.src(['*', '!b*']) ``` #### Options - Values passed to the options must be of the expected type, otherwise they will be ignored. - All options can be passed a function instead of a value. The function will be called with the [vinyl] `File` object as its only argument and must return a value of the expected type for that option. ##### `options.buffer` Whether or not you want to buffer the file contents into memory. Setting to `false` will make `file.contents` a paused Stream. Type: `Boolean` Default: `true` ##### `options.read` Whether or not you want the file to be read at all. Useful for stuff like removing files. Setting to `false` will make `file.contents = null` and will disable writing the file to disk via `.dest()`. Type: `Boolean` Default: `true` ##### `options.since` Only streams files that have been modified since the time specified. Type: `Date` or `Number` Default: `undefined` ##### `options.removeBOM` Causes the BOM to be removed on UTF-8 encoded files. Set to `false` if you need the BOM for some reason. Type: `Boolean` Default: `true` ##### `options.sourcemaps` Enables sourcemap support on files passed through the stream. Will load inline sourcemaps and resolve sourcemap links from files. Type: `Boolean` Default: `false` ##### `options.resolveSymlinks` Whether or not to recursively resolve symlinks to their targets. Set to `false` to preserve them as symlinks and make `file.symlink` equal the original symlink's target path. Type: `Boolean` Default: `true` ##### `options.dot` Whether or not you want globs to match on dot files (e.g. `.gitignore`). __Note: This option is not resolved from a function because it is passed verbatim to node-glob.__ Type: `Boolean` Default: `false` ##### other Any glob-related options are documented in [glob-stream] and [node-glob] and are forwarded verbatim. ### `dest(folder[, options])` Takes a folder path string or a function as the first argument and an options object as the second. If given a function, it will be called with each [vinyl] `File` object and must return a folder path. Returns a stream that accepts [vinyl] `File` objects, writes them to disk at the folder/cwd specified, and passes them downstream so you can keep piping these around. Once the file is written to disk, an attempt is made to determine if the `stat.mode`, `stat.mtime` and `stat.atime` of the [vinyl] `File` object differ from the file on the filesystem. If they differ and the running process owns the file, the corresponding filesystem metadata is updated. If they don't differ or the process doesn't own the file, the attempt is skipped silently. __This functionality is disabled on Windows operating systems or any other OS that doesn't support `process.getuid` or `process.geteuid` in node. This is due to Windows having very unexpected results through usage of `fs.fchmod` and `fs.futimes`.__ __Note: The `fs.futimes()` method internally converts `stat.mtime` and `stat.atime` timestamps to seconds; this division by `1000` may cause some loss of precision in 32-bit Node.js.__ If the file has a `symlink` attribute specifying a target path, then a symlink will be created. __Note: The file will be modified after being written to this stream.__ - `cwd`, `base`, and `path` will be overwritten to match the folder. - `stat` will be updated to match the file on the filesystem. - `contents` will have it's position reset to the beginning if it is a stream. #### Options - Values passed to the options must be of the expected type, otherwise they will be ignored. - All options can be passed a function instead of a value. The function will be called with the [vinyl] `File` object as its only argument and must return a value of the expected type for that option. ##### `options.cwd` The working directory the folder is relative to. Type: `String` Default: `process.cwd()` ##### `options.mode` The mode the files should be created with. This option is only resolved if the [vinyl] `File` is not symbolic. Type: `Number` Default: The `mode` of the input file (`file.stat.mode`) if any, or the process mode if the input file has no `mode` property. ##### `options.dirMode` The mode directories should be created with. Type: `Number` Default: The process `mode`. ##### `options.overwrite` Whether or not existing files with the same path should be overwritten. Type: `Boolean` Default: `true` (always overwrite existing files) ##### `options.append` Whether or not new data should be appended after existing file contents (if any). Type: `Boolean` Default: `false` (always replace existing contents, if any) ##### `options.sourcemaps` Enables sourcemap support on files passed through the stream. Will write inline soucemaps if specified as `true`. Specifying a `String` path will write external sourcemaps at the given path. Examples: ```js // Write as inline comments vfs.dest('./', { sourcemaps: true }); // Write as files in the same folder vfs.dest('./', { sourcemaps: '.' }); ``` Type: `Boolean` or `String` Default: `undefined` (do not write sourcemaps) ##### `options.relativeSymlinks` When creating a symlink, whether or not the created symlink should be relative. If `false`, the symlink will be absolute. __Note: This option will be ignored if a `junction` is being created, as they must be absolute.__ Type: `Boolean` Default: `false` ##### `options.useJunctions` When creating a symlink, whether or not a directory symlink should be created as a `junction`. This option is only relevant on Windows and ignored elsewhere. Please refer to the [Symbolic Links on Windows][symbolic-caveats] section below. Type: `Boolean` Default: `true` ### `symlink(folder[, options])` Takes a folder path string or a function as the first argument and an options object as the second. If given a function, it will be called with each [vinyl] `File` object and must return a folder path. Returns a stream that accepts [vinyl] `File` objects, creates a symbolic link (i.e. symlink) at the folder/cwd specified, and passes them downstream so you can keep piping these around. __Note: The file will be modified after being written to this stream.__ - `cwd`, `base`, and `path` will be overwritten to match the folder. - `stat` will be updated to match the symlink on the filesystem. - `contents` will be set to `null`. - `symlink` will be added or replaced to be the original path. __Note: On Windows, directory links are created using Junctions by default. Use the `useJunctions` option to disable this behavior.__ #### Options - Values passed to the options must be of the expected type, otherwise they will be ignored. - All options can be passed a function instead of a value. The function will be called with the [vinyl] `File` object as its only argument and must return a value of the expected type for that option. ##### `options.cwd` The working directory the folder is relative to. Type: `String` Default: `process.cwd()` ##### `options.dirMode` The mode directories should be created with. Type: `Number` Default: The process mode. ##### `options.overwrite` Whether or not existing files with the same path should be overwritten. Type: `Boolean` Default: `true` (always overwrite existing files) ##### `options.relativeSymlinks` Whether or not the created symlinks should be relative. If `false`, the symlink will be absolute. __Note: This option will be ignored if a `junction` is being created, as they must be absolute.__ Type: `Boolean` Default: `false` ##### `options.useJunctions` When creating a symlink, whether or not a directory symlink should be created as a `junction`. This option is only relevant on Windows and ignored elsewhere. Please refer to the [Symbolic Links on Windows][symbolic-caveats] section below. Type: `Boolean` Default: `true` #### Symbolic Links on Windows When creating symbolic links on Windows, we pass a `type` argument to Node's `fs` module which specifies the kind of target we link to (one of `'file'`, `'dir'` or `'junction'`). Specifically, this will be `'file'` when the target is a regular file, `'junction'` if the target is a directory, or `'dir'` if the target is a directory and the user overrides the `useJunctions` option default. However, if the user tries to make a "dangling" link (pointing to a non-existent target) we won't be able to determine automatically which type we should use. In these cases, `vinyl-fs` will behave slightly differently depending on whether the dangling link is being created via `symlink()` or via `dest()`. For dangling links created via `symlink()`, the incoming vinyl represents the target and so we will look to its stats to guess the desired type. In particular, if `isDirectory()` returns false then we'll create a `'file'` type link, otherwise we will create a `'junction'` or a `'dir'` type link depending on the value of the `useJunctions` option. For dangling links created via `dest()`, the incoming vinyl represents the link - typically read off disk via `src()` with the `resolveSymlinks` option set to false. In this case, we won't be able to make any reasonable guess as to the type of link and we default to using `'file'`, which may cause unexpected behavior if you are creating a "dangling" link to a directory. It is advised to avoid this scenario. [symbolic-caveats]: #symbolic-links-on-windows [glob-stream]: https://github.com/gulpjs/glob-stream [node-glob]: https://github.com/isaacs/node-glob [gaze]: https://github.com/shama/gaze [glob-watcher]: https://github.com/wearefractal/glob-watcher [vinyl]: https://github.com/wearefractal/vinyl [downloads-image]: http://img.shields.io/npm/dm/vinyl-fs.svg [npm-url]: https://www.npmjs.com/package/vinyl-fs [npm-image]: http://img.shields.io/npm/v/vinyl-fs.svg [travis-url]: https://travis-ci.org/gulpjs/vinyl-fs [travis-image]: http://img.shields.io/travis/gulpjs/vinyl-fs.svg?label=travis-ci [appveyor-url]: https://ci.appveyor.com/project/gulpjs/vinyl-fs [appveyor-image]: https://img.shields.io/appveyor/ci/gulpjs/vinyl-fs.svg?label=appveyor [coveralls-url]: https://coveralls.io/r/gulpjs/vinyl-fs [coveralls-image]: http://img.shields.io/coveralls/gulpjs/vinyl-fs/master.svg [gitter-url]: https://gitter.im/gulpjs/gulp [gitter-image]: https://badges.gitter.im/gulpjs/gulp.svg vinyl-fs-3.0.3/appveyor.yml000066400000000000000000000010051327613003100156440ustar00rootroot00000000000000# http://www.appveyor.com/docs/appveyor-yml # http://www.appveyor.com/docs/lang/nodejs-iojs environment: matrix: # node.js - nodejs_version: "0.10" - nodejs_version: "0.12" - nodejs_version: "4" - nodejs_version: "5" - nodejs_version: "6" - nodejs_version: "8" - nodejs_version: "9" install: - ps: Install-Product node $env:nodejs_version - npm install test_script: - node --version - npm --version - cmd: npm test build: off # build version format version: "{build}" vinyl-fs-3.0.3/index.js000066400000000000000000000002061327613003100147230ustar00rootroot00000000000000'use strict'; module.exports = { src: require('./lib/src'), dest: require('./lib/dest'), symlink: require('./lib/symlink'), }; vinyl-fs-3.0.3/lib/000077500000000000000000000000001327613003100140265ustar00rootroot00000000000000vinyl-fs-3.0.3/lib/constants.js000066400000000000000000000001611327613003100163760ustar00rootroot00000000000000'use strict'; module.exports = { MASK_MODE: parseInt('7777', 8), DEFAULT_FILE_MODE: parseInt('0666', 8), }; vinyl-fs-3.0.3/lib/dest/000077500000000000000000000000001327613003100147655ustar00rootroot00000000000000vinyl-fs-3.0.3/lib/dest/index.js000066400000000000000000000021311327613003100164270ustar00rootroot00000000000000'use strict'; var lead = require('lead'); var pumpify = require('pumpify'); var mkdirpStream = require('fs-mkdirp-stream'); var createResolver = require('resolve-options'); var config = require('./options'); var prepare = require('./prepare'); var sourcemap = require('./sourcemap'); var writeContents = require('./write-contents'); var folderConfig = { outFolder: { type: 'string', }, }; function dest(outFolder, opt) { if (!outFolder) { throw new Error('Invalid dest() folder argument.' + ' Please specify a non-empty string or a function.'); } var optResolver = createResolver(config, opt); var folderResolver = createResolver(folderConfig, { outFolder: outFolder }); function dirpath(file, callback) { var dirMode = optResolver.resolve('dirMode', file); callback(null, file.dirname, dirMode); } var saveStream = pumpify.obj( prepare(folderResolver, optResolver), sourcemap(optResolver), mkdirpStream.obj(dirpath), writeContents(optResolver) ); // Sink the output stream to start flowing return lead(saveStream); } module.exports = dest; vinyl-fs-3.0.3/lib/dest/options.js000066400000000000000000000012421327613003100170150ustar00rootroot00000000000000'use strict'; var config = { cwd: { type: 'string', default: process.cwd, }, mode: { type: 'number', default: function(file) { return file.stat ? file.stat.mode : null; }, }, dirMode: { type: 'number', }, overwrite: { type: 'boolean', default: true, }, append: { type: 'boolean', default: false, }, sourcemaps: { type: ['string', 'boolean'], default: false, }, // Symlink options relativeSymlinks: { type: 'boolean', default: false, }, // This option is ignored on non-Windows platforms useJunctions: { type: 'boolean', default: true, }, }; module.exports = config; vinyl-fs-3.0.3/lib/dest/prepare.js000066400000000000000000000023711327613003100167640ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var Vinyl = require('vinyl'); var through = require('through2'); function prepareWrite(folderResolver, optResolver) { if (!folderResolver) { throw new Error('Invalid output folder'); } function normalize(file, enc, cb) { if (!Vinyl.isVinyl(file)) { return cb(new Error('Received a non-Vinyl object in `dest()`')); } // TODO: Remove this after people upgrade vinyl/transition from gulp-util if (typeof file.isSymbolic !== 'function') { file = new Vinyl(file); } var outFolderPath = folderResolver.resolve('outFolder', file); if (!outFolderPath) { return cb(new Error('Invalid output folder')); } var cwd = path.resolve(optResolver.resolve('cwd', file)); var basePath = path.resolve(cwd, outFolderPath); var writePath = path.resolve(basePath, file.relative); // Wire up new properties file.cwd = cwd; file.base = basePath; file.path = writePath; if (!file.isSymbolic()) { var mode = optResolver.resolve('mode', file); file.stat = (file.stat || new fs.Stats()); file.stat.mode = mode; } cb(null, file); } return through.obj(normalize); } module.exports = prepareWrite; vinyl-fs-3.0.3/lib/dest/sourcemap.js000066400000000000000000000014361327613003100173250ustar00rootroot00000000000000'use strict'; var through = require('through2'); var sourcemap = require('vinyl-sourcemap'); function sourcemapStream(optResolver) { function saveSourcemap(file, enc, callback) { var self = this; var srcMap = optResolver.resolve('sourcemaps', file); if (!srcMap) { return callback(null, file); } var srcMapLocation = (typeof srcMap === 'string' ? srcMap : undefined); sourcemap.write(file, srcMapLocation, onWrite); function onWrite(sourcemapErr, updatedFile, sourcemapFile) { if (sourcemapErr) { return callback(sourcemapErr); } self.push(updatedFile); if (sourcemapFile) { self.push(sourcemapFile); } callback(); } } return through.obj(saveSourcemap); } module.exports = sourcemapStream; vinyl-fs-3.0.3/lib/dest/write-contents/000077500000000000000000000000001327613003100177525ustar00rootroot00000000000000vinyl-fs-3.0.3/lib/dest/write-contents/index.js000066400000000000000000000026541327613003100214260ustar00rootroot00000000000000'use strict'; var through = require('through2'); var writeDir = require('./write-dir'); var writeStream = require('./write-stream'); var writeBuffer = require('./write-buffer'); var writeSymbolicLink = require('./write-symbolic-link'); var fo = require('../../file-operations'); function writeContents(optResolver) { function writeFile(file, enc, callback) { // Write it as a symlink if (file.isSymbolic()) { return writeSymbolicLink(file, optResolver, onWritten); } // If directory then mkdirp it if (file.isDirectory()) { return writeDir(file, optResolver, onWritten); } // Stream it to disk yo if (file.isStream()) { return writeStream(file, optResolver, onWritten); } // Write it like normal if (file.isBuffer()) { return writeBuffer(file, optResolver, onWritten); } // If no contents then do nothing if (file.isNull()) { return onWritten(); } // This is invoked by the various writeXxx modules when they've finished // writing the contents. function onWritten(writeErr) { var flags = fo.getFlags({ overwrite: optResolver.resolve('overwrite', file), append: optResolver.resolve('append', file), }); if (fo.isFatalOverwriteError(writeErr, flags)) { return callback(writeErr); } callback(null, file); } } return through.obj(writeFile); } module.exports = writeContents; vinyl-fs-3.0.3/lib/dest/write-contents/write-buffer.js000066400000000000000000000012221327613003100227060ustar00rootroot00000000000000'use strict'; var fo = require('../../file-operations'); function writeBuffer(file, optResolver, onWritten) { var flags = fo.getFlags({ overwrite: optResolver.resolve('overwrite', file), append: optResolver.resolve('append', file), }); var opt = { mode: file.stat.mode, flags: flags, }; fo.writeFile(file.path, file.contents, opt, onWriteFile); function onWriteFile(writeErr, fd) { if (writeErr) { return fo.closeFd(writeErr, fd, onWritten); } fo.updateMetadata(fd, file, onUpdate); function onUpdate(updateErr) { fo.closeFd(updateErr, fd, onWritten); } } } module.exports = writeBuffer; vinyl-fs-3.0.3/lib/dest/write-contents/write-dir.js000066400000000000000000000016371327613003100222250ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var mkdirp = require('fs-mkdirp-stream/mkdirp'); var fo = require('../../file-operations'); function writeDir(file, optResolver, onWritten) { mkdirp(file.path, file.stat.mode, onMkdirp); function onMkdirp(mkdirpErr) { if (mkdirpErr) { return onWritten(mkdirpErr); } fs.open(file.path, 'r', onOpen); } function onOpen(openErr, fd) { // If we don't have access, just move along if (isInaccessible(openErr)) { return fo.closeFd(null, fd, onWritten); } if (openErr) { return fo.closeFd(openErr, fd, onWritten); } fo.updateMetadata(fd, file, onUpdate); function onUpdate(updateErr) { fo.closeFd(updateErr, fd, onWritten); } } } function isInaccessible(err) { if (!err) { return false; } if (err.code === 'EACCES') { return true; } return false; } module.exports = writeDir; vinyl-fs-3.0.3/lib/dest/write-contents/write-stream.js000066400000000000000000000032231327613003100227330ustar00rootroot00000000000000'use strict'; var fo = require('../../file-operations'); var readStream = require('../../src/read-contents/read-stream'); function writeStream(file, optResolver, onWritten) { var flags = fo.getFlags({ overwrite: optResolver.resolve('overwrite', file), append: optResolver.resolve('append', file), }); var opt = { mode: file.stat.mode, // TODO: need to test this flags: flags, }; // TODO: is this the best API? var outStream = fo.createWriteStream(file.path, opt, onFlush); file.contents.once('error', onComplete); outStream.once('error', onComplete); outStream.once('finish', onComplete); // TODO: should this use a clone? file.contents.pipe(outStream); function onComplete(streamErr) { // Cleanup event handlers before closing file.contents.removeListener('error', onComplete); outStream.removeListener('error', onComplete); outStream.removeListener('finish', onComplete); // Need to guarantee the fd is closed before forwarding the error outStream.once('close', onClose); outStream.end(); function onClose(closeErr) { onWritten(streamErr || closeErr); } } // Cleanup function onFlush(fd, callback) { // TODO: removing this before readStream because it replaces the stream file.contents.removeListener('error', onComplete); // TODO: this is doing sync stuff & the callback seems unnecessary // TODO: Replace the contents stream or use a clone? readStream(file, complete); function complete() { if (typeof fd !== 'number') { return callback(); } fo.updateMetadata(fd, file, callback); } } } module.exports = writeStream; vinyl-fs-3.0.3/lib/dest/write-contents/write-symbolic-link.js000066400000000000000000000045531327613003100242230ustar00rootroot00000000000000'use strict'; var os = require('os'); var path = require('path'); var fo = require('../../file-operations'); var isWindows = (os.platform() === 'win32'); function writeSymbolicLink(file, optResolver, onWritten) { if (!file.symlink) { return onWritten(new Error('Missing symlink property on symbolic vinyl')); } var isRelative = optResolver.resolve('relativeSymlinks', file); var flags = fo.getFlags({ overwrite: optResolver.resolve('overwrite', file), append: optResolver.resolve('append', file), }); if (!isWindows) { // On non-Windows, just use 'file' return createLinkWithType('file'); } fo.reflectStat(file.symlink, file, onReflect); function onReflect(statErr) { if (statErr && statErr.code !== 'ENOENT') { return onWritten(statErr); } // This option provides a way to create a Junction instead of a // Directory symlink on Windows. This comes with the following caveats: // * NTFS Junctions cannot be relative. // * NTFS Junctions MUST be directories. // * NTFS Junctions must be on the same file system. // * Most products CANNOT detect a directory is a Junction: // This has the side effect of possibly having a whole directory // deleted when a product is deleting the Junction directory. // For example, JetBrains product lines will delete the entire contents // of the TARGET directory because the product does not realize it's // a symlink as the JVM and Node return false for isSymlink. // This function is Windows only, so we don't need to check again var useJunctions = optResolver.resolve('useJunctions', file); var dirType = useJunctions ? 'junction' : 'dir'; // Dangling links are always 'file' var type = !statErr && file.isDirectory() ? dirType : 'file'; createLinkWithType(type); } function createLinkWithType(type) { // This is done after prepare() to use the adjusted file.base property if (isRelative && type !== 'junction') { file.symlink = path.relative(file.base, file.symlink); } var opts = { flags: flags, type: type, }; fo.symlink(file.symlink, file.path, opts, onSymlink); function onSymlink(symlinkErr) { if (symlinkErr) { return onWritten(symlinkErr); } fo.reflectLinkStat(file.path, file, onWritten); } } } module.exports = writeSymbolicLink; vinyl-fs-3.0.3/lib/file-operations.js000066400000000000000000000245121327613003100174700ustar00rootroot00000000000000'use strict'; var util = require('util'); var fs = require('graceful-fs'); var assign = require('object.assign'); var date = require('value-or-function').date; var Writable = require('readable-stream').Writable; var constants = require('./constants'); var APPEND_MODE_REGEXP = /a/; function closeFd(propagatedErr, fd, callback) { if (typeof fd !== 'number') { return callback(propagatedErr); } fs.close(fd, onClosed); function onClosed(closeErr) { if (propagatedErr || closeErr) { return callback(propagatedErr || closeErr); } callback(); } } function isValidUnixId(id) { if (typeof id !== 'number') { return false; } if (id < 0) { return false; } return true; } function getFlags(options) { var flags = !options.append ? 'w' : 'a'; if (!options.overwrite) { flags += 'x'; } return flags; } function isFatalOverwriteError(err, flags) { if (!err) { return false; } if (err.code === 'EEXIST' && flags[1] === 'x') { // Handle scenario for file overwrite failures. return false; } // Otherwise, this is a fatal error return true; } function isFatalUnlinkError(err) { if (!err || err.code === 'ENOENT') { return false; } return true; } function getModeDiff(fsMode, vinylMode) { var modeDiff = 0; if (typeof vinylMode === 'number') { modeDiff = (vinylMode ^ fsMode) & constants.MASK_MODE; } return modeDiff; } function getTimesDiff(fsStat, vinylStat) { var mtime = date(vinylStat.mtime) || 0; if (!mtime) { return; } var atime = date(vinylStat.atime) || 0; if (+mtime === +fsStat.mtime && +atime === +fsStat.atime) { return; } if (!atime) { atime = date(fsStat.atime) || undefined; } var timesDiff = { mtime: vinylStat.mtime, atime: atime, }; return timesDiff; } function getOwnerDiff(fsStat, vinylStat) { if (!isValidUnixId(vinylStat.uid) && !isValidUnixId(vinylStat.gid)) { return; } if ((!isValidUnixId(fsStat.uid) && !isValidUnixId(vinylStat.uid)) || (!isValidUnixId(fsStat.gid) && !isValidUnixId(vinylStat.gid))) { return; } var uid = fsStat.uid; // Default to current uid. if (isValidUnixId(vinylStat.uid)) { uid = vinylStat.uid; } var gid = fsStat.gid; // Default to current gid. if (isValidUnixId(vinylStat.gid)) { gid = vinylStat.gid; } if (uid === fsStat.uid && gid === fsStat.gid) { return; } var ownerDiff = { uid: uid, gid: gid, }; return ownerDiff; } function isOwner(fsStat) { var hasGetuid = (typeof process.getuid === 'function'); var hasGeteuid = (typeof process.geteuid === 'function'); // If we don't have either, assume we don't have permissions. // This should only happen on Windows. // Windows basically noops fchmod and errors on futimes called on directories. if (!hasGeteuid && !hasGetuid) { return false; } var uid; if (hasGeteuid) { uid = process.geteuid(); } else { uid = process.getuid(); } if (fsStat.uid !== uid && uid !== 0) { return false; } return true; } function reflectStat(path, file, callback) { // Set file.stat to the reflect current state on disk fs.stat(path, onStat); function onStat(statErr, stat) { if (statErr) { return callback(statErr); } file.stat = stat; callback(); } } function reflectLinkStat(path, file, callback) { // Set file.stat to the reflect current state on disk fs.lstat(path, onLstat); function onLstat(lstatErr, stat) { if (lstatErr) { return callback(lstatErr); } file.stat = stat; callback(); } } function updateMetadata(fd, file, callback) { fs.fstat(fd, onStat); function onStat(statErr, stat) { if (statErr) { return callback(statErr); } // Check if mode needs to be updated var modeDiff = getModeDiff(stat.mode, file.stat.mode); // Check if atime/mtime need to be updated var timesDiff = getTimesDiff(stat, file.stat); // Check if uid/gid need to be updated var ownerDiff = getOwnerDiff(stat, file.stat); // Set file.stat to the reflect current state on disk assign(file.stat, stat); // Nothing to do if (!modeDiff && !timesDiff && !ownerDiff) { return callback(); } // Check access, `futimes`, `fchmod` & `fchown` only work if we own // the file, or if we are effectively root (`fchown` only when root). if (!isOwner(stat)) { return callback(); } if (modeDiff) { return mode(); } if (timesDiff) { return times(); } owner(); function mode() { var mode = stat.mode ^ modeDiff; fs.fchmod(fd, mode, onFchmod); function onFchmod(fchmodErr) { if (!fchmodErr) { file.stat.mode = mode; } if (timesDiff) { return times(fchmodErr); } if (ownerDiff) { return owner(fchmodErr); } callback(fchmodErr); } } function times(propagatedErr) { fs.futimes(fd, timesDiff.atime, timesDiff.mtime, onFutimes); function onFutimes(futimesErr) { if (!futimesErr) { file.stat.atime = timesDiff.atime; file.stat.mtime = timesDiff.mtime; } if (ownerDiff) { return owner(propagatedErr || futimesErr); } callback(propagatedErr || futimesErr); } } function owner(propagatedErr) { fs.fchown(fd, ownerDiff.uid, ownerDiff.gid, onFchown); function onFchown(fchownErr) { if (!fchownErr) { file.stat.uid = ownerDiff.uid; file.stat.gid = ownerDiff.gid; } callback(propagatedErr || fchownErr); } } } } function symlink(srcPath, destPath, opts, callback) { // Because fs.symlink does not allow atomic overwrite option with flags, we // delete and recreate if the link already exists and overwrite is true. if (opts.flags === 'w') { // TODO What happens when we call unlink with windows junctions? fs.unlink(destPath, onUnlink); } else { fs.symlink(srcPath, destPath, opts.type, onSymlink); } function onUnlink(unlinkErr) { if (isFatalUnlinkError(unlinkErr)) { return callback(unlinkErr); } fs.symlink(srcPath, destPath, opts.type, onSymlink); } function onSymlink(symlinkErr) { if (isFatalOverwriteError(symlinkErr, opts.flags)) { return callback(symlinkErr); } callback(); } } /* Custom writeFile implementation because we need access to the file descriptor after the write is complete. Most of the implementation taken from node core. */ function writeFile(filepath, data, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } if (!Buffer.isBuffer(data)) { return callback(new TypeError('Data must be a Buffer')); } if (!options) { options = {}; } // Default the same as node var mode = options.mode || constants.DEFAULT_FILE_MODE; var flags = options.flags || 'w'; var position = APPEND_MODE_REGEXP.test(flags) ? null : 0; fs.open(filepath, flags, mode, onOpen); function onOpen(openErr, fd) { if (openErr) { return onComplete(openErr); } fs.write(fd, data, 0, data.length, position, onComplete); function onComplete(writeErr) { callback(writeErr, fd); } } } function createWriteStream(path, options, flush) { return new WriteStream(path, options, flush); } // Taken from node core and altered to receive a flush function and simplified // To be used for cleanup (like updating times/mode/etc) function WriteStream(path, options, flush) { // Not exposed so we can avoid the case where someone doesn't use `new` if (typeof options === 'function') { flush = options; options = null; } options = options || {}; Writable.call(this, options); this.flush = flush; this.path = path; this.mode = options.mode || constants.DEFAULT_FILE_MODE; this.flags = options.flags || 'w'; // Used by node's `fs.WriteStream` this.fd = null; this.start = null; this.open(); // Dispose on finish. this.once('finish', this.close); } util.inherits(WriteStream, Writable); WriteStream.prototype.open = function() { var self = this; fs.open(this.path, this.flags, this.mode, onOpen); function onOpen(openErr, fd) { if (openErr) { self.destroy(); self.emit('error', openErr); return; } self.fd = fd; self.emit('open', fd); } }; // Use our `end` method since it is patched for flush WriteStream.prototype.destroySoon = WriteStream.prototype.end; WriteStream.prototype._destroy = function(err, cb) { this.close(function(err2) { cb(err || err2); }); }; WriteStream.prototype.close = function(cb) { var that = this; if (cb) { this.once('close', cb); } if (this.closed || typeof this.fd !== 'number') { if (typeof this.fd !== 'number') { this.once('open', closeOnOpen); return; } return process.nextTick(function() { that.emit('close'); }); } this.closed = true; fs.close(this.fd, function(er) { if (er) { that.emit('error', er); } else { that.emit('close'); } }); this.fd = null; }; WriteStream.prototype._final = function(callback) { if (typeof this.flush !== 'function') { return callback(); } this.flush(this.fd, callback); }; function closeOnOpen() { this.close(); } WriteStream.prototype._write = function(data, encoding, callback) { var self = this; // This is from node core but I have no idea how to get code coverage on it if (!Buffer.isBuffer(data)) { return this.emit('error', new Error('Invalid data')); } if (typeof this.fd !== 'number') { return this.once('open', onOpen); } fs.write(this.fd, data, 0, data.length, null, onWrite); function onOpen() { self._write(data, encoding, callback); } function onWrite(writeErr) { if (writeErr) { self.destroy(); callback(writeErr); return; } callback(); } }; module.exports = { closeFd: closeFd, isValidUnixId: isValidUnixId, getFlags: getFlags, isFatalOverwriteError: isFatalOverwriteError, isFatalUnlinkError: isFatalUnlinkError, getModeDiff: getModeDiff, getTimesDiff: getTimesDiff, getOwnerDiff: getOwnerDiff, isOwner: isOwner, reflectStat: reflectStat, reflectLinkStat: reflectLinkStat, updateMetadata: updateMetadata, symlink: symlink, writeFile: writeFile, createWriteStream: createWriteStream, }; vinyl-fs-3.0.3/lib/src/000077500000000000000000000000001327613003100146155ustar00rootroot00000000000000vinyl-fs-3.0.3/lib/src/index.js000066400000000000000000000016361327613003100162700ustar00rootroot00000000000000'use strict'; var gs = require('glob-stream'); var pumpify = require('pumpify'); var toThrough = require('to-through'); var isValidGlob = require('is-valid-glob'); var createResolver = require('resolve-options'); var config = require('./options'); var prepare = require('./prepare'); var wrapVinyl = require('./wrap-vinyl'); var sourcemap = require('./sourcemap'); var readContents = require('./read-contents'); var resolveSymlinks = require('./resolve-symlinks'); function src(glob, opt) { var optResolver = createResolver(config, opt); if (!isValidGlob(glob)) { throw new Error('Invalid glob argument: ' + glob); } var streams = [ gs(glob, opt), wrapVinyl(optResolver), resolveSymlinks(optResolver), prepare(optResolver), readContents(optResolver), sourcemap(optResolver), ]; var outputStream = pumpify.obj(streams); return toThrough(outputStream); } module.exports = src; vinyl-fs-3.0.3/lib/src/options.js000066400000000000000000000006111327613003100166440ustar00rootroot00000000000000'use strict'; var config = { buffer: { type: 'boolean', default: true, }, read: { type: 'boolean', default: true, }, since: { type: 'date', }, removeBOM: { type: 'boolean', default: true, }, sourcemaps: { type: 'boolean', default: false, }, resolveSymlinks: { type: 'boolean', default: true, }, }; module.exports = config; vinyl-fs-3.0.3/lib/src/prepare.js000066400000000000000000000006741327613003100166200ustar00rootroot00000000000000'use strict'; var through = require('through2'); function prepareRead(optResolver) { function normalize(file, enc, callback) { var since = optResolver.resolve('since', file); // Skip this file if since option is set and current file is too old if (file.stat && file.stat.mtime <= since) { return callback(); } return callback(null, file); } return through.obj(normalize); } module.exports = prepareRead; vinyl-fs-3.0.3/lib/src/read-contents/000077500000000000000000000000001327613003100173635ustar00rootroot00000000000000vinyl-fs-3.0.3/lib/src/read-contents/index.js000066400000000000000000000025201327613003100210270ustar00rootroot00000000000000'use strict'; var through = require('through2'); var readDir = require('./read-dir'); var readStream = require('./read-stream'); var readBuffer = require('./read-buffer'); var readSymbolicLink = require('./read-symbolic-link'); function readContents(optResolver) { function readFile(file, enc, callback) { // Skip reading contents if read option says so var read = optResolver.resolve('read', file); if (!read) { return callback(null, file); } // Don't fail to read a directory if (file.isDirectory()) { return readDir(file, optResolver, onRead); } // Process symbolic links included with `resolveSymlinks` option if (file.stat && file.stat.isSymbolicLink()) { return readSymbolicLink(file, optResolver, onRead); } // Read and pass full contents var buffer = optResolver.resolve('buffer', file); if (buffer) { return readBuffer(file, optResolver, onRead); } // Don't buffer anything - just pass streams return readStream(file, optResolver, onRead); // This is invoked by the various readXxx modules when they've finished // reading the contents. function onRead(readErr) { if (readErr) { return callback(readErr); } return callback(null, file); } } return through.obj(readFile); } module.exports = readContents; vinyl-fs-3.0.3/lib/src/read-contents/read-buffer.js000066400000000000000000000007741327613003100221130ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var removeBomBuffer = require('remove-bom-buffer'); function bufferFile(file, optResolver, onRead) { fs.readFile(file.path, onReadFile); function onReadFile(readErr, data) { if (readErr) { return onRead(readErr); } var removeBOM = optResolver.resolve('removeBOM', file); if (removeBOM) { file.contents = removeBomBuffer(data); } else { file.contents = data; } onRead(); } } module.exports = bufferFile; vinyl-fs-3.0.3/lib/src/read-contents/read-dir.js000066400000000000000000000001761327613003100214140ustar00rootroot00000000000000'use strict'; function readDir(file, optResolver, onRead) { // Do nothing for now onRead(); } module.exports = readDir; vinyl-fs-3.0.3/lib/src/read-contents/read-stream.js000066400000000000000000000012621327613003100221260ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var removeBomStream = require('remove-bom-stream'); var lazystream = require('lazystream'); var createResolver = require('resolve-options'); function streamFile(file, optResolver, onRead) { if (typeof optResolver === 'function') { onRead = optResolver; optResolver = createResolver(); } var filePath = file.path; var removeBOM = optResolver.resolve('removeBOM', file); file.contents = new lazystream.Readable(function() { var contents = fs.createReadStream(filePath); if (removeBOM) { return contents.pipe(removeBomStream()); } return contents; }); onRead(); } module.exports = streamFile; vinyl-fs-3.0.3/lib/src/read-contents/read-symbolic-link.js000066400000000000000000000005261327613003100234110ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); function readLink(file, optResolver, onRead) { fs.readlink(file.path, onReadlink); function onReadlink(readErr, target) { if (readErr) { return onRead(readErr); } // Store the link target path file.symlink = target; onRead(); } } module.exports = readLink; vinyl-fs-3.0.3/lib/src/resolve-symlinks.js000066400000000000000000000014431327613003100205030ustar00rootroot00000000000000'use strict'; var through = require('through2'); var fo = require('../file-operations'); function resolveSymlinks(optResolver) { // A stat property is exposed on file objects as a (wanted) side effect function resolveFile(file, enc, callback) { fo.reflectLinkStat(file.path, file, onReflect); function onReflect(statErr) { if (statErr) { return callback(statErr); } if (!file.stat.isSymbolicLink()) { return callback(null, file); } var resolveSymlinks = optResolver.resolve('resolveSymlinks', file); if (!resolveSymlinks) { return callback(null, file); } // Get target's stats fo.reflectStat(file.path, file, onReflect); } } return through.obj(resolveFile); } module.exports = resolveSymlinks; vinyl-fs-3.0.3/lib/src/sourcemap.js000066400000000000000000000011011327613003100171420ustar00rootroot00000000000000'use strict'; var through = require('through2'); var sourcemap = require('vinyl-sourcemap'); function sourcemapStream(optResolver) { function addSourcemap(file, enc, callback) { var srcMap = optResolver.resolve('sourcemaps', file); if (!srcMap) { return callback(null, file); } sourcemap.add(file, onAdd); function onAdd(sourcemapErr, updatedFile) { if (sourcemapErr) { return callback(sourcemapErr); } callback(null, updatedFile); } } return through.obj(addSourcemap); } module.exports = sourcemapStream; vinyl-fs-3.0.3/lib/src/wrap-vinyl.js000066400000000000000000000004321327613003100172620ustar00rootroot00000000000000'use strict'; var File = require('vinyl'); var through = require('through2'); function wrapVinyl() { function wrapFile(globFile, enc, callback) { var file = new File(globFile); callback(null, file); } return through.obj(wrapFile); } module.exports = wrapVinyl; vinyl-fs-3.0.3/lib/symlink/000077500000000000000000000000001327613003100155145ustar00rootroot00000000000000vinyl-fs-3.0.3/lib/symlink/index.js000066400000000000000000000020001327613003100171510ustar00rootroot00000000000000'use strict'; var pumpify = require('pumpify'); var lead = require('lead'); var mkdirpStream = require('fs-mkdirp-stream'); var createResolver = require('resolve-options'); var config = require('./options'); var prepare = require('./prepare'); var linkFile = require('./link-file'); var folderConfig = { outFolder: { type: 'string', }, }; function symlink(outFolder, opt) { if (!outFolder) { throw new Error('Invalid symlink() folder argument.' + ' Please specify a non-empty string or a function.'); } var optResolver = createResolver(config, opt); var folderResolver = createResolver(folderConfig, { outFolder: outFolder }); function dirpath(file, callback) { var dirMode = optResolver.resolve('dirMode', file); callback(null, file.dirname, dirMode); } var stream = pumpify.obj( prepare(folderResolver, optResolver), mkdirpStream.obj(dirpath), linkFile(optResolver) ); // Sink the stream to start flowing return lead(stream); } module.exports = symlink; vinyl-fs-3.0.3/lib/symlink/link-file.js000066400000000000000000000052351327613003100177310ustar00rootroot00000000000000'use strict'; var os = require('os'); var path = require('path'); var through = require('through2'); var fo = require('../file-operations'); var isWindows = (os.platform() === 'win32'); function linkStream(optResolver) { function linkFile(file, enc, callback) { var isRelative = optResolver.resolve('relativeSymlinks', file); var flags = fo.getFlags({ overwrite: optResolver.resolve('overwrite', file), append: false, }); if (!isWindows) { // On non-Windows, just use 'file' return createLinkWithType('file'); } fo.reflectStat(file.symlink, file, onReflectTarget); function onReflectTarget(statErr) { if (statErr && statErr.code !== 'ENOENT') { return callback(statErr); } // If target doesn't exist, the vinyl will still carry the target stats. // Let's use those to determine which kind of dangling link to create. // This option provides a way to create a Junction instead of a // Directory symlink on Windows. This comes with the following caveats: // * NTFS Junctions cannot be relative. // * NTFS Junctions MUST be directories. // * NTFS Junctions must be on the same file system. // * Most products CANNOT detect a directory is a Junction: // This has the side effect of possibly having a whole directory // deleted when a product is deleting the Junction directory. // For example, JetBrains product lines will delete the entire contents // of the TARGET directory because the product does not realize it's // a symlink as the JVM and Node return false for isSymlink. // This function is Windows only, so we don't need to check again var useJunctions = optResolver.resolve('useJunctions', file); var dirType = useJunctions ? 'junction' : 'dir'; var type = !statErr && file.isDirectory() ? dirType : 'file'; createLinkWithType(type); } function createLinkWithType(type) { // This is done after prepare() to use the adjusted file.base property if (isRelative && type !== 'junction') { file.symlink = path.relative(file.base, file.symlink); } var opts = { flags: flags, type: type, }; fo.symlink(file.symlink, file.path, opts, onSymlink); } function onSymlink(symlinkErr) { if (symlinkErr) { return callback(symlinkErr); } fo.reflectLinkStat(file.path, file, onReflectLink); } function onReflectLink(reflectErr) { if (reflectErr) { return callback(reflectErr); } callback(null, file); } } return through.obj(linkFile); } module.exports = linkStream; vinyl-fs-3.0.3/lib/symlink/options.js000066400000000000000000000006211327613003100175440ustar00rootroot00000000000000'use strict'; var config = { cwd: { type: 'string', default: process.cwd, }, dirMode: { type: 'number', }, overwrite: { type: 'boolean', default: true, }, relativeSymlinks: { type: 'boolean', default: false, }, // This option is ignored on non-Windows platforms useJunctions: { type: 'boolean', default: true, }, }; module.exports = config; vinyl-fs-3.0.3/lib/symlink/prepare.js000066400000000000000000000026301327613003100175110ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var Vinyl = require('vinyl'); var through = require('through2'); function prepareSymlink(folderResolver, optResolver) { if (!folderResolver) { throw new Error('Invalid output folder'); } function normalize(file, enc, cb) { if (!Vinyl.isVinyl(file)) { return cb(new Error('Received a non-Vinyl object in `symlink()`')); } // TODO: Remove this after people upgrade vinyl/transition from gulp-util if (typeof file.isSymbolic !== 'function') { file = new Vinyl(file); } var cwd = path.resolve(optResolver.resolve('cwd', file)); var outFolderPath = folderResolver.resolve('outFolder', file); if (!outFolderPath) { return cb(new Error('Invalid output folder')); } var basePath = path.resolve(cwd, outFolderPath); var writePath = path.resolve(basePath, file.relative); // Wire up new properties // Note: keep the target stats for now, we may need them in link-file file.stat = (file.stat || new fs.Stats()); file.cwd = cwd; file.base = basePath; // This is the path we are linking *TO* file.symlink = file.path; file.path = writePath; // We have to set contents to null for a link // Otherwise `isSymbolic()` returns false file.contents = null; cb(null, file); } return through.obj(normalize); } module.exports = prepareSymlink; vinyl-fs-3.0.3/package.json000066400000000000000000000032031327613003100155440ustar00rootroot00000000000000{ "name": "vinyl-fs", "version": "3.0.3", "description": "Vinyl adapter for the file system.", "author": "Gulp Team (http://gulpjs.com/)", "contributors": [ "Eric Schoffstall ", "Blaine Bublitz " ], "repository": "gulpjs/vinyl-fs", "license": "MIT", "engines": { "node": ">= 0.10" }, "main": "index.js", "files": [ "LICENSE", "index.js", "lib" ], "scripts": { "lint": "eslint . && jscs index.js lib/ test/", "pretest": "npm run lint", "test": "mocha --async-only", "cover": "istanbul cover _mocha --report lcovonly", "coveralls": "npm run cover && istanbul-coveralls" }, "dependencies": { "fs-mkdirp-stream": "^1.0.0", "glob-stream": "^6.1.0", "graceful-fs": "^4.0.0", "is-valid-glob": "^1.0.0", "lazystream": "^1.0.0", "lead": "^1.0.0", "object.assign": "^4.0.4", "pumpify": "^1.3.5", "readable-stream": "^2.3.3", "remove-bom-buffer": "^3.0.0", "remove-bom-stream": "^1.2.0", "resolve-options": "^1.1.0", "through2": "^2.0.0", "to-through": "^2.0.0", "value-or-function": "^3.0.0", "vinyl": "^2.0.0", "vinyl-sourcemap": "^1.1.0" }, "devDependencies": { "eslint": "^1.10.3", "eslint-config-gulp": "^2.0.0", "expect": "^1.19.0", "istanbul": "^0.4.3", "istanbul-coveralls": "^1.0.3", "jscs": "^2.4.0", "jscs-preset-gulp": "^1.0.0", "mississippi": "^1.2.0", "mocha": "^3.5.0", "rimraf": "^2.6.1" }, "keywords": [ "gulp", "vinyl-adapter", "vinyl", "file", "file system", "fs", "streams" ] } vinyl-fs-3.0.3/test/000077500000000000000000000000001327613003100142375ustar00rootroot00000000000000vinyl-fs-3.0.3/test/.eslintrc000066400000000000000000000000351327613003100160610ustar00rootroot00000000000000{ "extends": "gulp/test" } vinyl-fs-3.0.3/test/dest-modes.js000066400000000000000000000217161327613003100166500ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var statMode = require('./utils/stat-mode'); var mockError = require('./utils/mock-error'); var isWindows = require('./utils/is-windows'); var applyUmask = require('./utils/apply-umask'); var always = require('./utils/always'); var testConstants = require('./utils/test-constants'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var inputBase = testConstants.inputBase; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var outputPath = testConstants.outputPath; var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var inputNestedPath = testConstants.inputNestedPath; var outputNestedPath = testConstants.outputNestedPath; var contents = testConstants.contents; var clean = cleanup(outputBase); describe('.dest() with custom modes', function() { beforeEach(clean); afterEach(clean); it('sets the mode of a written buffer file if set on the vinyl object', function(done) { // Changing the mode of a file is not supported by node.js in Windows. // Windows is treated as though it does not have permission to make this operation. if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('677'); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mode: expectedMode, }, }); function assert() { expect(statMode(outputPath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('sets the sticky bit on the mode of a written stream file if set on the vinyl object', function(done) { if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('1677'); var file = new File({ base: inputBase, path: inputPath, contents: from([contents]), stat: { mode: expectedMode, }, }); function assert() { expect(statMode(outputPath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('sets the mode of a written stream file if set on the vinyl object', function(done) { if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('677'); var file = new File({ base: inputBase, path: inputPath, contents: from([contents]), stat: { mode: expectedMode, }, }); function assert() { expect(statMode(outputPath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('sets the mode of a written directory if set on the vinyl object', function(done) { if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('677'); var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), mode: expectedMode, }, }); function assert() { expect(statMode(outputDirpath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('sets sticky bit on the mode of a written directory if set on the vinyl object', function(done) { if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('1677'); var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), mode: expectedMode, }, }); function assert() { expect(statMode(outputDirpath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('writes new files with the mode specified in options', function(done) { if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('777'); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert() { expect(statMode(outputPath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname, mode: expectedMode }), concat(assert), ], done); }); it('updates the file mode to match the vinyl mode', function(done) { if (isWindows) { this.skip(); return; } var startMode = applyUmask('655'); var expectedMode = applyUmask('722'); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mode: expectedMode, }, }); function assert() { expect(statMode(outputPath)).toEqual(expectedMode); } fs.mkdirSync(outputBase); fs.closeSync(fs.openSync(outputPath, 'w')); fs.chmodSync(outputPath, startMode); pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('updates the directory mode to match the vinyl mode', function(done) { if (isWindows) { this.skip(); return; } var startMode = applyUmask('2777'); var expectedMode = applyUmask('727'); var file1 = new File({ base: inputBase, path: outputDirpath, stat: { isDirectory: always(true), mode: startMode, }, }); var file2 = new File({ base: inputBase, path: outputDirpath, stat: { isDirectory: always(true), mode: expectedMode, }, }); function assert() { expect(statMode(outputDirpath)).toEqual(expectedMode); } pipe([ from.obj([file1, file2]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('uses different modes for files and directories', function(done) { if (isWindows) { this.skip(); return; } var expectedDirMode = applyUmask('2777'); var expectedFileMode = applyUmask('755'); var file = new File({ base: inputBase, path: inputNestedPath, contents: new Buffer(contents), }); function assert() { expect(statMode(outputDirpath)).toEqual(expectedDirMode); expect(statMode(outputNestedPath)).toEqual(expectedFileMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname, mode: expectedFileMode, dirMode: expectedDirMode, }), concat(assert), ], done); }); it('does not fchmod a matching file', function(done) { if (isWindows) { this.skip(); return; } var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var expectedMode = applyUmask('777'); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mode: expectedMode, }, }); function assert() { expect(fchmodSpy.calls.length).toEqual(0); expect(statMode(outputPath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('sees a file with special chmod (setuid/setgid/sticky) as distinct', function(done) { if (isWindows) { this.skip(); return; } var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var startMode = applyUmask('3722'); var expectedMode = applyUmask('722'); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mode: expectedMode, }, }); function assert() { expect(fchmodSpy.calls.length).toEqual(1); } fs.mkdirSync(outputBase); fs.closeSync(fs.openSync(outputPath, 'w')); fs.chmodSync(outputPath, startMode); pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('reports fchmod errors', function(done) { if (isWindows) { this.skip(); return; } var expectedMode = applyUmask('722'); var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(mockError); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mode: expectedMode, }, }); function assert(err) { expect(err).toExist(); expect(fchmodSpy.calls.length).toEqual(1); done(); } fs.mkdirSync(outputBase); fs.closeSync(fs.openSync(outputPath, 'w')); pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), ], assert); }); }); vinyl-fs-3.0.3/test/dest-owner.js000066400000000000000000000037031327613003100166670ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var isWindows = require('./utils/is-windows'); var testConstants = require('./utils/test-constants'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var inputBase = testConstants.inputBase; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var contents = testConstants.contents; var clean = cleanup(outputBase); describe('.dest() with custom owner', function() { beforeEach(clean); afterEach(clean); it('calls fchown when the uid and/or gid are provided on the vinyl stat', function(done) { if (isWindows) { this.skip(); return; } var fchownSpy = expect.spyOn(fs, 'fchown').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { uid: 1001, gid: 1001, }, }); function assert() { expect(fchownSpy.calls.length).toEqual(1); expect(fchownSpy.calls[0].arguments[1]).toEqual(1001); expect(fchownSpy.calls[0].arguments[2]).toEqual(1001); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('does not call fchown when the uid and gid provided on the vinyl stat are invalid', function(done) { if (isWindows) { this.skip(); return; } var fchownSpy = expect.spyOn(fs, 'fchown').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { uid: -1, gid: -1, }, }); function assert() { expect(fchownSpy.calls.length).toEqual(0); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/dest-symlinks.js000066400000000000000000000431031327613003100174040ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var isWindows = require('./utils/is-windows'); var always = require('./utils/always'); var testConstants = require('./utils/test-constants'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var inputBase = testConstants.inputBase; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var outputPath = testConstants.outputPath; var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var contents = testConstants.contents; // For not-exists tests var neInputBase = testConstants.neInputBase; var neOutputBase = testConstants.neOutputBase; var neInputDirpath = testConstants.neInputDirpath; var neOutputDirpath = testConstants.neOutputDirpath; var clean = cleanup(outputBase); describe('.dest() with symlinks', function() { beforeEach(clean); afterEach(clean); it('creates symlinks when `file.isSymbolic()` is true', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { var symlink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(file.symlink).toEqual(symlink); expect(files[0].symlink).toEqual(symlink); expect(files[0].isSymbolic()).toBe(true); expect(files[0].path).toEqual(outputPath); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('does not create symlinks when `file.isSymbolic()` is false', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(false), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { var symlinkExists = fs.existsSync(outputPath); expect(files.length).toEqual(1); expect(symlinkExists).toBe(false); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('errors if missing a `.symlink` property', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); function assert(err) { expect(err).toExist(); expect(err.message).toEqual('Missing symlink property on symbolic vinyl'); done(); } pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('emits Vinyl files that are (still) symbolic', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { expect(files.length).toEqual(1); expect(files[0].isSymbolic()).toEqual(true); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('can create relative links', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(outputLink).toEqual(path.normalize('../fixtures/test.txt')); expect(files[0].isSymbolic()).toBe(true); } pipe([ from.obj([file]), vfs.dest(outputBase, { relativeSymlinks: true }), concat(assert), ], done); }); it('(*nix) creates a link for a directory', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(outputLink).toEqual(inputDirpath); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('(windows) creates a junction for a directory', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); // When creating a junction, it seems Windows appends a separator expect(outputLink).toEqual(inputDirpath + path.sep); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('(windows) options can disable junctions for a directory', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(outputLink).toEqual(inputDirpath); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase, { useJunctions: false }), concat(assert), ], done); }); it('(windows) options can disable junctions for a directory (as a function)', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function useJunctions(f) { expect(f).toExist(); expect(f).toBe(file); return false; } function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(outputLink).toEqual(inputDirpath); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase, { useJunctions: useJunctions }), concat(assert), ], done); }); it('(*nix) can create relative links for directories', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(outputLink).toEqual(path.normalize('../fixtures/foo')); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase, { relativeSymlinks: true }), concat(assert), ], done); }); it('(*nix) receives a virtual symbolic directory and creates a symlink', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: neInputBase, path: neInputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = neInputDirpath; function assert(files) { var lstats = fs.lstatSync(neOutputDirpath); var outputLink = fs.readlinkSync(neOutputDirpath); var linkTargetExists = fs.existsSync(outputLink); expect(files.length).toEqual(1); expect(outputLink).toEqual(neInputDirpath); expect(linkTargetExists).toEqual(false); expect(lstats.isSymbolicLink()).toEqual(true); } pipe([ // This could also be from a different Vinyl adapter from.obj([file]), vfs.dest(neOutputBase), concat(assert), ], done); }); // There's no way to determine the proper type of link to create with a dangling link // So we just create a 'file' type symlink // There's also no real way to test the type that was created it('(windows) receives a virtual symbolic directory and creates a symlink', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: neInputBase, path: neInputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = neInputDirpath; function assert(files) { var lstats = fs.lstatSync(neOutputDirpath); var outputLink = fs.readlinkSync(neOutputDirpath); var linkTargetExists = fs.existsSync(outputLink); expect(files.length).toEqual(1); expect(outputLink).toEqual(neInputDirpath); expect(linkTargetExists).toEqual(false); expect(lstats.isSymbolicLink()).toEqual(true); } pipe([ // This could also be from a different Vinyl adapter from.obj([file]), vfs.dest(neOutputBase), concat(assert), ], done); }); it('(windows) relativeSymlinks option is ignored when junctions are used', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); // When creating a junction, it seems Windows appends a separator expect(outputLink).toEqual(inputDirpath + path.sep); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase, { useJunctions: true, relativeSymlinks: true }), concat(assert), ], done); }); it('(windows) supports relativeSymlinks option when link is not for a directory', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(outputLink).toEqual(path.normalize('../fixtures/test.txt')); } pipe([ from.obj([file]), // The useJunctions option is ignored when file is not a directory vfs.dest(outputBase, { useJunctions: true, relativeSymlinks: true }), concat(assert), ], done); }); it('(windows) can create relative links for directories when junctions are disabled', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputDirpath; function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(outputLink).toEqual(path.normalize('../fixtures/foo')); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.dest(outputBase, { useJunctions: false, relativeSymlinks: true }), concat(assert), ], done); }); it('does not overwrite links with overwrite option set to false', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents); } // Write expected file which should not be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: false }), concat(assert), ], done); }); it('overwrites links with overwrite option set to true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: true }), concat(assert), ], done); }); it('does not overwrite links with overwrite option set to a function that returns false', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function overwrite(f) { expect(f).toEqual(file); return false; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents); } // Write expected file which should not be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: overwrite }), concat(assert), ], done); }); it('overwrites links with overwrite option set to a function that returns true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, stat: { isSymbolicLink: always(true), }, }); // `src()` adds this side-effect with `resolveSymlinks` option set to false file.symlink = inputPath; function overwrite(f) { expect(f).toEqual(file); return true; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: overwrite }), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/dest-times.js000066400000000000000000000121411327613003100166520ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var isWindows = require('./utils/is-windows'); var testConstants = require('./utils/test-constants'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var inputBase = testConstants.inputBase; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var outputPath = testConstants.outputPath; var contents = testConstants.contents; var clean = cleanup(outputBase); describe('.dest() with custom times', function() { beforeEach(clean); afterEach(clean); it('does not call futimes when no mtime is provided on the vinyl stat', function(done) { // Changing the time of a directory errors in Windows. // Windows is treated as though it does not have permission to make this operation. if (isWindows) { this.skip(); return; } var earlier = Date.now() - 1001; var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: {}, }); function assert() { var stats = fs.lstatSync(outputPath); expect(futimesSpy.calls.length).toEqual(0); expect(stats.atime.getTime()).toBeGreaterThan(earlier); expect(stats.mtime.getTime()).toBeGreaterThan(earlier); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('calls futimes when an mtime is provided on the vinyl stat', function(done) { if (isWindows) { this.skip(); return; } // Use new mtime var mtime = new Date(Date.now() - 2048); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mtime: mtime, }, }); function assert() { expect(futimesSpy.calls.length).toEqual(1); // Compare args instead of fs.lstats(), since mtime may be drifted in x86 Node.js var mtimeSpy = futimesSpy.calls[0].arguments[2]; expect(mtimeSpy.getTime()) .toEqual(mtime.getTime()); expect(file.stat.mtime).toEqual(mtime); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('does not call futimes when provided mtime on the vinyl stat is invalid', function(done) { if (isWindows) { this.skip(); return; } var earlier = Date.now() - 1001; var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mtime: new Date(undefined), }, }); function assert() { var stats = fs.lstatSync(outputPath); expect(futimesSpy.calls.length).toEqual(0); expect(stats.mtime.getTime()).toBeGreaterThan(earlier); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('calls futimes when provided mtime on the vinyl stat is valid but provided atime is invalid', function(done) { if (isWindows) { this.skip(); return; } // Use new mtime var mtime = new Date(Date.now() - 2048); var invalidAtime = new Date(undefined); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { atime: invalidAtime, mtime: mtime, }, }); function assert() { expect(futimesSpy.calls.length).toEqual(1); var mtimeSpy = futimesSpy.calls[0].arguments[2]; expect(mtimeSpy.getTime()).toEqual(mtime.getTime()); } pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); it('writes file atime and mtime using the vinyl stat', function(done) { if (isWindows) { this.skip(); return; } // Use new atime/mtime var atime = new Date(Date.now() - 2048); var mtime = new Date(Date.now() - 1024); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { atime: atime, mtime: mtime, }, }); function assert() { expect(futimesSpy.calls.length).toEqual(1); var atimeSpy = futimesSpy.calls[0].arguments[1]; var mtimeSpy = futimesSpy.calls[0].arguments[2]; expect(atimeSpy.getTime()).toEqual(atime.getTime()); expect(mtimeSpy.getTime()).toEqual(mtime.getTime()); expect(file.stat.mtime).toEqual(mtime); expect(file.stat.atime).toEqual(atime); }; pipe([ from.obj([file]), vfs.dest(outputBase, { cwd: __dirname }), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/dest.js000066400000000000000000000627051327613003100155460ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var statMode = require('./utils/stat-mode'); var mockError = require('./utils/mock-error'); var applyUmask = require('./utils/apply-umask'); var testStreams = require('./utils/test-streams'); var always = require('./utils/always'); var testConstants = require('./utils/test-constants'); var breakPrototype = require('./utils/break-prototype'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var count = testStreams.count; var rename = testStreams.rename; var includes = testStreams.includes; var slowCount = testStreams.slowCount; var string = testStreams.string; function noop() {} var inputRelative = testConstants.inputRelative; var outputRelative = testConstants.outputRelative; var inputBase = testConstants.inputBase; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var outputPath = testConstants.outputPath; var outputRenamePath = testConstants.outputRenamePath; var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var contents = testConstants.contents; var sourcemapContents = testConstants.sourcemapContents; function makeSourceMap() { return { version: 3, file: inputRelative, names: [], mappings: '', sources: [inputRelative], sourcesContent: [contents], }; } var clean = cleanup(outputBase); describe('.dest()', function() { beforeEach(clean); afterEach(clean); it('throws on no folder argument', function(done) { function noFolder() { vfs.dest(); } expect(noFolder).toThrow('Invalid dest() folder argument. Please specify a non-empty string or a function.'); done(); }); it('throws on empty string folder argument', function(done) { function emptyFolder() { vfs.dest(''); } expect(emptyFolder).toThrow('Invalid dest() folder argument. Please specify a non-empty string or a function.'); done(); }); it('accepts the sourcemap option as true', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), sourceMap: makeSourceMap(), }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); } pipe([ from.obj([file]), vfs.dest(outputBase, { sourcemaps: true }), concat(assert), ], done); }); it('accepts the sourcemap option as a string', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), sourceMap: makeSourceMap(), }); function assert(files) { expect(files.length).toEqual(2); expect(files).toInclude(file); } pipe([ from.obj([file]), vfs.dest(outputBase, { sourcemaps: '.' }), concat(assert), ], done); }); it('inlines sourcemaps when option is true', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), sourceMap: makeSourceMap(), }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].contents.toString()).toMatch(new RegExp(sourcemapContents)); } pipe([ from.obj([file]), vfs.dest(outputBase, { sourcemaps: true }), concat(assert), ], done); }); it('generates an extra File when option is a string', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), sourceMap: makeSourceMap(), }); function assert(files) { expect(files.length).toEqual(2); expect(files).toInclude(file); expect(files[0].contents.toString()).toMatch(new RegExp('//# sourceMappingURL=test.txt.map')); expect(files[1].contents).toEqual(JSON.stringify(makeSourceMap())); } pipe([ from.obj([file]), vfs.dest(outputBase, { sourcemaps: '.' }), concat(assert), ], done); }); it('passes through writes with cwd', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].cwd).toEqual(__dirname, 'cwd should have changed'); } pipe([ from.obj([file]), vfs.dest(outputRelative, { cwd: __dirname }), concat(assert), ], done); }); it('passes through writes with default cwd', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].cwd).toEqual(process.cwd(), 'cwd should not have changed'); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('does not write null files', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { var exists = fs.existsSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(exists).toEqual(false); }; pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('writes buffer files to the right folder with relative cwd', function(done) { var cwd = path.relative(process.cwd(), __dirname); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].cwd).toEqual(__dirname, 'cwd should have changed'); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(outputContents).toEqual(contents); } pipe([ from.obj([file]), vfs.dest(outputRelative, { cwd: cwd }), concat(assert), ], done); }); it('writes buffer files to the right folder with function and relative cwd', function(done) { var cwd = path.relative(process.cwd(), __dirname); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function outputFn(f) { expect(f).toExist(); expect(f).toExist(file); return outputRelative; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].cwd).toEqual(__dirname, 'cwd should have changed'); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(outputContents).toEqual(contents); } pipe([ from.obj([file]), vfs.dest(outputFn, { cwd: cwd }), concat(assert), ], done); }); it('writes buffer files to the right folder', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(outputContents).toEqual(contents); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('writes streaming files to the right folder', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: from([contents]), }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(outputContents).toEqual(contents); }; pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('writes large streaming files to the right folder', function(done) { var size = 40000; var file = new File({ base: inputBase, path: inputPath, contents: string(size), }); function assert(files) { var stats = fs.lstatSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(stats.size).toEqual(size); }; pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('writes directories to the right folder', function(done) { var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.lstatSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); // TODO: normalize this path expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(stats.isDirectory()).toEqual(true); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('allows piping multiple dests in streaming mode', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert() { var outputContents1 = fs.readFileSync(outputPath, 'utf8'); var outputContents2 = fs.readFileSync(outputRenamePath, 'utf8'); expect(outputContents1).toEqual(contents); expect(outputContents2).toEqual(contents); } pipe([ from.obj([file]), includes({ path: inputPath }), vfs.dest(outputBase), rename(outputRenamePath), includes({ path: outputRenamePath }), vfs.dest(outputBase), concat(assert), ], done); }); it('writes new files with the default user mode', function(done) { var expectedMode = applyUmask('666'); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); expect(statMode(outputPath)).toEqual(expectedMode); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('reports i/o errors', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(err) { expect(err).toExist(); done(); } fs.mkdirSync(outputBase); fs.closeSync(fs.openSync(outputPath, 'w')); fs.chmodSync(outputPath, 0); pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('reports stat errors', function(done) { var expectedMode = applyUmask('722'); var fstatSpy = expect.spyOn(fs, 'fstat').andCall(mockError); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: { mode: expectedMode, }, }); function assert(err) { expect(err).toExist(); expect(fstatSpy.calls.length).toEqual(1); done(); } fs.mkdirSync(outputBase); fs.closeSync(fs.openSync(outputPath, 'w')); pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('does not overwrite files with overwrite option set to false', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents); } // Write expected file which should not be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: false }), concat(assert), ], done); }); it('overwrites files with overwrite option set to true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: true }), concat(assert), ], done); }); it('does not overwrite files with overwrite option set to a function that returns false', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function overwrite(f) { expect(f).toEqual(file); return false; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents); } // Write expected file which should not be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: overwrite }), concat(assert), ], done); }); it('overwrites files with overwrite option set to a function that returns true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function overwrite(f) { expect(f).toEqual(file); return true; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { overwrite: overwrite }), concat(assert), ], done); }); it('appends content with append option set to true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents + contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { append: true }), concat(assert), ], done); }); it('appends content with append option set to a function that returns true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function append(f) { expect(f).toEqual(file); return true; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents + contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.dest(outputBase, { append: append }), concat(assert), ], done); }); it('emits a finish event', function(done) { var destStream = vfs.dest(outputBase); destStream.once('finish', done); var file = new File({ base: inputBase, path: inputPath, contents: new Buffer('1234567890'), }); pipe([ from.obj([file]), destStream, ]); }); it('does not get clogged by highWaterMark', function(done) { var expectedCount = 17; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); highwatermarkFiles.push(file); } pipe([ from.obj(highwatermarkFiles), count(expectedCount), // Must be in the Writable position to test this // So concat-stream cannot be used vfs.dest(outputBase), ], done); }); it('allows backpressure when piped to another, slower stream', function(done) { this.timeout(20000); var expectedCount = 24; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); highwatermarkFiles.push(file); } pipe([ from.obj(highwatermarkFiles), count(expectedCount), vfs.dest(outputBase), slowCount(expectedCount), ], done); }); it('respects readable listeners on destination stream', function(done) { var file = new File({ base: inputBase, path: inputDirpath, contents: null, }); var destStream = vfs.dest(outputBase); var readables = 0; destStream.on('readable', function() { var data = destStream.read(); if (data != null) { readables++; } }); function assert(err) { expect(readables).toEqual(1); done(err); } pipe([ from.obj([file]), destStream, ], assert); }); it('respects data listeners on destination stream', function(done) { var file = new File({ base: inputBase, path: inputDirpath, contents: null, }); var destStream = vfs.dest(outputBase); var datas = 0; destStream.on('data', function() { datas++; }); function assert(err) { expect(datas).toEqual(1); done(err); } pipe([ from.obj([file]), destStream, ], assert); }); it('sinks the stream if all the readable event handlers are removed', function(done) { var expectedCount = 17; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); highwatermarkFiles.push(file); } var destStream = vfs.dest(outputBase); destStream.on('readable', noop); pipe([ from.obj(highwatermarkFiles), count(expectedCount), // Must be in the Writable position to test this // So concat-stream cannot be used destStream, ], done); process.nextTick(function() { destStream.removeListener('readable', noop); }); }); it('sinks the stream if all the data event handlers are removed', function(done) { var expectedCount = 17; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); highwatermarkFiles.push(file); } var destStream = vfs.dest(outputBase); destStream.on('data', noop); pipe([ from.obj(highwatermarkFiles), count(expectedCount), // Must be in the Writable position to test this // So concat-stream cannot be used destStream, ], done); process.nextTick(function() { destStream.removeListener('data', noop); }); }); it('successfully processes files with streaming contents', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: from([contents]), }); pipe([ from.obj([file]), vfs.dest(outputBase), ], done); }); it('errors when a non-Vinyl object is emitted', function(done) { var file = {}; function assert(err) { expect(err).toExist(); expect(err.message).toEqual('Received a non-Vinyl object in `dest()`'); done(); } pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('errors when a buffer-mode stream is piped to it', function(done) { var file = new Buffer('test'); function assert(err) { expect(err).toExist(); expect(err.message).toEqual('Received a non-Vinyl object in `dest()`'); done(); } pipe([ from([file]), vfs.dest(outputBase), ], assert); }); it('errors if we cannot mkdirp', function(done) { var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(mockError); var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(err) { expect(err).toExist(); expect(mkdirSpy.calls.length).toEqual(1); done(); } pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('errors if vinyl object is a directory and we cannot mkdirp', function(done) { var ogMkdir = fs.mkdir; var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { if (mkdirSpy.calls.length > 1) { mockError.apply(this, arguments); } else { ogMkdir.apply(this, arguments); } }); var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(err) { expect(err).toExist(); expect(mkdirSpy.calls.length).toEqual(2); done(); } pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); // TODO: is this correct behavior? had to adjust it it('does not error if vinyl object is a directory and we cannot open it', function(done) { var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), mode: applyUmask('000'), }, }); function assert() { var exists = fs.existsSync(outputDirpath); expect(exists).toEqual(true); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('errors if vinyl object is a directory and open errors', function(done) { var openSpy = expect.spyOn(fs, 'open').andCall(mockError); var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(err) { expect(err).toExist(); expect(openSpy.calls.length).toEqual(1); done(); } pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('errors if content stream errors', function(done) { var contentStream = from(function(size, cb) { cb(new Error('mocked error')); }); var file = new File({ base: inputBase, path: inputPath, contents: contentStream, }); function assert(err) { expect(err).toExist(); done(); } pipe([ from.obj([file]), vfs.dest(outputBase), ], assert); }); it('does not pass options on to through2', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); // Reference: https://github.com/gulpjs/vinyl-fs/issues/153 var read = expect.createSpy().andReturn(false); function assert() { // Called never because it's not a valid option expect(read.calls.length).toEqual(0); } pipe([ from.obj([file]), vfs.dest(outputBase, { read: read }), concat(assert), ], done); }); it('does not marshall a Vinyl object with isSymbolic method', function(done) { var file = new File({ base: outputBase, path: outputPath, }); function assert(files) { expect(files.length).toEqual(1); // Avoid comparing stats because they get reflected delete files[0].stat; expect(files[0]).toMatch(file); expect(files[0]).toBe(file); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); it('marshalls a Vinyl object without isSymbolic to a newer Vinyl', function(done) { var file = new File({ base: outputBase, path: outputPath, }); breakPrototype(file); function assert(files) { expect(files.length).toEqual(1); // Avoid comparing stats because they get reflected delete files[0].stat; expect(files[0]).toMatch(file); expect(files[0]).toNotBe(file); } pipe([ from.obj([file]), vfs.dest(outputBase), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/file-operations.js000066400000000000000000001100061327613003100176730ustar00rootroot00000000000000'use strict'; var path = require('path'); var buffer = require('buffer'); var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var mkdirp = require('fs-mkdirp-stream/mkdirp'); var fo = require('../lib/file-operations'); var constants = require('../lib/constants'); var DEFAULT_FILE_MODE = constants.DEFAULT_FILE_MODE; var cleanup = require('./utils/cleanup'); var statMode = require('./utils/stat-mode'); var mockError = require('./utils/mock-error'); var isWindows = require('./utils/is-windows'); var applyUmask = require('./utils/apply-umask'); var testStreams = require('./utils/test-streams'); var testConstants = require('./utils/test-constants'); var closeFd = fo.closeFd; var isOwner = fo.isOwner; var writeFile = fo.writeFile; var getModeDiff = fo.getModeDiff; var getTimesDiff = fo.getTimesDiff; var getOwnerDiff = fo.getOwnerDiff; var isValidUnixId = fo.isValidUnixId; var getFlags = fo.getFlags; var isFatalOverwriteError = fo.isFatalOverwriteError; var isFatalUnlinkError = fo.isFatalUnlinkError; var reflectStat = fo.reflectStat; var reflectLinkStat = fo.reflectLinkStat; var updateMetadata = fo.updateMetadata; var createWriteStream = fo.createWriteStream; var pipe = miss.pipe; var from = miss.from; var string = testStreams.string; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var neInputDirpath = testConstants.neInputDirpath; var outputPath = testConstants.outputPath; var symlinkPath = testConstants.symlinkDirpath; var contents = testConstants.contents; var clean = cleanup(outputBase); function noop() {} describe('isOwner', function() { var ownerStat = { uid: 9001, }; var nonOwnerStat = { uid: 9002, }; var getuidSpy; var geteuidSpy; beforeEach(function(done) { if (typeof process.geteuid !== 'function') { process.geteuid = noop; } // Windows :( if (typeof process.getuid !== 'function') { process.getuid = noop; } getuidSpy = expect.spyOn(process, 'getuid').andReturn(ownerStat.uid); geteuidSpy = expect.spyOn(process, 'geteuid').andReturn(ownerStat.uid); done(); }); afterEach(function(done) { expect.restoreSpies(); if (process.geteuid === noop) { delete process.geteuid; } // Windows :( if (process.getuid === noop) { delete process.getuid; } done(); }); // TODO: test for having neither it('uses process.geteuid() when available', function(done) { isOwner(ownerStat); expect(getuidSpy.calls.length).toEqual(0); expect(geteuidSpy.calls.length).toEqual(1); done(); }); it('uses process.getuid() when geteuid() is not available', function(done) { delete process.geteuid; isOwner(ownerStat); expect(getuidSpy.calls.length).toEqual(1); done(); }); it('returns false when non-root and non-owner', function(done) { var result = isOwner(nonOwnerStat); expect(result).toEqual(false); done(); }); it('returns true when owner and non-root', function(done) { var result = isOwner(ownerStat); expect(result).toEqual(true); done(); }); it('returns true when non-owner but root', function(done) { expect.spyOn(process, 'geteuid').andReturn(0); // 0 is root uid var result = isOwner(nonOwnerStat); expect(result).toEqual(true); done(); }); }); describe('isValidUnixId', function() { it('returns true if the given id is a valid unix id', function(done) { var result = isValidUnixId(1000); expect(result).toEqual(true); done(); }); it('returns false if the given id is not a number', function(done) { var result = isValidUnixId('root'); expect(result).toEqual(false); done(); }); it('returns false when the given id is less than 0', function(done) { var result = isValidUnixId(-1); expect(result).toEqual(false); done(); }); }); describe('getFlags', function() { it('returns wx if overwrite is false and append is false', function(done) { var result = getFlags({ overwrite: false, append: false, }); expect(result).toEqual('wx'); done(); }); it('returns w if overwrite is true and append is false', function(done) { var result = getFlags({ overwrite: true, append: false, }); expect(result).toEqual('w'); done(); }); it('returns ax if overwrite is false and append is true', function(done) { var result = getFlags({ overwrite: false, append: true, }); expect(result).toEqual('ax'); done(); }); it('returns a if overwrite is true and append is true', function(done) { var result = getFlags({ overwrite: true, append: true, }); expect(result).toEqual('a'); done(); }); }); describe('isFatalOverwriteError', function() { it('returns false if not given any error', function(done) { var result = isFatalOverwriteError(null); expect(result).toEqual(false); done(); }); it('returns true if code != EEXIST', function(done) { var result = isFatalOverwriteError({ code: 'EOTHER' }); expect(result).toEqual(true); done(); }); it('returns false if code == EEXIST and flags == wx', function(done) { var result = isFatalOverwriteError({ code: 'EEXIST' }, 'wx'); expect(result).toEqual(false); done(); }); it('returns false if code == EEXIST and flags == ax', function(done) { var result = isFatalOverwriteError({ code: 'EEXIST' }, 'ax'); expect(result).toEqual(false); done(); }); it('returns true if error.code == EEXIST and flags == w', function(done) { var result = isFatalOverwriteError({ code: 'EEXIST' }, 'w'); expect(result).toEqual(true); done(); }); it('returns true if error.code == EEXIST and flags == a', function(done) { var result = isFatalOverwriteError({ code: 'EEXIST' }, 'a'); expect(result).toEqual(true); done(); }); }); describe('isFatalUnlinkError', function() { it('returns false if not given any error', function(done) { var result = isFatalUnlinkError(null); expect(result).toEqual(false); done(); }); it('returns false if code == ENOENT', function(done) { var result = isFatalUnlinkError({ code: 'ENOENT' }, 'wx'); expect(result).toEqual(false); done(); }); it('returns true if code != ENOENT', function(done) { var result = isFatalUnlinkError({ code: 'EOTHER' }); expect(result).toEqual(true); done(); }); }); describe('getModeDiff', function() { it('returns 0 if both modes are the same', function(done) { var fsMode = applyUmask('777'); var vfsMode = applyUmask('777'); var result = getModeDiff(fsMode, vfsMode); expect(result).toEqual(0); done(); }); it('returns 0 if vinyl mode is not a number', function(done) { var fsMode = applyUmask('777'); var vfsMode = undefined; var result = getModeDiff(fsMode, vfsMode); expect(result).toEqual(0); done(); }); it('returns a value greater than 0 if modes are different', function(done) { var fsMode = applyUmask('777'); var vfsMode = applyUmask('744'); var result = getModeDiff(fsMode, vfsMode); expect(result).toBeGreaterThan(0); done(); }); it('returns the proper diff', function(done) { var fsMode = applyUmask('777'); var vfsMode = applyUmask('744'); var expectedDiff = applyUmask('33'); var result = getModeDiff(fsMode, vfsMode); expect(result).toEqual(expectedDiff); done(); }); it('does not matter the order of diffing', function(done) { var fsMode = applyUmask('655'); var vfsMode = applyUmask('777'); var expectedDiff = applyUmask('122'); var result = getModeDiff(fsMode, vfsMode); expect(result).toEqual(expectedDiff); done(); }); it('includes the sticky/setuid/setgid bits', function(done) { var fsMode = applyUmask('1777'); var vfsMode = applyUmask('4777'); var expectedDiff = applyUmask('5000'); var result = getModeDiff(fsMode, vfsMode); expect(result).toEqual(expectedDiff); done(); }); }); describe('getTimesDiff', function() { it('returns undefined if vinyl mtime is not a valid date', function(done) { var fsStat = { mtime: new Date(), }; var vfsStat = { mtime: new Date(undefined), }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); it('returns undefined if vinyl mtime & atime are both equal to counterparts', function(done) { var now = Date.now(); var fsStat = { mtime: new Date(now), atime: new Date(now), }; var vfsStat = { mtime: new Date(now), atime: new Date(now), }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); // TODO: is this proper/expected? it('returns undefined if vinyl mtimes equals the counterpart and atimes are null', function(done) { var now = Date.now(); var fsStat = { mtime: new Date(now), atime: null, }; var vfsStat = { mtime: new Date(now), atime: null, }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); it('returns a diff object if mtimes do not match', function(done) { var now = Date.now(); var then = now - 1000; var fsStat = { mtime: new Date(now), }; var vfsStat = { mtime: new Date(then), }; var expected = { mtime: new Date(then), atime: undefined, }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); it('returns a diff object if atimes do not match', function(done) { var now = Date.now(); var then = now - 1000; var fsStat = { mtime: new Date(now), atime: new Date(now), }; var vfsStat = { mtime: new Date(now), atime: new Date(then), }; var expected = { mtime: new Date(now), atime: new Date(then), }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); it('returns the fs atime if the vinyl atime is invalid', function(done) { var now = Date.now(); var fsStat = { mtime: new Date(now), atime: new Date(now), }; var vfsStat = { mtime: new Date(now), atime: new Date(undefined), }; var expected = { mtime: new Date(now), atime: new Date(now), }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); // TODO: is this proper/expected? it('makes atime diff undefined if fs and vinyl atime are invalid', function(done) { var now = Date.now(); var fsStat = { mtime: new Date(now), atime: new Date(undefined), }; var vfsStat = { mtime: new Date(now), atime: new Date(undefined), }; var expected = { mtime: new Date(now), atime: undefined, }; var result = getTimesDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); }); describe('getOwnerDiff', function() { it('returns undefined if vinyl uid & gid are invalid', function(done) { var fsStat = { uid: 1000, gid: 1000, }; var vfsStat = { uid: undefined, gid: undefined, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); it('returns undefined if vinyl uid & gid are both equal to counterparts', function(done) { var fsStat = { uid: 1000, gid: 1000, }; var vfsStat = { uid: 1000, gid: 1000, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); it('returns a diff object if uid or gid do not match', function(done) { var fsStat = { uid: 1000, gid: 1000, }; var vfsStat = { uid: 1001, gid: 1000, }; var expected = { uid: 1001, gid: 1000, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(expected); vfsStat = { uid: 1000, gid: 1001, }; expected = { uid: 1000, gid: 1001, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); it('returns the fs uid if the vinyl uid is invalid', function(done) { var fsStat = { uid: 1000, gid: 1000, }; var vfsStat = { uid: undefined, gid: 1001, }; var expected = { uid: 1000, gid: 1001, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(expected); var vfsStat = { uid: -1, gid: 1001, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); it('returns the fs gid if the vinyl gid is invalid', function(done) { var fsStat = { uid: 1000, gid: 1000, }; var vfsStat = { uid: 1001, gid: undefined, }; var expected = { uid: 1001, gid: 1000, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(expected); var vfsStat = { uid: 1001, gid: -1, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(expected); done(); }); it('returns undefined if fs and vinyl uid are invalid', function(done) { var fsStat = { uid: undefined, gid: 1000, }; var vfsStat = { uid: undefined, gid: 1001, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(undefined); var fsStat = { uid: -1, gid: 1000, }; var vfsStat = { uid: -1, gid: 1001, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); it('returns undefined if fs and vinyl gid are invalid', function(done) { var fsStat = { uid: 1000, gid: undefined, }; var vfsStat = { uid: 1001, gid: undefined, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(undefined); fsStat = { uid: 1000, gid: -1, }; vfsStat = { uid: 1001, gid: -1, }; var result = getOwnerDiff(fsStat, vfsStat); expect(result).toEqual(undefined); done(); }); }); describe('closeFd', function() { it('calls the callback with propagated error if fd is not a number', function(done) { var propagatedError = new Error(); closeFd(propagatedError, null, function(err) { expect(err).toEqual(propagatedError); done(); }); }); it('calls the callback with close error if no error to propagate', function(done) { closeFd(null, -1, function(err) { expect(err).toExist(); done(); }); }); it('calls the callback with propagated error if close errors', function(done) { var propagatedError = new Error(); closeFd(propagatedError, -1, function(err) { expect(err).toEqual(propagatedError); done(); }); }); it('calls the callback with propagated error if close succeeds', function(done) { var propagatedError = new Error(); var fd = fs.openSync(inputPath, 'r'); var closeSpy = expect.spyOn(fs, 'close').andCallThrough(); closeFd(propagatedError, fd, function(err) { closeSpy.restore(); expect(closeSpy.calls.length).toEqual(1); expect(err).toEqual(propagatedError); done(); }); }); it('calls the callback with no error if close succeeds & no propagated error', function(done) { var fd = fs.openSync(inputPath, 'r'); var spy = expect.spyOn(fs, 'close').andCallThrough(); closeFd(null, fd, function(err) { spy.restore(); expect(spy.calls.length).toEqual(1); expect(err).toEqual(undefined); done(); }); }); }); describe('writeFile', function() { beforeEach(clean); afterEach(clean); beforeEach(function(done) { mkdirp(outputBase, done); }); it('writes a file to the filesystem, does not close and returns the fd', function(done) { writeFile(outputPath, new Buffer(contents), function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, function() { var written = fs.readFileSync(outputPath, 'utf8'); expect(written).toEqual(contents); done(); }); }); }); it('defaults to writing files with 0666 mode', function(done) { var expected = applyUmask('666'); writeFile(outputPath, new Buffer(contents), function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, function() { expect(statMode(outputPath)).toEqual(expected); done(); }); }); }); it('accepts a different mode in options', function(done) { // Changing the mode of a file is not supported by node.js in Windows. if (isWindows) { this.skip(); return; } var expected = applyUmask('777'); var options = { mode: expected, }; writeFile(outputPath, new Buffer(contents), options, function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, function() { expect(statMode(outputPath)).toEqual(expected); done(); }); }); }); it('defaults to opening files with write flag', function(done) { var length = contents.length; writeFile(outputPath, new Buffer(contents), function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.read(fd, new Buffer(length), 0, length, 0, function(readErr) { expect(readErr).toExist(); fs.close(fd, done); }); }); }); it('accepts a different flags in options', function(done) { var length = contents.length; var options = { flags: 'w+', }; writeFile(outputPath, new Buffer(contents), options, function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.read(fd, new Buffer(length), 0, length, 0, function(readErr, _, written) { expect(readErr).toNotExist(); expect(written.toString()).toEqual(contents); fs.close(fd, done); }); }); }); it('appends to a file if append flag is given', function(done) { var initial = 'test'; var toWrite = '-a-thing'; fs.writeFileSync(outputPath, initial, 'utf8'); var expected = initial + toWrite; var options = { flags: 'a', }; writeFile(outputPath, new Buffer(toWrite), options, function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, function() { var written = fs.readFileSync(outputPath, 'utf8'); expect(written).toEqual(expected); done(); }); }); }); it('does not pass a file descriptor if open call errors', function(done) { var notExistDir = path.join(__dirname, './not-exist-dir/writeFile.txt'); writeFile(notExistDir, new Buffer(contents), function(err, fd) { expect(err).toExist(); expect(typeof fd === 'number').toEqual(false); done(); }); }); it('passes a file descriptor if write call errors', function(done) { var options = { flags: 'r', }; writeFile(inputPath, new Buffer(contents), options, function(err, fd) { expect(err).toExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, done); }); }); it('passes an error if called with string as data', function(done) { writeFile(outputPath, contents, function(err) { expect(err).toExist(); done(); }); }); it('does not error on SlowBuffer', function(done) { if (!buffer.SlowBuffer) { this.skip(); return; } var length = contents.length; var buf = new Buffer(contents); var content = new buffer.SlowBuffer(length); buf.copy(content, 0, 0, length); writeFile(outputPath, content, function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, function() { var written = fs.readFileSync(outputPath, 'utf8'); expect(written).toEqual(contents); done(); }); }); }); it('does not error if options is falsey', function(done) { writeFile(outputPath, new Buffer(contents), null, function(err, fd) { expect(err).toNotExist(); expect(typeof fd === 'number').toEqual(true); fs.close(fd, done); }); }); }); describe('reflectStat', function() { beforeEach(clean); afterEach(clean); beforeEach(function(done) { mkdirp(outputBase, done); }); it('passes the error if stat fails', function(done) { var file = new File(); reflectStat(neInputDirpath, file, function(err) { expect(err).toExist(); done(); }); }); it('updates the vinyl with filesystem stats', function(done) { var file = new File(); fs.symlinkSync(inputPath, symlinkPath); reflectStat(symlinkPath, file, function() { // There appears to be a bug in the Windows implementation which causes // the sync versions of stat and lstat to return unsigned 32-bit ints // whilst the async versions returns signed 32-bit ints... This affects // dev but possibly others as well? fs.stat(symlinkPath, function(err, stat) { expect(file.stat).toEqual(stat); done(); }); }); }); }); describe('reflectLinkStat', function() { beforeEach(clean); afterEach(clean); beforeEach(function(done) { mkdirp(outputBase, done); }); it('passes the error if lstat fails', function(done) { var file = new File(); reflectLinkStat(neInputDirpath, file, function(err) { expect(err).toExist(); done(); }); }); it('updates the vinyl with filesystem symbolic stats', function(done) { var file = new File(); fs.symlinkSync(inputPath, symlinkPath); reflectLinkStat(symlinkPath, file, function() { // There appears to be a bug in the Windows implementation which causes // the sync versions of stat and lstat to return unsigned 32-bit ints // whilst the async versions returns signed 32-bit ints... This affects // dev but possibly others as well? fs.lstat(symlinkPath, function(err, stat) { expect(file.stat).toEqual(stat); done(); }); }); }); }); describe('updateMetadata', function() { beforeEach(clean); afterEach(clean); beforeEach(function(done) { mkdirp(outputBase, done); }); afterEach(function(done) { if (process.geteuid === noop) { delete process.geteuid; } done(); }); it('passes the error if fstat fails', function(done) { // Changing the time of a directory errors in Windows. // Changing the mode of a file is not supported by node.js in Windows. // Windows is treated as though it does not have permission to make these operations. if (isWindows) { this.skip(); return; } var fd = 9001; var file = new File({ base: outputBase, path: outputPath, contents: null, stat: {}, }); updateMetadata(fd, file, function(err) { expect(err).toExist(); done(); }); }); it('updates the vinyl object with fs stats', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: outputBase, path: outputPath, contents: null, stat: {}, }); var fd = fs.openSync(outputPath, 'w+'); var stats = fs.fstatSync(fd); updateMetadata(fd, file, function() { // Not sure why .toEqual doesn't match these Object.keys(file.stat).forEach(function(key) { expect(file.stat[key]).toEqual(stats[key]); }); fs.close(fd, done); }); }); it('does not touch the fs if nothing to update', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: outputBase, path: outputPath, contents: null, stat: {}, }); var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var fd = fs.openSync(outputPath, 'w+'); updateMetadata(fd, file, function() { expect(fchmodSpy.calls.length).toEqual(0); expect(futimesSpy.calls.length).toEqual(0); fs.close(fd, done); }); }); it('does not touch the fs if process is not owner of the file', function(done) { if (isWindows) { this.skip(); return; } if (typeof process.geteuid !== 'function') { process.geteuid = noop; } var earlier = Date.now() - 1000; var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mtime: new Date(earlier), }, }); expect.spyOn(process, 'geteuid').andReturn(9002); var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var fd = fs.openSync(outputPath, 'w+'); updateMetadata(fd, file, function() { expect(fchmodSpy.calls.length).toEqual(0); expect(futimesSpy.calls.length).toEqual(0); fs.close(fd, done); }); }); it('updates times on fs and vinyl object if there is a diff', function(done) { if (isWindows) { this.skip(); return; } var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); // Use new atime/mtime var atime = new Date(Date.now() - 2048); var mtime = new Date(Date.now() - 1024); var mtimeEarlier = mtime.getTime() - 1000; var atimeEarlier = atime.getTime() - 1000; var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mtime: new Date(mtimeEarlier), atime: new Date(atimeEarlier), }, }); var fd = fs.openSync(outputPath, 'w+'); updateMetadata(fd, file, function() { expect(futimesSpy.calls.length).toEqual(1); // Var stats = fs.fstatSync(fd); var atimeSpy = futimesSpy.calls[0].arguments[1]; var mtimeSpy = futimesSpy.calls[0].arguments[2]; expect(file.stat.mtime).toEqual(new Date(mtimeEarlier)); expect(mtimeSpy.getTime()).toEqual(mtimeEarlier); expect(file.stat.atime).toEqual(new Date(atimeEarlier)); expect(atimeSpy.getTime()).toEqual(atimeEarlier); fs.close(fd, done); }); }); it('forwards futimes error and descriptor upon error', function(done) { if (isWindows) { this.skip(); return; } var futimesSpy = expect.spyOn(fs, 'futimes').andCall(mockError); var now = Date.now(); var then = now - 1000; var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mtime: new Date(then), atime: new Date(then), }, }); var fd = fs.openSync(outputPath, 'w+'); expect(typeof fd === 'number').toEqual(true); updateMetadata(fd, file, function(err) { expect(err).toExist(); expect(futimesSpy.calls.length).toEqual(1); fs.close(fd, done); }); }); it('updates the mode on fs and vinyl object if there is a diff', function(done) { if (isWindows) { this.skip(); return; } var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var mode = applyUmask('777'); var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mode: mode, }, }); var fd = fs.openSync(outputPath, 'w+'); updateMetadata(fd, file, function() { expect(fchmodSpy.calls.length).toEqual(1); var stats = fs.fstatSync(fd); expect(file.stat.mode).toEqual(stats.mode); fs.close(fd, done); }); }); it('updates the sticky bit on mode on fs and vinyl object if there is a diff', function(done) { if (isWindows) { this.skip(); return; } var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var mode = applyUmask('1777'); var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mode: mode, }, }); var fd = fs.openSync(outputPath, 'w+'); updateMetadata(fd, file, function() { expect(fchmodSpy.calls.length).toEqual(1); var stats = fs.fstatSync(fd); expect(file.stat.mode).toEqual(stats.mode); fs.close(fd, done); }); }); it('forwards fchmod error and descriptor upon error', function(done) { if (isWindows) { this.skip(); return; } var mode = applyUmask('777'); var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mode: mode, }, }); var fd = fs.openSync(outputPath, 'w+'); var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(mockError); updateMetadata(fd, file, function(err) { expect(err).toExist(); expect(fchmodSpy.calls.length).toEqual(1); fs.close(fd, done); }); }); it('updates the mode & times on fs and vinyl object if there is a diff', function(done) { if (isWindows) { this.skip(); return; } var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); // Use new atime/mtime var atime = new Date(Date.now() - 2048); var mtime = new Date(Date.now() - 1024); var mtimeEarlier = mtime.getTime() - 1000; var atimeEarlier = atime.getTime() - 1000; var mode = applyUmask('777'); var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mtime: new Date(mtimeEarlier), atime: new Date(atimeEarlier), mode: mode, }, }); var fd = fs.openSync(outputPath, 'w+'); updateMetadata(fd, file, function() { expect(fchmodSpy.calls.length).toEqual(1); expect(futimesSpy.calls.length).toEqual(1); var atimeSpy = futimesSpy.calls[0].arguments[1]; var mtimeSpy = futimesSpy.calls[0].arguments[2]; expect(file.stat.mtime).toEqual(new Date(mtimeEarlier)); expect(mtimeSpy.getTime()).toEqual(mtimeEarlier); expect(file.stat.atime).toEqual(new Date(atimeEarlier)); expect(atimeSpy.getTime()).toEqual(atimeEarlier); fs.close(fd, done); }); }); it('forwards fchmod error and descriptor through futimes if there is a time diff', function(done) { if (isWindows) { this.skip(); return; } var mockedErr = new Error('mocked error'); var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { cb(mockedErr); }); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var now = Date.now(); var then = now - 1000; var mode = applyUmask('777'); var file = new File({ base: outputBase, path: outputPath, contents: null, stat: { mtime: new Date(then), atime: new Date(then), mode: mode, }, }); var fd = fs.openSync(outputPath, 'w'); updateMetadata(fd, file, function(err) { expect(err).toExist(); expect(err).toEqual(mockedErr); expect(fchmodSpy.calls.length).toEqual(1); expect(futimesSpy.calls.length).toEqual(1); fs.close(fd, done); }); }); // TODO: forward fchown error tests }); describe('createWriteStream', function() { beforeEach(clean); afterEach(clean); beforeEach(function(done) { // For some reason, the outputDir sometimes exists on Windows // So we use our mkdirp to create it mkdirp(outputBase, done); }); it('accepts just a file path and writes to it', function(done) { function assert(err) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(outputContents).toEqual(contents); done(err); } pipe([ from([contents]), createWriteStream(outputPath), ], assert); }); it('accepts just a file path and writes a large file to it', function(done) { var size = 40000; function assert(err) { var stats = fs.lstatSync(outputPath); expect(stats.size).toEqual(size); done(err); } pipe([ string(size), createWriteStream(outputPath), ], assert); }); it('accepts flags option', function(done) { // Write 13 stars then 12345 because the length of expected is 13 fs.writeFileSync(outputPath, '*************12345'); function assert(err) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(outputContents).toEqual(contents + '12345'); done(err); } pipe([ from([contents]), // Replaces from the beginning of the file createWriteStream(outputPath, { flags: 'r+' }), ], assert); }); it('accepts append flag as option & places cursor at the end', function(done) { fs.writeFileSync(outputPath, '12345'); function assert(err) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(outputContents).toEqual('12345' + contents); done(err); } pipe([ from([contents]), // Appends to the end of the file createWriteStream(outputPath, { flags: 'a' }), ], assert); }); it('accepts mode option', function(done) { if (isWindows) { console.log('Changing the mode of a file is not supported by node.js in Windows.'); this.skip(); return; } var mode = applyUmask('777'); function assert(err) { expect(statMode(outputPath)).toEqual(mode); done(err); } pipe([ from([contents]), createWriteStream(outputPath, { mode: mode }), ], assert); }); it('uses default file mode if no mode options', function(done) { var defaultMode = applyUmask(DEFAULT_FILE_MODE); function assert(err) { expect(statMode(outputPath)).toEqual(defaultMode); done(err); } pipe([ from([contents]), createWriteStream(outputPath), ], assert); }); it('accepts a flush function that is called before close emitted', function(done) { var flushCalled = false; var outStream = createWriteStream(outputPath, {}, function(fd, cb) { flushCalled = true; cb(); }); function assert(err) { expect(flushCalled).toEqual(true); done(err); } pipe([ from([contents]), outStream, ], assert); }); it('can specify flush without options argument', function(done) { var flushCalled = false; var outStream = createWriteStream(outputPath, function(fd, cb) { flushCalled = true; cb(); }); function assert(err) { expect(flushCalled).toEqual(true); done(err); } pipe([ from([contents]), outStream, ], assert); }); it('passes the file descriptor to flush', function(done) { var flushCalled = false; var outStream = createWriteStream(outputPath, function(fd, cb) { expect(fd).toBeA('number'); flushCalled = true; cb(); }); function assert(err) { expect(flushCalled).toEqual(true); done(err); } pipe([ from([contents]), outStream, ], assert); }); it('passes a callback to flush to call when work is done', function(done) { var flushCalled = false; var timeoutCalled = false; var outStream = createWriteStream(outputPath, function(fd, cb) { flushCalled = true; setTimeout(function() { timeoutCalled = true; cb(); }, 250); }); function assert(err) { expect(flushCalled).toEqual(true); expect(timeoutCalled).toEqual(true); done(err); } pipe([ from([contents]), outStream, ], assert); }); it('emits an error if open fails', function(done) { var badOutputPath = path.join(outputBase, './non-exist/test.coffee'); function assert(err) { expect(err).toBeAn(Error); done(); } pipe([ from([contents]), createWriteStream(badOutputPath), ], assert); }); it('emits an error if write fails', function(done) { // Create the file so it can be opened with `r` fs.writeFileSync(outputPath, contents); function assert(err) { expect(err).toBeAn(Error); done(); } pipe([ from([contents]), createWriteStream(outputPath, { flags: 'r' }), ], assert); }); }); vinyl-fs-3.0.3/test/fixtures/000077500000000000000000000000001327613003100161105ustar00rootroot00000000000000vinyl-fs-3.0.3/test/fixtures/bom-utf16be.txt000066400000000000000000000003641327613003100207030ustar00rootroot00000000000000This file is saved as UTF-16-BE. It contains some garbage at the start that looks like a UTF-8-encoded BOM (but isn t). vinyl-fs-3.0.3/test/fixtures/bom-utf16le.txt000066400000000000000000000003641327613003100207150ustar00rootroot00000000000000This file is saved as UTF-16-LE. It contains some garbage at the start that looks like a UTF-8-encoded BOM (but isn t). vinyl-fs-3.0.3/test/fixtures/bom-utf8.txt000066400000000000000000000000561327613003100203130ustar00rootroot00000000000000This file is saved as UTF-8 with BOM. 𝌆 vinyl-fs-3.0.3/test/fixtures/foo/000077500000000000000000000000001327613003100166735ustar00rootroot00000000000000vinyl-fs-3.0.3/test/fixtures/foo/bar/000077500000000000000000000000001327613003100174375ustar00rootroot00000000000000vinyl-fs-3.0.3/test/fixtures/foo/bar/baz.txt000066400000000000000000000000161327613003100207510ustar00rootroot00000000000000symlink works vinyl-fs-3.0.3/test/fixtures/not-owned/000077500000000000000000000000001327613003100200225ustar00rootroot00000000000000vinyl-fs-3.0.3/test/fixtures/not-owned/not-owned.txt000066400000000000000000000000151327613003100224710ustar00rootroot00000000000000Hello World! vinyl-fs-3.0.3/test/fixtures/test.txt000066400000000000000000000000151327613003100176240ustar00rootroot00000000000000Hello World! vinyl-fs-3.0.3/test/integration.js000066400000000000000000000123151327613003100171220ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var miss = require('mississippi'); var expect = require('expect'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var isWindows = require('./utils/is-windows'); var testStreams = require('./utils/test-streams'); var testConstants = require('./utils/test-constants'); var pipe = miss.pipe; var concat = miss.concat; var count = testStreams.count; var base = testConstants.outputBase; var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var symlinkDirpath = testConstants.symlinkDirpath; var inputBase = path.join(base, './in/'); var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var symlinkDirpath = testConstants.symlinkDirpath; var inputGlob = path.join(inputBase, './*.txt'); var outputBase = path.join(base, './out/'); var outputSymlink = path.join(symlinkDirpath, './foo'); var outputDirpathSymlink = path.join(outputDirpath, './foo'); var content = testConstants.content; var clean = cleanup(base); describe('integrations', function() { beforeEach(clean); afterEach(clean); it('does not exhaust available file descriptors when streaming thousands of files', function(done) { // This can be a very slow test on boxes with slow disk i/o this.timeout(0); // Make a ton of files. Changed from hard links due to Windows failures var expectedCount = 6000; fs.mkdirSync(base); fs.mkdirSync(inputBase); for (var idx = 0; idx < expectedCount; idx++) { var filepath = path.join(inputBase, './test' + idx + '.txt'); fs.writeFileSync(filepath, content); } pipe([ vfs.src(inputGlob, { buffer: false }), count(expectedCount), vfs.dest(outputBase), ], done); }); it('(*nix) sources a directory, creates a symlink and copies it', function(done) { if (isWindows) { this.skip(); return; } function assert(files) { var symlinkResult = fs.readlinkSync(outputSymlink); var destResult = fs.readlinkSync(outputDirpathSymlink); expect(symlinkResult).toEqual(inputDirpath); expect(destResult).toEqual(inputDirpath); expect(files[0].isSymbolic()).toBe(true); expect(files[0].symlink).toEqual(inputDirpath); } pipe([ vfs.src(inputDirpath), vfs.symlink(symlinkDirpath), vfs.dest(outputDirpath), concat(assert), ], done); }); it('(windows) sources a directory, creates a junction and copies it', function(done) { if (!isWindows) { this.skip(); return; } function assert(files) { // Junctions add an ending separator var expected = inputDirpath + path.sep; var symlinkResult = fs.readlinkSync(outputSymlink); var destResult = fs.readlinkSync(outputDirpathSymlink); expect(symlinkResult).toEqual(expected); expect(destResult).toEqual(expected); expect(files[0].isSymbolic()).toBe(true); expect(files[0].symlink).toEqual(inputDirpath); } pipe([ vfs.src(inputDirpath), vfs.symlink(symlinkDirpath), vfs.dest(outputDirpath), concat(assert), ], done); }); it('(*nix) sources a symlink and copies it', function(done) { if (isWindows) { this.skip(); return; } fs.mkdirSync(base); fs.mkdirSync(symlinkDirpath); fs.symlinkSync(inputDirpath, outputSymlink); function assert(files) { var destResult = fs.readlinkSync(outputDirpathSymlink); expect(destResult).toEqual(inputDirpath); expect(files[0].isSymbolic()).toEqual(true); expect(files[0].symlink).toEqual(inputDirpath); } pipe([ vfs.src(outputSymlink, { resolveSymlinks: false }), vfs.dest(outputDirpath), concat(assert), ], done); }); it('(windows) sources a directory symlink and copies it', function(done) { if (!isWindows) { this.skip(); return; } fs.mkdirSync(base); fs.mkdirSync(symlinkDirpath); fs.symlinkSync(inputDirpath, outputSymlink, 'dir'); function assert(files) { // 'dir' symlinks add an ending separator var expected = inputDirpath + path.sep; var destResult = fs.readlinkSync(outputDirpathSymlink); expect(destResult).toEqual(expected); expect(files[0].isSymbolic()).toEqual(true); expect(files[0].symlink).toEqual(inputDirpath); } pipe([ vfs.src(outputSymlink, { resolveSymlinks: false }), vfs.dest(outputDirpath), concat(assert), ], done); }); it('(windows) sources a junction and copies it', function(done) { if (!isWindows) { this.skip(); return; } fs.mkdirSync(base); fs.mkdirSync(symlinkDirpath); fs.symlinkSync(inputDirpath, outputSymlink, 'junction'); function assert(files) { // Junctions add an ending separator var expected = inputDirpath + path.sep; var destResult = fs.readlinkSync(outputDirpathSymlink); expect(destResult).toEqual(expected); expect(files[0].isSymbolic()).toEqual(true); expect(files[0].symlink).toEqual(inputDirpath); } pipe([ vfs.src(outputSymlink, { resolveSymlinks: false }), vfs.dest(outputDirpath), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/not-owned.js000066400000000000000000000050571327613003100165160ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var applyUmask = require('./utils/apply-umask'); var testConstants = require('./utils/test-constants'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var notOwnedBase = testConstants.notOwnedBase; var notOwnedPath = testConstants.notOwnedPath; var contents = testConstants.contents; var clean = cleanup(); describe('.dest() on not owned files', function() { var fileStats = fs.statSync(notOwnedPath); beforeEach(clean); afterEach(clean); var seenActions = false; function needsAction() { var problems = []; var actions = []; if (fileStats.uid !== 0) { problems.push('Test files not owned by root.'); actions.push(' sudo chown root ' + notOwnedPath); } if ((fileStats.mode & parseInt('022', 8)) !== parseInt('022', 8)) { problems.push('Test files not readable/writable by non-owners.'); actions.push(' sudo chmod 666 ' + notOwnedPath); } if (actions.length > 0) { if (!seenActions) { console.log(problems.join('\n')); console.log('Please run the following commands and try again:'); console.log(actions.join('\n')); seenActions = true; } return true; } return false; } it('does not error if mtime is different', function(done) { if (needsAction()) { this.skip(); return; } var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var earlier = Date.now() - 1000; var file = new File({ base: notOwnedBase, path: notOwnedPath, contents: new Buffer(contents), stat: { mtime: new Date(earlier), }, }); function assert() { expect(futimesSpy.calls.length).toEqual(0); } pipe([ from.obj([file]), vfs.dest(notOwnedBase), concat(assert), ], done); }); it('does not error if mode is different', function(done) { if (needsAction()) { this.skip(); return; } var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var file = new File({ base: notOwnedBase, path: notOwnedPath, contents: new Buffer(contents), stat: { mode: applyUmask('777'), }, }); function assert() { expect(fchmodSpy.calls.length).toEqual(0); } pipe([ from.obj([file]), vfs.dest(notOwnedBase), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/src-symlinks.js000066400000000000000000000120751327613003100172400ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var testConstants = require('./utils/test-constants'); var pipe = miss.pipe; var concat = miss.concat; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var symlinkNestedTarget = testConstants.symlinkNestedTarget; var symlinkPath = testConstants.symlinkPath; var symlinkDirpath = testConstants.symlinkDirpath; var symlinkMultiDirpath = testConstants.symlinkMultiDirpath; var symlinkMultiDirpathSecond = testConstants.symlinkMultiDirpathSecond; var symlinkNestedFirst = testConstants.symlinkNestedFirst; var symlinkNestedSecond = testConstants.symlinkNestedSecond; var clean = cleanup(outputBase); describe('.src() with symlinks', function() { beforeEach(clean); afterEach(clean); beforeEach(function(done) { fs.mkdirSync(outputBase); fs.mkdirSync(outputDirpath); fs.symlinkSync(inputDirpath, symlinkDirpath); fs.symlinkSync(symlinkDirpath, symlinkMultiDirpath); fs.symlinkSync(symlinkMultiDirpath, symlinkMultiDirpathSecond); fs.symlinkSync(inputPath, symlinkPath); fs.symlinkSync(symlinkNestedTarget, symlinkNestedSecond); fs.symlinkSync(symlinkNestedSecond, symlinkNestedFirst); done(); }); it('resolves symlinks correctly', function(done) { function assert(files) { expect(files.length).toEqual(1); // The path should be the symlink itself expect(files[0].path).toEqual(symlinkNestedFirst); // But the content should be what's in the actual file expect(files[0].contents.toString()).toEqual('symlink works\n'); // And the stats should have been updated expect(files[0].stat.isSymbolicLink()).toEqual(false); expect(files[0].stat.isFile()).toEqual(true); } pipe([ vfs.src(symlinkNestedFirst), concat(assert), ], done); }); it('resolves directory symlinks correctly', function(done) { function assert(files) { expect(files.length).toEqual(1); // The path should be the symlink itself expect(files[0].path).toEqual(symlinkDirpath); // But the contents should be null expect(files[0].contents).toEqual(null); // And the stats should have been updated expect(files[0].stat.isSymbolicLink()).toEqual(false); expect(files[0].stat.isDirectory()).toEqual(true); } pipe([ vfs.src(symlinkDirpath), concat(assert), ], done); }); it('resolves nested symlinks to directories correctly', function(done) { function assert(files) { expect(files.length).toEqual(1); // The path should be the symlink itself expect(files[0].path).toEqual(symlinkMultiDirpathSecond); // But the contents should be null expect(files[0].contents).toEqual(null); // And the stats should have been updated expect(files[0].stat.isSymbolicLink()).toEqual(false); expect(files[0].stat.isDirectory()).toEqual(true); } pipe([ vfs.src(symlinkMultiDirpathSecond), concat(assert), ], done); }); it('preserves file symlinks with resolveSymlinks option set to false', function(done) { var expectedRelativeSymlinkPath = fs.readlinkSync(symlinkPath); function assert(files) { expect(files.length).toEqual(1); expect(files[0].path).toEqual(symlinkPath); expect(files[0].symlink).toEqual(expectedRelativeSymlinkPath); } pipe([ vfs.src(symlinkPath, { resolveSymlinks: false }), concat(assert), ], done); }); it('preserves directory symlinks with resolveSymlinks option set to false', function(done) { var expectedRelativeSymlinkPath = fs.readlinkSync(symlinkDirpath); function assert(files) { expect(files.length).toEqual(1); expect(files[0].path).toEqual(symlinkDirpath); expect(files[0].symlink).toEqual(expectedRelativeSymlinkPath); } pipe([ vfs.src(symlinkDirpath, { resolveSymlinks: false }), concat(assert), ], done); }); it('receives a file with symbolic link stats when resolveSymlinks is a function', function(done) { function resolveSymlinks(file) { expect(file).toExist(); expect(file.stat).toExist(); expect(file.stat.isSymbolicLink()).toEqual(true); return true; } function assert(files) { expect(files.length).toEqual(1); // And the stats should have been updated expect(files[0].stat.isSymbolicLink()).toEqual(false); expect(files[0].stat.isFile()).toEqual(true); } pipe([ vfs.src(symlinkNestedFirst, { resolveSymlinks: resolveSymlinks }), concat(assert), ], done); }); it('only calls resolveSymlinks once-per-file if it is a function', function(done) { var spy = expect.createSpy().andReturn(true); function assert() { expect(spy.calls.length).toEqual(1); } pipe([ vfs.src(symlinkNestedFirst, { resolveSymlinks: spy }), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/src.js000066400000000000000000000251361327613003100153730ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var testConstants = require('./utils/test-constants'); var pipe = miss.pipe; var from = miss.from; var concat = miss.concat; var through = miss.through; var inputBase = testConstants.inputBase; var inputPath = testConstants.inputPath; var inputDirpath = testConstants.inputDirpath; var bomInputPath = testConstants.bomInputPath; var beEncodedInputPath = testConstants.beEncodedInputPath; var leEncodedInputPath = testConstants.leEncodedInputPath; var contents = testConstants.contents; describe('.src()', function() { it('throws on invalid glob (empty)', function(done) { var stream; try { stream = vfs.src(); } catch (err) { expect(err).toExist(); expect(stream).toNotExist(); done(); } }); it('throws on invalid glob (empty string)', function(done) { var stream; try { stream = vfs.src(''); } catch (err) { expect(err).toExist(); expect(stream).toNotExist(); done(); } }); it('throws on invalid glob (number)', function(done) { var stream; try { stream = vfs.src(123); } catch (err) { expect(err).toExist(); expect(stream).toNotExist(); done(); } }); it('throws on invalid glob (nested array)', function(done) { var stream; try { stream = vfs.src([['./fixtures/*.coffee']]); } catch (err) { expect(err).toExist(); expect(stream).toNotExist(); expect(err.message).toInclude('Invalid glob argument'); done(); } }); it('throws on invalid glob (empty string in array)', function(done) { var stream; try { stream = vfs.src(['']); } catch (err) { expect(err).toExist(); expect(stream).toNotExist(); done(); } }); it('throws on invalid glob (empty array)', function(done) { var stream; try { stream = vfs.src([]); } catch (err) { expect(err).toExist(); expect(stream).toNotExist(); done(); } }); it('emits an error on file not existing', function(done) { function assert(err) { expect(err).toExist(); done(); } pipe([ vfs.src('./fixtures/noexist.coffee'), concat(), ], assert); }); it('passes through writes', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), stat: fs.statSync(inputPath), }); var srcStream = vfs.src(inputPath); function assert(files) { expect(files.length).toEqual(2); expect(files[0]).toEqual(file); } srcStream.write(file); pipe([ srcStream, concat(assert), ], done); }); it('removes BOM from utf8-encoded files by default', function(done) { // U+FEFF takes up 3 bytes in UTF-8: http://mothereff.in/utf-8#%EF%BB%BF var expectedContent = fs.readFileSync(bomInputPath).slice(3); function assert(files) { expect(files.length).toEqual(1); expect(files[0].contents).toMatch(expectedContent); } pipe([ vfs.src(bomInputPath), concat(assert), ], done); }); it('does not remove BOM from utf8-encoded files if option is false', function(done) { var expectedContent = fs.readFileSync(bomInputPath); function assert(files) { expect(files.length).toEqual(1); expect(files[0].contents).toMatch(expectedContent); } pipe([ vfs.src(bomInputPath, { removeBOM: false }), concat(assert), ], done); }); // This goes for any non-UTF-8 encoding. // UTF-16-BE is enough to demonstrate this is done properly. it('does not remove anything that looks like a utf8-encoded BOM from utf16be-encoded files', function(done) { var expectedContent = fs.readFileSync(beEncodedInputPath); function assert(files) { expect(files.length).toEqual(1); expect(files[0].contents).toMatch(expectedContent); }; pipe([ vfs.src(beEncodedInputPath), concat(assert), ], done); }); it('does not remove anything that looks like a utf8-encoded BOM from utf16be-encoded files with streaming contents', function(done) { var expectedContent = fs.readFileSync(beEncodedInputPath); function assertContent(contents) { expect(contents).toMatch(expectedContent); } function compareContents(file, enc, cb) { pipe([ file.contents, concat(assertContent), ], function(err) { cb(err, file); }); } function assert(files) { expect(files.length).toEqual(1); expect(files[0].isStream()).toEqual(true); } pipe([ vfs.src(beEncodedInputPath, { buffer: false }), through.obj(compareContents), concat(assert), ], done); }); // This goes for any non-UTF-8 encoding. // UTF-16-LE is enough to demonstrate this is done properly. it('does not remove anything that looks like a utf8-encoded BOM from utf16le-encoded files', function(done) { var expectedContent = fs.readFileSync(leEncodedInputPath); function assert(files) { expect(files.length).toEqual(1); expect(files[0].contents).toMatch(expectedContent); } pipe([ vfs.src(leEncodedInputPath), concat(assert), ], done); }); it('does not remove anything that looks like a utf8-encoded BOM from utf16le-encoded files with streaming contents', function(done) { var expectedContent = fs.readFileSync(leEncodedInputPath); function assertContent(contents) { expect(contents).toMatch(expectedContent); } function compareContents(file, enc, cb) { pipe([ file.contents, concat(assertContent), ], function(err) { cb(err, file); }); } function assert(files) { expect(files.length).toEqual(1); expect(files[0].isStream()).toEqual(true); } pipe([ vfs.src(leEncodedInputPath, { buffer: false }), through.obj(compareContents), concat(assert), ], done); }); it('globs files with default settings', function(done) { function assert(files) { expect(files.length).toEqual(4); } pipe([ vfs.src('./fixtures/*.txt', { cwd: __dirname }), concat(assert), ], done); }); it('globs files with default settings and relative cwd', function(done) { var cwd = path.relative(process.cwd(), __dirname); function assert(files) { expect(files.length).toEqual(4); } pipe([ vfs.src('./fixtures/*.txt', { cwd: cwd }), concat(assert), ], done); }); // TODO: need to normalize the path of a directory vinyl object it('globs a directory with default settings', function(done) { var inputDirGlob = path.join(inputBase, './f*/'); function assert(files) { expect(files.length).toEqual(1); expect(files[0].isNull()).toEqual(true); expect(files[0].isDirectory()).toEqual(true); } pipe([ vfs.src(inputDirGlob), concat(assert), ], done); }); it('globs a directory with default settings and relative cwd', function(done) { var cwd = path.relative(process.cwd(), __dirname); function assert(files) { expect(files.length).toEqual(1); expect(files[0].isNull()).toEqual(true); expect(files[0].isDirectory()).toEqual(true); } pipe([ vfs.src('./fixtures/f*/', { cwd: cwd }), concat(assert), ], done); }); it('streams a directory with default settings', function(done) { function assert(files) { expect(files.length).toEqual(1); expect(files[0].path).toEqual(inputDirpath); expect(files[0].isNull()).toEqual(true); expect(files[0].isDirectory()).toEqual(true); } pipe([ vfs.src(inputDirpath), concat(assert), ], done); }); it('streams file with with no contents using read: false option', function(done) { function assert(files) { expect(files.length).toEqual(1); expect(files[0].path).toEqual(inputPath); expect(files[0].isNull()).toEqual(true); expect(files[0].contents).toNotExist(); } pipe([ vfs.src(inputPath, { read: false }), concat(assert), ], done); }); it('streams a file changed after since', function(done) { var lastUpdateDate = new Date(+fs.statSync(inputPath).mtime - 1000); function assert(files) { expect(files.length).toEqual(1); expect(files[0].path).toEqual(inputPath); } pipe([ vfs.src(inputPath, { since: lastUpdateDate }), concat(assert), ], done); }); it('does not stream a file changed before since', function(done) { var lastUpdateDate = new Date(+fs.statSync(inputPath).mtime + 1000); function assert(files) { expect(files.length).toEqual(0); } pipe([ vfs.src(inputPath, { since: lastUpdateDate }), concat(assert), ], done); }); it('streams a file with streaming contents', function(done) { var expectedContent = fs.readFileSync(inputPath); function assertContent(contents) { expect(contents).toMatch(expectedContent); } function compareContents(file, enc, cb) { pipe([ file.contents, concat(assertContent), ], function(err) { cb(err, file); }); } function assert(files) { expect(files.length).toEqual(1); expect(files[0].path).toEqual(inputPath); expect(files[0].isStream()).toEqual(true); } pipe([ vfs.src(inputPath, { buffer: false }), through.obj(compareContents), concat(assert), ], done); }); it('can be used as a through stream and adds new files to the end', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: fs.readFileSync(inputPath), stat: fs.statSync(inputPath), }); function assert(files) { expect(files.length).toEqual(2); expect(files[0]).toEqual(file); } pipe([ from.obj([file]), vfs.src(inputPath), concat(assert), ], done); }); it('can be used at beginning and in the middle', function(done) { function assert(files) { expect(files.length).toEqual(2); } pipe([ vfs.src(inputPath), vfs.src(inputPath), concat(assert), ], done); }); it('does not pass options on to through2', function(done) { // Reference: https://github.com/gulpjs/vinyl-fs/issues/153 var read = expect.createSpy().andReturn(false); function assert() { // Called once to resolve the option expect(read.calls.length).toEqual(1); } pipe([ vfs.src(inputPath, { read: read }), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/symlink.js000066400000000000000000000633401327613003100162710ustar00rootroot00000000000000'use strict'; var path = require('path'); var fs = require('graceful-fs'); var File = require('vinyl'); var expect = require('expect'); var miss = require('mississippi'); var vfs = require('../'); var cleanup = require('./utils/cleanup'); var isWindows = require('./utils/is-windows'); var testStreams = require('./utils/test-streams'); var always = require('./utils/always'); var testConstants = require('./utils/test-constants'); var breakPrototype = require('./utils/break-prototype'); var from = miss.from; var pipe = miss.pipe; var concat = miss.concat; var count = testStreams.count; var slowCount = testStreams.slowCount; function noop() {} var outputRelative = testConstants.outputRelative; var inputBase = testConstants.inputBase; var outputBase = testConstants.outputBase; var inputPath = testConstants.inputPath; var outputPath = testConstants.outputPath; var inputDirpath = testConstants.inputDirpath; var outputDirpath = testConstants.outputDirpath; var contents = testConstants.contents; var clean = cleanup(outputBase); describe('symlink stream', function() { beforeEach(clean); afterEach(clean); it('throws on no folder argument', function(done) { function noFolder() { vfs.symlink(); } expect(noFolder).toThrow('Invalid symlink() folder argument. Please specify a non-empty string or a function.'); done(); }); it('throws on empty string folder argument', function(done) { function emptyFolder() { vfs.symlink(''); } expect(emptyFolder).toThrow('Invalid symlink() folder argument. Please specify a non-empty string or a function.'); done(); }); it('passes through writes with cwd', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].cwd).toEqual(__dirname, 'cwd should have changed'); } pipe([ from.obj([file]), vfs.symlink(outputRelative, { cwd: __dirname }), concat(assert), ], done); }); it('passes through writes with default cwd', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].cwd).toEqual(process.cwd(), 'cwd should not have changed'); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('creates a link to the right folder with relative cwd', function(done) { var cwd = path.relative(process.cwd(), __dirname); var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(files[0].isSymbolic()).toBe(true, 'file should be symbolic'); expect(outputLink).toEqual(inputPath); } pipe([ from.obj([file]), vfs.symlink(outputRelative, { cwd: cwd }), concat(assert), ], done); }); it('creates a link to the right folder with function and relative cwd', function(done) { var cwd = path.relative(process.cwd(), __dirname); var file = new File({ base: inputBase, path: inputPath, contents: null, }); function outputFn(f) { expect(f).toExist(); expect(f).toEqual(file); return outputRelative; } function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(files[0].isSymbolic()).toBe(true, 'file should be symbolic'); expect(outputLink).toEqual(inputPath); } pipe([ from.obj([file]), vfs.symlink(outputFn, { cwd: cwd }), concat(assert), ], done); }); it('creates a link for a file with buffered contents', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: new Buffer(contents), }); function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(files[0].isSymbolic()).toBe(true, 'file should be symbolic'); expect(outputLink).toEqual(inputPath); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('can create relative links', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(files[0].isSymbolic()).toBe(true, 'file should be symbolic'); expect(outputLink).toEqual(path.normalize('../fixtures/test.txt')); } pipe([ from.obj([file]), vfs.symlink(outputBase, { relativeSymlinks: true }), concat(assert), ], done); }); it('creates a link for a file with streaming contents', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: from([contents]), }); function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(files[0].isSymbolic()).toBe(true, 'file should be symbolic'); expect(outputLink).toEqual(inputPath); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('emits Vinyl objects that are symbolic', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { expect(files.length).toEqual(1); expect(files[0].isSymbolic()).toEqual(true); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('(*nix) creates a link for a directory', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(inputDirpath); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('(windows) creates a junction for a directory', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); // When creating a junction, it seems Windows appends a separator expect(files[0].symlink + path.sep).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(inputDirpath + path.sep); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('(windows) options can disable junctions for a directory', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(inputDirpath); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase, { useJunctions: false }), concat(assert), ], done); }); it('(windows) options can disable junctions for a directory (as a function)', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function useJunctions(f) { expect(f).toExist(); expect(f).toBe(file); return false; } function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(inputDirpath); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase, { useJunctions: useJunctions }), concat(assert), ], done); }); it('(*nix) can create relative links for directories', function(done) { if (isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(path.normalize('../fixtures/foo')); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase, { relativeSymlinks: true }), concat(assert), ], done); }); it('(windows) relativeSymlinks option is ignored when junctions are used', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); // When creating a junction, it seems Windows appends a separator expect(files[0].symlink + path.sep).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(inputDirpath + path.sep); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase, { useJunctions: true, relativeSymlinks: true }), concat(assert), ], done); }); it('(windows) supports relativeSymlinks option when link is not for a directory', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { var outputLink = fs.readlinkSync(outputPath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputPath, 'path should have changed'); expect(outputLink).toEqual(path.normalize('../fixtures/test.txt')); } pipe([ from.obj([file]), // The useJunctions option is ignored when file is not a directory vfs.symlink(outputBase, { useJunctions: true, relativeSymlinks: true }), concat(assert), ], done); }); it('(windows) can create relative links for directories when junctions are disabled', function(done) { if (!isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputDirpath, contents: null, stat: { isDirectory: always(true), }, }); function assert(files) { var stats = fs.statSync(outputDirpath); var lstats = fs.lstatSync(outputDirpath); var outputLink = fs.readlinkSync(outputDirpath); expect(files.length).toEqual(1); expect(files).toInclude(file); expect(files[0].base).toEqual(outputBase, 'base should have changed'); expect(files[0].path).toEqual(outputDirpath, 'path should have changed'); expect(files[0].symlink).toEqual(outputLink, 'symlink should be set'); expect(outputLink).toEqual(path.normalize('../fixtures/foo')); expect(stats.isDirectory()).toEqual(true); expect(lstats.isDirectory()).toEqual(false); } pipe([ from.obj([file]), vfs.symlink(outputBase, { useJunctions: false, relativeSymlinks: true }), concat(assert), ], done); }); it('reports IO errors', function(done) { // Changing the mode of a file is not supported by node.js in Windows. // This test is skipped on Windows because we have to chmod the file to 0. if (isWindows) { this.skip(); return; } var file = new File({ base: inputBase, path: inputPath, contents: null, }); fs.mkdirSync(outputBase); fs.chmodSync(outputBase, 0); function assert(err) { expect(err.code).toEqual('EACCES'); done(); } pipe([ from.obj([file]), vfs.symlink(outputDirpath), ], assert); }); it('does not overwrite links with overwrite option set to false', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents); } // Write expected file which should not be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.symlink(outputBase, { overwrite: false }), concat(assert), ], done); }); it('overwrites links with overwrite option set to true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, }); function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.symlink(outputBase, { overwrite: true }), concat(assert), ], done); }); it('does not overwrite links with overwrite option set to a function that returns false', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, }); function overwrite(f) { expect(f).toEqual(file); return false; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(existingContents); } // Write expected file which should not be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.symlink(outputBase, { overwrite: overwrite }), concat(assert), ], done); }); it('overwrites links with overwrite option set to a function that returns true', function(done) { var existingContents = 'Lorem Ipsum'; var file = new File({ base: inputBase, path: inputPath, contents: null, }); function overwrite(f) { expect(f).toEqual(file); return true; } function assert(files) { var outputContents = fs.readFileSync(outputPath, 'utf8'); expect(files.length).toEqual(1); expect(outputContents).toEqual(contents); } // This should be overwritten fs.mkdirSync(outputBase); fs.writeFileSync(outputPath, existingContents); pipe([ from.obj([file]), vfs.symlink(outputBase, { overwrite: overwrite }), concat(assert), ], done); }); it('emits an end event', function(done) { var symlinkStream = vfs.symlink(outputBase); symlinkStream.on('end', done); var file = new File({ base: inputBase, path: inputPath, contents: null, }); pipe([ from.obj([file]), symlinkStream, ]); }); it('emits a finish event', function(done) { var symlinkStream = vfs.symlink(outputBase); symlinkStream.on('finish', done); var file = new File({ base: inputBase, path: inputPath, contents: null, }); pipe([ from.obj([file]), symlinkStream, ]); }); it('errors when a non-Vinyl object is emitted', function(done) { var file = {}; function assert(err) { expect(err).toExist(); expect(err.message).toEqual('Received a non-Vinyl object in `symlink()`'); done(); } pipe([ from.obj([file]), vfs.symlink(outputBase), ], assert); }); it('errors when a buffer-mode stream is piped to it', function(done) { var file = new Buffer('test'); function assert(err) { expect(err).toExist(); expect(err.message).toEqual('Received a non-Vinyl object in `symlink()`'); done(); } pipe([ from([file]), vfs.symlink(outputBase), ], assert); }); it('does not get clogged by highWaterMark', function(done) { var expectedCount = 17; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); highwatermarkFiles.push(file); } pipe([ from.obj(highwatermarkFiles), count(expectedCount), // Must be in the Writable position to test this // So concat-stream cannot be used vfs.symlink(outputBase), ], done); }); it('allows backpressure when piped to another, slower stream', function(done) { this.timeout(20000); var expectedCount = 24; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); highwatermarkFiles.push(file); } pipe([ from.obj(highwatermarkFiles), count(expectedCount), vfs.symlink(outputBase), slowCount(expectedCount), ], done); }); it('respects readable listeners on symlink stream', function(done) { var file = new File({ base: inputBase, path: inputDirpath, contents: null, }); var symlinkStream = vfs.symlink(outputBase); var readables = 0; symlinkStream.on('readable', function() { var data = symlinkStream.read(); if (data != null) { readables++; } }); function assert(err) { expect(readables).toEqual(1); done(err); } pipe([ from.obj([file]), symlinkStream, ], assert); }); it('respects data listeners on symlink stream', function(done) { var file = new File({ base: inputBase, path: inputDirpath, contents: null, }); var symlinkStream = vfs.symlink(outputBase); var datas = 0; symlinkStream.on('data', function() { datas++; }); function assert(err) { expect(datas).toEqual(1); done(err); } pipe([ from.obj([file]), symlinkStream, ], assert); }); it('sinks the stream if all the readable event handlers are removed', function(done) { var expectedCount = 17; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); highwatermarkFiles.push(file); } var symlinkStream = vfs.symlink(outputBase); symlinkStream.on('readable', noop); pipe([ from.obj(highwatermarkFiles), count(expectedCount), // Must be in the Writable position to test this // So concat-stream cannot be used symlinkStream, ], done); process.nextTick(function() { symlinkStream.removeListener('readable', noop); }); }); it('sinks the stream if all the data event handlers are removed', function(done) { var expectedCount = 17; var highwatermarkFiles = []; for (var idx = 0; idx < expectedCount; idx++) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); highwatermarkFiles.push(file); } var symlinkStream = vfs.symlink(outputBase); symlinkStream.on('data', noop); pipe([ from.obj(highwatermarkFiles), count(expectedCount), // Must be in the Writable position to test this // So concat-stream cannot be used symlinkStream, ], done); process.nextTick(function() { symlinkStream.removeListener('data', noop); }); }); it('does not pass options on to through2', function(done) { var file = new File({ base: inputBase, path: inputPath, contents: null, }); // Reference: https://github.com/gulpjs/vinyl-fs/issues/153 var read = expect.createSpy().andReturn(false); function assert() { // Called never because it's not a valid option expect(read.calls.length).toEqual(0); } pipe([ from.obj([file]), vfs.symlink(outputBase, { read: read }), concat(assert), ], done); }); it('does not marshall a Vinyl object with isSymbolic method', function(done) { var file = new File({ base: outputBase, path: outputPath, }); function assert(files) { expect(files.length).toEqual(1); // Avoid comparing stats because they get reflected delete files[0].stat; expect(files[0]).toMatch(file); expect(files[0]).toBe(file); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); it('marshalls a Vinyl object without isSymbolic to a newer Vinyl', function(done) { var file = new File({ base: outputBase, path: outputPath, // Pre-set this because it is set by symlink symlink: outputPath, }); breakPrototype(file); function assert(files) { expect(files.length).toEqual(1); // Avoid comparing stats because they get reflected delete files[0].stat; expect(files[0]).toMatch(file); expect(files[0]).toNotBe(file); } pipe([ from.obj([file]), vfs.symlink(outputBase), concat(assert), ], done); }); }); vinyl-fs-3.0.3/test/utils/000077500000000000000000000000001327613003100153775ustar00rootroot00000000000000vinyl-fs-3.0.3/test/utils/always.js000066400000000000000000000001611327613003100172330ustar00rootroot00000000000000'use strict'; function always(value) { return function() { return value; }; } module.exports = always; vinyl-fs-3.0.3/test/utils/apply-umask.js000066400000000000000000000002641327613003100202020ustar00rootroot00000000000000'use strict'; function applyUmask(mode) { if (typeof mode !== 'number') { mode = parseInt(mode, 8); } return (mode & ~process.umask()); } module.exports = applyUmask; vinyl-fs-3.0.3/test/utils/break-prototype.js000066400000000000000000000011171327613003100210640ustar00rootroot00000000000000'use strict'; var File = require('vinyl'); function breakPrototype(file) { // Set up a broken prototype var oldProto = {}; Object.getOwnPropertyNames(File.prototype).forEach(function(key) { if (key !== 'isSymbolic') { var desc = Object.getOwnPropertyDescriptor(File.prototype, key); Object.defineProperty(oldProto, key, desc); } }); // Assign the broken prototype to our instance if (typeof Object.setPrototypeOf === 'function') { Object.setPrototypeOf(file, oldProto); } else { file.__proto__ = oldProto; } } module.exports = breakPrototype; vinyl-fs-3.0.3/test/utils/cleanup.js000066400000000000000000000005551327613003100173710ustar00rootroot00000000000000'use strict'; var rimraf = require('rimraf'); var expect = require('expect'); function cleanup(glob) { return function(cb) { this.timeout(20000); expect.restoreSpies(); if (!glob) { return cb(); } // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 rimraf(glob, cb); }; } module.exports = cleanup; vinyl-fs-3.0.3/test/utils/is-windows.js000066400000000000000000000001621327613003100200370ustar00rootroot00000000000000'use strict'; var os = require('os'); var isWindows = (os.platform() === 'win32'); module.exports = isWindows; vinyl-fs-3.0.3/test/utils/mock-error.js000066400000000000000000000002361327613003100200160ustar00rootroot00000000000000'use strict'; function mockError() { var callback = arguments[arguments.length - 1]; callback(new Error('mocked error')); } module.exports = mockError; vinyl-fs-3.0.3/test/utils/stat-mode.js000066400000000000000000000004201327613003100176260ustar00rootroot00000000000000'use strict'; var fs = require('graceful-fs'); var constants = require('../../lib/constants'); function masked(mode) { return mode & constants.MASK_MODE; } function statMode(outputPath) { return masked(fs.lstatSync(outputPath).mode); } module.exports = statMode; vinyl-fs-3.0.3/test/utils/test-constants.js000066400000000000000000000063251327613003100207340ustar00rootroot00000000000000'use strict'; var path = require('path'); // Input/output relative paths var inputRelative = './fixtures'; var outputRelative = './out-fixtures'; // Input/Output base directories var inputBase = path.join(__dirname, '..', inputRelative); var outputBase = path.join(__dirname, '..', outputRelative); // Used for file tests var inputPath = path.join(inputBase, './test.txt'); var outputPath = path.join(outputBase, './test.txt'); // Used for directory tests var inputDirpath = path.join(inputBase, './foo'); var outputDirpath = path.join(outputBase, './foo'); // Used for nested tests var inputNestedPath = path.join(inputDirpath, './test.txt'); var outputNestedPath = path.join(outputDirpath, './test.txt'); // Used for rename tests var outputRenamePath = path.join(outputBase, './foo2.txt'); // Used for not-owned tests var notOwnedBase = path.join(inputBase, './not-owned/'); var notOwnedPath = path.join(notOwnedBase, 'not-owned.txt'); // Used for BOM tests var bomInputPath = path.join(inputBase, './bom-utf8.txt'); var beEncodedInputPath = path.join(inputBase, './bom-utf16be.txt'); var leEncodedInputPath = path.join(inputBase, './bom-utf16le.txt'); // Used for symlink tests var symlinkNestedTarget = path.join(inputBase, './foo/bar/baz.txt'); var symlinkPath = path.join(outputBase, './test-symlink'); var symlinkDirpath = path.join(outputBase, './test-symlink-dir'); var symlinkMultiDirpath = path.join(outputBase, './test-multi-layer-symlink-dir'); var symlinkMultiDirpathSecond = path.join(outputBase, './test-multi-layer-symlink-dir2'); var symlinkNestedFirst = path.join(outputBase, './test-multi-layer-symlink'); var symlinkNestedSecond = path.join(outputBase, './foo/baz-link.txt'); // Paths that don't exist var neInputBase = path.join(inputBase, './not-exists/'); var neOutputBase = path.join(outputBase, './not-exists/'); var neInputDirpath = path.join(neInputBase, './foo'); var neOutputDirpath = path.join(neOutputBase, './foo'); // Used for contents of files var contents = 'Hello World!\n'; var sourcemapContents = '//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9maXh0dXJlcyIsIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzIjpbIi4vZml4dHVyZXMiXSwic291cmNlc0NvbnRlbnQiOlsiSGVsbG8gV29ybGQhXG4iXX0='; module.exports = { inputRelative: inputRelative, outputRelative: outputRelative, inputBase: inputBase, outputBase: outputBase, inputPath: inputPath, outputPath: outputPath, inputDirpath: inputDirpath, outputDirpath: outputDirpath, inputNestedPath: inputNestedPath, outputNestedPath: outputNestedPath, outputRenamePath: outputRenamePath, notOwnedBase: notOwnedBase, notOwnedPath: notOwnedPath, bomInputPath: bomInputPath, beEncodedInputPath: beEncodedInputPath, leEncodedInputPath: leEncodedInputPath, symlinkNestedTarget: symlinkNestedTarget, symlinkPath: symlinkPath, symlinkDirpath: symlinkDirpath, symlinkMultiDirpath: symlinkMultiDirpath, symlinkMultiDirpathSecond: symlinkMultiDirpathSecond, symlinkNestedFirst: symlinkNestedFirst, symlinkNestedSecond: symlinkNestedSecond, neInputBase: neInputBase, neOutputBase: neOutputBase, neInputDirpath: neInputDirpath, neOutputDirpath: neOutputDirpath, contents: contents, sourcemapContents: sourcemapContents, }; vinyl-fs-3.0.3/test/utils/test-streams.js000066400000000000000000000024151327613003100203720ustar00rootroot00000000000000'use strict'; var miss = require('mississippi'); var expect = require('expect'); var to = miss.to; var from = miss.from; var through = miss.through; function string(length) { return from(function(size, next) { if (length <= 0) { next(null, null); return; } var chunkSize = size <= length ? size : length; length -= size; var chunk = ''; for (var x = 0; x < chunkSize; x++) { chunk += 'a'; } next(null, chunk); }); } function rename(filepath) { return through.obj(function(file, enc, cb) { file.path = filepath; cb(null, file); }); } function includes(obj) { return through.obj(function(file, enc, cb) { expect(file).toInclude(obj); cb(null, file); }); } function count(value) { var count = 0; return through.obj(function(file, enc, cb) { count++; cb(null, file); }, function(cb) { expect(count).toEqual(value); cb(); }); } function slowCount(value) { var count = 0; return to.obj(function(file, enc, cb) { count++; setTimeout(function() { cb(null, file); }, 250); }, function(cb) { expect(count).toEqual(value); cb(); }); } module.exports = { string: string, rename: rename, includes: includes, count: count, slowCount: slowCount, };