diff --git a/astro.config.mjs b/astro.config.mjs index 2e673d1..9a1e1fe 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,5 +1,6 @@ import { defineConfig } from 'astro/config'; import { dynamicConfig } from './dynamic-astro-config.js'; +import { createRequire } from 'module'; // We are utilizing a dynamic astro config in this // template. When the template is used to create @@ -7,5 +8,22 @@ import { dynamicConfig } from './dynamic-astro-config.js'; // the dynamic configuration will be updated to reflect // the new project +// --- Build-time config logging --- +const require = createRequire(import.meta.url); +const project = require('./src/content/project/project.json'); + +console.log('[AVAnnotate build] project.json raw values:'); +console.log(' project.slug :', project.project.slug); +console.log(' project.github_org :', project.project.github_org); +console.log(' project.media_player :', project.project.media_player); +console.log(' publish.include_slug_in_base:', project.publish.include_slug_in_base); +console.log(' publish.publish_pages_app :', project.publish.publish_pages_app); + +console.log('[AVAnnotate build] resolved Astro config values:'); +console.log(' site :', dynamicConfig.site); +console.log(' base :', dynamicConfig.base); +console.log(' srcDir:', dynamicConfig.srcDir); +// --------------------------------- + // https://astro.build/config export default defineConfig(dynamicConfig); // Do not change! diff --git a/dynamic-astro-config.js b/dynamic-astro-config.js index 9da77fa..68a5c81 100644 --- a/dynamic-astro-config.js +++ b/dynamic-astro-config.js @@ -8,10 +8,21 @@ import relativeLinks from 'astro-relative-links'; import project from './src/content/project/project.json'; +// include_slug_in_base is written by the admin client and is the authoritative +// source; fall back to publish_pages_app for projects generated before this +// field was introduced. +const shouldIncludeSlugInBase = project.publish.include_slug_in_base !== undefined + ? project.publish.include_slug_in_base + : project.publish.publish_pages_app; + export const dynamicConfig = { integrations: [react(), tailwind(), relativeLinks()], - site: import.meta.env.PROD ? `https://${project.project.github_org}.github.io/${project.project.slug}` : undefined, - base: import.meta.env.PROD ? project.publish.publish_pages_app ? `${project.project.slug}` : '' : 'dist', + site: import.meta.env.PROD + ? shouldIncludeSlugInBase + ? `https://${project.project.github_org}.github.io/${project.project.slug}` + : `https://${project.project.github_org}.github.io` + : undefined, + base: import.meta.env.PROD ? (shouldIncludeSlugInBase ? `${project.project.slug}` : '') : 'dist', srcDir: import.meta.env.PROD ? project.project.media_player === 'avannotate' ? './src' : project.project.media_player === 'aviary' ? diff --git a/src/components/Header.astro b/src/components/Header.astro index c89abff..4a2470d 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -1,33 +1,30 @@ --- import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import Container from './Container.astro'; import Sidebar from './Sidebar'; import { getPages } from 'src/utils/pages'; +import { normalizeBasePath } from 'src/utils/basePath'; const projectData = await getEntry('project', 'project'); const pages = await getPages(); const { pageSlug } = Astro.params; -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); +const homePath = basePath === '' ? '/' : `${basePath}/`; ---
- {projectData.data.project.title} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 3e5b8d2..e0a1cdd 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -5,31 +5,31 @@ import type { PageCollectionEntry, ProjectCollectionEntry, } from 'src/utils/pages.ts'; +import { normalizeBasePath } from 'src/utils/basePath'; interface SidebarProps { - baseUrl: string | undefined; + basePath: string; pages: PageCollectionEntry[]; project: ProjectCollectionEntry; slug?: string; url: URL; } -const getHref = (page: PageCollectionEntry, baseUrl: string | undefined) => { +const getHref = (page: PageCollectionEntry, normalizedBasePath: string) => { if (page.data.autogenerate.type === 'home') { - return baseUrl ? `/${baseUrl}` : '/'; + return normalizedBasePath === '' ? '/' : `${normalizedBasePath}/`; } if (page.data.autogenerate.enabled) { - return `${baseUrl ? `/${baseUrl}` : ''}/events/${ - page.data.slug || page.id - }`; + return `${normalizedBasePath}/events/${page.data.slug || page.id}`; } - return `${baseUrl ? `/${baseUrl}` : ''}/pages/${page.data.slug || page.id}`; + return `${normalizedBasePath}/pages/${page.data.slug || page.id}`; }; const Sidebar: React.FC = (props) => { const [show, setShow] = useState(false); + const basePath = normalizeBasePath(props.basePath); const homeUuid = useMemo( () => props.pages.find((p) => p.data.autogenerate.type === 'home')?.id, @@ -37,9 +37,8 @@ const Sidebar: React.FC = (props) => { ); const isIndex = useMemo( - () => - `${props.baseUrl ? `/${props.baseUrl}` : ''}/tags` === props.url.pathname, - [props.baseUrl, props.url] + () => `${basePath}/tags` === props.url.pathname, + [basePath, props.url] ); // highlight the current page @@ -82,7 +81,7 @@ const Sidebar: React.FC = (props) => {
{props.pages.map((page) => ( - +

= (props) => { ))} {props.project.data.project.tags && props.project.data.project.tags.tags.length > 0 && ( - +

Index

diff --git a/src/components/TableOfContents.astro b/src/components/TableOfContents.astro index 782e204..7e6cd7c 100644 --- a/src/components/TableOfContents.astro +++ b/src/components/TableOfContents.astro @@ -1,17 +1,13 @@ --- import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import { getPages } from 'src/utils/pages'; +import { normalizeBasePath } from 'src/utils/basePath'; const projectData = await getEntry('project', 'project'); const projectFileData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData?.data.publish.publish_pages_app - ? projectData?.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const pages = await getPages(); @@ -49,7 +45,7 @@ pages return (

{page.data.title}

@@ -61,7 +57,7 @@ pages projectFileData!.data.project.tags.tags.length > 0 && (
Index diff --git a/src/content/config.ts b/src/content/config.ts index 85a3f3b..5e3b197 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -129,6 +129,7 @@ const projectCollection = defineCollection({ }), publish: z.object({ publish_pages_app: z.boolean(), + include_slug_in_base: z.boolean().nullish(), publish_sha: z.string(), publish_iso_date: z.string(), }), diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 58a1a87..caf6410 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -5,7 +5,6 @@ import '@fontsource-variable/inter'; import Header from '@components/Header.astro'; import Footer from '@components/Footer.astro'; import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import { getCollection } from 'astro:content'; interface Props { @@ -17,10 +16,6 @@ interface Props { const projectData = await getEntry('project', 'project'); const eventData = await getCollection('events'); -const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; - const { background, class: className, @@ -41,7 +36,7 @@ const manifests = eventData.map((e) => { - + diff --git a/src/pages/events/[pageSlug].astro b/src/pages/events/[pageSlug].astro index d2b17f8..a62309e 100644 --- a/src/pages/events/[pageSlug].astro +++ b/src/pages/events/[pageSlug].astro @@ -1,18 +1,10 @@ --- import type { GetStaticPaths } from 'astro'; -import { dynamicConfig } from 'dynamic-astro-config'; import Layout from '@layouts/Layout.astro'; -import { getEntry } from 'astro:content'; import EventViewer from '@components/EventViewer/index.astro'; import { getPage, getPages } from 'src/utils/pages'; import { randomUUID } from 'crypto'; - -const projectData = await getEntry('project', 'project'); - -// @ts-ignore -const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; +import { joinBasePath } from '@utils/basePath'; export const getStaticPaths = (async () => { // filter out custom pages @@ -26,13 +18,13 @@ export const getStaticPaths = (async () => { const { pageSlug } = Astro.params; if (!pageSlug) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(import.meta.env.BASE_URL)); } const page = await getPage(pageSlug); if (!page.data.autogenerate.type_id) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(import.meta.env.BASE_URL)); } const playerId = randomUUID(); diff --git a/src/pages/pages/[pageSlug].astro b/src/pages/pages/[pageSlug].astro index e63609d..5554b2c 100644 --- a/src/pages/pages/[pageSlug].astro +++ b/src/pages/pages/[pageSlug].astro @@ -1,18 +1,10 @@ --- import type { GetStaticPaths } from 'astro'; -import { dynamicConfig } from 'dynamic-astro-config'; import Layout from '@layouts/Layout.astro'; import Container from '@components/Container.astro'; -import { getEntry } from 'astro:content'; import RichText from '../../components/RichText/index.astro'; import { getPage, getPages } from 'src/utils/pages'; - -const projectData = await getEntry('project', 'project'); - -// @ts-ignore -const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; +import { joinBasePath } from '@utils/basePath'; export const getStaticPaths = (async () => { const eventPages = await getPages((p) => !p.data.autogenerate.enabled); @@ -25,7 +17,7 @@ export const getStaticPaths = (async () => { const { pageSlug } = Astro.params; if (!pageSlug) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(import.meta.env.BASE_URL)); } const page = await getPage(pageSlug); diff --git a/src/pages/tags/detail/[category]/[tag].astro b/src/pages/tags/detail/[category]/[tag].astro index 1273d29..f9c4ff5 100644 --- a/src/pages/tags/detail/[category]/[tag].astro +++ b/src/pages/tags/detail/[category]/[tag].astro @@ -17,20 +17,16 @@ import type { CollectionEntry } from 'astro:content'; import { getCollection } from 'astro:content'; import { getEntry } from 'astro:content'; import { randomUUID } from 'crypto'; -import { dynamicConfig } from 'dynamic-astro-config'; import { ArrowLeft } from 'react-bootstrap-icons'; import Annotations from '@components/EventViewer/AnnotationUI/Annotations/index.astro'; import AnnotationNanostorePopulator from '@components/EventViewer/AnnotationUI/Annotations/AnnotationNanostorePopulator'; import type { DisplayedAnnotation } from '@ty/index'; import PlayerStateNanostorePopulator from '@components/EventViewer/PlayerStateNanostorePopulator'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); export const getStaticPaths = (async () => { const projectData = await getEntry('project', 'project'); @@ -50,19 +46,19 @@ const capitalizedTag = getTagDisplay(fromTagParam(tag)); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags`, + link: joinBasePath(basePath, 'tags'), }, { label: 'Tag Groups', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags/detail`, + link: joinBasePath(basePath, 'tags/detail'), }, { label: capitalizedCategory, - link: `${baseUrl ? `/${baseUrl}` : ''}/tags/detail/${category}`, + link: joinBasePath(basePath, `tags/detail/${category}`), }, { label: capitalizedTag, @@ -81,7 +77,7 @@ const categoryData = projectData.data.project.tags.tagGroups.find( ); if (!categoryData) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } type SetData = { @@ -117,7 +113,7 @@ for (const set of annotationSets) {
diff --git a/src/pages/tags/detail/[category]/index.astro b/src/pages/tags/detail/[category]/index.astro index a1676f1..85591bd 100644 --- a/src/pages/tags/detail/[category]/index.astro +++ b/src/pages/tags/detail/[category]/index.astro @@ -8,7 +8,6 @@ import Layout from '@layouts/Layout.astro'; import type { GetStaticPaths } from 'astro'; import { getCollection } from 'astro:content'; import { getEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config'; import { ArrowLeft, ArrowCounterclockwise } from 'react-bootstrap-icons'; import { tagColors as colors, @@ -20,14 +19,11 @@ import { sortTags, toTagParam, } from '@utils/tags.ts'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); export const getStaticPaths = (async () => { const projectData = await getEntry('project', 'project'); @@ -43,15 +39,15 @@ const capitalizedCategory = getTagDisplay(fromTagParam(category)); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags`, + link: joinBasePath(basePath, 'tags'), }, { label: 'Tag Groups', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags/detail`, + link: joinBasePath(basePath, 'tags/detail'), }, { label: capitalizedCategory, @@ -70,7 +66,7 @@ const categoryData = projectData.data.project.tags.tagGroups.find( ); if (!categoryData) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } const tags = projectData.data.project.tags.tags @@ -95,7 +91,7 @@ tags.forEach((t, idx) => (tagColors[t] = colors[idx % colors.length]));
diff --git a/src/pages/tags/detail/index.astro b/src/pages/tags/detail/index.astro index be09ea9..c75fa71 100644 --- a/src/pages/tags/detail/index.astro +++ b/src/pages/tags/detail/index.astro @@ -1,7 +1,6 @@ --- import Layout from '@layouts/Layout.astro'; import Container from '@components/Container.astro'; -import { dynamicConfig } from '../../../../dynamic-astro-config'; import Breadcrumbs from '@components/Breadcrumbs.astro'; import { getEntry } from 'astro:content'; import { getCollection } from 'astro:content'; @@ -15,23 +14,20 @@ import { setHasCategory, setsHasCategory, } from '@utils/tags'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', - link: `${baseUrl ? `/${baseUrl}` : ''}/tags`, + link: joinBasePath(basePath, 'tags'), }, { label: 'Tag Groups', @@ -44,7 +40,7 @@ annotationSets.sort((a, b) => compareAnnotations(a, b, events)); // check whether there actually are any annotation files if (annotationSets.length === 0) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } const formattedCategoryNames = projectData.data.project.tags.tagGroups.map( (cat) => getTagDisplay(cat.category) @@ -104,7 +100,10 @@ const sortedTagGroups = projectData.data.project.tags.tagGroups.sort((a, b) => View Details diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro index 9bd2001..d7c2661 100644 --- a/src/pages/tags/index.astro +++ b/src/pages/tags/index.astro @@ -1,24 +1,20 @@ --- import Layout from '../../layouts/Layout.astro'; import Container from '../../components/Container.astro'; -import { dynamicConfig } from '../../../dynamic-astro-config'; import Breadcrumbs from '../../components/Breadcrumbs.astro'; import TagPill from '@components/TagUtils/TagPill'; import { getEntry } from 'astro:content'; import { getCollection } from 'astro:content'; import { toTagParam, sortTags } from '@utils/tags'; +import { joinBasePath, normalizeBasePath } from '@utils/basePath'; const projectData = await getEntry('project', 'project'); -const baseUrl = import.meta.env.PROD - ? projectData.data.publish.publish_pages_app - ? projectData.data.project.slug - : undefined - : dynamicConfig.base; +const basePath = normalizeBasePath(import.meta.env.BASE_URL); const crumbs = [ { label: 'Home', - link: `${baseUrl ? `/${baseUrl}` : '/'}`, + link: joinBasePath(basePath), }, { label: 'Index', @@ -30,7 +26,7 @@ const tags = projectData.data.project.tags; //check whether there actually are any annotation files const annotationSets = await getCollection('annotations'); if (!Object.keys(annotationSets).length) { - return Astro.redirect(`/${baseUrl}`); + return Astro.redirect(joinBasePath(basePath)); } const detectTag = ( @@ -98,7 +94,7 @@ tags.tags.sort(sortTags).forEach((tag: { tag: string; category: string }) => {

Index

View Details @@ -117,7 +113,10 @@ tags.tags.sort(sortTags).forEach((tag: { tag: string; category: string }) => { tag.count ? ( { + const trimmedBasePath = basePath.trim(); + + if (trimmedBasePath === '' || trimmedBasePath === '/') { + return ''; + } + + const noTrailingSlash = trimmedBasePath.replace(/\/$/, ''); + return noTrailingSlash.startsWith('/') ? noTrailingSlash : `/${noTrailingSlash}`; +}; + +export const joinBasePath = (basePath: string, segment = '') => { + const normalizedBasePath = normalizeBasePath(basePath); + const normalizedSegment = segment.trim().replace(/^\/+|\/+$/g, ''); + + if (normalizedSegment === '') { + return normalizedBasePath === '' ? '/' : normalizedBasePath; + } + + return `${normalizedBasePath}/${normalizedSegment}`; +}; diff --git a/src/utils/events.ts b/src/utils/events.ts index 0def31b..935526b 100644 --- a/src/utils/events.ts +++ b/src/utils/events.ts @@ -1,17 +1,10 @@ -import { getEntry, type CollectionEntry } from 'astro:content'; -import { dynamicConfig } from 'dynamic-astro-config.js'; +import { type CollectionEntry } from 'astro:content'; export const getCaptionSets = async ( event: CollectionEntry<'events'>, file: string, annotationSets: CollectionEntry<'annotations'>[] ) => { - const projectData = await getEntry('project', 'project'); - - const baseUrl = import.meta.env.PROD - ? projectData?.data.project.slug - : dynamicConfig.base; - let captionSets: { url: string; label: string }[]; const eventCaptionSet = event.data.audiovisual_files[file]?.caption_set; @@ -20,7 +13,7 @@ export const getCaptionSets = async ( captionSets = eventCaptionSet.map((s: any) => { const set = annotationSets.find((set) => set.id === s.annotation_page_id); return { - url: `/${baseUrl}/${s.annotation_page_id}.vtt`, + url: `${import.meta.env.BASE_URL}${s.annotation_page_id}.vtt`, label: set?.data.set || '', }; });