@velcro/resolver
Version:
Resolve references to absolute urls using the node module resolution algorithm using an generic host interface
356 lines • 13.7 kB
TypeScript
/// <reference types="node" />
import { CancellationToken, Decoder, MapSet, PackageJson, Uri, PackageMainField, Thenable } from "@velcro/common";
declare const version = "__VERSION__";
type MaybeThenable<T> = T | Thenable<T>;
interface ResolverStrategy {
/**
* Produce a url given the components of a bare module specifier.
*
* @param ctx A `ResolverContext` that should be used for making calls to other strategy methods
* @param name The name of a bare module
* @param spec The optional `@version` of a bare module specifier
* @param path The optional path at the end of the bare module specifier
*/
getUrlForBareModule?(ctx: ResolverContext, name: string, spec: string, path: string): MaybeThenable<ResolverStrategy.BareModuleResult>;
/**
* Determine the canonical uri for a given uri.
*
* For example, you might consider symlink targets their canonicalized path or you might
* consider the canonicalized path of https://unpkg.com/react to be
* https://unpkg.com/react@16.13.1/index.js.
*
* Dealing only in canonical uris means that anything produced from those can be cached.
*
* @param ctx A `ResolverContext` that should be used for making calls to other strategy methods
* @param uri The uri to canonicalize
*/
getCanonicalUrl(ctx: ResolverContext, uri: Uri): MaybeThenable<ResolverStrategy.CanonicalizeResult>;
/**
* Get the logical resolve root for a given uri.
*
* For example, a filesystem-based strategy might consider the root to be `file:///`. Or,
* if it was scoped to /home/filearts, the root might be `file:///home/filearts/`.
*
* Any uri that is not a 'child' of the resolve root should be considered out of scope for a given
* strategy.
*
* @param ctx A `ResolverContext` that should be used for making calls to other strategy methods
* @param uri The uri for which the logical resolve root uri should be found
*/
getResolveRoot(ctx: ResolverContext, uri: Uri): MaybeThenable<ResolverStrategy.ResolveRootResult>;
/**
* Get the settings for a given uri
*
* This indirection allows resolver strategies to have per-strategy or even per-uri settings.
*
* @param ctx A `ResolverContext` that should be used for making calls to other strategy methods
* @param uri The uri for which to load settings
*/
getSettings(ctx: ResolverContext, uri: Uri): MaybeThenable<ResolverStrategy.SettingsResult>;
/**
* Produce a list of resolved entries that are direct children of the given uri.
*
* This is the moral equivalent to something like non-recursive `fs.readdir()`. It is only
* designed to show files and folders (for now).
*
* @param ctx A `ResolverContext` that should be used for making calls to other strategy methods
* @param uri The uri at which to list entries
*/
listEntries(ctx: ResolverContext, uri: Uri): MaybeThenable<ResolverStrategy.ListEntriesResult>;
/**
* Read the content at the uri as an `ArrayBuffer`
*
* ArrayBuffers are the lowest-common-denominator across the web and node and can easily be
* decoded with standard web apis like `StringDecoder`. In Node.js, `Buffer` objects are also
* `ArrayBuffer`s, allowing the tooling to be built on that primitive.
*
* This is helpful for the understanding that not all uris are expected to produce meaningful
* text representations.
*
* @param ctx A `ResolverContext` that should be used for making calls to other strategy methods
* @param uri The uri at which to read the content
*/
readFileContent(ctx: ResolverContext, uri: Uri): MaybeThenable<ResolverStrategy.ReadFileContentResult>;
}
interface ResolverStrategyWithRoot extends ResolverStrategy {
/**
* The root uri of the strategy.
*
* A common parent to all uris that this strategy can handle.
*
* This may sometimes be the same value as would be returned by `getResolveRoot` but will
* sometimes be a parent of that. Take, for example Unpkg; there, we may want to express
* that a strategy should 'own' all uris under https://unpkg.com/ even though the resolve
* root for https://unpkg.com/react@16.13.1/index.js will actually be
* https://unpkg.com/react@16.13.1/.
*
* Notably, the `CompoundResolverStrategy` requires all child strategies implement the
* `ResolverStrategyWithRoot` interface because it dispatches operations on different
* uris according to each strategy's `rootUri`.
*/
rootUri: Uri;
}
declare namespace ResolverStrategy {
enum EntryKind {
File = "file",
Directory = "directory"
}
interface Entry<TKind extends EntryKind = EntryKind> {
uri: Uri;
type: TKind;
}
type BareModuleResult = {
found: boolean;
uri: Uri | null;
};
interface CanonicalizeResult {
uri: Uri;
}
interface ResolveRootResult {
uri: Uri;
}
interface SettingsResult {
settings: Resolver.Settings;
}
interface ListEntriesResult {
entries: Entry[];
}
interface ReadFileContentResult {
content: ArrayBuffer;
}
}
declare abstract class AbstractResolverStrategy implements ResolverStrategy {
getCanonicalUrl(_ctx: ResolverContext, uri: Uri): ReturnType<ResolverStrategy["getCanonicalUrl"]>;
getSettings(ctx: ResolverContext, _uri: Uri): ReturnType<ResolverStrategy["getSettings"]>;
/**
* Create a new ResolverStrategy having one or more methods overridden.
*
* You might use this if you want to override specific behaviour of another strategy without
* wanting to re-implement the whole strategy.
*
* If you need to invoke an overridden method, the overridden strategy will be available
* on `this.parent`.
*
* @param overrides A map of ResolverStrategy methods that you would like to override
*/
withOverrides(overrides: {
[TMethodName in keyof ResolverStrategy]?: ResolverStrategy[TMethodName];
}): ResolverStrategy;
abstract getResolveRoot(ctx: ResolverContext, uri: Uri): ReturnType<ResolverStrategy["getResolveRoot"]>;
abstract listEntries(ctx: ResolverContext, uri: Uri): ReturnType<ResolverStrategy["listEntries"]>;
abstract readFileContent(ctx: ResolverContext, uri: Uri): ReturnType<ResolverStrategy["readFileContent"]>;
}
declare abstract class AbstractResolverStrategyWithRoot extends AbstractResolverStrategy implements ResolverStrategyWithRoot {
readonly rootUri: Uri;
constructor(rootUri: Uri);
}
declare class Resolver {
private disposed;
readonly rootCtx: ResolverContext;
private readonly settings;
private readonly strategy;
private readonly tokenSource;
constructor(strategy: ResolverStrategy, settings: Resolver.Settings);
decode(buf: BufferSource | string): string;
dispose(): void;
getCanonicalUrl(uri: string | Uri): Promise<ResolverStrategy.CanonicalizeResult & {
visited: ResolverContext.Visit[];
}> | (ResolverStrategy.CanonicalizeResult & {
visited: ResolverContext.Visit[];
});
getResolveRoot(uri: string | Uri): Promise<ResolverStrategy.ResolveRootResult & {
visited: ResolverContext.Visit[];
}> | (ResolverStrategy.ResolveRootResult & {
visited: ResolverContext.Visit[];
});
getSettings(uri: string | Uri): Promise<ResolverStrategy.SettingsResult & {
visited: ResolverContext.Visit[];
}> | (ResolverStrategy.SettingsResult & {
visited: ResolverContext.Visit[];
});
getUrlForBareModule(name: string, spec: string, path: string): Promise<ResolverStrategy.BareModuleResult & {
visited: ResolverContext.Visit[];
}> | (ResolverStrategy.BareModuleResult & {
visited: ResolverContext.Visit[];
});
invalidate(uri: string | Uri): boolean;
listEntries(uri: Uri): Promise<ResolverStrategy.ListEntriesResult & {
visited: ResolverContext.Visit[];
}> | (ResolverStrategy.ListEntriesResult & {
visited: ResolverContext.Visit[];
});
readFileContent(uri: Uri): Promise<ResolverStrategy.ReadFileContentResult & {
visited: ResolverContext.Visit[];
}> | (ResolverStrategy.ReadFileContentResult & {
visited: ResolverContext.Visit[];
});
readParentPackageJson(uri: Uri): Promise<({
found: true;
packageJson: import("@velcro/common").PackageJson;
uri: Uri;
visitedDirs: Uri[];
} & {
visited: ResolverContext.Visit[];
}) | ({
found: false;
packageJson: null;
uri: null;
} & {
visited: ResolverContext.Visit[];
})> | ({
found: true;
packageJson: import("@velcro/common").PackageJson;
uri: Uri;
visitedDirs: Uri[];
} & {
visited: ResolverContext.Visit[];
}) | ({
found: false;
packageJson: null;
uri: null;
} & {
visited: ResolverContext.Visit[];
});
resolve(spec: Uri): ReturnType<ResolverContext["resolve"]>;
resolve(spec: string, fromUri: Uri): ReturnType<ResolverContext["resolve"]>;
}
declare namespace Resolver {
interface Settings {
debug?: boolean;
extensions: string[];
packageMain: PackageMainField[];
}
}
type InvalidationRecord = {
cacheKey: string;
operationCache: Map<string, unknown>;
};
type ResolveResult = {
found: false;
uri: null;
parentPackageJson?: {
packageJson: PackageJson;
uri: Uri;
};
} | {
found: true;
uri: null;
parentPackageJson?: {
packageJson: PackageJson;
uri: Uri;
};
rootUri: Uri;
} | {
found: true;
uri: Uri;
parentPackageJson?: {
packageJson: PackageJson;
uri: Uri;
};
rootUri: Uri;
};
type ReadParentPackageJsonResult = {
found: true;
packageJson: PackageJson;
uri: Uri;
visitedDirs: Uri[];
} | {
found: false;
packageJson: null;
uri: null;
};
type StrategyResult<T> = Promise<T & {
visited: ResolverContext.Visit[];
}> | (T & {
visited: ResolverContext.Visit[];
});
declare class Visits {
readonly uri: {
toString(): string;
};
private readonly parent?;
private readonly visits;
constructor(uri: {
toString(): string;
}, parent?: Visits);
child(uri: {
toString(): string;
}): Visits;
push(visit: ResolverContext.Visit): void;
toArray(): ResolverContext.Visit[];
}
declare class ResolverContext {
static create(resolver: Resolver, strategy: ResolverStrategy, settings: Resolver.Settings, token: CancellationToken, options?: {
debug?: boolean;
}): ResolverContext;
private readonly cache;
private readonly cacheInvalidations;
private readonly debugMode;
readonly decoder: ResolverContext.Options["decoder"];
private readonly mapResultWithVisits;
readonly path: ReadonlyArray<string>;
private readonly resolver;
readonly settings: Readonly<ResolverContext.Options["settings"]>;
private readonly strategy;
private readonly tokenSource;
private readonly visits;
protected constructor(options: ResolverContext.Options);
get token(): CancellationToken;
get visited(): ResolverContext.Visit[];
dispose(): void;
forOperation(operationName: string, uri: {
toString(): string;
}, options?: {
resetPath?: boolean;
resetVisits?: boolean;
}): ResolverContext;
getCanonicalUrl(uri: Uri): StrategyResult<ResolverStrategy.CanonicalizeResult>;
getResolveRoot(uri: Uri): StrategyResult<ResolverStrategy.ResolveRootResult>;
getSettings(uri: Uri): StrategyResult<ResolverStrategy.SettingsResult>;
getUrlForBareModule(name: string, spec: string, path: string): StrategyResult<ResolverStrategy.BareModuleResult>;
invalidate(uri: Uri): boolean;
listEntries(uri: Uri): StrategyResult<ResolverStrategy.ListEntriesResult>;
readFileContent(uri: Uri): StrategyResult<ResolverStrategy.ReadFileContentResult>;
readParentPackageJson(uri: Uri): StrategyResult<ReadParentPackageJsonResult>;
recordVisit(uri: Uri, type?: ResolverContext.VisitKind): void;
resolve(spec: string, fromUri: Uri): StrategyResult<ResolveResult>;
resolveUri(uri: Uri): StrategyResult<ResolveResult>;
runInChildContext<T>(operationName: string, uri: {
toString(): string;
}, contextFn: (ctx: ResolverContext) => T): T;
runInIsolatedContext<T>(operationName: string, uri: {
toString(): string;
}, contextFn: (ctx: ResolverContext) => T): T;
private runInContext;
private createStoreResultFn;
private runWithCache;
private _wrapError;
debug(...args: Parameters<Console["warn"]>): void;
}
declare namespace ResolverContext {
interface Options {
cache: Map<string, Map<string, unknown>>;
cacheInvalidations: MapSet<string, InvalidationRecord>;
debug: boolean;
decoder: Decoder;
path: string[];
resolver: Resolver;
settings: Resolver.Settings;
strategy: ResolverStrategy;
token: CancellationToken;
visits: Visits;
}
enum VisitKind {
Directory = "Directory",
File = "File"
}
type Visit = {
type: VisitKind.Directory;
uri: Uri;
} | {
type: VisitKind.File;
uri: Uri;
};
}
export { version, ResolverContext, Resolver, ResolverStrategy, ResolverStrategyWithRoot, AbstractResolverStrategy, AbstractResolverStrategyWithRoot };
//# sourceMappingURL=index.d.ts.map