UNPKG

@velcro/resolver

Version:

Resolve references to absolute urls using the node module resolution algorithm using an generic host interface

1,645 lines 62.4 kB
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = global || self, factory(global.Velcro = global.Velcro || {}));
}(this, function (exports) { 'use strict';

  (function (ResolvedEntryKind) {
      ResolvedEntryKind["Directory"] = "directory";
      ResolvedEntryKind["File"] = "file";
  })(exports.ResolvedEntryKind || (exports.ResolvedEntryKind = {}));
  function isValidPackageJson(json) {
      return (typeof json === 'object' &&
          json !== null &&
          !hasInvalidOptionalStringField(json, 'name') &&
          !hasInvalidOptionalStringField(json, 'version') &&
          !hasInvalidBrowserField(json) &&
          !hasInvalidOptionalStringField(json, 'main') &&
          !hasInvalidOptionalStringField(json, 'module') &&
          !hasInvalidOptionalStringField(json, 'jsnext:main') &&
          !hasInvalidOptionalStringField(json, 'unpkg') &&
          !hasInvalidDependenciesField(json, 'dependencies') &&
          !hasInvalidDependenciesField(json, 'devDependencies') &&
          !hasInvalidDependenciesField(json, 'peerDependencies'));
  }
  function hasInvalidBrowserField(json) {
      let error = '';
      const browser = json.browser;
      if (browser) {
          if (typeof browser === 'object') {
              for (const key in browser) {
                  if (typeof key !== 'string') {
                      error = `The key ${key} of .browser must be a string`;
                      break;
                  }
                  if (typeof browser[key] !== 'string' && browser[key] !== false) {
                      error = `The value ${key} of .browser must be a string or false`;
                      break;
                  }
              }
          }
      }
      return error;
  }
  function hasInvalidOptionalStringField(json, field) {
      return json[field] !== undefined && typeof json[field] !== 'string';
  }
  function hasInvalidDependenciesField(json, field) {
      return (json[field] !== undefined &&
          typeof json[field] === 'object' &&
          json[field] !== null &&
          !Object.keys(json[field]).every(key => typeof key === 'string' && typeof json[field][key] === 'string'));
  }

  const CHAR_DOT = 46; /* . */
  const CHAR_FORWARD_SLASH = 47; /* / */
  const TRAILING_SLASH_RX = /\/?$/;
  function ensureTrailingSlash(pathname) {
      return pathname.replace(TRAILING_SLASH_RX, '/');
  }
  function parseBufferAsPackageJson(decoder, content, spec) {
      try {
          const text = decoder.decode(content);
          return parseTextAsPackageJson(text, spec);
      }
      catch (err) {
          throw new Error(`Error decoding manifest buffer for package ${spec}: ${err.message}`);
      }
  }
  function parseTextAsPackageJson(text, spec) {
      let json;
      try {
          json = JSON.parse(text);
      }
      catch (err) {
          throw new Error(`Error parsing manifest as json for package ${spec}: ${err.message}`);
      }
      if (!isValidPackageJson(json)) {
          throw new Error(`Invalid manifest for the package ${spec}`);
      }
      return json;
  }
  function getFirstPathSegmentAfterPrefix(child, parent) {
      const childHref = child.pathname;
      const parentHref = parent.pathname;
      const parentOffset = parentHref.charAt(parentHref.length - 1) === '/' ? -1 : 0;
      for (let i = 0; i <= childHref.length; i++) {
          if (i < parentHref.length) {
              if (childHref.charAt(i) !== parentHref.charAt(i)) {
                  throw new Error(`The child entry ${child.href} does not have the pathname of ${parent.href} as a prefix`);
              }
          }
          else if (i === parentHref.length + parentOffset) {
              if (childHref.charAt(i) !== '/') {
                  throw new Error(`The child entry ${child.href} does not have the pathname of ${parent.href} as a prefix`);
              }
          }
          else if (childHref.charAt(i) === '/') {
              return childHref.slice(parentHref.length + 1 + parentOffset, i);
          }
      }
      return childHref.slice(parentHref.length + 1 + parentOffset);
  }
  function validateString(value, name) {
      if (typeof value !== 'string') {
          throw new TypeError(`The '${name}' argument must be of type string but got ${typeof value}`);
      }
  }
  function basename(path, ext) {
      if (ext !== undefined) {
          validateString(ext, 'ext');
      }
      validateString(path, 'path');
      let start = 0;
      let end = -1;
      let matchedSlash = true;
      let i;
      if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
          if (ext.length === path.length && ext === path) {
              return '';
          }
          let extIdx = ext.length - 1;
          let firstNonSlashEnd = -1;
          for (i = path.length - 1; i >= start; --i) {
              const code = path.charCodeAt(i);
              if (isPathSeparator(code)) {
                  // If we reached a path separator that was not part of a set of path
                  // separators at the end of the string, stop now
                  if (!matchedSlash) {
                      start = i + 1;
                      break;
                  }
              }
              else {
                  if (firstNonSlashEnd === -1) {
                      // We saw the first non-path separator, remember this index in case
                      // we need it if the extension ends up not matching
                      matchedSlash = false;
                      firstNonSlashEnd = i + 1;
                  }
                  if (extIdx >= 0) {
                      // Try to match the explicit extension
                      if (code === ext.charCodeAt(extIdx)) {
                          if (--extIdx === -1) {
                              // We matched the extension, so mark this as the end of our path
                              // component
                              end = i;
                          }
                      }
                      else {
                          // Extension does not match, so our result is the entire path
                          // component
                          extIdx = -1;
                          end = firstNonSlashEnd;
                      }
                  }
              }
          }
          if (start === end) {
              end = firstNonSlashEnd;
          }
          else if (end === -1) {
              end = path.length;
          }
          return path.slice(start, end);
      }
      else {
          for (i = path.length - 1; i >= start; --i) {
              if (isPathSeparator(path.charCodeAt(i))) {
                  // If we reached a path separator that was not part of a set of path
                  // separators at the end of the string, stop now
                  if (!matchedSlash) {
                      start = i + 1;
                      break;
                  }
              }
              else if (end === -1) {
                  // We saw the first non-path separator, mark this as the end of our
                  // path component
                  matchedSlash = false;
                  end = i + 1;
              }
          }
          if (end === -1) {
              return '';
          }
          return path.slice(start, end);
      }
  }
  function extname(path) {
      validateString(path, 'path');
      let start = 0;
      let startDot = -1;
      let startPart = 0;
      let end = -1;
      let matchedSlash = true;
      // Track the state of characters (if any) we see before our first dot and
      // after any path separator we find
      let preDotState = 0;
      for (let i = path.length - 1; i >= start; --i) {
          const code = path.charCodeAt(i);
          if (isPathSeparator(code)) {
              // If we reached a path separator that was not part of a set of path
              // separators at the end of the string, stop now
              if (!matchedSlash) {
                  startPart = i + 1;
                  break;
              }
              continue;
          }
          if (end === -1) {
              // We saw the first non-path separator, mark this as the end of our
              // extension
              matchedSlash = false;
              end = i + 1;
          }
          if (code === CHAR_DOT) {
              // If this is our first dot, mark it as the start of our extension
              if (startDot === -1) {
                  startDot = i;
              }
              else if (preDotState !== 1) {
                  preDotState = 1;
              }
          }
          else if (startDot !== -1) {
              // We saw a non-dot and non-path separator before our dot, so we should
              // have a good chance at having a non-empty extension
              preDotState = -1;
          }
      }
      if (startDot === -1 ||
          end === -1 ||
          // We saw a non-dot character immediately before the dot
          preDotState === 0 ||
          // The (right-most) trimmed path component is exactly '..'
          (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
          return '';
      }
      return path.slice(startDot, end);
  }
  function dirname(path) {
      validateString(path, 'path');
      const len = path.length;
      if (len === 0) {
          return '.';
      }
      let rootEnd = -1;
      let end = -1;
      let matchedSlash = true;
      let offset = 0;
      const code = path.charCodeAt(0);
      // Try to match a root
      if (len > 1) {
          if (isPathSeparator(code)) {
              // Possible UNC root
              rootEnd = offset = 1;
              if (isPathSeparator(path.charCodeAt(1))) {
                  // Matched double path separator at beginning
                  let j = 2;
                  let last = j;
                  // Match 1 or more non-path separators
                  for (; j < len; ++j) {
                      if (isPathSeparator(path.charCodeAt(j))) {
                          break;
                      }
                  }
                  if (j < len && j !== last) {
                      // Matched!
                      last = j;
                      // Match 1 or more path separators
                      for (; j < len; ++j) {
                          if (!isPathSeparator(path.charCodeAt(j))) {
                              break;
                          }
                      }
                      if (j < len && j !== last) {
                          // Matched!
                          last = j;
                          // Match 1 or more non-path separators
                          for (; j < len; ++j) {
                              if (isPathSeparator(path.charCodeAt(j))) {
                                  break;
                              }
                          }
                          if (j === len) {
                              // We matched a UNC root only
                              return path;
                          }
                          if (j !== last) {
                              // We matched a UNC root with leftovers
                              // Offset by 1 to include the separator after the UNC root to
                              // treat it as a "normal root" on top of a (UNC) root
                              rootEnd = offset = j + 1;
                          }
                      }
                  }
              }
          }
      }
      else if (isPathSeparator(code)) {
          // `path` contains just a path separator, exit early to avoid
          // unnecessary work
          return path;
      }
      for (let i = len - 1; i >= offset; --i) {
          if (isPathSeparator(path.charCodeAt(i))) {
              if (!matchedSlash) {
                  end = i;
                  break;
              }
          }
          else {
              // We saw the first non-path separator
              matchedSlash = false;
          }
      }
      if (end === -1) {
          if (rootEnd === -1) {
              return '.';
          }
          else {
              end = rootEnd;
          }
      }
      return path.slice(0, end);
  }
  function join(initialSegment, ...pathSegments) {
      let pathname = initialSegment;
      for (let i = 0; i < pathSegments.length; i++) {
          let segment = pathSegments[i];
          if (segment.startsWith('/')) {
              segment = segment.slice(1);
          }
          if (pathname.endsWith('/')) {
              pathname += segment;
          }
          else {
              pathname += `/${segment}`;
          }
      }
      return pathname;
  }
  function resolve(...pathSegments) {
      let resolvedPath = '';
      let resolvedAbsolute = false;
      for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
          let path;
          if (i >= 0) {
              path = pathSegments[i];
          }
          else {
              break;
          }
          validateString(path, 'path');
          // Skip empty entries
          if (path.length === 0) {
              continue;
          }
          resolvedPath = path + '/' + resolvedPath;
          resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
      }
      // 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 = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPathSeparator);
      if (resolvedAbsolute) {
          if (resolvedPath.length > 0) {
              return '/' + resolvedPath;
          }
          else {
              return '/';
          }
      }
      else if (resolvedPath.length > 0) {
          return resolvedPath;
      }
      else {
          return '.';
      }
  }
  function isPathSeparator(code) {
      return code === CHAR_FORWARD_SLASH;
  }
  function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
      let res = '';
      let lastSegmentLength = 0;
      let lastSlash = -1;
      let dots = 0;
      let code = -1;
      for (let i = 0; i <= path.length; ++i) {
          if (i < path.length) {
              code = path.charCodeAt(i);
          }
          else if (isPathSeparator(code)) {
              break;
          }
          else {
              code = CHAR_FORWARD_SLASH;
          }
          if (isPathSeparator(code)) {
              if (lastSlash === i - 1 || dots === 1) ;
              else if (lastSlash !== i - 1 && dots === 2) {
                  if (res.length < 2 ||
                      lastSegmentLength !== 2 ||
                      res.charCodeAt(res.length - 1) !== CHAR_DOT ||
                      res.charCodeAt(res.length - 2) !== CHAR_DOT) {
                      if (res.length > 2) {
                          const lastSlashIndex = res.lastIndexOf(separator);
                          if (lastSlashIndex === -1) {
                              res = '';
                              lastSegmentLength = 0;
                          }
                          else {
                              res = res.slice(0, lastSlashIndex);
                              lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
                          }
                          lastSlash = i;
                          dots = 0;
                          continue;
                      }
                      else if (res.length === 2 || res.length === 1) {
                          res = '';
                          lastSegmentLength = 0;
                          lastSlash = i;
                          dots = 0;
                          continue;
                      }
                  }
                  if (allowAboveRoot) {
                      if (res.length > 0) {
                          res += `${separator}..`;
                      }
                      else {
                          res = '..';
                      }
                      lastSegmentLength = 2;
                  }
              }
              else {
                  if (res.length > 0) {
                      res += separator + path.slice(lastSlash + 1, i);
                  }
                  else {
                      res = path.slice(lastSlash + 1, i);
                  }
                  lastSegmentLength = i - lastSlash - 1;
              }
              lastSlash = i;
              dots = 0;
          }
          else if (code === CHAR_DOT && dots !== -1) {
              ++dots;
          }
          else {
              dots = -1;
          }
      }
      return res;
  }

  var util = /*#__PURE__*/Object.freeze({
    __proto__: null,
    ensureTrailingSlash: ensureTrailingSlash,
    parseBufferAsPackageJson: parseBufferAsPackageJson,
    getFirstPathSegmentAfterPrefix: getFirstPathSegmentAfterPrefix,
    basename: basename,
    extname: extname,
    dirname: dirname,
    join: join,
    resolve: resolve
  });

  /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  // Avoid circular dependency on EventEmitter by implementing a subset of the interface.
  class ErrorHandler {
      constructor() {
          this.listeners = [];
          this.unexpectedErrorHandler = function (e) {
              setTimeout(() => {
                  if (e.stack) {
                      throw new Error(e.message + '\n\n' + e.stack);
                  }
                  throw e;
              }, 0);
          };
      }
      addListener(listener) {
          this.listeners.push(listener);
          return () => {
              this._removeListener(listener);
          };
      }
      emit(e) {
          this.listeners.forEach(listener => {
              listener(e);
          });
      }
      _removeListener(listener) {
          this.listeners.splice(this.listeners.indexOf(listener), 1);
      }
      setUnexpectedErrorHandler(newUnexpectedErrorHandler) {
          this.unexpectedErrorHandler = newUnexpectedErrorHandler;
      }
      getUnexpectedErrorHandler() {
          return this.unexpectedErrorHandler;
      }
      onUnexpectedError(e) {
          this.unexpectedErrorHandler(e);
          this.emit(e);
      }
      // For external errors, we don't want the listeners to be called
      onUnexpectedExternalError(e) {
          this.unexpectedErrorHandler(e);
      }
  }
  const errorHandler = new ErrorHandler();
  function onUnexpectedError(e) {
      // ignore errors from cancelled promises
      if (!isPromiseCanceledError(e)) {
          errorHandler.onUnexpectedError(e);
      }
      return undefined;
  }
  class ExtendableError extends Error {
      constructor(message) {
          super(message);
          this.name = this.constructor.name;
          if (typeof Error.captureStackTrace === 'function') {
              Error.captureStackTrace(this, this.constructor);
          }
          else {
              this.stack = new Error(message).stack;
          }
      }
  }
  class CanceledError extends ExtendableError {
  }
  /**
   * Checks if the given error is a promise in canceled state
   */
  function isPromiseCanceledError(error) {
      return error instanceof CanceledError;
  }
  function dispose(first, ...rest) {
      if (Array.isArray(first)) {
          first.forEach(d => d && d.dispose());
          return [];
      }
      else if (rest.length === 0) {
          if (first) {
              first.dispose();
              return first;
          }
          return undefined;
      }
      else {
          dispose(first);
          dispose(rest);
          return [];
      }
  }
  function combinedDisposable(...disposables) {
      return { dispose: () => dispose(disposables) };
  }
  class DisposableStore {
      constructor() {
          this._toDispose = new Set();
          this._isDisposed = false;
      }
      /**
       * Dispose of all registered disposables and mark this object as disposed.
       *
       * Any future disposables added to this object will be disposed of on `add`.
       */
      dispose() {
          this._isDisposed = true;
          this.clear();
      }
      /**
       * Dispose of all registered disposables but do not mark this object as disposed.
       */
      clear() {
          this._toDispose.forEach(item => item.dispose());
          this._toDispose.clear();
      }
      add(t) {
          if (this._isDisposed) {
              console.warn('Registering disposable on object that has already been disposed.');
              t.dispose();
          }
          else {
              this._toDispose.add(t);
          }
          return t;
      }
  }
  class Disposable {
      constructor() {
          this._store = new DisposableStore();
      }
      dispose() {
          this._store.dispose();
      }
      _register(t) {
          return this._store.add(t);
      }
  }
  Disposable.None = Object.freeze({ dispose() { } });

  /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  const FIN = { done: true, value: undefined };

  /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  class Node {
      constructor(element) {
          this.element = element;
          this.next = Node.Undefined;
          this.prev = Node.Undefined;
      }
  }
  Node.Undefined = new Node(undefined);
  class LinkedList {
      constructor() {
          this._first = Node.Undefined;
          this._last = Node.Undefined;
          this._size = 0;
      }
      get size() {
          return this._size;
      }
      isEmpty() {
          return this._first === Node.Undefined;
      }
      clear() {
          this._first = Node.Undefined;
          this._last = Node.Undefined;
          this._size = 0;
      }
      unshift(element) {
          return this._insert(element, false);
      }
      push(element) {
          return this._insert(element, true);
      }
      _insert(element, atTheEnd) {
          const newNode = new Node(element);
          if (this._first === Node.Undefined) {
              this._first = newNode;
              this._last = newNode;
          }
          else if (atTheEnd) {
              // push
              const oldLast = this._last;
              this._last = newNode;
              newNode.prev = oldLast;
              oldLast.next = newNode;
          }
          else {
              // unshift
              const oldFirst = this._first;
              this._first = newNode;
              newNode.next = oldFirst;
              oldFirst.prev = newNode;
          }
          this._size += 1;
          let didRemove = false;
          return () => {
              if (!didRemove) {
                  didRemove = true;
                  this._remove(newNode);
              }
          };
      }
      shift() {
          if (this._first === Node.Undefined) {
              return undefined;
          }
          else {
              const res = this._first.element;
              this._remove(this._first);
              return res;
          }
      }
      pop() {
          if (this._last === Node.Undefined) {
              return undefined;
          }
          else {
              const res = this._last.element;
              this._remove(this._last);
              return res;
          }
      }
      _remove(node) {
          if (node.prev !== Node.Undefined && node.next !== Node.Undefined) {
              // middle
              const anchor = node.prev;
              anchor.next = node.next;
              node.next.prev = anchor;
          }
          else if (node.prev === Node.Undefined && node.next === Node.Undefined) {
              // only node
              this._first = Node.Undefined;
              this._last = Node.Undefined;
          }
          else if (node.next === Node.Undefined) {
              // last
              this._last = this._last.prev;
              this._last.next = Node.Undefined;
          }
          else if (node.prev === Node.Undefined) {
              // first
              this._first = this._first.next;
              this._first.prev = Node.Undefined;
          }
          // done
          this._size -= 1;
      }
      iterator() {
          let element;
          let node = this._first;
          return {
              next() {
                  if (node === Node.Undefined) {
                      return FIN;
                  }
                  if (!element) {
                      element = { done: false, value: node.element };
                  }
                  else {
                      element.value = node.element;
                  }
                  node = node.next;
                  return element;
              },
          };
      }
      toArray() {
          const result = [];
          for (let node = this._first; node !== Node.Undefined; node = node.next) {
              result.push(node.element);
          }
          return result;
      }
  }

  /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  var Event;
  (function (Event) {
      const _disposable = { dispose() { } };
      Event.None = function () {
          return _disposable;
      };
      /**
       * Given an event, returns another event which only fires once.
       */
      function once(event) {
          return (listener, thisArgs = null, disposables) => {
              // we need this, in case the event fires during the listener call
              let didFire = false;
              let result;
              result = event(e => {
                  if (didFire) {
                      return;
                  }
                  else if (result) {
                      result.dispose();
                  }
                  else {
                      didFire = true;
                  }
                  return listener.call(thisArgs, e);
              }, null, disposables);
              if (didFire) {
                  result.dispose();
              }
              return result;
          };
      }
      Event.once = once;
      /**
       * Given an event and a `map` function, returns another event which maps each element
       * throught the mapping function.
       */
      function map(event, map) {
          return snapshot((listener, thisArgs = null, disposables) => event(i => listener.call(thisArgs, map(i)), null, disposables));
      }
      Event.map = map;
      /**
       * Given an event and an `each` function, returns another identical event and calls
       * the `each` function per each element.
       */
      function forEach(event, each) {
          return snapshot((listener, thisArgs = null, disposables) => event(i => {
              each(i);
              listener.call(thisArgs, i);
          }, null, disposables));
      }
      Event.forEach = forEach;
      function filter(event, filter) {
          return snapshot((listener, thisArgs = null, disposables) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables));
      }
      Event.filter = filter;
      /**
       * Given an event, returns the same event but typed as `Event<void>`.
       */
      function signal(event) {
          return event;
      }
      Event.signal = signal;
      /**
       * Given a collection of events, returns a single event which emits
       * whenever any of the provided events emit.
       */
      function any(...events) {
          return (listener, thisArgs = null, disposables) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
      }
      Event.any = any;
      /**
       * Given an event and a `merge` function, returns another event which maps each element
       * and the cummulative result throught the `merge` function. Similar to `map`, but with memory.
       */
      function reduce(event, merge, initial) {
          let output = initial;
          return map(event, e => {
              output = merge(output, e);
              return output;
          });
      }
      Event.reduce = reduce;
      /**
       * Given a chain of event processing functions (filter, map, etc), each
       * function will be invoked per event & per listener. Snapshotting an event
       * chain allows each function to be invoked just once per event.
       */
      function snapshot(event) {
          let listener;
          const emitter = new Emitter({
              onFirstListenerAdd() {
                  listener = event(emitter.fire, emitter);
              },
              onLastListenerRemove() {
                  listener.dispose();
              },
          });
          return emitter.event;
      }
      Event.snapshot = snapshot;
      function debounce(event, merge, delay = 100, leading = false, leakWarningThreshold) {
          let subscription;
          let output = undefined;
          let handle = undefined;
          let numDebouncedCalls = 0;
          const emitter = new Emitter({
              leakWarningThreshold,
              onFirstListenerAdd() {
                  subscription = event(cur => {
                      numDebouncedCalls++;
                      output = merge(output, cur);
                      if (leading && !handle) {
                          emitter.fire(output);
                      }
                      clearTimeout(handle);
                      handle = setTimeout(() => {
                          const _output = output;
                          output = undefined;
                          handle = undefined;
                          if (!leading || numDebouncedCalls > 1) {
                              emitter.fire(_output);
                          }
                          numDebouncedCalls = 0;
                      }, delay);
                  });
              },
              onLastListenerRemove() {
                  subscription.dispose();
              },
          });
          return emitter.event;
      }
      Event.debounce = debounce;
      /**
       * Given an event, it returns another event which fires only once and as soon as
       * the input event emits. The event data is the number of millis it took for the
       * event to fire.
       */
      function stopwatch(event) {
          const start = new Date().getTime();
          return map(once(event), _ => new Date().getTime() - start);
      }
      Event.stopwatch = stopwatch;
      /**
       * Given an event, it returns another event which fires only when the event
       * element changes.
       */
      function latch(event) {
          let firstCall = true;
          let cache;
          return filter(event, value => {
              const shouldEmit = firstCall || value !== cache;
              firstCall = false;
              cache = value;
              return shouldEmit;
          });
      }
      Event.latch = latch;
      /**
       * Buffers the provided event until a first listener comes
       * along, at which point fire all the events at once and
       * pipe the event from then on.
       *
       * ```typescript
       * const emitter = new Emitter<number>();
       * const event = emitter.event;
       * const bufferedEvent = buffer(event);
       *
       * emitter.fire(1);
       * emitter.fire(2);
       * emitter.fire(3);
       * // nothing...
       *
       * const listener = bufferedEvent(num => console.log(num));
       * // 1, 2, 3
       *
       * emitter.fire(4);
       * // 4
       * ```
       */
      function buffer(event, nextTick = false, _buffer = []) {
          let buffer = _buffer.slice();
          let listener = event(e => {
              if (buffer) {
                  buffer.push(e);
              }
              else {
                  emitter.fire(e);
              }
          });
          const flush = () => {
              if (buffer) {
                  buffer.forEach(e => emitter.fire(e));
              }
              buffer = null;
          };
          const emitter = new Emitter({
              onFirstListenerAdd() {
                  if (!listener) {
                      listener = event(e => emitter.fire(e));
                  }
              },
              onFirstListenerDidAdd() {
                  if (buffer) {
                      if (nextTick) {
                          setTimeout(flush, 0);
                      }
                      else {
                          flush();
                      }
                  }
              },
              onLastListenerRemove() {
                  if (listener) {
                      listener.dispose();
                  }
                  listener = null;
              },
          });
          return emitter.event;
      }
      Event.buffer = buffer;
      class ChainableEvent {
          constructor(event) {
              this.event = event;
          }
          map(fn) {
              return new ChainableEvent(map(this.event, fn));
          }
          forEach(fn) {
              return new ChainableEvent(forEach(this.event, fn));
          }
          filter(fn) {
              return new ChainableEvent(filter(this.event, fn));
          }
          reduce(merge, initial) {
              return new ChainableEvent(reduce(this.event, merge, initial));
          }
          latch() {
              return new ChainableEvent(latch(this.event));
          }
          on(listener, thisArgs, disposables) {
              return this.event(listener, thisArgs, disposables);
          }
          once(listener, thisArgs, disposables) {
              return once(this.event)(listener, thisArgs, disposables);
          }
      }
      function chain(event) {
          return new ChainableEvent(event);
      }
      Event.chain = chain;
      function fromNodeEventEmitter(emitter, eventName, map = id => id) {
          const fn = (...args) => result.fire(map(...args));
          const onFirstListenerAdd = () => emitter.on(eventName, fn);
          const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
          const result = new Emitter({ onFirstListenerAdd, onLastListenerRemove });
          return result.event;
      }
      Event.fromNodeEventEmitter = fromNodeEventEmitter;
      function fromPromise(promise) {
          const emitter = new Emitter();
          let shouldEmit = false;
          promise
              .then(undefined, () => null)
              .then(() => {
              if (!shouldEmit) {
                  setTimeout(() => emitter.fire(undefined), 0);
              }
              else {
                  emitter.fire(undefined);
              }
          });
          shouldEmit = true;
          return emitter.event;
      }
      Event.fromPromise = fromPromise;
      function toPromise(event) {
          return new Promise(c => once(event)(c));
      }
      Event.toPromise = toPromise;
  })(Event || (Event = {}));
  let _globalLeakWarningThreshold = -1;
  class LeakageMonitor {
      constructor(customThreshold, name = Math.random()
          .toString(18)
          .slice(2, 5)) {
          this.customThreshold = customThreshold;
          this.name = name;
          this._warnCountdown = 0;
      }
      dispose() {
          if (this._stacks) {
              this._stacks.clear();
          }
      }
      check(listenerCount) {
          let threshold = _globalLeakWarningThreshold;
          if (typeof this.customThreshold === 'number') {
              threshold = this.customThreshold;
          }
          if (threshold <= 0 || listenerCount < threshold) {
              return undefined;
          }
          if (!this._stacks) {
              this._stacks = new Map();
          }
          const stack = new Error()
              .stack.split('\n')
              .slice(3)
              .join('\n');
          const count = this._stacks.get(stack) || 0;
          this._stacks.set(stack, count + 1);
          this._warnCountdown -= 1;
          if (this._warnCountdown <= 0) {
              // only warn on first exceed and then every time the limit
              // is exceeded by 50% again
              this._warnCountdown = threshold * 0.5;
              // find most frequent listener and print warning
              let topStack;
              let topCount = 0;
              this._stacks.forEach((count, stack) => {
                  if (!topStack || topCount < count) {
                      topStack = stack;
                      topCount = count;
                  }
              });
              console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`);
              console.warn(topStack);
          }
          return () => {
              const count = this._stacks.get(stack) || 0;
              this._stacks.set(stack, count - 1);
          };
      }
  }
  /**
   * The Emitter can be used to expose an Event to the public
   * to fire it from the insides.
   * Sample:
      class Document {
          private _onDidChange = new Emitter<(value:string)=>any>();
          public onDidChange = this._onDidChange.event;
          // getter-style
          // get onDidChange(): Event<(value:string)=>any> {
          // 	return this._onDidChange.event;
          // }
          private _doIt() {
              //...
              this._onDidChange.fire(value);
          }
      }
   */
  class Emitter {
      constructor(options) {
          this._disposed = false;
          this._options = options;
          this._leakageMon =
              _globalLeakWarningThreshold > 0
                  ? new LeakageMonitor(this._options && this._options.leakWarningThreshold)
                  : undefined;
      }
      /**
       * For the public to allow to subscribe
       * to events from this Emitter
       */
      get event() {
          if (!this._event) {
              this._event = (listener, thisArgs, disposables) => {
                  if (!this._listeners) {
                      this._listeners = new LinkedList();
                  }
                  const firstListener = this._listeners.isEmpty();
                  if (firstListener && this._options && this._options.onFirstListenerAdd) {
                      this._options.onFirstListenerAdd(this);
                  }
                  const remove = this._listeners.push(!thisArgs ? listener : [listener, thisArgs]);
                  if (firstListener && this._options && this._options.onFirstListenerDidAdd) {
                      this._options.onFirstListenerDidAdd(this);
                  }
                  if (this._options && this._options.onListenerDidAdd) {
                      this._options.onListenerDidAdd(this, listener, thisArgs);
                  }
                  // check and record this emitter for potential leakage
                  let removeMonitor;
                  if (this._leakageMon) {
                      removeMonitor = this._leakageMon.check(this._listeners.size);
                  }
                  let result;
                  result = {
                      dispose: () => {
                          if (removeMonitor) {
                              removeMonitor();
                          }
                          result.dispose = Emitter._noop;
                          if (!this._disposed) {
                              remove();
                              if (this._options && this._options.onLastListenerRemove) {
                                  const hasListeners = this._listeners && !this._listeners.isEmpty();
                                  if (!hasListeners) {
                                      this._options.onLastListenerRemove(this);
                                  }
                              }
                          }
                      },
                  };
                  if (Array.isArray(disposables)) {
                      disposables.push(result);
                  }
                  return result;
              };
          }
          return this._event;
      }
      /**
       * To be kept private to fire an event to
       * subscribers
       */
      fire(event) {
          if (this._listeners) {
              // put all [listener,event]-pairs into delivery queue
              // then emit all event. an inner/nested event might be
              // the driver of this
              if (!this._deliveryQueue) {
                  this._deliveryQueue = new LinkedList();
              }
              for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
                  this._deliveryQueue.push([e.value, event]);
              }
              while (this._deliveryQueue.size > 0) {
                  const [listener, event] = this._deliveryQueue.shift();
                  try {
                      if (typeof listener === 'function') {
                          listener.call(undefined, event);
                      }
                      else {
                          listener[0].call(listener[1], event);
                      }
                  }
                  catch (e) {
                      onUnexpectedError(e);
                  }
              }
          }
      }
      dispose() {
          if (this._listeners) {
              this._listeners.clear();
          }
          if (this._deliveryQueue) {
              this._deliveryQueue.clear();
          }
          if (this._leakageMon) {
              this._leakageMon.dispose();
          }
          this._disposed = true;
      }
  }
  Emitter._noop = function () { };

  /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  const shortcutEvent = Object.freeze(function (callback, context) {
      const handle = setTimeout(callback.bind(context), 0);
      return {
          dispose() {
              clearTimeout(handle);
          },
      };
  });

  (function (CancellationToken) {
      function isCancellationToken(thing) {
          if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
              return true;
          }
          if (thing instanceof MutableToken) {
              return true;
          }
          if (!thing || typeof thing !== 'object') {
              return false;
          }
          return (typeof thing.isCancellationRequested === 'boolean' &&
              typeof thing.onCancellationRequested === 'function');
      }
      CancellationToken.isCancellationToken = isCancellationToken;
      CancellationToken.None = Object.freeze({
          isCancellationRequested: false,
          onCancellationRequested: Event.None,
      });
      CancellationToken.Cancelled = Object.freeze({
          isCancellationRequested: true,
          onCancellationRequested: shortcutEvent,
      });
  })(exports.CancellationToken || (exports.CancellationToken = {}));
  class MutableToken {
      constructor() {
          this._isCancelled = false;
          this._emitter = null;
      }
      cancel() {
          if (!this._isCancelled) {
              this._isCancelled = true;
              if (this._emitter) {
                  this._emitter.fire(undefined);
                  this.dispose();
              }
          }
      }
      get isCancellationRequested() {
          return this._isCancelled;
      }
      get onCancellationRequested() {
          if (this._isCancelled) {
              return shortcutEvent;
          }
          if (!this._emitter) {
              this._emitter = new Emitter();
          }
          return this._emitter.event;
      }
      dispose() {
          if (this._emitter) {
              this._emitter.dispose();
              this._emitter = null;
          }
      }
  }
  class CancellationTokenSource {
      constructor(parent) {
          this._token = undefined;
          this._parentListener = undefined;
          this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);
      }
      get token() {
          if (!this._token) {
              // be lazy and create the token only when
              // actually needed
              this._token = new MutableToken();
          }
          return this._token;
      }
      cancel() {
          if (!this._token) {
              // save an object by returning the default
              // cancelled token when cancellation happens
              // before someone asks for the token
              this._token = exports.CancellationToken.Cancelled;
          }
          else if (this._token instanceof MutableToken) {
              // actually cancel
              this._token.cancel();
          }
      }
      dispose() {
          if (this._parentListener) {
              this._parentListener.dispose();
          }
          if (!this._token) {
              // ensure to initialize with an empty token if we had none
              this._token = exports.CancellationToken.None;
          }
          else if (this._token instanceof MutableToken) {
              // actually dispose
              this._token.dispose();
          }
      }
  }
  (function () {
      if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') {
          const dummyIdle = Object.freeze({
              didTimeout: true,
              timeRemaining() {
                  return 15;
              },
          });
      }
  })();

  class Decoder {
      constructor() {
          if (typeof TextDecoder !== 'undefined') {
              this.decoder = new TextDecoder();
          }
      }
      decode(buf) {
          const str = this.decoder ? this.decoder.decode(buf) : Buffer.from(buf).toString('utf-8');
          return str.charCodeAt(0) === 0xfeff ? str.slice(1) : str;
      }
  }

  class ExtendableError$1 extends Error {
      constructor(message) {
          super(message);
          this.name = this.constructor.name;
          if (typeof Error.captureStackTrace === 'function') {
              Error.captureStackTrace(this, this.constructor);
          }
          else {
              this.stack = new Error(message).stack;
          }
      }
  }
  class EntryNotFoundError extends ExtendableError$1 {
      constructor(url) {
          super(`Not found ${url.href}`);
          this.url = url;
      }
  }

  class AbstractResolverHost {
      getCanonicalUrl(_resolver, url) {
          return Promise.resolve(url);
      }
  }

  const TRAILING_SLASH_RX$1 = /\/?$/;
  class Resolver {
      constructor(host, options = {}) {
          this.host = host;
          this.decoder = new Decoder();
          this.extensions = Array.from(options.extensions || Resolver.defaultExtensions);
          this.packageMain = options.packageMain || ['main'];
      }
      async resolve(url, options = {}) {
          if (!(url instanceof URL)) {
              try {
                  url = new URL(url);
              }
              catch (err) {
                  throw new Error(`Invalid URL: ${url}: ${err.message}`);
              }
          }
          let token = options.token;
          if (!token) {
              const tokenSource = new CancellationTokenSource();
              token = tokenSource.token;
          }
          const optionsWithDefaults = {
              extensions: options.extensions || this.extensions,
              ignoreBrowserOverrides: typeof options.ignoreBrowserOverrides === 'undefined' ? false : options.ignoreBrowserOverrides,
              packageMain: options.packageMain || this.packageMain,
              token,
          };
          const canonicalUrlPromise = this.host.getCanonicalUrl(this, url, { token });
          // To figure out if the url should be resolved as a file or as a directory, we need to first canonicalize the url
          // if the host supports this and resolve the root url for the given asset.
          const [canonicalUrl, rootUrl] = await Promise.all([
              canonicalUrlPromise,
              this.host.getResolveRoot(this, url, { token }),
          ]);
          if (token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          const rootHref = rootUrl.href;
          const rootHrefWithoutTrailingSlash = rootHref.replace(TRAILING_SLASH_RX$1, '');
          const canonicalHref = canonicalUrl.href;
          if (!canonicalHref.startsWith(rootHrefWithoutTrailingSlash)) {
              throw new Error(`Unable to resolve a module whose path ${canonicalHref} is above the host's root ${rootHref}`);
          }
          const resolvedUrl = rootHrefWithoutTrailingSlash === canonicalHref || rootHref == canonicalHref
              ? await this.resolveAsDirectory(canonicalUrl, optionsWithDefaults)
              : await this.resolveAsFile(canonicalUrl, optionsWithDefaults);
          if (token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          return {
              ignored: resolvedUrl === false,
              resolvedUrl: resolvedUrl || undefined,
              rootUrl,
          };
      }
      /**
       * Resolve a reference treating it as a directory
       *
       * 1. If there is a `package.json` file and this has a `main` entry, use that
       * 2. Assume `index` if no main file is found in the `package.json` manifest
       *
       * The outcome of this process will then be resolved as if it were a file.
       */
      async resolveAsDirectory(url, options) {
          const [rootUrl, entries] = await Promise.all([
              this.host.getResolveRoot(this, url, { token: options.token }),
              this.host.listEntries(this, url, { token: options.token }),
          ]);
          if (options.token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          let mainPathname = 'index';
          // Step 1: Look for a package.json with an main field
          const packageJsonEntry = entries.find(entry => basename(entry.url.pathname) === 'package.json');
          if (packageJsonEntry) {
              const packageJsonContent = await this.host.readFileContent(this, packageJsonEntry.url, { token: options.token });
              if (options.token.isCancellationRequested) {
                  throw new CanceledError('Canceled');
              }
              const packageJson = parseBufferAsPackageJson(this.decoder, packageJsonContent, url.href);
              for (const packageMain of this.packageMain) {
                  const pathname = packageJson[packageMain];
                  if (typeof pathname === 'string') {
                      mainPathname = pathname;
                      break;
                  }
              }
          }
          const mainUrl = new URL(resolve(url.pathname, mainPathname), rootUrl);
          return this.resolveAsFile(mainUrl, options);
      }
      /**
       * Resolve a reference treating it as a file
       *
       * 1. List entries in the containing directory
       * 2. Look for an exact file match or a file match with one of the supplied extensions
       * 3. Look for a matching child directory and attempt to resolve that as a directory
       */
      async resolveAsFile(url, options) {
          if (url.pathname === '' || url.pathname === '/') {
              throw new TypeError(`Unable to resolve the root as a file: ${url.href}`);
          }
          const rootUrl = await this.host.getResolveRoot(this, url, { token: options.token });
          if (options.token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          // The parent package.json is only interesting if we are going to look at the `browser`
          // field and then consider browser mapping overrides in there.
          const parentPackageJson = this.packageMain.includes('browser') && !options.ignoreBrowserOverrides
              ? await this.readParentPackageJson(url, { token: options.token })
              : undefined;
          if (options.token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          const browserOverrides = new Map();
          if (parentPackageJson && typeof parentPackageJson.packageJson.browser === 'object') {
              const browserMap = parentPackageJson.packageJson.browser;
              const packageJsonDir = dirname(parentPackageJson.url.pathname);
              for (const entry in browserMap) {
                  const impliedUrl = new URL(resolve(packageJsonDir, entry), parentPackageJson.url);
                  const targetSpec = browserMap[entry];
                  const target = targetSpec === false ? false : new URL(resolve(packageJsonDir, targetSpec), parentPackageJson.url);
                  if (impliedUrl.href === url.href) {
                      if (target === false) {
                          return false;
                      }
                      // console.warn('REMAPPED %s to %s', url, target);
                      // We found an exact match so let's make sure we resolve the re-mapped file but
                      // also that we don't go through the browser overrides rodeo again.
                      return this.resolveAsFile(target, { ...options, ignoreBrowserOverrides: true });
                  }
                  browserOverrides.set(impliedUrl.href, target);
              }
          }
          const containingUrl = new URL(ensureTrailingSlash(dirname(url.pathname)), rootUrl);
          const filename = basename(url.pathname);
          const entries = await this.host.listEntries(this, containingUrl, { token: options.token });
          if (options.token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          const entryDirectoryMap = new Map();
          const entryFileMap = new Map();
          for (const entry of entries) {
              if (entry.url.href === url.href && entry.type == exports.ResolvedEntryKind.File) {
                  // Found an exact match
                  return entry.url;
              }
              if (entry.type === exports.ResolvedEntryKind.Directory) {
                  const childFilename = getFirstPathSegmentAfterPrefix(entry.url, containingUrl);
                  entryDirectoryMap.set(childFilename, entry);
              }
              else if (entry.type === exports.ResolvedEntryKind.File) {
                  const childFilename = basename(entry.url.pathname);
                  entryFileMap.set(childFilename, entry);
              }
          }
          // Look for browser overrides
          for (const ext of options.extensions) {
              const mapping = browserOverrides.get(`${url.href}${ext}`);
              if (mapping === false) {
                  // console.warn('REMAPPED %s to undefined', url);
                  return false;
              }
              else if (mapping) {
                  // console.warn('REMAPPED %s to %s', url, mapping);
                  return this.resolveAsFile(mapping, { ...options, ignoreBrowserOverrides: true });
              }
              const match = entryFileMap.get(`${filename}${ext}`);
              if (match) {
                  if (match.type !== exports.ResolvedEntryKind.File) {
                      continue;
                  }
                  return match.url;
              }
          }
          // First, attempt to find a matching file or directory
          const match = entryDirectoryMap.get(filename);
          if (match) {
              if (match.type !== exports.ResolvedEntryKind.Directory) {
                  throw new Error(`Invariant violation ${match.type} is unexpected`);
              }
              return this.resolveAsDirectory(match.url, options);
          }
          throw new EntryNotFoundError(url);
      }
      async readParentPackageJson(url, options = {}) {
          url = await this.host.getCanonicalUrl(this, url, { token: options.token });
          if (options.token && options.token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          const hostRootUrl = await this.host.getResolveRoot(this, url, { token: options.token });
          if (options.token && options.token.isCancellationRequested) {
              throw new CanceledError('Canceled');
          }
          const hostRootHref = ensureTrailingSlash(hostRootUrl.href);
          const containingDirUrl = new URL(ensureTrailingSlash(dirname(url.pathname)), url);
          const readPackageJsonOrRecurse = async (dir) => {
              if (!dir.href.startsWith(hostRootHref)) {
                  // Terminal condition for recursion
                  return undefined;
              }
              const entries = await this.host.listEntries(this, dir, { token: options.token });
              if (options.token && options.token.isCancellationRequested) {
                  throw new CanceledError('Canceled');
              }
              const packageJsonEntry = entries.find(entry => entry.type === exports.ResolvedEntryKind.File && entry.url.pathname.endsWith('/package.json'));
              if (packageJsonEntry) {
                  // Found! Let's try to parse
                  try {
                      const parentPackageJsonContent = await this.host.readFileContent(this, packageJsonEntry.url, {
                          token: options.token,
                      });
                      if (options.token && options.token.isCancellationRequested) {
                          throw new CanceledError('Canceled');
                      }
                      const packageJson = parseBufferAsPackageJson(this.decoder, parentPackageJsonContent, packageJsonEntry.url.href);
                      return { packageJson, url: packageJsonEntry.url };
                  }
                  catch (err) {
                      if (err instanceof CanceledError || (err && err.name === 'CanceledError')) {
                          throw err;
                      }
                      console.warn(`Error reading the parent package manifest for ${url.href} from ${packageJsonEntry.url.href}: ${err.message}`);
                  }
              }
              // Not found here, let's try one up
              const parentDir = new URL(ensureTrailingSlash(dirname(dir.pathname)), dir);
              // Skip infinite recursion
              if (parentDir.href === dir.href) {
                  return undefined;
              }
              return readPackageJsonOrRecurse(parentDir);
          };
          return readPackageJsonOrRecurse(containingDirUrl);
      }
  }
  Resolver.defaultExtensions = [
      '.js',
      '.jsx',
      '.es6',
      '.es',
      '.mjs',
      '.ts',
      '.tsx',
      '.json',
  ];
  Resolver.path = {
      basename,
      dirname,
      extname,
      resolve,
  };

  exports.AbstractResolverHost = AbstractResolverHost;
  exports.CanceledError = CanceledError;
  exports.CancellationTokenSource = CancellationTokenSource;
  exports.Decoder = Decoder;
  exports.EntryNotFoundError = EntryNotFoundError;
  exports.Resolver = Resolver;
  exports.isValidPackageJson = isValidPackageJson;
  exports.util = util;

  Object.defineProperty(exports, '__esModule', { value: true });

}));