diff --git a/README.md b/README.md index c631fb7..689a668 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ fs = new IDBFS.FileSystem({ }, fsReady); ``` -###IDBFS.FileSystem.providers - Storage Providers +####IDBFS.FileSystem.providers - Storage Providers IDBFS can be configured to use a number of different storage providers. The provider object encapsulates all aspects of data access, making it possible to swap in different backend storage options. There are currently 4 different @@ -131,6 +131,39 @@ if( IDBFS.FileSystem.providers.WebSQL.isSupported() ) { You can also write your own provider if you need a different backend. See the code in `src/providers` for details. +####IDBFS.Path + +The node.js [path module](http://nodejs.org/api/path.html) is available via the `IDBFS.Path` object. It is +identical to the node.js version with the following differences: +* No support for `exits()` or `existsSync()`. Use `fs.stat()` instead. +* No notion of a current working directory in `resolve` (the root dir is used instead) + +```javascript +var path = IDBFS.Path; +var dir = path.dirname('/foo/bar/baz/asdf/quux'); +// dir is now '/foo/bar/baz/asdf' + +var base = path.basename('/foo/bar/baz/asdf/quux.html'); +// base is now 'quux.html' + +var ext = path.extname('index.html'); +// ext is now '.html' + +var newpath = path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); +// new path is now '/foo/bar/baz/asdf' +``` + +For more info see the docs in the [path module](http://nodejs.org/api/path.html) for a particular method: +* `path.normalize(p)` +* `path.join([path1], [path2], [...])` +* `path.resolve([from ...], to)` +* `path.relative(from, to)` +* `path.dirname(p)` +* `path.basename(p, [ext])` +* `path.extname(p)` +* `path.sep` +* `path.delimiter` + #### fs.stat(path, callback) Asynchronous stat(2). Callback gets `(error, stats)`, where `stats` is an object like diff --git a/src/fs.js b/src/fs.js index e594a1a..fd342bd 100644 --- a/src/fs.js +++ b/src/fs.js @@ -1721,7 +1721,8 @@ define(function(require) { }; return { - FileSystem: FileSystem + FileSystem: FileSystem, + Path: require('src/path') }; }); diff --git a/src/path.js b/src/path.js index ad71c61..ec21da3 100644 --- a/src/path.js +++ b/src/path.js @@ -19,18 +19,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -// https://github.com/joyent/node/blob/41e53e557992a7d552a8e23de035f9463da25c99/lib/path.js - -define(function(require) { - - // Split a filename into [root, dir, basename, ext], unix version - // 'root' is just a slash, or nothing. - var splitPathRe = - /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; - var splitPath = function(filename) { - var result = splitPathRe.exec(filename); - return [result[1] || '', result[2] || '', result[3] || '', result[4] || '']; - }; +// Based on https://github.com/joyent/node/blob/41e53e557992a7d552a8e23de035f9463da25c99/lib/path.js +define(function() { // resolves . and .. elements in a path array with directory names there // must be no slashes, empty elements, or device names (c:\) in the array @@ -62,11 +52,48 @@ define(function(require) { return parts; } + // Split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + var splitPathRe = + /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; + var splitPath = function(filename) { + var result = splitPathRe.exec(filename); + return [result[1] || '', result[2] || '', result[3] || '', result[4] || '']; + }; + + // path.resolve([from ...], to) + function resolve() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + // XXXidbfs: we don't have process.cwd() so we use '/' as a fallback + var path = (i >= 0) ? arguments[i] : '/'; + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + } + // path.normalize(path) - // posix version function normalize(path) { - var isAbsolute = path.charAt(0) === '/'; - // var trailingSlash = path.substr(-1) === '/'; + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.substr(-1) === '/'; // Normalize the path path = normalizeArray(path.split('/').filter(function(p) { @@ -76,14 +103,61 @@ define(function(require) { if (!path && !isAbsolute) { path = '.'; } - /* if (path && trailingSlash) { path += '/'; } - */ return (isAbsolute ? '/' : '') + path; - }; + } + + function join() { + var paths = Array.prototype.slice.call(arguments, 0); + return normalize(paths.filter(function(p, index) { + return p && typeof p === 'string'; + }).join('/')); + } + + // path.relative(from, to) + function relative(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); + } function dirname(path) { var result = splitPath(path), @@ -101,8 +175,7 @@ define(function(require) { } return root + dir; - }; - + } function basename(path, ext) { var f = splitPath(path)[2]; @@ -110,18 +183,27 @@ define(function(require) { if (ext && f.substr(-1 * ext.length) === ext) { f = f.substr(0, f.length - ext.length); } + // XXXidbfs: node.js just does `return f` return f === "" ? "/" : f; - }; - - function isAbsolute(path) { - return path.charAt(0) === '/'; } + function extname(path) { + return splitPath(path)[3]; + } + + // XXXidbfs: we don't support path.exists() or path.existsSync(), which + // are deprecated, and need a FileSystem instance to work. Use fs.stat(). + return { normalize: normalize, + resolve: resolve, + join: join, + relative: relative, + sep: '/', + delimiter: ':', dirname: dirname, basename: basename, - isAbsolute: isAbsolute - } + extname: extname + }; -}); \ No newline at end of file +});