Skip to content

Commit

Permalink
feat: show more error messages while evaluation (#47)
Browse files Browse the repository at this point in the history
* chore: fix example config

* feat: show more error messages while evaluation
  • Loading branch information
tkamenoko authored Oct 9, 2024
1 parent ccd6c88 commit 3635edd
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 106 deletions.
2 changes: 1 addition & 1 deletion examples/vite-react-ts/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"strict": true,
"types": ["vite/client", "node"]
},
"include": ["./src/**/*"]
"include": ["./src/**/*", "vite.config.ts"]
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Module, ModuleLinker } from "node:vm";
import { loadModule } from "../loadModule";
import { loadBuiltinModule } from "../loaders/loadBuiltinModule";
import { loadNodeModule } from "../loaders/loadNodeModule";
import type { TransformContext } from "../types";
Expand Down Expand Up @@ -29,43 +30,31 @@ export function createLinker({
const basePath =
referencingPath === "*target*" ? modulePath : referencingPath;

const loadedModule =
(await loadBuiltinModule({
modulesCache,
referencingModule,
specifier,
})) ??
(await loadNodeModule({ modulesCache, referencingModule, specifier })) ??
(await loadViteModule({
ctx,
modulesCache,
referencingModule,
specifier,
importMeta,
})) ??
(await loadAbsoluteModule({
ctx,
modulesCache,
referencingModule,
specifier,
importMeta,
})) ??
(await loadRelativeModule({
basePath,
ctx,
modulesCache,
referencingModule,
specifier,
importMeta,
}));
const loadModuleArgs = {
basePath,
ctx,
modulesCache,
referencingModule,
specifier,
importMeta,
};

if (loadedModule) {
return loadedModule;
const { error, module } = await loadModule(
[
loadBuiltinModule,
loadNodeModule,
loadViteModule,
loadAbsoluteModule,
loadRelativeModule,
],
loadModuleArgs,
);

if (module) {
return module;
}

throw new Error(
`Failed to load "${specifier}" from "${referencingModule.identifier}"`,
);
throw error;
};
return { linker };
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import vm, { type Module } from "node:vm";
import { isVirtualCssModuleId } from "#@/vite/virtualCssModuleId";
import { isVirtualGlobalStyleId } from "#@/vite/virtualGlobalStyleId";
import { buildInitializeImportMeta } from "../../initializeImportMeta";
import type { TransformContext } from "../../types";
import type { LoadModuleReturn, TransformContext } from "../../types";

type LoadAbsoluteModuleArgs = {
modulesCache: Map<string, Module>;
Expand All @@ -18,7 +18,7 @@ export async function loadAbsoluteModule({
referencingModule,
ctx,
importMeta,
}: LoadAbsoluteModuleArgs): Promise<Module | null> {
}: LoadAbsoluteModuleArgs): Promise<LoadModuleReturn> {
// casablanca modules must be resolved by casablanca plugin.
const skipSelf =
!isVirtualCssModuleId(specifier) && !isVirtualGlobalStyleId(specifier);
Expand All @@ -27,20 +27,31 @@ export async function loadAbsoluteModule({
skipSelf,
});
if (!resolvedAbsolutePath) {
return null;
return {
error: new Error(`Failed to resolve "${specifier}"`),
module: null,
};
}
const cached = modulesCache.get(resolvedAbsolutePath.id);
if (cached) {
return cached;
return { error: null, module: cached };
}
const loaded = await ctx
.load({
id: resolvedAbsolutePath.id,
resolveDependencies: true,
})
.catch(() => null);
if (!loaded?.code) {
return null;
.then((r) =>
typeof r.code === "string"
? { code: r.code, error: null }
: {
code: null,
error: new Error(`Failed to load "${resolvedAbsolutePath.id}"`),
},
)
.catch((e) => ({ code: null, error: e }));
if (typeof loaded.code !== "string") {
return { error: loaded.error, module: null };
}
const m = new vm.SourceTextModule(loaded.code, {
context: referencingModule.context,
Expand All @@ -51,5 +62,5 @@ export async function loadAbsoluteModule({
}),
});
modulesCache.set(resolvedAbsolutePath.id, m);
return m;
return { error: null, module: m };
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import vm, { type Module } from "node:vm";
import { buildInitializeImportMeta } from "../../initializeImportMeta";
import type { TransformContext } from "../../types";
import type { LoadModuleReturn, TransformContext } from "../../types";

type LoadRelativeModuleArgs = {
modulesCache: Map<string, Module>;
Expand All @@ -18,24 +18,35 @@ export async function loadRelativeModule({
ctx,
basePath,
importMeta,
}: LoadRelativeModuleArgs): Promise<Module | null> {
}: LoadRelativeModuleArgs): Promise<LoadModuleReturn> {
// resolve id as relative path
const resolvedRelativePath = await ctx.resolve(specifier, basePath);
if (!resolvedRelativePath) {
return null;
return {
error: new Error(`Failed to resolve "${specifier}"`),
module: null,
};
}
const cached = modulesCache.get(resolvedRelativePath.id);
if (cached) {
return cached;
return { error: null, module: cached };
}
const loaded = await ctx
.load({
id: resolvedRelativePath.id,
resolveDependencies: true,
})
.catch(() => null);
if (typeof loaded?.code !== "string") {
return null;
.then((r) =>
typeof r.code === "string"
? { code: r.code, error: null }
: {
code: null,
error: new Error(`Failed to load "${resolvedRelativePath.id}"`),
},
)
.catch((e) => ({ code: null, error: e }));
if (typeof loaded.code !== "string") {
return { error: loaded.error, module: null };
}

const m = new vm.SourceTextModule(loaded.code, {
Expand All @@ -47,5 +58,5 @@ export async function loadRelativeModule({
}),
});
modulesCache.set(resolvedRelativePath.id, m);
return m;
return { error: null, module: m };
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import vm, { type Module } from "node:vm";
import { buildInitializeImportMeta } from "../../initializeImportMeta";
import type { TransformContext } from "../../types";
import type { LoadModuleReturn, TransformContext } from "../../types";

type LoadViteModuleArgs = {
modulesCache: Map<string, Module>;
Expand All @@ -16,20 +16,28 @@ export async function loadViteModule({
referencingModule,
ctx,
importMeta,
}: LoadViteModuleArgs): Promise<Module | null> {
}: LoadViteModuleArgs): Promise<LoadModuleReturn> {
const cached = modulesCache.get(specifier);
if (cached) {
return cached;
return { error: null, module: cached };
}
// resolve id as vite-specific module
const loaded = await ctx
.load({
id: specifier,
resolveDependencies: true,
})
.catch(() => null);
if (!loaded?.code) {
return null;
.then((r) =>
r.code
? { code: r.code, error: null }
: {
code: null,
error: new Error(`Failed to load "${specifier}"`),
},
)
.catch((e) => ({ code: null, error: e }));
if (!loaded.code) {
return { error: loaded.error, module: null };
}

const m = new vm.SourceTextModule(loaded.code, {
Expand All @@ -41,5 +49,5 @@ export async function loadViteModule({
}),
});
modulesCache.set(specifier, m);
return m;
return { error: null, module: m };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { LoadModuleReturn } from "./types";

export async function loadModule<T extends { specifier: string }>(
loaders: ((a: T) => Promise<LoadModuleReturn>)[],
args: T,
): Promise<LoadModuleReturn> {
const tailLoader = loaders.pop();
if (!tailLoader) {
throw new Error("Loaders are empty.");
}
const errors: Error[] = [];
for (const loader of loaders) {
const result = await loader(args);
if (result.module) {
return result;
}
errors.push(result.error);
}
const result = await tailLoader(args);
if (result.module) {
return result;
}
errors.push(result.error);
const errorMsg = `\
Failed to load "${args.specifier}".
Captured errors while loading:
------
${errors.map((e) => `${e}`).join("\n------\n")}
------
`;
return {
error: new Error(errorMsg),
module: null,
};
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isBuiltin } from "node:module";
import vm, { type Module } from "node:vm";
import type { LoadModuleReturn } from "../types";

export async function loadBuiltinModule({
modulesCache,
Expand All @@ -9,20 +10,26 @@ export async function loadBuiltinModule({
modulesCache: Map<string, Module>;
specifier: string;
referencingModule: Module;
}): Promise<Module | null> {
}): Promise<LoadModuleReturn> {
const cached = modulesCache.get(specifier);
if (cached) {
return cached;
return { error: null, module: cached };
}
if (!isBuiltin(specifier)) {
return null;
return {
error: new Error(`"${specifier}" is not a builtin module.`),
module: null,
};
}
const normalizedSpecifier = specifier.startsWith("node:")
? specifier
: `node:${specifier}`;
const imported = await import(normalizedSpecifier).catch(() => null);
if (!imported) {
return null;
return {
error: new Error(`Module "${specifier}" was not found in builtins.`),
module: null,
};
}
const exportNames = Object.keys(imported);
const m = new vm.SyntheticModule(
Expand All @@ -41,5 +48,5 @@ export async function loadBuiltinModule({
modulesCache.set(normalizedSpecifier, m);
isBuiltin(shortSpecifier) ? modulesCache.set(shortSpecifier, m) : void 0;

return m;
return { error: null, module: m };
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import vm, { type Module } from "node:vm";
import type { LoadModuleReturn } from "../types";

export async function loadNodeModule({
modulesCache,
Expand All @@ -8,14 +9,17 @@ export async function loadNodeModule({
modulesCache: Map<string, Module>;
specifier: string;
referencingModule: Module;
}): Promise<Module | null> {
}): Promise<LoadModuleReturn> {
const cached = modulesCache.get(specifier);
if (cached) {
return cached;
return { error: null, module: cached };
}
const imported = await import(specifier).catch(() => null);
if (!imported) {
return null;
return {
error: new Error(`Module "${specifier}" was not found in node_modules.`),
module: null,
};
}
const exportNames = Object.keys(imported);
const m = new vm.SyntheticModule(
Expand All @@ -31,5 +35,5 @@ export async function loadNodeModule({
},
);
modulesCache.set(specifier, m);
return m;
return { error: null, module: m };
}
Loading

0 comments on commit 3635edd

Please sign in to comment.