UNPKG

vite-tsconfig-paths

Version:

Vite resolver for TypeScript compilerOptions.paths

387 lines (383 loc) 13.4 kB
// src/index.ts import * as fs from "fs"; import globRex from "globrex"; import { resolve as resolve3 } from "path"; import { inspect } from "util"; import { normalizePath as normalizePath2, searchForWorkspaceRoot } from "vite"; // src/mappings.ts import { resolve } from "path"; function resolvePathMappings(paths, base) { const sortedPatterns = Object.keys(paths).sort( (a, b) => getPrefixLength(b) - getPrefixLength(a) ); const resolved = []; for (let pattern of sortedPatterns) { const relativePaths = paths[pattern]; pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)"); resolved.push({ pattern: new RegExp("^" + pattern + "$"), paths: relativePaths.map((relativePath) => resolve(base, relativePath)) }); } return resolved; } function getPrefixLength(pattern) { const prefixLength = pattern.indexOf("*"); return pattern.substr(0, prefixLength).length; } function escapeStringRegexp(string) { return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d"); } // src/path.ts import * as os from "os"; import * as path from "path"; import { normalizePath } from "vite"; import { dirname } from "path"; var isWindows = os.platform() == "win32"; var resolve2 = isWindows ? (...paths) => normalizePath(path.win32.resolve(...paths)) : path.posix.resolve; var isAbsolute = isWindows ? path.win32.isAbsolute : path.posix.isAbsolute; var join = path.posix.join; var relative = path.posix.relative; var basename = path.posix.basename; // src/debug.ts import createDebug from "debug"; var debug = createDebug("vite-tsconfig-paths"); var debugResolve = createDebug("vite-tsconfig-paths:resolve"); if (process.env.TEST) { createDebug.log = console.log.bind(console); } // src/index.ts var notApplicable = [void 0, false]; var notFound = [void 0, true]; var src_default = (opts = {}) => { let resolversByDir; return { name: "vite-tsconfig-paths", enforce: "pre", async configResolved(config) { let projectRoot = config.root; let workspaceRoot; let { root } = opts; if (root) { root = resolve3(projectRoot, root); } else { workspaceRoot = searchForWorkspaceRoot(projectRoot); } debug("options.root ==", root); debug("project root ==", projectRoot); debug("workspace root ==", workspaceRoot); if (root) { projectRoot = root; workspaceRoot = root; } const tsconfck = await import("tsconfck"); const projects = opts.projects ? opts.projects.map((file) => { if (!file.endsWith(".json")) { file = join(file, "tsconfig.json"); } return resolve3(projectRoot, file); }) : await tsconfck.findAll(workspaceRoot, { configNames: opts.configNames || ["tsconfig.json", "jsconfig.json"], skip(dir) { if (dir === ".git" || dir === "node_modules") { return true; } if (typeof opts.skip === "function") { return opts.skip(dir); } return false; } }); debug("projects:", projects); let hasTypeScriptDep = false; if (opts.parseNative) { try { const pkgJson = fs.readFileSync( join(workspaceRoot, "package.json"), "utf8" ); const pkg = JSON.parse(pkgJson); const deps = { ...pkg.dependencies, ...pkg.devDependencies }; hasTypeScriptDep = "typescript" in deps; } catch (e) { if (e.code != "ENOENT") { throw e; } } } let firstError; const parseOptions = { cache: new tsconfck.TSConfckCache() }; const parsedProjects = new Set( await Promise.all( projects.map((tsconfigFile) => { if (tsconfigFile === null) { debug("tsconfig file not found:", tsconfigFile); return null; } return (hasTypeScriptDep ? tsconfck.parseNative(tsconfigFile, parseOptions) : tsconfck.parse(tsconfigFile, parseOptions)).catch((error) => { if (opts.ignoreConfigErrors) { debug("tsconfig file caused a parsing error:", tsconfigFile); } else { config.logger.error( '[tsconfig-paths] An error occurred while parsing "' + tsconfigFile + '". See below for details.' + (firstError ? "" : " To disable this message, set the `ignoreConfigErrors` option to true."), { error } ); if (config.logger.hasErrorLogged(error)) { console.error(error); } firstError = error; } return null; }); }) ) ); resolversByDir = {}; parsedProjects.forEach((project) => { if (!project) { return; } if (project.referenced) { project.referenced.forEach((projectRef) => { parsedProjects.add(projectRef); }); parsedProjects.delete(project); parsedProjects.add(project); project.referenced = void 0; } else { const resolver = createResolver(project); if (resolver) { const projectDir = normalizePath2(dirname(project.tsconfigFile)); const resolvers = resolversByDir[projectDir] || (resolversByDir[projectDir] = []); resolvers.push(resolver); } } }); }, async resolveId(id, importer, options) { if (debugResolve.enabled) { debugResolve("resolving:", { id, importer }); } if (!importer) { debugResolve("importer is empty or undefined. skipping..."); return; } if (relativeImportRE.test(id)) { debugResolve("id is a relative import. skipping..."); return; } if (isAbsolute(id)) { debugResolve("id is an absolute path. skipping..."); return; } if (id.includes("\0")) { debugResolve("id is a virtual module. skipping..."); return; } const resolveOptions = { ...options, skipSelf: true }; const viteResolve = async (id2, importer2) => { var _a; return (_a = await this.resolve(id2, importer2, resolveOptions)) == null ? void 0 : _a.id; }; let prevProjectDir; let projectDir = normalizePath2(dirname(importer)); loop: while (projectDir && projectDir != prevProjectDir) { const resolvers = resolversByDir[projectDir]; if (resolvers) { for (const resolve4 of resolvers) { const [resolved, matched] = await resolve4(viteResolve, id, importer); if (resolved) { return resolved; } if (matched) { break loop; } } } prevProjectDir = projectDir; projectDir = dirname(prevProjectDir); } } }; function resolvePathsRootDir(project) { var _a, _b, _c, _d; if ("result" in project) { return (_b = (_a = project.result.options) == null ? void 0 : _a.pathsBasePath) != null ? _b : dirname(project.tsconfigFile); } const baseUrl = (_c = project.tsconfig.compilerOptions) == null ? void 0 : _c.baseUrl; if (baseUrl) { return baseUrl; } const projectWithPaths = (_d = project.extended) == null ? void 0 : _d.find( (p) => { var _a2; return (_a2 = p.tsconfig.compilerOptions) == null ? void 0 : _a2.paths; } ); return dirname((projectWithPaths != null ? projectWithPaths : project).tsconfigFile); } function createResolver(project) { var _a, _b, _c, _d; const configPath = normalizePath2(project.tsconfigFile); const config = project.tsconfig; debug("config loaded:", inspect({ configPath, config }, false, 10, true)); if (((_a = config.files) == null ? void 0 : _a.length) == 0 && !((_b = config.include) == null ? void 0 : _b.length)) { debug( `[!] skipping "${configPath}" as no files can be matched since "files" is empty and "include" is missing or empty` ); return null; } const options = config.compilerOptions || {}; const { baseUrl, paths } = options; if (!baseUrl && !paths) { debug(`[!] missing baseUrl and paths: "${configPath}"`); return null; } const resolveWithBaseUrl = baseUrl ? (viteResolve, id, importer) => { const absoluteId = join(baseUrl, id); debugResolve("trying with baseUrl:", absoluteId); return viteResolve(absoluteId, importer); } : void 0; let resolveId; if (paths) { const pathsRootDir = resolvePathsRootDir(project); const pathMappings = resolvePathMappings(paths, pathsRootDir); const resolveWithPaths = async (viteResolve, id, importer) => { for (const mapping of pathMappings) { const match = id.match(mapping.pattern); if (!match) { continue; } for (let pathTemplate of mapping.paths) { let starCount = 0; const mappedId = pathTemplate.replace(/\*/g, () => { const matchIndex = Math.min(++starCount, match.length - 1); return match[matchIndex]; }); debugResolve("found match, trying to resolve:", mappedId); const resolved = await viteResolve(mappedId, importer); if (resolved) { return resolved; } } } }; if (resolveWithBaseUrl) { resolveId = (viteResolve, id, importer) => resolveWithPaths(viteResolve, id, importer).then((resolved) => { return resolved != null ? resolved : resolveWithBaseUrl(viteResolve, id, importer); }); } else { resolveId = resolveWithPaths; } } else { resolveId = resolveWithBaseUrl; } const configDir = dirname(configPath); let { outDir } = options; if (outDir && isAbsolute(outDir)) { outDir = relative(configDir, outDir); } const isIncludedRelative = getIncluder( (_c = config.include) == null ? void 0 : _c.map((p) => ensureRelative(configDir, p)), (_d = config.exclude) == null ? void 0 : _d.map((p) => ensureRelative(configDir, p)), outDir ); const importerExtRE = opts.loose ? /./ : options.allowJs || basename(configPath).startsWith("jsconfig.") ? jsLikeRE : /\.[mc]?tsx?$/; const resolutionCache = /* @__PURE__ */ new Map(); return async (viteResolve, id, importer) => { var _a2; importer = normalizePath2(importer); const importerFile = importer.replace(/[#?].+$/, ""); if (!importerExtRE.test(importerFile)) { debugResolve("importer has unsupported extension. skipping..."); return notApplicable; } const relativeImporterFile = relative(configDir, importerFile); if (!isIncludedRelative(relativeImporterFile)) { debugResolve("importer is not included. skipping..."); return notApplicable; } const suffix = (_a2 = /\?.+$/.exec(id)) == null ? void 0 : _a2[0]; if (suffix) { id = id.slice(0, -suffix.length); } let resolvedId = resolutionCache.get(id); if (!resolvedId) { resolvedId = await resolveId(viteResolve, id, importer); if (!resolvedId) { return notFound; } resolutionCache.set(id, resolvedId); if (debugResolve.enabled) { debugResolve("resolved without error:", { id, importer, resolvedId, configPath }); } } if (suffix) { resolvedId += suffix; } return [resolvedId, true]; }; } }; var jsLikeRE = /\.(vue|svelte|mdx|[mc]?[jt]sx?)$/; var relativeImportRE = /^\.\.?(\/|$)/; var defaultInclude = ["**/*"]; var defaultExclude = [ "**/node_modules", "**/bower_components", "**/jspm_packages" ]; function getIncluder(includePaths = defaultInclude, excludePaths = defaultExclude, outDir) { if (outDir) { excludePaths = excludePaths.concat(outDir); } if (includePaths.length || excludePaths.length) { const includers = []; const excluders = []; includePaths.forEach(addCompiledGlob, includers); excludePaths.forEach(addCompiledGlob, excluders); debug(`compiled globs:`, { includers, excluders }); return (path2) => { path2 = path2.replace(/\?.+$/, ""); if (!relativeImportRE.test(path2)) { path2 = "./" + path2; } const test = (glob) => glob.test(path2); return includers.some(test) && !excluders.some(test); }; } return () => true; } function addCompiledGlob(glob) { const endsWithGlob = glob.split("/").pop().includes("*"); const relativeGlob = relativeImportRE.test(glob) ? glob : "./" + glob; if (endsWithGlob) { this.push(compileGlob(relativeGlob)); } else { this.push(compileGlob(relativeGlob + "/**")); if (/\.\w+$/.test(glob)) { this.push(compileGlob(relativeGlob)); } } } function compileGlob(glob) { return globRex(glob, { extended: true, globstar: true }).regex; } function ensureRelative(dir, path2) { return isAbsolute(path2) ? relative(dir, path2) : path2; } export { src_default as default }; //# sourceMappingURL=index.js.map