UNPKG

yuidoc-asp

Version:

YUIDoc, YUI's JavaScript Documentation engine tweaked to work with VB/VBScript comments.

486 lines (403 loc) 14.3 kB
/** * Copyright (c) 2011, Yahoo! Inc. All rights reserved. * Code licensed under the BSD License: * https://github.com/yui/yuidoc/blob/master/LICENSE */ YUI.add('files', function (Y) { /** * Ported fileutils methods from [Selleck](http://github.com/rgrove/selleck) * @class Files * @module yuidoc */ Y.Files = {}; /* Selleck Copyright (c) 2011 Yahoo! Inc. Licensed under the BSD License. */ var fs = require('graceful-fs'), fsPath = require('path'), useFS = (fs.exists) ? fs : fsPath; function exists(file, cb) { if (cb) { useFS.exists(file, cb); } else { return useFS.existsSync(file); } } Y.Files.exists = exists; /** Copy a directory from one location to another @method copyDirectory @param {Path} source The source directory @param {Path} dest The destination directory @param {Boolean} [overwrite=false] Whether or not to overwrite destination files if they already exist. @param {Function} callback The callback to be executed when complete. **/ function copyDirectory(source, dest, overwrite, callback) { // Allow callback as third arg. if (typeof overwrite === 'function') { callback = overwrite; overwrite = null; } fs.stat(source, afterSourceStat); function afterSourceStat(err, stats) { if (err) { return callback(err); } if (!stats.isDirectory()) { return callback(new Error("Source is not a directory: " + source)); } fs.lstat(dest, afterDestStat); } function afterDestStat(err, stats) { if (err && err.code !== 'ENOENT') { return callback(err); } if (stats) { // If the destination is a file or a link, either delete it or // bubble an error if overwrite isn't true. if (stats.isFile() || stats.isSymbolicLink()) { if (overwrite) { deletePath(dest); // TODO: make this async } else { callback(new Error("Destination already exists: " + dest)); return; } } afterMkDir(); } else { fs.mkdir(dest, 0755, afterMkDir); } } function afterMkDir(err) { if (err && err.code !== 'EEXIST') { return callback(err); } fs.readdir(source, afterReadDir); } function afterReadDir(err, files) { if (err) { return callback(err); } var pending = files.length, filename; if (!pending) { return callback(); } while ((filename = files.shift())) { /*jshint loopfunc:true */ copyPath(fsPath.join(source, filename), fsPath.join(dest, filename), overwrite, function (err) { if (err) { return callback(err); } pending -= 1; if (!pending) { callback(); } }); } } } Y.Files.copyDirectory = copyDirectory; /** Copy a file from one location to another @method copyFile @param {Path} source The source file @param {Path} dest The destination file @param {Boolean} [overwrite=false] Whether or not to overwrite destination files if they already exist. @param {Callback} callback The callback to be executed when complete. @param {Error} callback.err The Error returned from Node **/ function copyFile(source, dest, overwrite, callback) { // Allow callback as third arg. if (typeof overwrite === 'function') { callback = overwrite; overwrite = null; } fs.lstat(source, function (err, sourceStats) { if (err) { return callback(err); } if (!sourceStats.isFile()) { return callback(new Error("Source is not a file: " + source)); } fs.lstat(dest, function (err, destStats) { var rs; if (err && err.code !== 'ENOENT') { return callback(err); } if (destStats) { if (overwrite) { deletePath(dest); // TODO: make this async } else { callback(new Error("Destination already exists: " + dest)); return; } } rs = fs.createReadStream(source); rs.pipe(fs.createWriteStream(dest, { mode: 0655 })); rs.on('end', callback); }); }); } Y.Files.copyFile = copyFile; /** If _source_ is a file, copies it to _dest_. If it's a directory, recursively copies it and all files and directories it contains to _dest_. Note that when attempting to copy a file into a directory, you should specify the full path to the new file (including the new filename). Otherwise, it will be interpreted as an attempt to copy the _source_ file *over* the _dest_ directory instead of *into* it. Known issues: - Doesn't preserve ownership or permissions on copied files/directories. @method copyPath @param {String} source Source path. @param {String} dest Destination path. @param {Boolean} [overwrite=false] Whether or not to overwrite destination files if they already exist. @param {Callback} callback The callback to execute when completed. @param {Error} callback.err **/ function copyPath(source, dest, overwrite, callback) { var sourceStats = statSync(source); // Allow callback as third arg. if (typeof overwrite === 'function') { callback = overwrite; overwrite = null; } if (!sourceStats) { callback(new Error("Source not found: " + source)); return; } if (sourceStats.isFile()) { copyFile(source, dest, overwrite, callback); } else if (sourceStats.isDirectory()) { copyDirectory(source, dest, overwrite, callback); } else { callback(new Error("Source is neither a file nor a directory: " + source)); } } Y.Files.copyPath = copyPath; // TODO: copySymbolicLink()? /** If _path_ is a file, deletes it. If _path_ is a directory, recursively deletes it and all files and directories it contains. This method is synchronous. @method deletePath @param {String} path File or directory to delete. **/ function deletePath(path) { var stats = fs.lstatSync(path); if (stats.isFile() || stats.isSymbolicLink()) { fs.unlinkSync(path); } else if (stats.isDirectory()) { fs.readdirSync(path).forEach(function (filename) { deletePath(fsPath.join(path, filename)); }); fs.rmdirSync(path); } } Y.Files.deletePath = deletePath; /** Check to see if this is a directory @method isDirectory @param {Path} path The path to check @param {Boolean} [link=false] Also validate a symlink @return {Boolean} True if it is a directory **/ function isDirectory(path, link) { var stat, result = false; link = (link === false) ? false : true; try { stat = fs.lstatSync(path); if (stat) { if (stat.isSymbolicLink() && link) { stat = fs.statSync(path); } result = stat.isDirectory(); } } catch (e) { result = false; } return result; } Y.Files.isDirectory = isDirectory; /** Check to see if this is a File @method isFile @param {Path} path The path to check @param {Boolean} [link=false] Also validate a symlink @return {Boolean} True if it is a file **/ function isFile(path, link) { var stat, result = false; try { stat = fs.lstatSync(path); if (stat) { if (stat.isSymbolicLink() && link) { stat = fs.statSync(path); } result = stat.isFile(); } } catch (e) { result = false; } return result; } Y.Files.isFile = isFile; /** Check to see if this is a SymLink @method isSymbolicLink @param {Path} path The path to check @return {Boolean} True if it is a link **/ function isSymbolicLink(path) { var stats = lstatSync(path); return stats ? stats.isSymbolicLink() : false; } Y.Files.isSymbolicLink = isSymbolicLink; /** Like `fs.lstatSync()`, but returns `null` instead of throwing when _path_ doesn't exist. Will still throw on other types of errors. @method lstatSync @param {String} path Path to stat. @return {fs.Stats|null} `fs.Stats` object, or `null` if _path_ doesn't exist. **/ function lstatSync(path) { try { return fs.lstatSync(path); } catch (ex) { if (ex.code === 'ENOENT') { return null; } throw ex; } } Y.Files.lstatSync = lstatSync; /** Like `fs.statSync()`, but returns `null` instead of throwing when _path_ doesn't exist. Will still throw on other types of errors. @method statSync @param {String} path Path to stat. @return {fs.Stats|null} `fs.Stats` object, or `null` if _path_ doesn't exist. **/ function statSync(path) { try { return fs.statSync(path); } catch (ex) { if (ex.code === 'ENOENT') { return null; } throw ex; } } Y.Files.statSync = statSync; /** Copy the theme assets directory @method copyAssets @param {Path} from The source directory @param {Path} dest The destination directory @param {Boolean} deleteFirst Should the directory be deleted if it exists @param {Function} callback The callback to be executed */ function copyAssets() { var args = Array.prototype.slice.call(arguments), callback = args.pop(), from = args.shift(), to = args.shift(), deleteFirst = args.shift(); if (from[0] === from[1]) { if (isDirectory(from[0])) { if (deleteFirst && isDirectory(to)) { deletePath(to); } copyPath(from[0], to, true, callback); } else { callback(); } } else { if (isDirectory(from[0])) { if (deleteFirst && isDirectory(to)) { deletePath(to); } copyPath(from[0], to, true, function () { if (isDirectory(from[1])) { copyPath(from[1], to, true, callback); } else { callback(); } }); } else { callback(); } } } Y.Files.copyAssets = copyAssets; /** Helper method for getting JSON data from a local file @method getJSON @param {Path} filename The filename to parse JSON from @return {Object} The JSON data **/ Y.Files.getJSON = function (filename) { var data = {}; if (exists(filename)) { data = JSON.parse(fs.readFileSync(filename, 'utf8')); } return data; }; /** Helper method for writing files to disk. It wraps the NodeJS file API @method writeFile @param {Path} file The filename to write to @param {String} data The data to write @param {Callback} callback* **/ function writeFile(file, data, cb) { var out, args = arguments, flags = { flags: "w", encoding: Y.charset, mode: 0644 }; if (cb) { fs.writeFile(file, data, flags, function (err) { if (err && err.message.match(/^EMFILE, Too many open files/)) { Y.log('Writefile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files'); writeFile.timer++; Y.later(writeFile.timer, Y, writeFile, args); return; } cb(); }); } else { out = fs.createWriteStream(file, flags); out.write(data); out.end(); } } writeFile.timer = 100; Y.Files.writeFile = writeFile; function readFile(file, enc, cb) { var args = arguments; fs.readFile(file, enc, function (err, data) { if (err && err.message.match(/^EMFILE, Too many open files/)) { Y.log('Readfile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files'); readFile.timer++; Y.later(readFile.timer, Y, readFile, args); return; } cb(err, data); }); } readFile.timer = 100; Y.Files.readFile = readFile; });