mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
CLI: Preserve layout imports for slots
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@fumadocs/cli': minor
|
||||
---
|
||||
|
||||
Preserve layout imports for slots
|
||||
@@ -276,8 +276,6 @@ export class ComponentCompiler {
|
||||
this.registry = compiler.raw;
|
||||
}
|
||||
|
||||
// see https://github.com/shadcn-ui/ui/blob/396275e46a58333caa1fa0a991bd9bc5237d2ee3/packages/shadcn/src/utils/updaters/update-files.ts#L585
|
||||
// to hit the fast-path step, we need to import `target` path first because it's detected from `fileSet`, a set of output file paths
|
||||
private toImportPath(file: ComponentFile): string {
|
||||
let filePath = file.target ?? file.path;
|
||||
|
||||
|
||||
@@ -11,11 +11,14 @@ import picocolors from 'picocolors';
|
||||
import { ComponentInstaller } from '@/registry/installer';
|
||||
import type { RegistryClient } from '@/registry/client';
|
||||
import { UIRegistries } from '@/commands/shared';
|
||||
import { pluginPreserveLayouts } from '@/registry/plugins/preserve';
|
||||
|
||||
export async function add(input: string[], client: RegistryClient) {
|
||||
const config = client.config;
|
||||
let target: string[];
|
||||
const installer = new ComponentInstaller(client);
|
||||
const installer = new ComponentInstaller(client, {
|
||||
plugins: [pluginPreserveLayouts()],
|
||||
});
|
||||
const registry = UIRegistries[config.uiLibrary];
|
||||
|
||||
if (input.length === 0) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { install } from '@/commands/add';
|
||||
import type { RegistryClient } from '@/registry/client';
|
||||
import { ComponentInstaller } from '@/registry/installer';
|
||||
import { UIRegistries } from '@/commands/shared';
|
||||
import { pluginPreserveLayouts } from '@/registry/plugins/preserve';
|
||||
|
||||
interface TargetInfo {
|
||||
target: string[];
|
||||
@@ -13,7 +14,9 @@ interface TargetInfo {
|
||||
export async function customise(client: RegistryClient) {
|
||||
intro(picocolors.bgBlack(picocolors.whiteBright('Customise Fumadocs UI')));
|
||||
const config = client.config;
|
||||
const installer = new ComponentInstaller(client);
|
||||
const installer = new ComponentInstaller(client, {
|
||||
plugins: [pluginPreserveLayouts()],
|
||||
});
|
||||
const registry = UIRegistries[config.uiLibrary];
|
||||
|
||||
const result = await group(
|
||||
|
||||
@@ -10,27 +10,70 @@ import { createCache } from '@/utils/cache';
|
||||
import { parse, type ParseResult } from 'oxc-parser';
|
||||
import MagicString from 'magic-string';
|
||||
|
||||
interface DownloadedComponent extends Omit<Component, 'subComponents'> {
|
||||
variables?: Record<string, unknown>;
|
||||
interface PluginContext {
|
||||
installer: ComponentInstaller;
|
||||
}
|
||||
|
||||
interface TransformContext extends PluginContext, InstallContext {
|
||||
s: MagicString;
|
||||
parsed: ParseResult;
|
||||
file: File;
|
||||
component: DownloadedComponent;
|
||||
}
|
||||
|
||||
interface InstallContext {
|
||||
pathToFile: Map<string, File>;
|
||||
io: IOInterface;
|
||||
/** full variables of the current component. */
|
||||
$variables: Record<string, unknown>;
|
||||
/** the last item is always the current component. */
|
||||
stack: DownloadedComponent[];
|
||||
}
|
||||
|
||||
interface DownloadedComponent extends Component {
|
||||
$subComponents: DownloadedComponent[];
|
||||
$registry: RegistryClient;
|
||||
}
|
||||
|
||||
type Awaitable<T> = T | Promise<T>;
|
||||
|
||||
export interface ComponentInstallerPlugin {
|
||||
/**
|
||||
* transform file downloaded from components
|
||||
* transform file before writing (before default transformation)
|
||||
*/
|
||||
transformFile?: (context: {
|
||||
s: MagicString;
|
||||
parsed: ParseResult;
|
||||
beforeTransform?: (context: TransformContext) => Awaitable<void>;
|
||||
|
||||
file: File;
|
||||
component: DownloadedComponent;
|
||||
}) => void | Promise<void>;
|
||||
/**
|
||||
* transform file before writing (after default transformation)
|
||||
*/
|
||||
afterTransform?: (context: TransformContext) => Awaitable<void>;
|
||||
|
||||
/**
|
||||
* transform component before install
|
||||
*/
|
||||
beforeInstall?: (
|
||||
comp: DownloadedComponent,
|
||||
context: InstallContext & PluginContext,
|
||||
) => Awaitable<DownloadedComponent | undefined>;
|
||||
|
||||
beforeDownload?: (
|
||||
context: PluginContext & {
|
||||
name: string;
|
||||
},
|
||||
) => void | Promise<void>;
|
||||
|
||||
afterDownload?: (
|
||||
context: PluginContext & {
|
||||
name: string;
|
||||
result: DownloadedComponent;
|
||||
},
|
||||
) => void | Promise<void>;
|
||||
}
|
||||
|
||||
export interface IOInterface {
|
||||
onWarn: (message: string) => void;
|
||||
confirmFileOverride: (options: { path: string }) => Promise<boolean>;
|
||||
onFileDownloaded: (options: { path: string; file: File; component: DownloadedComponent }) => void;
|
||||
onFileDownloaded: (options: { path: string; file: File; component: Component }) => void;
|
||||
}
|
||||
|
||||
export interface ComponentInstallerOptions {
|
||||
@@ -40,7 +83,7 @@ export interface ComponentInstallerOptions {
|
||||
|
||||
export class ComponentInstaller {
|
||||
private readonly installedFiles = new Set<string>();
|
||||
private readonly downloadCache = createCache<DownloadedComponent[]>();
|
||||
private readonly downloadCache = createCache<DownloadedComponent>();
|
||||
private readonly cwd: string;
|
||||
private readonly plugins: ComponentInstallerPlugin[];
|
||||
readonly dependencies: Record<string, string | null> = {};
|
||||
@@ -54,58 +97,97 @@ export class ComponentInstaller {
|
||||
this.plugins = options.plugins ?? [];
|
||||
}
|
||||
|
||||
private async installComponent(comp: DownloadedComponent, ctx: InstallContext) {
|
||||
// avoid circular refs
|
||||
if (ctx.stack.indexOf(comp) !== ctx.stack.length - 1) return;
|
||||
|
||||
const pluginCtx = { installer: this, ...ctx };
|
||||
for (const plugin of this.plugins) {
|
||||
comp = (await plugin.beforeInstall?.(comp, pluginCtx)) ?? comp;
|
||||
}
|
||||
|
||||
Object.assign(this.dependencies, comp.dependencies);
|
||||
Object.assign(this.devDependencies, comp.devDependencies);
|
||||
|
||||
for (const file of comp.files) {
|
||||
const outPath = this.resolveOutputPath(file);
|
||||
if (this.installedFiles.has(outPath)) continue;
|
||||
this.installedFiles.add(outPath);
|
||||
|
||||
const output = typescriptExtensions.includes(path.extname(outPath))
|
||||
? await this.transform(file, comp, ctx)
|
||||
: file.content;
|
||||
|
||||
const status = await fs
|
||||
.readFile(outPath)
|
||||
.then((res) => {
|
||||
if (res.toString().trim() === output.trim()) return 'ignore';
|
||||
return 'need-update';
|
||||
})
|
||||
.catch(() => 'write');
|
||||
|
||||
if (status === 'ignore') continue;
|
||||
|
||||
if (status === 'need-update') {
|
||||
const override = await ctx.io.confirmFileOverride({ path: outPath });
|
||||
if (!override) continue;
|
||||
}
|
||||
|
||||
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
||||
await fs.writeFile(outPath, output);
|
||||
ctx.io.onFileDownloaded({ path: outPath, file, component: comp });
|
||||
}
|
||||
|
||||
for (const child of comp.$subComponents) {
|
||||
const stack = [...ctx.stack, child];
|
||||
const variables = { ...ctx.$variables };
|
||||
if (child.$registry.registryId !== comp.$registry.registryId) {
|
||||
const info = await child.$registry.fetchRegistryInfo();
|
||||
Object.assign(variables, info.variables);
|
||||
}
|
||||
Object.assign(variables, child.variables);
|
||||
|
||||
await this.installComponent(child, { ...ctx, stack, $variables: variables });
|
||||
}
|
||||
}
|
||||
|
||||
async install(name: string, io: IOInterface) {
|
||||
let downloaded: DownloadedComponent[];
|
||||
let downloaded: DownloadedComponent;
|
||||
// detect linked registry
|
||||
const info = await this.rootClient.fetchRegistryInfo();
|
||||
const rootInfo = await this.rootClient.fetchRegistryInfo();
|
||||
const registry = rootInfo.registries?.find((registry) => name.startsWith(`${registry}/`));
|
||||
|
||||
for (const registry of info.registries ?? []) {
|
||||
if (name.startsWith(`${registry}/`)) {
|
||||
downloaded = await this.download(
|
||||
name.slice(registry.length + 1),
|
||||
this.rootClient.createLinkedRegistryClient(registry),
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (registry) {
|
||||
downloaded = await this.download(
|
||||
name.slice(registry.length + 1),
|
||||
this.rootClient.createLinkedRegistryClient(registry),
|
||||
);
|
||||
} else {
|
||||
downloaded = await this.download(name, this.rootClient);
|
||||
}
|
||||
|
||||
downloaded ??= await this.download(name, this.rootClient);
|
||||
const allComponents = new Set<DownloadedComponent>();
|
||||
function scan(comp: DownloadedComponent) {
|
||||
if (allComponents.has(comp)) return;
|
||||
|
||||
for (const item of downloaded) {
|
||||
Object.assign(this.dependencies, item.dependencies);
|
||||
Object.assign(this.devDependencies, item.devDependencies);
|
||||
allComponents.add(comp);
|
||||
for (const child of comp.$subComponents) scan(child);
|
||||
}
|
||||
|
||||
for (const comp of downloaded) {
|
||||
for (const file of comp.files) {
|
||||
const outPath = this.resolveOutputPath(file);
|
||||
if (this.installedFiles.has(outPath)) continue;
|
||||
this.installedFiles.add(outPath);
|
||||
scan(downloaded);
|
||||
|
||||
const output = typescriptExtensions.includes(path.extname(outPath))
|
||||
? await this.transform(io, name, file, comp, downloaded)
|
||||
: file.content;
|
||||
|
||||
const status = await fs
|
||||
.readFile(outPath)
|
||||
.then((res) => {
|
||||
if (res.toString().trim() === output.trim()) return 'ignore';
|
||||
return 'need-update';
|
||||
})
|
||||
.catch(() => 'write');
|
||||
|
||||
if (status === 'ignore') continue;
|
||||
|
||||
if (status === 'need-update') {
|
||||
const override = await io.confirmFileOverride({ path: outPath });
|
||||
if (!override) continue;
|
||||
}
|
||||
|
||||
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
||||
await fs.writeFile(outPath, output);
|
||||
io.onFileDownloaded({ path: outPath, file, component: comp });
|
||||
}
|
||||
const pathToFile = new Map<string, File>();
|
||||
for (const comp of allComponents) {
|
||||
for (const file of comp.files) pathToFile.set(file.target ?? file.path, file);
|
||||
}
|
||||
|
||||
const info = await downloaded.$registry.fetchRegistryInfo();
|
||||
await this.installComponent(downloaded, {
|
||||
pathToFile,
|
||||
io,
|
||||
$variables: { ...info.env, ...downloaded.variables },
|
||||
stack: [downloaded],
|
||||
});
|
||||
}
|
||||
|
||||
async deps() {
|
||||
@@ -122,84 +204,90 @@ export class ComponentInstaller {
|
||||
}
|
||||
|
||||
/**
|
||||
* return a list of components, merged with child components & variables.
|
||||
* download component & its sub components
|
||||
*/
|
||||
private async download(
|
||||
name: string,
|
||||
client: RegistryClient,
|
||||
contextVariables?: Record<string, unknown>,
|
||||
): Promise<DownloadedComponent[]> {
|
||||
const hash = `${client.registryId} ${name}`;
|
||||
const info = await client.fetchRegistryInfo();
|
||||
const variables = { ...contextVariables, ...info.env };
|
||||
for (const [k, v] of Object.entries(info.variables ?? {})) {
|
||||
variables[k] ??= v.default;
|
||||
}
|
||||
private async download(name: string, client: RegistryClient): Promise<DownloadedComponent> {
|
||||
return this.downloadCache.cached(
|
||||
JSON.stringify([client.registryId, name]),
|
||||
async (presolve) => {
|
||||
for (const plugin of this.plugins) {
|
||||
await plugin.beforeDownload?.({
|
||||
installer: this,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
const out = await this.downloadCache.cached(hash, async (presolve) => {
|
||||
const comp = await client.fetchComponent(name);
|
||||
const result: DownloadedComponent[] = [comp];
|
||||
// place it before downloading child components to avoid recursive downloads
|
||||
presolve(result);
|
||||
const comp = await client.fetchComponent(name);
|
||||
const result: DownloadedComponent = {
|
||||
...comp,
|
||||
$registry: client,
|
||||
$subComponents: [],
|
||||
};
|
||||
// place it before downloading child components to avoid recursive downloads
|
||||
presolve(result);
|
||||
|
||||
const child = await Promise.all(
|
||||
comp.subComponents.map((sub) => {
|
||||
if (typeof sub === 'string') return this.download(sub, client);
|
||||
const baseUrl =
|
||||
this.rootClient instanceof HttpRegistryClient
|
||||
? new URL(sub.baseUrl, `${this.rootClient.baseUrl}/`).href
|
||||
: sub.baseUrl;
|
||||
result.$subComponents = await Promise.all(
|
||||
comp.subComponents.map((sub) => {
|
||||
if (typeof sub === 'string') return this.download(sub, client);
|
||||
|
||||
return this.download(
|
||||
sub.component,
|
||||
new HttpRegistryClient(baseUrl, client.config),
|
||||
variables,
|
||||
);
|
||||
}),
|
||||
);
|
||||
for (const sub of child) result.push(...sub);
|
||||
return result;
|
||||
});
|
||||
let subClient: RegistryClient;
|
||||
if (this.rootClient instanceof HttpRegistryClient) {
|
||||
const baseUrl = new URL(sub.baseUrl, `${this.rootClient.baseUrl}/`).href;
|
||||
subClient =
|
||||
client instanceof HttpRegistryClient && client.baseUrl === baseUrl
|
||||
? client
|
||||
: new HttpRegistryClient(baseUrl, client.config);
|
||||
} else {
|
||||
subClient = new HttpRegistryClient(sub.baseUrl, client.config);
|
||||
}
|
||||
|
||||
return out.map((file) => ({ ...file, variables }));
|
||||
return this.download(sub.component, subClient);
|
||||
}),
|
||||
);
|
||||
|
||||
for (const plugin of this.plugins) {
|
||||
await plugin.afterDownload?.({
|
||||
installer: this,
|
||||
name,
|
||||
result,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private readonly pathToFileCache = createCache<Map<string, File>>();
|
||||
private async transform(
|
||||
io: IOInterface,
|
||||
taskId: string,
|
||||
file: File,
|
||||
component: DownloadedComponent,
|
||||
allComponents: DownloadedComponent[],
|
||||
ctx: InstallContext,
|
||||
): Promise<string> {
|
||||
const filePath = this.resolveOutputPath(file);
|
||||
const parsed = await parse(filePath, file.content);
|
||||
const s = new MagicString(file.content);
|
||||
const transformCtx: TransformContext = { installer: this, s, file, component, parsed, ...ctx };
|
||||
|
||||
for (const plugin of this.plugins) {
|
||||
await plugin.beforeTransform?.(transformCtx);
|
||||
}
|
||||
|
||||
// transform alias
|
||||
const prefix = '@/';
|
||||
const variables = Object.entries(component.variables ?? {});
|
||||
const pathToFile = await this.pathToFileCache.cached(taskId, () => {
|
||||
const map = new Map<string, File>();
|
||||
for (const comp of allComponents) {
|
||||
for (const file of comp.files) map.set(file.target ?? file.path, file);
|
||||
}
|
||||
return map;
|
||||
});
|
||||
|
||||
transformSpecifiers(parsed.program, s, (specifier) => {
|
||||
for (const [k, v] of variables) {
|
||||
specifier = specifier.replaceAll(`<${k}>`, v as string);
|
||||
for (const [k, v] of Object.entries(ctx.$variables)) {
|
||||
if (typeof v === 'string') specifier = specifier.replaceAll(`<${k}>`, v);
|
||||
}
|
||||
|
||||
if (specifier.startsWith(prefix)) {
|
||||
const lookup = specifier.substring(prefix.length);
|
||||
const target = pathToFile.get(lookup);
|
||||
const target = ctx.pathToFile.get(lookup);
|
||||
|
||||
if (target) {
|
||||
specifier = toImportSpecifier(filePath, this.resolveOutputPath(target));
|
||||
} else {
|
||||
io.onWarn(`cannot find the referenced file of ${specifier}`);
|
||||
ctx.io.onWarn(`cannot find the referenced file of ${specifier}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,12 +295,7 @@ export class ComponentInstaller {
|
||||
});
|
||||
|
||||
for (const plugin of this.plugins) {
|
||||
await plugin.transformFile?.({
|
||||
s,
|
||||
parsed,
|
||||
file,
|
||||
component,
|
||||
});
|
||||
await plugin.afterTransform?.(transformCtx);
|
||||
}
|
||||
|
||||
return s.toString();
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { transformSpecifiers } from '@/utils/ast';
|
||||
import type { ComponentInstallerPlugin } from '../installer';
|
||||
|
||||
/**
|
||||
* keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them direclty.
|
||||
*/
|
||||
export function pluginPreserveLayouts(): ComponentInstallerPlugin {
|
||||
const layoutNames = [
|
||||
'layouts/home',
|
||||
'layouts/flux',
|
||||
'layouts/notebook',
|
||||
'layouts/docs',
|
||||
'layouts/shared',
|
||||
];
|
||||
// original specifier -> new specifier
|
||||
const layoutComps: Record<string, string> = {
|
||||
'@/<dir>/layout/home/index.tsx': 'layouts/home',
|
||||
'@/<dir>/layout/shared/index.tsx': 'layouts/shared',
|
||||
'@/<dir>/layout/shared/client.tsx': 'layouts/shared',
|
||||
'@/<dir>/layout/notebook/index.tsx': 'layouts/notebook',
|
||||
'@/<dir>/layout/notebook/client.tsx': 'layouts/notebook',
|
||||
'@/<dir>/layout/notebook/page/index.tsx': 'layouts/notebook/page',
|
||||
'@/<dir>/layout/notebook/page/client.tsx': 'layouts/notebook/page',
|
||||
'@/<dir>/layout/docs/index.tsx': 'layouts/docs',
|
||||
'@/<dir>/layout/docs/client.tsx': 'layouts/docs',
|
||||
'@/<dir>/layout/docs/page/index.tsx': 'layouts/docs/page',
|
||||
'@/<dir>/layout/docs/page/client.tsx': 'layouts/docs/page',
|
||||
'@/<dir>/layout/flux/index.tsx': 'layouts/flux',
|
||||
'@/<dir>/layout/flux/page/index.tsx': 'layouts/flux/page',
|
||||
'@/<dir>/layout/flux/page/client.tsx': 'layouts/flux/page',
|
||||
};
|
||||
const layoutNameSet = new Set(layoutNames);
|
||||
|
||||
return {
|
||||
beforeInstall(comp, { stack }) {
|
||||
const isDirectInstall = layoutNameSet.has(stack[0].name);
|
||||
if (isDirectInstall) return;
|
||||
|
||||
return {
|
||||
...comp,
|
||||
$subComponents: comp.$subComponents.filter((child) => !layoutNameSet.has(child.name)),
|
||||
};
|
||||
},
|
||||
beforeTransform({ parsed, s, stack }) {
|
||||
const isDirectInstall = layoutNameSet.has(stack[0].name);
|
||||
if (isDirectInstall) return;
|
||||
|
||||
transformSpecifiers(parsed.program, s, (specifier) => {
|
||||
// skip if unrelated to layout component
|
||||
if (!(specifier in layoutComps)) return specifier;
|
||||
|
||||
return `fumadocs-ui/${layoutComps[specifier]}`;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -40,23 +40,29 @@ export const componentSchema = z.object({
|
||||
* list of sub components, either local (component name) or remote (registry info & component name)
|
||||
*/
|
||||
subComponents: z.array(z.string().or(httpSubComponent)).default([]),
|
||||
|
||||
/**
|
||||
* override variables for the current component & its sub components.
|
||||
*
|
||||
* this is powerful to customise how sub components are installed vs. installing them directly.
|
||||
*/
|
||||
variables: z.record(z.string(), z.unknown()).optional(),
|
||||
});
|
||||
|
||||
export const registryInfoSchema = z.object({
|
||||
/**
|
||||
* define used variables, variables can be referenced in the import specifiers of component files.
|
||||
* define metadata for variables, variables can be referenced in the target path of component files, or in plugins.
|
||||
*/
|
||||
variables: z
|
||||
.record(
|
||||
z.string(),
|
||||
z.object({
|
||||
description: z.string().optional(),
|
||||
default: z.unknown().optional(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
/**
|
||||
* provide variables to sub components
|
||||
* override variables for all components.
|
||||
*/
|
||||
env: z.record(z.string(), z.unknown()).optional(),
|
||||
indexes: z.array(indexSchema).default([]),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { StructuredData } from '@/mdx-plugins';
|
||||
import { type AnyObject, type OramaCloud } from '@orama/core';
|
||||
import type { AnyObject, OramaCloud } from '@orama/core';
|
||||
|
||||
export interface SyncOptions {
|
||||
/**
|
||||
@@ -129,7 +129,6 @@ function toIndex(page: OramaDocument): OramaIndex[] {
|
||||
id: `${page.id}-${(id++).toString()}`,
|
||||
title: page.title,
|
||||
url: page.url,
|
||||
// TODO: explicit declare enums
|
||||
page_id: page.id,
|
||||
tag: page.tag,
|
||||
section,
|
||||
|
||||
@@ -10,42 +10,6 @@ export function resolveExternal(
|
||||
toPackageName: string,
|
||||
toRegistryDir: string,
|
||||
): Reference | undefined {
|
||||
// TODO: this will cause the installed slots to reference `fumadocs-ui` even if the layout & context itself are already installed locally.
|
||||
// need change from CLI side.
|
||||
if (ref.type === 'sub-component' && ref.resolved.type === 'local') {
|
||||
let specifier: string | undefined;
|
||||
switch (ref.resolved.component.name) {
|
||||
case 'layouts/home':
|
||||
specifier = '/layouts/home';
|
||||
break;
|
||||
case 'layouts/docs':
|
||||
specifier = ref.resolved.file.path.includes('page')
|
||||
? '/layouts/docs/page'
|
||||
: '/layouts/docs';
|
||||
break;
|
||||
case 'layouts/notebook':
|
||||
specifier = ref.resolved.file.path.includes('page')
|
||||
? '/layouts/notebook/page'
|
||||
: '/layouts/notebook';
|
||||
break;
|
||||
case 'layouts/flux':
|
||||
specifier = ref.resolved.file.path.includes('page')
|
||||
? '/layouts/flux/page'
|
||||
: '/layouts/flux';
|
||||
break;
|
||||
case 'layouts/shared':
|
||||
specifier = '/layouts/shared';
|
||||
break;
|
||||
}
|
||||
|
||||
if (specifier)
|
||||
return {
|
||||
type: 'dependency',
|
||||
dep: toPackageName,
|
||||
specifier: toPackageName + specifier,
|
||||
};
|
||||
}
|
||||
|
||||
if (ref.type === 'file') {
|
||||
const file = path.relative(toRegistryDir, ref.file).replaceAll(path.sep, '/');
|
||||
|
||||
@@ -72,7 +36,7 @@ export async function findSlotComponents(dir: string): Promise<Component[]> {
|
||||
{
|
||||
path: file,
|
||||
type: 'components',
|
||||
target: path.join('<dir>/layout', file),
|
||||
target: path.join('<dir>/layout', name),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user