Skip to content

Commit

Permalink
refactor ModuleRequest to an adapter pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
ef4 committed Nov 27, 2024
1 parent 614989c commit d4ee663
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 470 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export { default as WaitForTrees, OutputPaths } from './wait-for-trees';
export { compile as jsHandlebarsCompile } from './js-handlebars';
export { todo, unsupported, warn, debug, expectWarning, throwOnWarnings } from './messages';
export { Resolver } from './module-resolver';
export type { ModuleRequest, Resolution } from './module-request';
export { ModuleRequest, type Resolution, type RequestAdapter, type RequestAdapterCreate } from './module-request';
export type { Options as ResolverOptions } from './module-resolver-options';
export { ResolverLoader } from './resolver-loader';
export { virtualContent } from './virtual-content';
Expand Down
141 changes: 129 additions & 12 deletions packages/core/src/module-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,136 @@ export type Resolution<T = unknown, E = unknown> =
// fallback behaviors here.
| { type: 'not_found'; err: E };

export interface ModuleRequest<Res extends Resolution = Resolution> {
export type RequestAdapterCreate<Init, Res extends Resolution> = (
params: Init
) => { initialState: InitialRequestState; adapter: RequestAdapter<Res> } | undefined;

export interface RequestAdapter<Res extends Resolution> {
readonly debugType: string;
resolve(request: ModuleRequest<Res>): Promise<Res>;
}

export interface InitialRequestState {
readonly specifier: string;
readonly fromFile: string;
readonly isVirtual: boolean;
readonly meta: Record<string, unknown> | undefined;
readonly debugType: string;
readonly isNotFound: boolean;
readonly resolvedTo: Res | undefined;
alias(newSpecifier: string): this;
rehome(newFromFile: string): this;
virtualize(virtualFilename: string): this;
withMeta(meta: Record<string, any> | undefined): this;
notFound(): this;
defaultResolve(): Promise<Res>;
resolveTo(resolution: Res): this;
}

export class ModuleRequest<Res extends Resolution = Resolution> implements ModuleRequest<Res> {
static create<Init, Res extends Resolution>(
createAdapter: RequestAdapterCreate<Init, Res>,
params: Init
): ModuleRequest<Res> | undefined {
let result = createAdapter(params);
if (result) {
return new ModuleRequest(result.adapter, result.initialState);
}
}

#adapter: RequestAdapter<Res>;
#specifier: string;
#fromFile: string;
#isVirtual: boolean;
#meta: Record<string, unknown> | undefined;
#isNotFound: boolean;
#resolvedTo: Res | undefined;

private constructor(
adapter: RequestAdapter<Res>,
initialize: InitialRequestState,
propagate?: { isVirtual: boolean; isNotFound: boolean; resolvedTo: Res | undefined }
) {
this.#adapter = adapter;
this.#specifier = initialize.specifier;
this.#fromFile = initialize.fromFile;
this.#meta = initialize.meta;
this.#isVirtual = propagate?.isVirtual ?? false;
this.#isNotFound = propagate?.isNotFound ?? false;
this.#resolvedTo = propagate?.resolvedTo;
}

get specifier(): string {
return this.#specifier;
}

get fromFile(): string {
return this.#fromFile;
}

get isVirtual(): boolean {
return this.#isVirtual;
}

get debugType(): string {
return this.#adapter.debugType;
}

get meta(): Record<string, unknown> | undefined {
return this.#meta;
}

get isNotFound(): boolean {
return this.#isNotFound;
}

get resolvedTo(): Res | undefined {
return this.#resolvedTo;
}

alias(newSpecifier: string): this {
if (this.#specifier === newSpecifier) {
return this;
}
let result = this.#clone();
result.#specifier = newSpecifier;
result.#isNotFound = false;
result.#resolvedTo = undefined;
return result;
}

rehome(newFromFile: string): this {
if (this.#fromFile === newFromFile) {
return this;
}
let result = this.#clone();
result.#fromFile = newFromFile;
result.#isNotFound = false;
result.#resolvedTo = undefined;
return result;
}

virtualize(virtualFileName: string): this {
let result = this.#clone();
result.#specifier = virtualFileName;
result.#isVirtual = true;
result.#isNotFound = false;
result.#resolvedTo = undefined;
return result;
}

withMeta(meta: Record<string, any> | undefined): this {
let result = this.#clone();
result.#meta = meta;
return result;
}

notFound(): this {
let result = this.#clone();
result.#isNotFound = true;
return result;
}

resolveTo(res: Res): this {
let result = this.#clone();
result.#resolvedTo = res;
return result;
}

defaultResolve(): Promise<Res> {
return this.#adapter.resolve(this);
}

#clone(): this {
return new ModuleRequest(this.#adapter, this, this) as this;
}
}
84 changes: 20 additions & 64 deletions packages/core/src/node-resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,31 @@ import { explicitRelative } from '@embroider/shared-internals';
import assertNever from 'assert-never';

// these would be circular, but they're type-only so it's fine
import type { ModuleRequest, Resolution } from './module-request';
import { ModuleRequest, type RequestAdapter, type RequestAdapterCreate, type Resolution } from './module-request';
import type { Resolver } from './module-resolver';

export class NodeModuleRequest implements ModuleRequest {
constructor(
private resolver: Resolver,
readonly specifier: string,
readonly fromFile: string,
readonly isVirtual: boolean,
readonly meta: Record<string, any> | undefined,
readonly isNotFound: boolean,
readonly resolvedTo: Resolution<NodeResolution, Error> | undefined
) {}
export class NodeRequestAdapter implements RequestAdapter<Resolution<NodeResolution, Error>> {
static create: RequestAdapterCreate<
{ resolver: Resolver; specifier: string; fromFile: string },
Resolution<NodeResolution, Error>
> = ({ resolver, specifier, fromFile }) => {
return {
initialState: {
specifier,
fromFile,
meta: undefined,
},
adapter: new NodeRequestAdapter(resolver),
};
};

private constructor(private resolver: Resolver) {}

get debugType() {
return 'node';
}

alias(specifier: string): this {
return new NodeModuleRequest(this.resolver, specifier, this.fromFile, false, this.meta, false, undefined) as this;
}
rehome(fromFile: string): this {
if (this.fromFile === fromFile) {
return this;
} else {
return new NodeModuleRequest(this.resolver, this.specifier, fromFile, false, this.meta, false, undefined) as this;
}
}
virtualize(filename: string): this {
return new NodeModuleRequest(this.resolver, filename, this.fromFile, true, this.meta, false, undefined) as this;
}
withMeta(meta: Record<string, any> | undefined): this {
return new NodeModuleRequest(
this.resolver,
this.specifier,
this.fromFile,
this.isVirtual,
meta,
this.isNotFound,
this.resolvedTo
) as this;
}
notFound(): this {
return new NodeModuleRequest(
this.resolver,
this.specifier,
this.fromFile,
this.isVirtual,
this.meta,
true,
undefined
) as this;
}

resolveTo(resolution: Resolution<NodeResolution, Error>): this {
return new NodeModuleRequest(
this.resolver,
this.specifier,
this.fromFile,
this.isVirtual,
this.meta,
this.isNotFound,
resolution
) as this;
}

async defaultResolve(): Promise<Resolution<NodeResolution, Error>> {
const request = this;
async resolve(request: ModuleRequest<Resolution<NodeResolution, Error>>): Promise<Resolution<NodeResolution, Error>> {
if (request.isVirtual) {
return {
type: 'found',
Expand Down Expand Up @@ -157,9 +114,8 @@ export async function nodeResolve(
specifier: string,
fromFile: string
): Promise<NodeResolution | NodeResolutionError> {
let resolution = await resolver.resolve(
new NodeModuleRequest(resolver, specifier, fromFile, false, undefined, false, undefined)
);
let request = ModuleRequest.create(NodeRequestAdapter.create, { resolver, fromFile, specifier });
let resolution = await resolver.resolve(request!);
switch (resolution.type) {
case 'not_found':
return resolution;
Expand Down
Loading

0 comments on commit d4ee663

Please sign in to comment.