basant307/AI_Governance_Project
048
1import * as url from "node:url";2import path from "node:path";3 4export type ResolverContext = {5 parentModule: "config-loader" | "fixer-formatter" | "linter-formatter" | "textlint" | "textlint-legacy";6};7type ResolverSkipResult = undefined;8/**9 * Resolve Hook10 */11export type ResolveHook = (12 specifier: string,13 context: ResolverContext14) =>15 | {16 url: string | undefined;17 }18 | ResolverSkipResult;19/**20 * dynamic import() hook21 */22export type ImportHook = (23 specifier: string,24 context: ResolverContext25) => Promise<26 | {27 exports: Record<string, unknown>;28 }29 | ResolverSkipResult30>;31 32const resolveHooks: ResolveHook[] = [];33const importHooks: ImportHook[] = [];34/**35 * Register Resolver Hook36 * Hook can return resolved URL37 * if hooks pass through, it should return `undefined` instead of object38 * @param hook39 */40export const registerResolveHook = (hook: ResolveHook) => {41 resolveHooks.push(hook);42};43 44/**45 * Try to resolve package name46 * if `packageName` is found, return resolved absolute path.47 * if `packageName` is not found, return `undefined`48 * @param packageName49 * @param context50 */51export const tryResolve = (packageName: string, context: ResolverContext): string | undefined => {52 try {53 for (const hook of resolveHooks) {54 const result = hook(packageName, context);55 // Skip if hook return undefined from hook56 if (!result) {57 continue;58 }59 if (result?.url) {60 return result.url;61 }62 }63 64 // TODO: consider using import.meta.resolve(packageName) for future ESM support65 // import.meta.resolve is supported in Node.js 20.6.0+66 return require.resolve(packageName);67 } catch {68 return undefined;69 }70};71 72/**73 * Register Import Hook74 * @param hook75 */76export const registerImportHook = (hook: ImportHook) => {77 importHooks.push(hook);78};79 80// Windows's path require to convert file://81// https://github.com/secretlint/secretlint/issues/20582const convertToFileUrl = (filePath: string) => {83 if (filePath.startsWith("file://")) {84 return filePath;85 }86 return url.pathToFileURL(filePath).href;87};88/**89 * dynamic import() with hooks90 * @param specifier file path or package name91 * @param context92 */93export const dynamicImport = async (94 specifier: string,95 context: ResolverContext96): Promise<{97 exports: Record<string, unknown> | undefined;98}> => {99 for (const hook of importHooks) {100 const result = await hook(specifier, context);101 if (result) {102 return result;103 }104 }105 // if the `specifier` is not absolute path, it should be package name106 if (!path.isAbsolute(specifier)) {107 return {108 exports: await import(specifier),109 };110 }111 return {112 exports: await import(convertToFileUrl(specifier)),113 };114};115 116/**117 * Clear all hooks118 */119export const clearHooks = () => {120 resolveHooks.length = 0;121 importHooks.length = 0;122};123 