mirror of
https://github.com/lucide-icons/lucide.git
synced 2026-11-10 13:18:25 +00:00
fix(site): store detail state in url
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { PageData } from 'vitepress';
|
||||
|
||||
export default async function getStructuredData(iconName: string, pageData: PageData) {
|
||||
const url = `https://lucide.dev/icons/${iconName}`;
|
||||
const url = `https://lucide.dev/icons/${iconName}/details`;
|
||||
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
|
||||
@@ -87,8 +87,8 @@ const Expand = createLucideIcon('Expand', expand);
|
||||
@click="
|
||||
go(
|
||||
icon.externalLibrary
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}`
|
||||
: `/icons/${icon.name}`,
|
||||
? `/icons/${icon.externalLibrary}/${icon.name}/details`
|
||||
: `/icons/${icon.name}/details`,
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
@@ -33,12 +33,21 @@ const deprecatedTitle = computed(() => {
|
||||
});
|
||||
});
|
||||
|
||||
const isDetailsPage = computed(() => {
|
||||
const relativePath = page.value?.relativePath ?? '';
|
||||
const name = props.icon.name;
|
||||
if (props.icon.externalLibrary) {
|
||||
return relativePath === `icons/${props.icon.externalLibrary}/${name}/details.md`;
|
||||
}
|
||||
return relativePath === `icons/${name}/details.md`;
|
||||
});
|
||||
|
||||
const iconPath = computed(() => {
|
||||
if (props.icon.externalLibrary) {
|
||||
return `icons/${props.icon.externalLibrary}/${props.icon.name}`;
|
||||
return `/icons/${props.icon.externalLibrary}/${props.icon.name}/details`;
|
||||
}
|
||||
|
||||
return `icons/${props.icon.name}`;
|
||||
return `/icons/${props.icon.name}/details`;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -79,7 +88,7 @@ const iconPath = computed(() => {
|
||||
|
||||
<div class="group buttons">
|
||||
<VPButton
|
||||
v-if="!page?.relativePath?.startsWith?.(iconPath)"
|
||||
v-if="!isDetailsPage"
|
||||
:href="iconPath"
|
||||
text="See in action"
|
||||
@click="go(iconPath)"
|
||||
|
||||
@@ -34,9 +34,17 @@ const icon = computed(() => {
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
});
|
||||
|
||||
const href = computed(() =>
|
||||
props.externalLibrary ? `/icons/${props.externalLibrary}/${props.name}` : `/icons/${props.name}`,
|
||||
);
|
||||
const href = computed(() => {
|
||||
if (props.overlayMode) {
|
||||
return props.externalLibrary
|
||||
? `/icons/${props.externalLibrary}/${props.name}`
|
||||
: `/icons/${props.name}`;
|
||||
}
|
||||
|
||||
return props.externalLibrary
|
||||
? `/icons/${props.externalLibrary}/${props.name}/details`
|
||||
: `/icons/${props.name}/details`;
|
||||
});
|
||||
|
||||
async function navigateToIcon(event) {
|
||||
if (event.shiftKey) {
|
||||
@@ -55,24 +63,14 @@ async function navigateToIcon(event) {
|
||||
if (props.overlayMode && showOverlay.value) {
|
||||
event.preventDefault();
|
||||
|
||||
window.history.pushState(
|
||||
{},
|
||||
'',
|
||||
props.externalLibrary
|
||||
? `/icons/${props.externalLibrary}/${props.name}`
|
||||
: `/icons/${props.name}`,
|
||||
);
|
||||
window.history.pushState({}, '', href.value);
|
||||
emit(
|
||||
'setActiveIcon',
|
||||
props.externalLibrary ? `${props.externalLibrary}:${props.name}` : props.name,
|
||||
);
|
||||
} else {
|
||||
event.preventDefault();
|
||||
go(
|
||||
props.externalLibrary
|
||||
? `/icons/${props.externalLibrary}/${props.name}`
|
||||
: `/icons/${props.name}`,
|
||||
);
|
||||
go(href.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,9 +137,28 @@ useScrollToCategory({
|
||||
searchQueryDebounced,
|
||||
});
|
||||
|
||||
function onPopState() {
|
||||
if (typeof window === 'undefined') return;
|
||||
const pathname = window.location.pathname;
|
||||
const match = pathname.match(/^\/icons\/(?:(lab)\/)?([^/]+)\/?$/);
|
||||
if (match) {
|
||||
const [, lib, name] = match;
|
||||
if (name && name !== 'categories' && name !== 'lab') {
|
||||
activeIconName.value = lib ? `${lib}:${name}` : name;
|
||||
return;
|
||||
}
|
||||
}
|
||||
activeIconName.value = null;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
containerProps.ref.value = document.documentElement;
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
useEventListener(window, 'popstate', onPopState);
|
||||
|
||||
if (!activeIconName.value && typeof window !== 'undefined') {
|
||||
onPopState();
|
||||
}
|
||||
});
|
||||
|
||||
function loadSearchMetadata() {
|
||||
|
||||
@@ -46,11 +46,21 @@ const initialGridItems = computed(() => {
|
||||
|
||||
const props = defineProps<{
|
||||
icons: IconEntity[];
|
||||
activeIcon?: string | null;
|
||||
}>();
|
||||
|
||||
const activeIconName = ref(null);
|
||||
const activeIconName = ref<string | null>(props.activeIcon ?? null);
|
||||
const selectedSort = ref(SORTING[0])
|
||||
|
||||
watch(
|
||||
() => props.activeIcon,
|
||||
(newVal) => {
|
||||
if (newVal !== undefined) {
|
||||
activeIconName.value = newVal;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
@@ -132,9 +142,28 @@ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(chunkedI
|
||||
overscan: 10,
|
||||
});
|
||||
|
||||
function onPopState() {
|
||||
if (typeof window === 'undefined') return;
|
||||
const pathname = window.location.pathname;
|
||||
const match = pathname.match(/^\/icons\/(?:(lab)\/)?([^/]+)\/?$/);
|
||||
if (match) {
|
||||
const [, lib, name] = match;
|
||||
if (name && name !== 'categories' && name !== 'lab') {
|
||||
activeIconName.value = lib ? `${lib}:${name}` : name;
|
||||
return;
|
||||
}
|
||||
}
|
||||
activeIconName.value = null;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
containerProps.ref.value = document.documentElement;
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
useEventListener(window, 'popstate', onPopState);
|
||||
|
||||
if (!activeIconName.value && typeof window !== 'undefined') {
|
||||
onPopState();
|
||||
}
|
||||
|
||||
// Check if we should focus the search input from URL parameter
|
||||
const route = useRoute();
|
||||
|
||||
@@ -10,47 +10,54 @@ export async function transformPageData(pageData: PageData) {
|
||||
pageData.params?.name
|
||||
) {
|
||||
const iconName = pageData.params.name;
|
||||
pageData.title = `${iconName} icon details`;
|
||||
const isDetailsPage = pageData.relativePath.includes('details');
|
||||
|
||||
const taggedAs = pageData.params?.tags?.length
|
||||
? `Tagged as: ${pageData.params.tags.join(', ')}.`
|
||||
: '';
|
||||
const categorizedIn = pageData.params?.category?.length
|
||||
? `Categorized in: ${pageData.params.category.join(', ')}.`
|
||||
: '';
|
||||
if (isDetailsPage) {
|
||||
pageData.title = `${iconName} icon details`;
|
||||
|
||||
pageData.description =
|
||||
`Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim();
|
||||
const taggedAs = pageData.params?.tags?.length
|
||||
? `Tagged as: ${pageData.params.tags.join(', ')}.`
|
||||
: '';
|
||||
const categorizedIn = pageData.params?.category?.length
|
||||
? `Categorized in: ${pageData.params.category.join(', ')}.`
|
||||
: '';
|
||||
|
||||
const structuredData = await getStructuredData(iconName, pageData);
|
||||
pageData.description =
|
||||
`Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim();
|
||||
|
||||
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
|
||||
const structuredData = await getStructuredData(iconName, pageData);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
const ogPath = await createIconOGImage(iconName, pageData.params?.tags || []);
|
||||
|
||||
if (ogPath) {
|
||||
const content = `https://lucide.dev${ogPath}`;
|
||||
pageData.frontmatter.head.push(
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'twitter:image',
|
||||
content,
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
pageData.frontmatter.head.push([
|
||||
'script',
|
||||
{ type: 'application/ld+json' },
|
||||
JSON.stringify(structuredData),
|
||||
]);
|
||||
} else {
|
||||
pageData.title = `${iconName} icon`;
|
||||
pageData.description = `Browse and customize the ${iconName} icon.`;
|
||||
}
|
||||
|
||||
pageData.frontmatter.head.push([
|
||||
'script',
|
||||
{ type: 'application/ld+json' },
|
||||
JSON.stringify(structuredData),
|
||||
]);
|
||||
}
|
||||
|
||||
if (pageData.relativePath.startsWith('guide/')) {
|
||||
|
||||
+14
-250
@@ -1,260 +1,24 @@
|
||||
---
|
||||
layout: doc
|
||||
footer: false
|
||||
aside: false
|
||||
editLink: false
|
||||
next: false
|
||||
prev: false
|
||||
title: Icons
|
||||
description: Browse all Lucide icons.
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Categories
|
||||
sidebar: true
|
||||
---
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import IconPreview from '~/.vitepress/theme/components/icons/IconPreview.vue'
|
||||
import IconPreviewSmall from '~/.vitepress/theme/components/icons/IconPreviewSmall.vue'
|
||||
import EditInStudioButton from '~/.vitepress/theme/components/icons/EditInStudioButton.vue'
|
||||
import IconInfo from '~/.vitepress/theme/components/icons/IconInfo.vue'
|
||||
import IconContributors from '~/.vitepress/theme/components/icons/IconContributors.vue'
|
||||
import IconShowcase from '~/.vitepress/theme/components/icons/IconShowcase.vue'
|
||||
import RelatedIcons from '~/.vitepress/theme/components/icons/RelatedIcons.vue'
|
||||
import CodeGroup from '~/.vitepress/theme/components/base/CodeGroup.vue'
|
||||
import Badge from '~/.vitepress/theme/components/base/Badge.vue'
|
||||
import Label from '~/.vitepress/theme/components/base/Label.vue'
|
||||
import PageTabs from '~/.vitepress/theme/components/base/PageTabs.vue'
|
||||
import { data } from './codeExamples.data'
|
||||
import { toCamelCase, toPascalCase } from '@lucide/shared'
|
||||
import { satisfies } from 'semver'
|
||||
import CarbonAdOverlay from '~/.vitepress/theme/components/icons/CarbonAdOverlay.vue';
|
||||
import { data } from './icons.data.ts'
|
||||
import IconsOverview from '~/.vitepress/theme/components/icons/IconsOverview.vue'
|
||||
import PageContainer from '~/.vitepress/theme/components/PageContainer.vue'
|
||||
import useIconsWithExternalLibs from '~/.vitepress/theme/composables/useIconsWithExternalLibs'
|
||||
|
||||
const { params } = useData()
|
||||
|
||||
const tabs = computed(() => data.codeExamples?.map(
|
||||
(codeExample) => codeExample.title) ?? []
|
||||
)
|
||||
|
||||
const codeExample = computed(() => data.codeExamples?.map(
|
||||
(codeExample) => {
|
||||
const pascalCaseName = toPascalCase( params.value.name)
|
||||
const camelCaseName = toCamelCase(params.value.name)
|
||||
|
||||
return codeExample.code
|
||||
.replace(/\$(?:<[^>]+>)*PascalCase/g, pascalCaseName)
|
||||
.replace(/\$CamelCase/g, camelCaseName)
|
||||
.replace(/\$Name/g, params.value.name)
|
||||
}
|
||||
).join('') ?? []
|
||||
)
|
||||
|
||||
function releaseTagLink(version) {
|
||||
const shouldAddV = satisfies(version, `<0.266.0`)
|
||||
|
||||
return `https://github.com/lucide-icons/lucide/releases/tag/${shouldAddV ? 'v' : ''}${version}`
|
||||
}
|
||||
const icons = useIconsWithExternalLibs(data.icons)
|
||||
</script>
|
||||
|
||||
<div class="layout">
|
||||
<div class="actions">
|
||||
<EditInStudioButton :icon="params" />
|
||||
</div>
|
||||
<div class="iconPreviews">
|
||||
<IconPreview
|
||||
id="previewer"
|
||||
:name="params.name"
|
||||
:iconNode="params.iconNode"
|
||||
class="preview"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="info">
|
||||
<IconInfo :icon="params" />
|
||||
<div class="meta">
|
||||
<div
|
||||
v-if="params.awaitingRelease"
|
||||
class="version"
|
||||
>
|
||||
<Label>Created:</Label>
|
||||
<Badge class="awaiting-release">Unreleased</Badge>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="params.createdRelease?.version"
|
||||
class="version"
|
||||
>
|
||||
<Label>Created:</Label>
|
||||
<Badge
|
||||
:href="releaseTagLink(params.createdRelease.version)"
|
||||
>
|
||||
{{params.createdRelease.version}}
|
||||
</Badge>
|
||||
</div>
|
||||
<div
|
||||
v-if="!params.awaitingRelease && params.changedRelease?.version"
|
||||
class="version"
|
||||
>
|
||||
<Label>Last changed:</Label>
|
||||
<Badge
|
||||
:href="releaseTagLink(params.changedRelease.version)"
|
||||
>
|
||||
{{params.changedRelease.version}}
|
||||
</Badge>
|
||||
</div>
|
||||
<IconContributors :icon="params" class="contributors"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="VPDoc content">
|
||||
<PageContainer>
|
||||
<IconsOverview :icons="icons" :activeIcon="params?.name" />
|
||||
</PageContainer>
|
||||
</div>
|
||||
|
||||
<PageTabs
|
||||
:tabs="['More like this', 'Code examples', 'See in action']"
|
||||
>
|
||||
<template #tab-0>
|
||||
<RelatedIcons
|
||||
v-if="params.relatedIcons"
|
||||
:icons="params.relatedIcons"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #tab-1>
|
||||
<CodeGroup
|
||||
:groups="tabs"
|
||||
groupName="icon-code-example"
|
||||
class="code"
|
||||
>
|
||||
<div
|
||||
class="blocks"
|
||||
v-html="codeExample"
|
||||
/>
|
||||
</CodeGroup>
|
||||
</template>
|
||||
|
||||
<template #tab-2>
|
||||
<IconShowcase
|
||||
:name="params.name"
|
||||
:iconNode="params.iconNode"
|
||||
/>
|
||||
</template>
|
||||
</PageTabs>
|
||||
|
||||
<CarbonAdOverlay :drawerOpen="false" />
|
||||
|
||||
<style scoped>
|
||||
.preview {
|
||||
grid-area: preview;
|
||||
margin-bottom: 24px;
|
||||
max-width: 240px;
|
||||
width: 240px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.layout {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: -12px -12px 24px 0;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.info {
|
||||
--tags-gradient-background: var(--vp-c-bg);
|
||||
|
||||
}
|
||||
|
||||
.icon-info {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.version .label {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.version, .contributors {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0px;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.version .awaiting-release {
|
||||
color: var(--vp-c-danger-3);
|
||||
}
|
||||
|
||||
.iconPreviews {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.smallPreview {
|
||||
flex-shrink: 2;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.layout {
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
grid-template-columns: 240px minmax(0, 1fr);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.iconPreviews {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.actions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.smallPreview {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.icon-page-sections h2.title {
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
margin-block-end: 64px;
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
.tab-list {
|
||||
margin: 24px -24px 24px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.tab-list {
|
||||
margin: 24px 0 24px;
|
||||
}
|
||||
|
||||
.iconPreviews {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import relatedIcons from '../.vitepress/data/relatedIcons.json';
|
||||
import iconNodes from '../.vitepress/data/iconNodes';
|
||||
import * as iconDetails from '../.vitepress/data/iconDetails';
|
||||
import { IconEntity } from '../.vitepress/theme/types';
|
||||
|
||||
export default {
|
||||
paths: async () => {
|
||||
return (Object.values(iconDetails) as unknown as IconEntity[]).map((iconEntity) => {
|
||||
const params = {
|
||||
...iconEntity,
|
||||
relatedIcons: relatedIcons[iconEntity.name].map((name: string) => ({
|
||||
name,
|
||||
iconNode: iconNodes[name],
|
||||
})),
|
||||
};
|
||||
|
||||
return {
|
||||
params,
|
||||
};
|
||||
});
|
||||
return Object.keys(iconNodes).map((name) => ({
|
||||
params: {
|
||||
name,
|
||||
},
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
---
|
||||
layout: doc
|
||||
footer: false
|
||||
aside: false
|
||||
editLink: false
|
||||
next: false
|
||||
prev: false
|
||||
title: Icons
|
||||
description: Browse all Lucide icons.
|
||||
layout: page
|
||||
outline: 2
|
||||
outlineTitle: Categories
|
||||
sidebar: true
|
||||
---
|
||||
<!--@include: ../[name].md -->
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
import { data } from '../icons.data.ts'
|
||||
import IconsOverview from '~/.vitepress/theme/components/icons/IconsOverview.vue'
|
||||
import PageContainer from '~/.vitepress/theme/components/PageContainer.vue'
|
||||
import useIconsWithExternalLibs from '~/.vitepress/theme/composables/useIconsWithExternalLibs'
|
||||
|
||||
const { params } = useData()
|
||||
const icons = useIconsWithExternalLibs(data.icons)
|
||||
</script>
|
||||
|
||||
<div class="VPDoc content">
|
||||
<PageContainer>
|
||||
<IconsOverview :icons="icons" :activeIcon="params?.name ? `lab:${params.name}` : undefined" />
|
||||
</PageContainer>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
import iconNodes from '../../.vitepress/data/lab/iconNodes';
|
||||
import iconPopularity from '../../.vitepress/data/lab/iconPopularity';
|
||||
import iconMetaData from '../../.vitepress/data/lab/iconMetaData';
|
||||
|
||||
export default {
|
||||
paths: async () => {
|
||||
return Object.entries(iconNodes).map(([name, iconNode]) => {
|
||||
const iconDetails = iconMetaData[name];
|
||||
|
||||
delete iconDetails['$schema'];
|
||||
|
||||
const params = {
|
||||
return Object.keys(iconNodes).map((name) => ({
|
||||
params: {
|
||||
name,
|
||||
iconNode,
|
||||
externalLibrary: 'lab',
|
||||
popularity: iconPopularity[name]?.count ?? 0,
|
||||
...iconDetails,
|
||||
};
|
||||
|
||||
return {
|
||||
params,
|
||||
};
|
||||
});
|
||||
},
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -10,16 +10,27 @@ const iconMetaData = await getIconMetaData(path.resolve(scriptDir, '../../icons'
|
||||
|
||||
const iconAliasesRedirectRoutes = Object.entries(iconMetaData)
|
||||
.filter(([, { aliases }]) => aliases?.length)
|
||||
.map(([iconName, { aliases }]) => {
|
||||
const aliasRouteMatches = aliases.map((alias) => typeof alias === 'string' ? alias : alias?.name).join('|');
|
||||
.flatMap(([iconName, { aliases }]) => {
|
||||
const aliasRouteMatches = aliases
|
||||
.map((alias) => (typeof alias === 'string' ? alias : alias?.name))
|
||||
.join('|');
|
||||
|
||||
return {
|
||||
src: `^/icons/(${aliasRouteMatches})$`,
|
||||
status: 308,
|
||||
headers: {
|
||||
Location: `/icons/${iconName}`,
|
||||
return [
|
||||
{
|
||||
src: `^/icons/(${aliasRouteMatches})$`,
|
||||
status: 308,
|
||||
headers: {
|
||||
Location: `/icons/${iconName}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
{
|
||||
src: `^/icons/(${aliasRouteMatches})/details$`,
|
||||
status: 308,
|
||||
headers: {
|
||||
Location: `/icons/${iconName}/details`,
|
||||
},
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const vercelOutputJSON = path.resolve(currentDir, '.vercel/output/config.json');
|
||||
|
||||
Reference in New Issue
Block a user