import * as path from "path"; import { assert, is } from "tsafe"; import * as fs from "fs"; import type { resolveNodeModuleToDenoModuleFactory } from "./resolveNodeModuleToDenoModule"; import type { getInstalledVersionPackageJsonFactory } from "./getInstalledVersionPackageJson"; import { ParsedImportExportStatement } from "./types/ParsedImportExportStatement"; import { consumeExecutableReplacerFactory } from "./replacer"; import { getProjectRoot } from "../tools/getProjectRoot"; /** * examples: * import { Evt } from "evt" -> import { Evt } from "https://deno.land/x/evt@.../mod.ts" * import { id } "evt/dist/tools/typeSafety" -> "https://deno.land/x/evt@.../deno_dist/tools/typeSafety/index.ts" * import * as interfaces from "./interfaces" -> import * as interfaces from "./interfaces/index.ts" */ export function denoifyImportExportStatementFactory( params: { userProvidedReplacerPath: string | undefined; getDestDirPath(params: { dirPath: string }): string; } & ReturnType & ReturnType ) { const { userProvidedReplacerPath, getDestDirPath, resolveNodeModuleToDenoModule, getInstalledVersionPackageJson } = params; const { consumeExecutableReplacer: consumeExecutableBuiltinsReplacer } = consumeExecutableReplacerFactory({ "executableFilePath": path.join( getProjectRoot(), fs.existsSync(path.join(getProjectRoot(), "dist")) ? "dist" : "", "bin", "replacer", "index.js" ) }); const { consumeExecutableReplacer: consumeExecutableUserProvidedReplacer } = userProvidedReplacerPath === undefined ? { "consumeExecutableReplacer": undefined } : consumeExecutableReplacerFactory({ "executableFilePath": userProvidedReplacerPath }); async function denoifyImportExportStatement(params: { /** Path of the file in which the import was */ dirPath: string; /** e.g: * import { Evt } from "evt" * import { id } "evt/dist/tools/typeSafety" * import * as interfaces from "./interfaces" * ... */ importExportStatement: string; }): Promise { const { dirPath, importExportStatement } = params; const parsedImportExportStatement = ParsedImportExportStatement.parse(params.importExportStatement); if (parsedImportExportStatement.parsedArgument.type === "URL") { //There is no reason to have url import in the source //file as url import are not supported by node it does not //hurt to leave the import unchanged. See #7 return importExportStatement; } const stringify = (argument: string) => ParsedImportExportStatement.stringify({ ...parsedImportExportStatement, "parsedArgument": ParsedImportExportStatement.ParsedArgument.parse(argument) }); if (parsedImportExportStatement.parsedArgument.type === "PROJECT LOCAL FILE") { const { relativePath } = parsedImportExportStatement.parsedArgument; if (/\.json$/i.test(relativePath)) { return stringify(relativePath); } for (const ext of ["ts", "tsx"]) { if (fs.existsSync(path.join(dirPath, `${relativePath}.${ext}`))) { return stringify(`${relativePath}.${ext}`); } if (relativePath.endsWith(`.${ext}`) && fs.existsSync(path.join(dirPath, relativePath))) { return stringify(relativePath); } } // relative esm module import const esmReg = /\.m?js$/; if (esmReg.test(path.extname(relativePath)) && fs.existsSync(path.join(dirPath, relativePath.replace(esmReg, ".ts")))) { return stringify(relativePath.replace(esmReg, ".ts")); } const out = path.posix.join(relativePath, "index.ts"); return stringify(out.startsWith(".") ? out : `./${out}`); } //NOTE: Should be inferable... assert(is>(parsedImportExportStatement)); const { nodeModuleName, specificImportPath } = parsedImportExportStatement.parsedArgument; const { version } = await getInstalledVersionPackageJson({ nodeModuleName }).catch(() => ({ "version": "0.0.0" })); for (const consumeExecutableReplacer of [consumeExecutableUserProvidedReplacer, consumeExecutableBuiltinsReplacer]) { const result = await consumeExecutableReplacer?.({ parsedImportExportStatement, version, "destDirPath": getDestDirPath({ dirPath }) }); if (result === undefined) { continue; } return result; } const nodeToDenoModuleResolutionResult = await resolveNodeModuleToDenoModule({ nodeModuleName }); if (nodeToDenoModuleResolutionResult.result === "UNKNOWN BUILTIN") { return stringify(`node:${ParsedImportExportStatement.ParsedArgument.stringify(parsedImportExportStatement.parsedArgument)}`); } const { getValidImportUrl } = nodeToDenoModuleResolutionResult; return stringify( await getValidImportUrl(!specificImportPath ? { "target": "DEFAULT EXPORT" } : { "target": "SPECIFIC FILE", specificImportPath }) ); } return { denoifyImportExportStatement }; }