mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-29 23:55:02 +00:00
fix: refactor
This commit is contained in:
@@ -22,6 +22,7 @@ import { transformCss } from "@/src/utils/updaters/update-css"
|
||||
import { transformCssVars } from "@/src/utils/updaters/update-css-vars"
|
||||
import {
|
||||
findCommonRoot,
|
||||
getPlannedFilePaths,
|
||||
resolveFilePath,
|
||||
rewriteResolvedImportsInContent,
|
||||
} from "@/src/utils/updaters/update-files"
|
||||
@@ -152,32 +153,10 @@ async function processFiles(
|
||||
const project = new Project({
|
||||
compilerOptions: {},
|
||||
})
|
||||
const plannedFilePaths = files
|
||||
.filter((file): file is NonNullable<typeof file> => !!file?.content)
|
||||
.map((file, index) => {
|
||||
let plannedFilePath = resolveFilePath(file, config, {
|
||||
isSrcDir: projectInfo?.isSrcDir,
|
||||
framework: projectInfo?.framework.name,
|
||||
commonRoot: findCommonRoot(
|
||||
files.map((entry) => entry.path),
|
||||
file.path
|
||||
),
|
||||
fileIndex: index,
|
||||
})
|
||||
|
||||
if (!plannedFilePath) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!config.tsx) {
|
||||
plannedFilePath = plannedFilePath.replace(/\.tsx?$/, (match) =>
|
||||
match === ".tsx" ? ".jsx" : ".js"
|
||||
)
|
||||
}
|
||||
|
||||
return path.relative(config.resolvedPaths.cwd, plannedFilePath)
|
||||
})
|
||||
.filter((filePath): filePath is string => !!filePath)
|
||||
const plannedFilePaths = getPlannedFilePaths(files, config, {
|
||||
isSrcDir: projectInfo?.isSrcDir,
|
||||
framework: projectInfo?.framework.name,
|
||||
})
|
||||
|
||||
for (let index = 0; index < files.length; index++) {
|
||||
const file = files[index]
|
||||
|
||||
152
packages/shadcn/src/utils/import-entries.ts
Normal file
152
packages/shadcn/src/utils/import-entries.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import path from "path"
|
||||
|
||||
export type ImportEmitMode = "strip_extension" | "preserve_extension"
|
||||
|
||||
export type ImportResolutionEntry = {
|
||||
key: string
|
||||
aliasBase: string
|
||||
target: string
|
||||
emitMode: ImportEmitMode
|
||||
hasWildcard: boolean
|
||||
rootDir: string
|
||||
}
|
||||
|
||||
export type ImportResolutionMatch = {
|
||||
path: string
|
||||
matchedAlias: string
|
||||
matchedTarget: string
|
||||
emitMode: ImportEmitMode
|
||||
}
|
||||
|
||||
export function resolveLocalPathTarget(target: unknown): string | null {
|
||||
if (typeof target === "string") {
|
||||
return target.startsWith(".") ? target : null
|
||||
}
|
||||
|
||||
if (Array.isArray(target)) {
|
||||
for (const value of target) {
|
||||
const resolved = resolveLocalPathTarget(value)
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
if (target && typeof target === "object") {
|
||||
for (const value of Object.values(target as Record<string, unknown>)) {
|
||||
const resolved = resolveLocalPathTarget(value)
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function getImportTargetEmitMode(target: string): ImportEmitMode {
|
||||
if (!target.includes("*")) {
|
||||
return "strip_extension"
|
||||
}
|
||||
|
||||
const suffix = target.slice(target.indexOf("*") + 1)
|
||||
|
||||
// A bare `*` target like `./src/components/*` expects the emitted specifier
|
||||
// to include the source extension (`#components/button.tsx`).
|
||||
if (!suffix) {
|
||||
return "preserve_extension"
|
||||
}
|
||||
|
||||
return /^\.[^/]+$/.test(suffix) ? "strip_extension" : "preserve_extension"
|
||||
}
|
||||
|
||||
export function resolveImportEntryMatch(
|
||||
importPath: string,
|
||||
entries: ImportResolutionEntry[]
|
||||
): ImportResolutionMatch | null {
|
||||
const exactMatch = entries.find(
|
||||
(entry) => !entry.hasWildcard && entry.key === importPath
|
||||
)
|
||||
|
||||
if (exactMatch) {
|
||||
return {
|
||||
path: path.resolve(exactMatch.rootDir, exactMatch.target),
|
||||
matchedAlias: exactMatch.key,
|
||||
matchedTarget: exactMatch.target,
|
||||
emitMode: exactMatch.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
const wildcardMatches = entries
|
||||
.filter((entry) => entry.hasWildcard)
|
||||
.sort((a, b) => b.key.length - a.key.length)
|
||||
|
||||
for (const entry of wildcardMatches) {
|
||||
const wildcardValue = getPatternWildcardValue(importPath, entry.key, {
|
||||
allowBareAliasBase: true,
|
||||
})
|
||||
|
||||
if (wildcardValue === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
return {
|
||||
path: path.resolve(
|
||||
entry.rootDir,
|
||||
applyWildcardTarget(entry.target, wildcardValue)
|
||||
),
|
||||
matchedAlias: entry.key,
|
||||
matchedTarget: entry.target,
|
||||
emitMode: entry.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function getPatternWildcardValue(
|
||||
importPath: string,
|
||||
pattern: string,
|
||||
options: {
|
||||
allowBareAliasBase?: boolean
|
||||
} = {}
|
||||
): string | null {
|
||||
if (!pattern.includes("*")) {
|
||||
return importPath === pattern ? "" : null
|
||||
}
|
||||
|
||||
const [prefix, suffix = ""] = pattern.split("*")
|
||||
|
||||
if (importPath.startsWith(prefix) && importPath.endsWith(suffix)) {
|
||||
return suffix
|
||||
? importPath.slice(prefix.length, -suffix.length)
|
||||
: importPath.slice(prefix.length)
|
||||
}
|
||||
|
||||
if (
|
||||
options.allowBareAliasBase &&
|
||||
suffix === "" &&
|
||||
prefix.endsWith("/") &&
|
||||
importPath === prefix.slice(0, -1)
|
||||
) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function applyWildcardTarget(target: string, wildcardValue: string) {
|
||||
if (!target.includes("*")) {
|
||||
return target
|
||||
}
|
||||
|
||||
const [prefix, suffix = ""] = target.split("*")
|
||||
|
||||
if (!wildcardValue) {
|
||||
return prefix.replace(/\/$/, "")
|
||||
}
|
||||
|
||||
return `${prefix}${wildcardValue}${suffix}`
|
||||
}
|
||||
@@ -1,22 +1,17 @@
|
||||
import path from "path"
|
||||
import { getPackageInfo } from "@/src/utils/get-package-info"
|
||||
import {
|
||||
getImportTargetEmitMode,
|
||||
resolveImportEntryMatch,
|
||||
resolveLocalPathTarget,
|
||||
type ImportEmitMode,
|
||||
type ImportResolutionEntry,
|
||||
type ImportResolutionMatch,
|
||||
} from "@/src/utils/import-entries"
|
||||
|
||||
export type ImportEmitMode = "strip_extension" | "preserve_extension"
|
||||
|
||||
export type PackageImportEntry = {
|
||||
key: string
|
||||
aliasBase: string
|
||||
target: string
|
||||
emitMode: ImportEmitMode
|
||||
hasWildcard: boolean
|
||||
}
|
||||
|
||||
export type PackageImportMatch = {
|
||||
path: string
|
||||
matchedAlias: string
|
||||
matchedTarget: string
|
||||
emitMode: ImportEmitMode
|
||||
}
|
||||
export type { ImportEmitMode } from "@/src/utils/import-entries"
|
||||
export type PackageImportEntry = ImportResolutionEntry
|
||||
export type PackageImportMatch = ImportResolutionMatch
|
||||
|
||||
const packageImportEntriesCache = new Map<string, PackageImportEntry[]>()
|
||||
|
||||
@@ -43,7 +38,7 @@ export function getPackageImportEntries(cwd: string): PackageImportEntry[] {
|
||||
continue
|
||||
}
|
||||
|
||||
const target = resolveLocalImportTarget(value)
|
||||
const target = resolveLocalPathTarget(value)
|
||||
if (!target) {
|
||||
continue
|
||||
}
|
||||
@@ -52,8 +47,9 @@ export function getPackageImportEntries(cwd: string): PackageImportEntry[] {
|
||||
key,
|
||||
aliasBase: key.endsWith("/*") ? key.slice(0, -2) : key,
|
||||
target,
|
||||
emitMode: getImportEmitMode(target),
|
||||
emitMode: getImportTargetEmitMode(target),
|
||||
hasWildcard: key.includes("*"),
|
||||
rootDir: cacheKey,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,41 +71,7 @@ export function resolvePackageImport(
|
||||
importPath: string,
|
||||
cwd: string
|
||||
): PackageImportMatch | null {
|
||||
const entries = getPackageImportEntries(cwd)
|
||||
|
||||
const exactMatch = entries.find(
|
||||
(entry) => !entry.hasWildcard && entry.key === importPath
|
||||
)
|
||||
if (exactMatch) {
|
||||
return {
|
||||
path: path.resolve(cwd, exactMatch.target),
|
||||
matchedAlias: exactMatch.key,
|
||||
matchedTarget: exactMatch.target,
|
||||
emitMode: exactMatch.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
const wildcardMatches = entries
|
||||
.filter((entry) => entry.hasWildcard)
|
||||
.sort((a, b) => b.key.length - a.key.length)
|
||||
|
||||
for (const entry of wildcardMatches) {
|
||||
const [keyPrefix, keySuffix] = entry.key.split("*")
|
||||
const wildcardValue = getWildcardValue(importPath, keyPrefix, keySuffix)
|
||||
|
||||
if (wildcardValue === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
return {
|
||||
path: path.resolve(cwd, applyWildcardTarget(entry.target, wildcardValue)),
|
||||
matchedAlias: entry.key,
|
||||
matchedTarget: entry.target,
|
||||
emitMode: entry.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
return resolveImportEntryMatch(importPath, getPackageImportEntries(cwd))
|
||||
}
|
||||
|
||||
export function getPackageImportAliases(cwd: string) {
|
||||
@@ -124,89 +86,6 @@ export function getPackageImportAliases(cwd: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveLocalImportTarget(target: unknown): string | null {
|
||||
if (typeof target === "string") {
|
||||
return target.startsWith(".") ? target : null
|
||||
}
|
||||
|
||||
if (Array.isArray(target)) {
|
||||
for (const value of target) {
|
||||
const resolved = resolveLocalImportTarget(value)
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
if (target && typeof target === "object") {
|
||||
for (const value of Object.values(target as Record<string, unknown>)) {
|
||||
const resolved = resolveLocalImportTarget(value)
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getImportEmitMode(target: string): ImportEmitMode {
|
||||
if (!target.includes("*")) {
|
||||
return "strip_extension"
|
||||
}
|
||||
|
||||
const suffix = target.slice(target.indexOf("*") + 1)
|
||||
|
||||
// A bare `*` target like `./src/components/*` expects the emitted specifier
|
||||
// to include the source extension (`#components/button.tsx`).
|
||||
if (!suffix) {
|
||||
return "preserve_extension"
|
||||
}
|
||||
|
||||
return /^\.[^/]+$/.test(suffix) ? "strip_extension" : "preserve_extension"
|
||||
}
|
||||
|
||||
function getWildcardValue(
|
||||
importPath: string,
|
||||
prefix: string,
|
||||
suffix: string
|
||||
): string | null {
|
||||
if (importPath.startsWith(prefix) && importPath.endsWith(suffix)) {
|
||||
return suffix
|
||||
? importPath.slice(prefix.length, -suffix.length)
|
||||
: importPath.slice(prefix.length)
|
||||
}
|
||||
|
||||
// `components.json` stores alias roots like `#components`, not raw
|
||||
// `package.json#imports` patterns like `#components/*`, so we accept the
|
||||
// bare alias base here to keep package-import support schema-compatible.
|
||||
if (
|
||||
suffix === "" &&
|
||||
prefix.endsWith("/") &&
|
||||
importPath === prefix.slice(0, -1)
|
||||
) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function applyWildcardTarget(target: string, wildcardValue: string) {
|
||||
if (!target.includes("*")) {
|
||||
return target
|
||||
}
|
||||
|
||||
const [prefix, suffix = ""] = target.split("*")
|
||||
|
||||
if (!wildcardValue) {
|
||||
return prefix.replace(/\/$/, "")
|
||||
}
|
||||
|
||||
return `${prefix}${wildcardValue}${suffix}`
|
||||
}
|
||||
|
||||
function findBestAlias(
|
||||
entries: PackageImportEntry[],
|
||||
kind: "components" | "ui" | "lib" | "hooks" | "utils"
|
||||
|
||||
@@ -26,59 +26,19 @@ export async function resolveImportWithMetadata(
|
||||
): Promise<ResolvedImport | null> {
|
||||
const cwd = config.cwd ?? config.absoluteBaseUrl
|
||||
|
||||
if (importPath.startsWith("#")) {
|
||||
const packageImport = resolvePackageImport(importPath, cwd)
|
||||
for (const resolver of [
|
||||
() => resolveFromPackageImports(importPath, cwd),
|
||||
() => resolveFromWorkspacePackageExports(importPath, cwd),
|
||||
() => resolveFromTsconfigPaths(importPath, config),
|
||||
]) {
|
||||
const resolved = await resolver()
|
||||
|
||||
if (packageImport) {
|
||||
return {
|
||||
path: packageImport.path,
|
||||
source: "package_imports",
|
||||
matchedAlias: packageImport.matchedAlias,
|
||||
matchedTarget: packageImport.matchedTarget,
|
||||
emitMode: packageImport.emitMode,
|
||||
}
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
const workspacePackageExport = await resolveWorkspacePackageExport(
|
||||
importPath,
|
||||
cwd
|
||||
)
|
||||
|
||||
if (workspacePackageExport) {
|
||||
return {
|
||||
path: workspacePackageExport.path,
|
||||
source: "workspace_package_exports",
|
||||
matchedAlias: workspacePackageExport.matchedAlias,
|
||||
matchedTarget: workspacePackageExport.matchedTarget,
|
||||
emitMode: workspacePackageExport.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
const matchedPath = createMatchPath(config.absoluteBaseUrl, config.paths)(
|
||||
importPath,
|
||||
undefined,
|
||||
() => true,
|
||||
[".ts", ".tsx", ".jsx", ".js", ".css"]
|
||||
)
|
||||
|
||||
if (!matchedPath) {
|
||||
return null
|
||||
}
|
||||
|
||||
const matchedPattern = findMatchingTsPathPattern(importPath, config.paths)
|
||||
|
||||
if (!matchedPattern && isScopedPackageSpecifier(importPath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
path: matchedPath,
|
||||
source: "tsconfig_paths",
|
||||
matchedAlias: matchedPattern?.key ?? importPath,
|
||||
matchedTarget: matchedPattern?.target ?? matchedPath,
|
||||
emitMode: "strip_extension",
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export async function resolveImport(
|
||||
@@ -109,6 +69,79 @@ function isScopedPackageSpecifier(importPath: string) {
|
||||
return /^@[^/]+\/[^/]+(?:\/.*)?$/.test(importPath)
|
||||
}
|
||||
|
||||
function toResolvedImport(
|
||||
source: ResolvedImport["source"],
|
||||
resolved: {
|
||||
path: string
|
||||
matchedAlias: string
|
||||
matchedTarget: string
|
||||
emitMode: ImportEmitMode
|
||||
} | null
|
||||
) {
|
||||
if (!resolved) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
path: resolved.path,
|
||||
source,
|
||||
matchedAlias: resolved.matchedAlias,
|
||||
matchedTarget: resolved.matchedTarget,
|
||||
emitMode: resolved.emitMode,
|
||||
} satisfies ResolvedImport
|
||||
}
|
||||
|
||||
function resolveFromPackageImports(importPath: string, cwd: string) {
|
||||
if (!importPath.startsWith("#")) {
|
||||
return null
|
||||
}
|
||||
|
||||
return toResolvedImport(
|
||||
"package_imports",
|
||||
resolvePackageImport(importPath, cwd)
|
||||
)
|
||||
}
|
||||
|
||||
async function resolveFromWorkspacePackageExports(
|
||||
importPath: string,
|
||||
cwd: string
|
||||
) {
|
||||
return toResolvedImport(
|
||||
"workspace_package_exports",
|
||||
await resolveWorkspacePackageExport(importPath, cwd)
|
||||
)
|
||||
}
|
||||
|
||||
function resolveFromTsconfigPaths(
|
||||
importPath: string,
|
||||
config: ResolveImportConfig
|
||||
): ResolvedImport | null {
|
||||
const matchedPath = createMatchPath(config.absoluteBaseUrl, config.paths)(
|
||||
importPath,
|
||||
undefined,
|
||||
() => true,
|
||||
[".ts", ".tsx", ".jsx", ".js", ".css"]
|
||||
)
|
||||
|
||||
if (!matchedPath) {
|
||||
return null
|
||||
}
|
||||
|
||||
const matchedPattern = findMatchingTsPathPattern(importPath, config.paths)
|
||||
|
||||
if (!matchedPattern && isScopedPackageSpecifier(importPath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
path: matchedPath,
|
||||
source: "tsconfig_paths",
|
||||
matchedAlias: matchedPattern?.key ?? importPath,
|
||||
matchedTarget: matchedPattern?.target ?? matchedPath,
|
||||
emitMode: "strip_extension",
|
||||
}
|
||||
}
|
||||
|
||||
function findMatchingTsPathPattern(
|
||||
importPath: string,
|
||||
paths: ConfigLoaderSuccessResult["paths"]
|
||||
|
||||
@@ -34,7 +34,7 @@ import { transformRtl } from "@/src/utils/transformers/transform-rtl"
|
||||
import { transformTwPrefixes } from "@/src/utils/transformers/transform-tw-prefix"
|
||||
import prompts from "prompts"
|
||||
import { Project, ScriptKind } from "ts-morph"
|
||||
import { loadConfig } from "tsconfig-paths"
|
||||
import { loadConfig, type ConfigLoaderSuccessResult } from "tsconfig-paths"
|
||||
import { z } from "zod"
|
||||
|
||||
export async function updateFiles(
|
||||
@@ -594,7 +594,7 @@ async function resolveImports(filePaths: string[], config: Config) {
|
||||
return updatedFiles
|
||||
}
|
||||
|
||||
function getPlannedFilePaths(
|
||||
export function getPlannedFilePaths(
|
||||
files: RegistryItem["files"],
|
||||
config: Config,
|
||||
options: {
|
||||
@@ -683,26 +683,11 @@ export async function rewriteResolvedImportsInContent({
|
||||
continue
|
||||
}
|
||||
|
||||
const probableImportFilePath = (
|
||||
await resolveImportWithMetadata(moduleSpecifier, {
|
||||
...tsConfig,
|
||||
cwd: config.resolvedPaths.cwd,
|
||||
})
|
||||
)?.path
|
||||
|
||||
const fallbackImportFilePath =
|
||||
!probableImportFilePath && !moduleSpecifier.startsWith(".")
|
||||
? resolveImportFromConfiguredAliases(moduleSpecifier, config)
|
||||
: null
|
||||
|
||||
if (!probableImportFilePath && !fallbackImportFilePath) {
|
||||
continue
|
||||
}
|
||||
|
||||
const resolvedImportFilePath = resolveModuleByProbablePath(
|
||||
probableImportFilePath ?? fallbackImportFilePath!,
|
||||
const resolvedImportFilePath = await resolveImportFilePathForRewrite(
|
||||
moduleSpecifier,
|
||||
filePaths,
|
||||
config
|
||||
config,
|
||||
tsConfig
|
||||
)
|
||||
|
||||
if (!resolvedImportFilePath) {
|
||||
@@ -727,6 +712,35 @@ export async function rewriteResolvedImportsInContent({
|
||||
return hasChanges ? workingSourceFile.getFullText() : content
|
||||
}
|
||||
|
||||
async function resolveImportFilePathForRewrite(
|
||||
moduleSpecifier: string,
|
||||
filePaths: string[],
|
||||
config: Config,
|
||||
tsConfig: Pick<ConfigLoaderSuccessResult, "absoluteBaseUrl" | "paths">
|
||||
) {
|
||||
const probableImportFilePath = (
|
||||
await resolveImportWithMetadata(moduleSpecifier, {
|
||||
...tsConfig,
|
||||
cwd: config.resolvedPaths.cwd,
|
||||
})
|
||||
)?.path
|
||||
|
||||
const fallbackImportFilePath =
|
||||
!probableImportFilePath && !moduleSpecifier.startsWith(".")
|
||||
? resolveImportFromConfiguredAliases(moduleSpecifier, config)
|
||||
: null
|
||||
|
||||
if (!probableImportFilePath && !fallbackImportFilePath) {
|
||||
return null
|
||||
}
|
||||
|
||||
return resolveModuleByProbablePath(
|
||||
probableImportFilePath ?? fallbackImportFilePath!,
|
||||
filePaths,
|
||||
config
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an absolute "probable" import path (no ext),
|
||||
* plus an array of absolute file paths you already know about,
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
import path from "path"
|
||||
import { getWorkspacePatterns } from "@/src/utils/get-monorepo-info"
|
||||
import { getPackageInfo } from "@/src/utils/get-package-info"
|
||||
import { type ImportEmitMode } from "@/src/utils/package-imports"
|
||||
import fg from "fast-glob"
|
||||
import fs from "fs-extra"
|
||||
import {
|
||||
getImportTargetEmitMode,
|
||||
resolveImportEntryMatch,
|
||||
resolveLocalPathTarget,
|
||||
type ImportEmitMode,
|
||||
type ImportResolutionEntry,
|
||||
type ImportResolutionMatch,
|
||||
} from "@/src/utils/import-entries"
|
||||
|
||||
type WorkspacePackageInfo = {
|
||||
packageName: string
|
||||
packageRoot: string
|
||||
}
|
||||
|
||||
type WorkspacePackageExportEntry = {
|
||||
key: string
|
||||
aliasBase: string
|
||||
target: string
|
||||
emitMode: ImportEmitMode
|
||||
hasWildcard: boolean
|
||||
packageRoot: string
|
||||
}
|
||||
|
||||
export type WorkspacePackageExportMatch = {
|
||||
path: string
|
||||
matchedAlias: string
|
||||
matchedTarget: string
|
||||
emitMode: ImportEmitMode
|
||||
}
|
||||
type WorkspacePackageExportEntry = ImportResolutionEntry
|
||||
export type WorkspacePackageExportMatch = ImportResolutionMatch
|
||||
|
||||
const workspacePackageCache = new Map<
|
||||
string,
|
||||
@@ -55,46 +49,10 @@ export async function resolveWorkspacePackageExport(
|
||||
return null
|
||||
}
|
||||
|
||||
const entries = getWorkspacePackageExportEntries(workspacePackage)
|
||||
|
||||
const exactMatch = entries.find(
|
||||
(entry) => !entry.hasWildcard && entry.aliasBase === importPath
|
||||
return resolveImportEntryMatch(
|
||||
importPath,
|
||||
getWorkspacePackageExportEntries(workspacePackage)
|
||||
)
|
||||
|
||||
if (exactMatch) {
|
||||
return {
|
||||
path: path.resolve(exactMatch.packageRoot, exactMatch.target),
|
||||
matchedAlias: exactMatch.aliasBase,
|
||||
matchedTarget: exactMatch.target,
|
||||
emitMode: exactMatch.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
const wildcardMatches = entries
|
||||
.filter((entry) => entry.hasWildcard)
|
||||
.sort((a, b) => b.aliasBase.length - a.aliasBase.length)
|
||||
|
||||
for (const entry of wildcardMatches) {
|
||||
const pattern = `${entry.aliasBase}/*`
|
||||
const [prefix, suffix = ""] = pattern.split("*")
|
||||
const wildcardValue = getWildcardValue(importPath, prefix, suffix)
|
||||
|
||||
if (wildcardValue === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
return {
|
||||
path: path.resolve(
|
||||
entry.packageRoot,
|
||||
applyWildcardTarget(entry.target, wildcardValue)
|
||||
),
|
||||
matchedAlias: pattern,
|
||||
matchedTarget: entry.target,
|
||||
emitMode: entry.emitMode,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getWorkspacePackageExportEntries(
|
||||
@@ -126,19 +84,21 @@ function getWorkspacePackageExportEntries(
|
||||
continue
|
||||
}
|
||||
|
||||
const target = resolveLocalExportTarget(value)
|
||||
const target = resolveLocalPathTarget(value)
|
||||
|
||||
if (!target) {
|
||||
continue
|
||||
}
|
||||
|
||||
const aliasBase = getAliasBase(workspacePackage.packageName, key)
|
||||
|
||||
entries.push({
|
||||
key,
|
||||
aliasBase: getAliasBase(workspacePackage.packageName, key),
|
||||
key: key.includes("*") ? `${aliasBase}/*` : aliasBase,
|
||||
aliasBase,
|
||||
target,
|
||||
emitMode: getExportEmitMode(target),
|
||||
emitMode: getImportTargetEmitMode(target),
|
||||
hasWildcard: key.includes("*"),
|
||||
packageRoot: workspacePackage.packageRoot,
|
||||
rootDir: workspacePackage.packageRoot,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -292,83 +252,3 @@ function getAliasBase(packageName: string, exportKey: string) {
|
||||
|
||||
return normalizedKey ? `${packageName}/${normalizedKey}` : packageName
|
||||
}
|
||||
|
||||
function resolveLocalExportTarget(target: unknown): string | null {
|
||||
if (typeof target === "string") {
|
||||
return target.startsWith(".") ? target : null
|
||||
}
|
||||
|
||||
if (Array.isArray(target)) {
|
||||
for (const value of target) {
|
||||
const resolved = resolveLocalExportTarget(value)
|
||||
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
if (target && typeof target === "object") {
|
||||
for (const value of Object.values(target as Record<string, unknown>)) {
|
||||
const resolved = resolveLocalExportTarget(value)
|
||||
|
||||
if (resolved) {
|
||||
return resolved
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getExportEmitMode(target: string): ImportEmitMode {
|
||||
if (!target.includes("*")) {
|
||||
return "strip_extension"
|
||||
}
|
||||
|
||||
const suffix = target.slice(target.indexOf("*") + 1)
|
||||
|
||||
if (!suffix) {
|
||||
return "preserve_extension"
|
||||
}
|
||||
|
||||
return /^\.[^/]+$/.test(suffix) ? "strip_extension" : "preserve_extension"
|
||||
}
|
||||
|
||||
function getWildcardValue(
|
||||
importPath: string,
|
||||
prefix: string,
|
||||
suffix: string
|
||||
): string | null {
|
||||
if (importPath.startsWith(prefix) && importPath.endsWith(suffix)) {
|
||||
return suffix
|
||||
? importPath.slice(prefix.length, -suffix.length)
|
||||
: importPath.slice(prefix.length)
|
||||
}
|
||||
|
||||
if (
|
||||
suffix === "" &&
|
||||
prefix.endsWith("/") &&
|
||||
importPath === prefix.slice(0, -1)
|
||||
) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function applyWildcardTarget(target: string, wildcardValue: string) {
|
||||
if (!target.includes("*")) {
|
||||
return target
|
||||
}
|
||||
|
||||
const [prefix, suffix = ""] = target.split("*")
|
||||
|
||||
if (!wildcardValue) {
|
||||
return prefix.replace(/\/$/, "")
|
||||
}
|
||||
|
||||
return `${prefix}${wildcardValue}${suffix}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user