UNPKG

3.71 kBJavaScriptView Raw
1/*
2 Copyright 2012-2015, Yahoo Inc.
3 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 */
5'use strict';
6
7const path = require('path');
8let parsePath = path.parse;
9let SEP = path.sep;
10const origParser = parsePath;
11const origSep = SEP;
12
13function makeRelativeNormalizedPath(str, sep) {
14 const parsed = parsePath(str);
15 let root = parsed.root;
16 let dir;
17 let file = parsed.base;
18 let quoted;
19 let pos;
20
21 // handle a weird windows case separately
22 if (sep === '\\') {
23 pos = root.indexOf(':\\');
24 if (pos >= 0) {
25 root = root.substring(0, pos + 2);
26 }
27 }
28 dir = parsed.dir.substring(root.length);
29
30 if (str === '') {
31 return [];
32 }
33
34 if (sep !== '/') {
35 quoted = new RegExp(sep.replace(/\W/g, '\\$&'), 'g');
36 dir = dir.replace(quoted, '/');
37 file = file.replace(quoted, '/'); // excessively paranoid?
38 }
39
40 if (dir !== '') {
41 dir = `${dir}/${file}`;
42 } else {
43 dir = file;
44 }
45 if (dir.substring(0, 1) === '/') {
46 dir = dir.substring(1);
47 }
48 dir = dir.split(/\/+/);
49 return dir;
50}
51
52class Path {
53 constructor(strOrArray) {
54 if (Array.isArray(strOrArray)) {
55 this.v = strOrArray;
56 } else if (typeof strOrArray === 'string') {
57 this.v = makeRelativeNormalizedPath(strOrArray, SEP);
58 } else {
59 throw new Error(
60 `Invalid Path argument must be string or array:${strOrArray}`
61 );
62 }
63 }
64
65 toString() {
66 return this.v.join('/');
67 }
68
69 hasParent() {
70 return this.v.length > 0;
71 }
72
73 parent() {
74 if (!this.hasParent()) {
75 throw new Error('Unable to get parent for 0 elem path');
76 }
77 const p = this.v.slice();
78 p.pop();
79 return new Path(p);
80 }
81
82 elements() {
83 return this.v.slice();
84 }
85
86 name() {
87 return this.v.slice(-1)[0];
88 }
89
90 contains(other) {
91 let i;
92 if (other.length > this.length) {
93 return false;
94 }
95 for (i = 0; i < other.length; i += 1) {
96 if (this.v[i] !== other.v[i]) {
97 return false;
98 }
99 }
100 return true;
101 }
102
103 ancestorOf(other) {
104 return other.contains(this) && other.length !== this.length;
105 }
106
107 descendantOf(other) {
108 return this.contains(other) && other.length !== this.length;
109 }
110
111 commonPrefixPath(other) {
112 const len = this.length > other.length ? other.length : this.length;
113 let i;
114 const ret = [];
115
116 for (i = 0; i < len; i += 1) {
117 if (this.v[i] === other.v[i]) {
118 ret.push(this.v[i]);
119 } else {
120 break;
121 }
122 }
123 return new Path(ret);
124 }
125
126 static compare(a, b) {
127 const al = a.length;
128 const bl = b.length;
129
130 if (al < bl) {
131 return -1;
132 }
133
134 if (al > bl) {
135 return 1;
136 }
137
138 const astr = a.toString();
139 const bstr = b.toString();
140 return astr < bstr ? -1 : astr > bstr ? 1 : 0;
141 }
142}
143
144['push', 'pop', 'shift', 'unshift', 'splice'].forEach(fn => {
145 Object.defineProperty(Path.prototype, fn, {
146 value(...args) {
147 return this.v[fn](...args);
148 }
149 });
150});
151
152Object.defineProperty(Path.prototype, 'length', {
153 enumerable: true,
154 get() {
155 return this.v.length;
156 }
157});
158
159module.exports = Path;
160Path.tester = {
161 setParserAndSep(p, sep) {
162 parsePath = p;
163 SEP = sep;
164 },
165 reset() {
166 parsePath = origParser;
167 SEP = origSep;
168 }
169};