resources.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as extpath from './extpath.js';
  6. import { Schemas } from './network.js';
  7. import * as paths from './path.js';
  8. import { isWindows } from './platform.js';
  9. import { compare as strCompare, equalsIgnoreCase } from './strings.js';
  10. import { URI, uriToFsPath } from './uri.js';
  11. export function originalFSPath(uri) {
  12. return uriToFsPath(uri, true);
  13. }
  14. export class ExtUri {
  15. constructor(_ignorePathCasing) {
  16. this._ignorePathCasing = _ignorePathCasing;
  17. }
  18. compare(uri1, uri2, ignoreFragment = false) {
  19. if (uri1 === uri2) {
  20. return 0;
  21. }
  22. return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment));
  23. }
  24. isEqual(uri1, uri2, ignoreFragment = false) {
  25. if (uri1 === uri2) {
  26. return true;
  27. }
  28. if (!uri1 || !uri2) {
  29. return false;
  30. }
  31. return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment);
  32. }
  33. getComparisonKey(uri, ignoreFragment = false) {
  34. return uri.with({
  35. path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
  36. fragment: ignoreFragment ? null : undefined
  37. }).toString();
  38. }
  39. isEqualOrParent(base, parentCandidate, ignoreFragment = false) {
  40. if (base.scheme === parentCandidate.scheme) {
  41. if (base.scheme === Schemas.file) {
  42. return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), this._ignorePathCasing(base)) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
  43. }
  44. if (isEqualAuthority(base.authority, parentCandidate.authority)) {
  45. return extpath.isEqualOrParent(base.path, parentCandidate.path, this._ignorePathCasing(base), '/') && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
  46. }
  47. }
  48. return false;
  49. }
  50. // --- path math
  51. joinPath(resource, ...pathFragment) {
  52. return URI.joinPath(resource, ...pathFragment);
  53. }
  54. basenameOrAuthority(resource) {
  55. return basename(resource) || resource.authority;
  56. }
  57. basename(resource) {
  58. return paths.posix.basename(resource.path);
  59. }
  60. extname(resource) {
  61. return paths.posix.extname(resource.path);
  62. }
  63. dirname(resource) {
  64. if (resource.path.length === 0) {
  65. return resource;
  66. }
  67. let dirname;
  68. if (resource.scheme === Schemas.file) {
  69. dirname = URI.file(paths.dirname(originalFSPath(resource))).path;
  70. }
  71. else {
  72. dirname = paths.posix.dirname(resource.path);
  73. if (resource.authority && dirname.length && dirname.charCodeAt(0) !== 47 /* Slash */) {
  74. console.error(`dirname("${resource.toString})) resulted in a relative path`);
  75. dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character
  76. }
  77. }
  78. return resource.with({
  79. path: dirname
  80. });
  81. }
  82. normalizePath(resource) {
  83. if (!resource.path.length) {
  84. return resource;
  85. }
  86. let normalizedPath;
  87. if (resource.scheme === Schemas.file) {
  88. normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path;
  89. }
  90. else {
  91. normalizedPath = paths.posix.normalize(resource.path);
  92. }
  93. return resource.with({
  94. path: normalizedPath
  95. });
  96. }
  97. relativePath(from, to) {
  98. if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) {
  99. return undefined;
  100. }
  101. if (from.scheme === Schemas.file) {
  102. const relativePath = paths.relative(originalFSPath(from), originalFSPath(to));
  103. return isWindows ? extpath.toSlashes(relativePath) : relativePath;
  104. }
  105. let fromPath = from.path || '/', toPath = to.path || '/';
  106. if (this._ignorePathCasing(from)) {
  107. // make casing of fromPath match toPath
  108. let i = 0;
  109. for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) {
  110. if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) {
  111. if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) {
  112. break;
  113. }
  114. }
  115. }
  116. fromPath = toPath.substr(0, i) + fromPath.substr(i);
  117. }
  118. return paths.posix.relative(fromPath, toPath);
  119. }
  120. resolvePath(base, path) {
  121. if (base.scheme === Schemas.file) {
  122. const newURI = URI.file(paths.resolve(originalFSPath(base), path));
  123. return base.with({
  124. authority: newURI.authority,
  125. path: newURI.path
  126. });
  127. }
  128. path = extpath.toPosixPath(path); // we allow path to be a windows path
  129. return base.with({
  130. path: paths.posix.resolve(base.path, path)
  131. });
  132. }
  133. // --- misc
  134. isAbsolutePath(resource) {
  135. return !!resource.path && resource.path[0] === '/';
  136. }
  137. isEqualAuthority(a1, a2) {
  138. return a1 === a2 || (a1 !== undefined && a2 !== undefined && equalsIgnoreCase(a1, a2));
  139. }
  140. hasTrailingPathSeparator(resource, sep = paths.sep) {
  141. if (resource.scheme === Schemas.file) {
  142. const fsp = originalFSPath(resource);
  143. return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep;
  144. }
  145. else {
  146. const p = resource.path;
  147. return (p.length > 1 && p.charCodeAt(p.length - 1) === 47 /* Slash */) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0
  148. }
  149. }
  150. removeTrailingPathSeparator(resource, sep = paths.sep) {
  151. // Make sure that the path isn't a drive letter. A trailing separator there is not removable.
  152. if (hasTrailingPathSeparator(resource, sep)) {
  153. return resource.with({ path: resource.path.substr(0, resource.path.length - 1) });
  154. }
  155. return resource;
  156. }
  157. addTrailingPathSeparator(resource, sep = paths.sep) {
  158. let isRootSep = false;
  159. if (resource.scheme === Schemas.file) {
  160. const fsp = originalFSPath(resource);
  161. isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep));
  162. }
  163. else {
  164. sep = '/';
  165. const p = resource.path;
  166. isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === 47 /* Slash */;
  167. }
  168. if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) {
  169. return resource.with({ path: resource.path + '/' });
  170. }
  171. return resource;
  172. }
  173. }
  174. /**
  175. * Unbiased utility that takes uris "as they are". This means it can be interchanged with
  176. * uri#toString() usages. The following is true
  177. * ```
  178. * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri))
  179. * ```
  180. */
  181. export const extUri = new ExtUri(() => false);
  182. export const isEqual = extUri.isEqual.bind(extUri);
  183. export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri);
  184. export const getComparisonKey = extUri.getComparisonKey.bind(extUri);
  185. export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri);
  186. export const basename = extUri.basename.bind(extUri);
  187. export const extname = extUri.extname.bind(extUri);
  188. export const dirname = extUri.dirname.bind(extUri);
  189. export const joinPath = extUri.joinPath.bind(extUri);
  190. export const normalizePath = extUri.normalizePath.bind(extUri);
  191. export const relativePath = extUri.relativePath.bind(extUri);
  192. export const resolvePath = extUri.resolvePath.bind(extUri);
  193. export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri);
  194. export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri);
  195. export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri);
  196. export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri);
  197. export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri);
  198. /**
  199. * Data URI related helpers.
  200. */
  201. export var DataUri;
  202. (function (DataUri) {
  203. DataUri.META_DATA_LABEL = 'label';
  204. DataUri.META_DATA_DESCRIPTION = 'description';
  205. DataUri.META_DATA_SIZE = 'size';
  206. DataUri.META_DATA_MIME = 'mime';
  207. function parseMetaData(dataUri) {
  208. const metadata = new Map();
  209. // Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
  210. // the metadata is: size:2313;label:SomeLabel;description:SomeDescription
  211. const meta = dataUri.path.substring(dataUri.path.indexOf(';') + 1, dataUri.path.lastIndexOf(';'));
  212. meta.split(';').forEach(property => {
  213. const [key, value] = property.split(':');
  214. if (key && value) {
  215. metadata.set(key, value);
  216. }
  217. });
  218. // Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
  219. // the mime is: image/png
  220. const mime = dataUri.path.substring(0, dataUri.path.indexOf(';'));
  221. if (mime) {
  222. metadata.set(DataUri.META_DATA_MIME, mime);
  223. }
  224. return metadata;
  225. }
  226. DataUri.parseMetaData = parseMetaData;
  227. })(DataUri || (DataUri = {}));