Line data Source code
1 : // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 : // for details. All rights reserved. Use of this source code is governed by a
3 : // BSD-style license that can be found in the LICENSE file.
4 :
5 : /// A comprehensive, cross-platform path manipulation library.
6 : ///
7 : /// ## Installing ##
8 : ///
9 : /// Use [pub][] to install this package. Add the following to your
10 : /// `pubspec.yaml` file.
11 : ///
12 : /// dependencies:
13 : /// path: any
14 : ///
15 : /// Then run `pub install`.
16 : ///
17 : /// For more information, see the [path package on pub.dartlang.org][pkg].
18 : ///
19 : /// [pub]: http://pub.dartlang.org
20 : /// [pkg]: http://pub.dartlang.org/packages/path
21 : ///
22 : /// ## Usage ##
23 : ///
24 : /// The path library was designed to be imported with a prefix, though you don't
25 : /// have to if you don't want to:
26 : ///
27 : /// import 'package:path/path.dart' as path;
28 : ///
29 : /// The most common way to use the library is through the top-level functions.
30 : /// These manipulate path strings based on your current working directory and
31 : /// the path style (POSIX, Windows, or URLs) of the host platform. For example:
32 : ///
33 : /// path.join("directory", "file.txt");
34 : ///
35 : /// This calls the top-level [join] function to join "directory" and "file.txt"
36 : /// using the current platform's directory separator.
37 : ///
38 : /// If you want to work with paths for a specific platform regardless of the
39 : /// underlying platform that the program is running on, you can create a
40 : /// [Context] and give it an explicit [Style]:
41 : ///
42 : /// var context = new path.Context(style: Style.windows);
43 : /// context.join("directory", "file.txt");
44 : ///
45 : /// This will join "directory" and "file.txt" using the Windows path separator,
46 : /// even when the program is run on a POSIX machine.
47 : import 'src/context.dart';
48 : import 'src/style.dart';
49 :
50 : export 'src/context.dart' hide createInternal;
51 : export 'src/path_exception.dart';
52 : export 'src/style.dart';
53 :
54 : /// A default context for manipulating POSIX paths.
55 : final Context posix = new Context(style: Style.posix);
56 :
57 : /// A default context for manipulating Windows paths.
58 : final Context windows = new Context(style: Style.windows);
59 :
60 : /// A default context for manipulating URLs.
61 : ///
62 : /// URL path equality is undefined for paths that differ only in their
63 : /// percent-encoding or only in the case of their host segment.
64 : final Context url = new Context(style: Style.url);
65 :
66 : /// The system path context.
67 : ///
68 : /// This differs from a context created with [new Context] in that its
69 : /// [Context.current] is always the current working directory, rather than being
70 : /// set once when the context is created.
71 : final Context context = createInternal();
72 :
73 : /// Returns the [Style] of the current context.
74 : ///
75 : /// This is the style that all top-level path functions will use.
76 10 : Style get style => context.style;
77 :
78 : /// Gets the path to the current working directory.
79 : ///
80 : /// In the browser, this means the current URL, without the last file segment.
81 : String get current {
82 5 : var uri = Uri.base;
83 :
84 : // Converting the base URI to a file path is pretty slow, and the base URI
85 : // rarely changes in practice, so we cache the result here.
86 5 : if (uri == _currentUriBase) return _current;
87 : _currentUriBase = uri;
88 :
89 15 : if (Style.platform == Style.url) {
90 0 : _current = uri.resolve('.').toString();
91 : return _current;
92 : } else {
93 5 : var path = uri.toFilePath();
94 : // Remove trailing '/' or '\'.
95 10 : var lastIndex = path.length - 1;
96 : assert(path[lastIndex] == '/' || path[lastIndex] == '\\');
97 5 : _current = path.substring(0, lastIndex);
98 : return _current;
99 : }
100 : }
101 :
102 : /// The last value returned by [Uri.base].
103 : ///
104 : /// This is used to cache the current working directory.
105 : Uri _currentUriBase;
106 :
107 : /// The last known value of the current working directory.
108 : ///
109 : /// This is cached because [current] is called frequently but rarely actually
110 : /// changes.
111 : String _current;
112 :
113 : /// Gets the path separator for the current platform. This is `\` on Windows
114 : /// and `/` on other platforms (including the browser).
115 0 : String get separator => context.separator;
116 :
117 : /// Creates a new path by appending the given path parts to [current].
118 : /// Equivalent to [join()] with [current] as the first argument. Example:
119 : ///
120 : /// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo'
121 : String absolute(String part1, [String part2, String part3, String part4,
122 : String part5, String part6, String part7]) =>
123 0 : context.absolute(part1, part2, part3, part4, part5, part6, part7);
124 :
125 : /// Gets the part of [path] after the last separator.
126 : ///
127 : /// path.basename('path/to/foo.dart'); // -> 'foo.dart'
128 : /// path.basename('path/to'); // -> 'to'
129 : ///
130 : /// Trailing separators are ignored.
131 : ///
132 : /// path.basename('path/to/'); // -> 'to'
133 0 : String basename(String path) => context.basename(path);
134 :
135 : /// Gets the part of [path] after the last separator, and without any trailing
136 : /// file extension.
137 : ///
138 : /// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
139 : ///
140 : /// Trailing separators are ignored.
141 : ///
142 : /// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
143 : String basenameWithoutExtension(String path) =>
144 0 : context.basenameWithoutExtension(path);
145 :
146 : /// Gets the part of [path] before the last separator.
147 : ///
148 : /// path.dirname('path/to/foo.dart'); // -> 'path/to'
149 : /// path.dirname('path/to'); // -> 'path'
150 : ///
151 : /// Trailing separators are ignored.
152 : ///
153 : /// path.dirname('path/to/'); // -> 'path'
154 : ///
155 : /// If an absolute path contains no directories, only a root, then the root
156 : /// is returned.
157 : ///
158 : /// path.dirname('/'); // -> '/' (posix)
159 : /// path.dirname('c:\'); // -> 'c:\' (windows)
160 : ///
161 : /// If a relative path has no directories, then '.' is returned.
162 : ///
163 : /// path.dirname('foo'); // -> '.'
164 : /// path.dirname(''); // -> '.'
165 0 : String dirname(String path) => context.dirname(path);
166 :
167 : /// Gets the file extension of [path]: the portion of [basename] from the last
168 : /// `.` to the end (including the `.` itself).
169 : ///
170 : /// path.extension('path/to/foo.dart'); // -> '.dart'
171 : /// path.extension('path/to/foo'); // -> ''
172 : /// path.extension('path.to/foo'); // -> ''
173 : /// path.extension('path/to/foo.dart.js'); // -> '.js'
174 : ///
175 : /// If the file name starts with a `.`, then that is not considered the
176 : /// extension:
177 : ///
178 : /// path.extension('~/.bashrc'); // -> ''
179 : /// path.extension('~/.notes.txt'); // -> '.txt'
180 0 : String extension(String path) => context.extension(path);
181 :
182 : // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
183 : /// Returns the root of [path], if it's absolute, or the empty string if it's
184 : /// relative.
185 : ///
186 : /// // Unix
187 : /// path.rootPrefix('path/to/foo'); // -> ''
188 : /// path.rootPrefix('/path/to/foo'); // -> '/'
189 : ///
190 : /// // Windows
191 : /// path.rootPrefix(r'path\to\foo'); // -> ''
192 : /// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
193 : ///
194 : /// // URL
195 : /// path.rootPrefix('path/to/foo'); // -> ''
196 : /// path.rootPrefix('http://dartlang.org/path/to/foo');
197 : /// // -> 'http://dartlang.org'
198 0 : String rootPrefix(String path) => context.rootPrefix(path);
199 :
200 : /// Returns `true` if [path] is an absolute path and `false` if it is a
201 : /// relative path.
202 : ///
203 : /// On POSIX systems, absolute paths start with a `/` (forward slash). On
204 : /// Windows, an absolute path starts with `\\`, or a drive letter followed by
205 : /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and
206 : /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
207 : ///
208 : /// URLs that start with `/` are known as "root-relative", since they're
209 : /// relative to the root of the current URL. Since root-relative paths are still
210 : /// absolute in every other sense, [isAbsolute] will return true for them. They
211 : /// can be detected using [isRootRelative].
212 0 : bool isAbsolute(String path) => context.isAbsolute(path);
213 :
214 : /// Returns `true` if [path] is a relative path and `false` if it is absolute.
215 : /// On POSIX systems, absolute paths start with a `/` (forward slash). On
216 : /// Windows, an absolute path starts with `\\`, or a drive letter followed by
217 : /// `:/` or `:\`.
218 0 : bool isRelative(String path) => context.isRelative(path);
219 :
220 : /// Returns `true` if [path] is a root-relative path and `false` if it's not.
221 : ///
222 : /// URLs that start with `/` are known as "root-relative", since they're
223 : /// relative to the root of the current URL. Since root-relative paths are still
224 : /// absolute in every other sense, [isAbsolute] will return true for them. They
225 : /// can be detected using [isRootRelative].
226 : ///
227 : /// No POSIX and Windows paths are root-relative.
228 0 : bool isRootRelative(String path) => context.isRootRelative(path);
229 :
230 : /// Joins the given path parts into a single path using the current platform's
231 : /// [separator]. Example:
232 : ///
233 : /// path.join('path', 'to', 'foo'); // -> 'path/to/foo'
234 : ///
235 : /// If any part ends in a path separator, then a redundant separator will not
236 : /// be added:
237 : ///
238 : /// path.join('path/', 'to', 'foo'); // -> 'path/to/foo
239 : ///
240 : /// If a part is an absolute path, then anything before that will be ignored:
241 : ///
242 : /// path.join('path', '/to', 'foo'); // -> '/to/foo'
243 : String join(String part1, [String part2, String part3, String part4,
244 : String part5, String part6, String part7, String part8]) =>
245 0 : context.join(part1, part2, part3, part4, part5, part6, part7, part8);
246 :
247 : /// Joins the given path parts into a single path using the current platform's
248 : /// [separator]. Example:
249 : ///
250 : /// path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
251 : ///
252 : /// If any part ends in a path separator, then a redundant separator will not
253 : /// be added:
254 : ///
255 : /// path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
256 : ///
257 : /// If a part is an absolute path, then anything before that will be ignored:
258 : ///
259 : /// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
260 : ///
261 : /// For a fixed number of parts, [join] is usually terser.
262 0 : String joinAll(Iterable<String> parts) => context.joinAll(parts);
263 :
264 : // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
265 : /// Splits [path] into its components using the current platform's [separator].
266 : ///
267 : /// path.split('path/to/foo'); // -> ['path', 'to', 'foo']
268 : ///
269 : /// The path will *not* be normalized before splitting.
270 : ///
271 : /// path.split('path/../foo'); // -> ['path', '..', 'foo']
272 : ///
273 : /// If [path] is absolute, the root directory will be the first element in the
274 : /// array. Example:
275 : ///
276 : /// // Unix
277 : /// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
278 : ///
279 : /// // Windows
280 : /// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
281 : ///
282 : /// // Browser
283 : /// path.split('http://dartlang.org/path/to/foo');
284 : /// // -> ['http://dartlang.org', 'path', 'to', 'foo']
285 0 : List<String> split(String path) => context.split(path);
286 :
287 : /// Canonicalizes [path].
288 : ///
289 : /// This is guaranteed to return the same path for two different input paths
290 : /// if and only if both input paths point to the same location. Unlike
291 : /// [normalize], it returns absolute paths when possible and canonicalizes
292 : /// ASCII case on Windows.
293 : ///
294 : /// Note that this does not resolve symlinks.
295 : ///
296 : /// If you want a map that uses path keys, it's probably more efficient to
297 : /// pass [equals] and [hash] to [new HashMap] than it is to canonicalize every
298 : /// key.
299 0 : String canonicalize(String path) => context.canonicalize(path);
300 :
301 : /// Normalizes [path], simplifying it by handling `..`, and `.`, and
302 : /// removing redundant path separators whenever possible.
303 : ///
304 : /// Note that this is *not* guaranteed to return the same result for two
305 : /// equivalent input paths. For that, see [canonicalize]. Or, if you're using
306 : /// paths as map keys, pass [equals] and [hash] to [new HashMap].
307 : ///
308 : /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
309 0 : String normalize(String path) => context.normalize(path);
310 :
311 : /// Attempts to convert [path] to an equivalent relative path from the current
312 : /// directory.
313 : ///
314 : /// // Given current directory is /root/path:
315 : /// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
316 : /// path.relative('/root/other.dart'); // -> '../other.dart'
317 : ///
318 : /// If the [from] argument is passed, [path] is made relative to that instead.
319 : ///
320 : /// path.relative('/root/path/a/b.dart',
321 : /// from: '/root/path'); // -> 'a/b.dart'
322 : /// path.relative('/root/other.dart',
323 : /// from: '/root/path'); // -> '../other.dart'
324 : ///
325 : /// If [path] and/or [from] are relative paths, they are assumed to be relative
326 : /// to the current directory.
327 : ///
328 : /// Since there is no relative path from one drive letter to another on Windows,
329 : /// or from one hostname to another for URLs, this will return an absolute path
330 : /// in those cases.
331 : ///
332 : /// // Windows
333 : /// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other'
334 : ///
335 : /// // URL
336 : /// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org');
337 : /// // -> 'http://dartlang.org'
338 : String relative(String path, {String from}) =>
339 0 : context.relative(path, from: from);
340 :
341 : /// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise.
342 : ///
343 : /// path.isWithin('/root/path', '/root/path/a'); // -> true
344 : /// path.isWithin('/root/path', '/root/other'); // -> false
345 : /// path.isWithin('/root/path', '/root/path') // -> false
346 0 : bool isWithin(String parent, String child) => context.isWithin(parent, child);
347 :
348 : /// Returns `true` if [path1] points to the same location as [path2], and
349 : /// `false` otherwise.
350 : ///
351 : /// The [hash] function returns a hash code that matches these equality
352 : /// semantics.
353 0 : bool equals(String path1, String path2) => context.equals(path1, path2);
354 :
355 : /// Returns a hash code for [path] such that, if [equals] returns `true` for two
356 : /// paths, their hash codes are the same.
357 : ///
358 : /// Note that the same path may have different hash codes on different platforms
359 : /// or with different [current] directories.
360 0 : int hash(String path) => context.hash(path);
361 :
362 : /// Removes a trailing extension from the last part of [path].
363 : ///
364 : /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
365 0 : String withoutExtension(String path) => context.withoutExtension(path);
366 :
367 : /// Returns the path represented by [uri], which may be a [String] or a [Uri].
368 : ///
369 : /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
370 : /// style, this will just convert [uri] to a string.
371 : ///
372 : /// // POSIX
373 : /// context.fromUri('file:///path/to/foo')
374 : /// // -> '/path/to/foo'
375 : ///
376 : /// // Windows
377 : /// context.fromUri('file:///C:/path/to/foo')
378 : /// // -> r'C:\path\to\foo'
379 : ///
380 : /// // URL
381 : /// context.fromUri('http://dartlang.org/path/to/foo')
382 : /// // -> 'http://dartlang.org/path/to/foo'
383 : ///
384 : /// If [uri] is relative, a relative path will be returned.
385 : ///
386 : /// path.fromUri('path/to/foo'); // -> 'path/to/foo'
387 0 : String fromUri(uri) => context.fromUri(uri);
388 :
389 : /// Returns the URI that represents [path].
390 : ///
391 : /// For POSIX and Windows styles, this will return a `file:` URI. For the URL
392 : /// style, this will just convert [path] to a [Uri].
393 : ///
394 : /// // POSIX
395 : /// path.toUri('/path/to/foo')
396 : /// // -> Uri.parse('file:///path/to/foo')
397 : ///
398 : /// // Windows
399 : /// path.toUri(r'C:\path\to\foo')
400 : /// // -> Uri.parse('file:///C:/path/to/foo')
401 : ///
402 : /// // URL
403 : /// path.toUri('http://dartlang.org/path/to/foo')
404 : /// // -> Uri.parse('http://dartlang.org/path/to/foo')
405 : ///
406 : /// If [path] is relative, a relative URI will be returned.
407 : ///
408 : /// path.toUri('path/to/foo')
409 : /// // -> Uri.parse('path/to/foo')
410 0 : Uri toUri(String path) => context.toUri(path);
411 :
412 : /// Returns a terse, human-readable representation of [uri].
413 : ///
414 : /// [uri] can be a [String] or a [Uri]. If it can be made relative to the
415 : /// current working directory, that's done. Otherwise, it's returned as-is. This
416 : /// gracefully handles non-`file:` URIs for [Style.posix] and [Style.windows].
417 : ///
418 : /// The returned value is meant for human consumption, and may be either URI-
419 : /// or path-formatted.
420 : ///
421 : /// // POSIX at "/root/path"
422 : /// path.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart'
423 : /// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org'
424 : ///
425 : /// // Windows at "C:\root\path"
426 : /// path.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart'
427 : /// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org'
428 : ///
429 : /// // URL at "http://dartlang.org/root/path"
430 : /// path.prettyUri('http://dartlang.org/root/path/a/b.dart');
431 : /// // -> r'a/b.dart'
432 : /// path.prettyUri('file:///root/path'); // -> 'file:///root/path'
433 10 : String prettyUri(uri) => context.prettyUri(uri);
|