@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
JavaScript
(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 });
}));