this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

♻️ Improve tech/tag alias handling

+71 -76
+17 -31
src/aliases.ts
··· 1 - const IS_ALIAS_TAG = "__is_alias"; 2 - 3 - /** 4 - * Create aliased entries for a given entry 5 - * @param aliasOf the slug of the entry this one is an alias of 6 - * @returns the aliases array for that aliased entry 7 - */ 8 - export function makeAliasEntries< 9 - Entry extends { aliases?: string[] | undefined; slug?: string | undefined }, 10 - >(entry: Entry) { 11 - return [ 12 - entry, 13 - ...(entry.aliases ?? []).map((alias) => ({ 14 - ...entry, 15 - slug: alias, 16 - aliases: [IS_ALIAS_TAG, entry.slug], 17 - })), 18 - ]; 19 - } 20 - 21 - export function resolveAliased(entry: { 22 - aliases?: null | string[]; 23 - slug: string; 24 - }) { 25 - if (!entry.aliases) return entry.slug; 26 - if (entry.aliases.includes(IS_ALIAS_TAG) && entry.aliases.length === 2) 27 - return entry.aliases[1]; 28 - return entry.slug; 29 - } 1 + export function resolveAliased(query: string, entries: Array<{ id: string }>) { 2 + const isAliasOf = (e: (typeof entries)[number]) => 3 + "isAliasOf" in e 4 + ? e.isAliasOf?.toString() 5 + : "data" in e && 6 + e.data && 7 + typeof e.data === "object" && 8 + "isAliasOf" in e.data 9 + ? e.data?.isAliasOf?.toString() 10 + : undefined; 30 11 31 - export function isAliased(entry: { aliases?: null | string[]; slug: string }) { 32 - return resolveAliased(entry) !== entry.slug; 12 + const entry = entries.find((e) => e.id === query || isAliasOf(e) === query); 13 + if (query === "library") 14 + console.log("resolveAliased", { 15 + query, 16 + aliasmap: Object.fromEntries(entries.map((e) => [e.id, isAliasOf(e)])), 17 + }); 18 + return entry ? (isAliasOf(entry) ?? entry?.id) : undefined; 33 19 }
+10 -2
src/components/TagReference.astro
··· 1 1 --- 2 2 import { getEntry } from "astro:content"; 3 - import { resolveAliased } from "../aliases"; 4 3 5 4 interface Props { 6 5 tag: string | { id: string; collection: "tags" }; ··· 9 8 const tag = await getEntry( 10 9 "tags", 11 10 typeof Astro.props.tag === "string" ? Astro.props.tag : Astro.props.tag.id, 12 - )?.then((tag) => (tag ? { ...tag.data, slug: resolveAliased(tag.data) } : "")); 11 + )?.then((tag) => 12 + tag 13 + ? { 14 + ...tag.data, 15 + slug: 16 + (tag.data as unknown as { isAliasOf: string | null }).isAliasOf ?? 17 + tag.id, 18 + } 19 + : "", 20 + ); 13 21 --- 14 22 15 23 {tag && <a href={`/${tag.slug}`}>#{tag.singular}</a>}
+24 -30
src/content.config.ts
··· 1 1 import { file, glob } from "astro/loaders"; 2 - import type { ZodObject, ZodRawShape } from "astro/zod"; 2 + import type { ZodNullable, ZodObject, ZodRawShape, ZodString } from "astro/zod"; 3 3 import { defineCollection, reference, z } from "astro:content"; 4 4 import * as YAML from "yaml"; 5 5 import PO from "pofile"; 6 - import { makeAliasEntries } from "./aliases"; 7 6 import { wakatimeCollection } from "./wakatime"; 8 7 import { readFile } from "node:fs/promises"; 9 8 import slug from "slug"; ··· 195 194 tags: yamlDataCollection( 196 195 "tags.yaml", 197 196 z.object({ 197 + isAliasOf: z.string().nullable().default(null), 198 198 slug: z.string(), 199 199 singular: z.string(), 200 200 plural: z.string(), 201 201 description: z.string().optional(), 202 - aliases: z.array(z.string()).optional().default([]), 203 202 "learn more at": z.string().url().optional(), 204 203 detect: z 205 204 .object({ ··· 208 207 .optional(), 209 208 }), 210 209 (tag) => tag.plural, 211 - (tag) => [tag.singular], 210 + (tag) => [...tag.aliases, tag.singular], 212 211 ), 213 212 technologies: yamlDataCollection( 214 213 "technologies.yaml", 215 214 z.object({ 215 + isAliasOf: z.string().nullable().default(null), 216 216 slug: z.string(), 217 217 name: z.string(), 218 218 by: z.string().optional(), 219 219 files: z.array(z.string()).optional(), 220 220 "learn more at": z.string().url().optional(), 221 221 description: z.string().optional(), 222 - aliases: z.array(z.string()).optional(), 223 222 autodetect: z.array(z.string()).optional(), 224 223 }), 224 + (tech) => tech.name, 225 + (tech) => tech.aliases, 225 226 ), 226 227 }; 227 228 ··· 232 233 filename: string, 233 234 schema: Schema, 234 235 slugify?: (data: z.infer<Schema>) => string, 235 - additionalAliases?: (data: z.infer<Schema>) => string[], 236 + aliases?: (data: z.infer<Schema> & { aliases: string[] }) => string[], 236 237 ) { 237 238 return defineCollection({ 238 239 schema, ··· 246 247 watcher?.add(filename); 247 248 248 249 if (Array.isArray(parsed)) { 249 - const entries = parsed 250 - .map((data) => { 251 - const out: z.infer<Schema> & { 252 - slug?: string; 253 - aliases?: string[]; 254 - } = { ...data }; 250 + const entries = parsed.flatMap((data) => { 251 + const out: z.infer<Schema> & { 252 + slug?: string; 253 + } = { ...data }; 255 254 256 - if (slugify) out.slug ??= slugify(data); 255 + const aliasIds = aliases?.({ aliases: [], ...data }) ?? []; 257 256 258 - const aliasable = z 259 - .object({ 260 - aliases: z.array(z.string()), 261 - slug: z.string(), 262 - }) 263 - .safeParse(out); 257 + if (slugify) out.slug ??= slugify(data); 264 258 265 - if (aliasable.success || additionalAliases) { 266 - out.aliases ??= []; 267 - if (additionalAliases) { 268 - out.aliases = [...out.aliases, ...additionalAliases(data)]; 269 - } 270 - return makeAliasEntries(out); 271 - } 272 - 273 - return [out]; 274 - }) 275 - .flat(); 259 + return [ 260 + { ...out, isAliasOf: null }, 261 + ...aliasIds.map((slug) => ({ 262 + ...out, 263 + slug, 264 + isAliasOf: out.slug, 265 + })), 266 + ]; 267 + }); 276 268 277 269 store.clear(); 278 270 for (const entry of entries) { 271 + if (entry.slug === "library") 272 + console.log("store.set", entry.slug, entry.isAliasOf); 279 273 store.set({ 280 274 id: entry.slug ?? slug(entry.title), 281 275 data: entry,
+11 -5
src/pages/collections/[collection].astro
··· 1 1 --- 2 2 import type { GetStaticPaths } from "astro"; 3 3 import { getCollection, getEntry } from "astro:content"; 4 - import Translated from "../../components/Translated.astro"; 4 + import picomatch from "picomatch"; 5 + import { resolveAliased } from "../../aliases.ts"; 5 6 import StrongHeader from "../../components/StrongHeader.astro"; 6 7 import WorksGrid from "../../components/WorksGrid.astro"; 7 8 import Layout from "../../layouts/Regular.astro"; 8 - import picomatch from "picomatch"; 9 9 10 10 export const getStaticPaths = (async () => { 11 11 const tags = await getCollection("collections"); 12 12 return tags.map((t) => ({ params: { collection: t.id } })); 13 13 }) satisfies GetStaticPaths; 14 + 15 + const tags = await getCollection("tags"); 14 16 15 17 const entry = await getEntry( 16 18 "collections", ··· 25 27 work: { 26 28 id: string; 27 29 metadata: { 28 - aliases: string[] | undefined; 29 - tags: Array<{ id: string; collection: "tags" }>; 30 + aliases: string[] | null; 31 + tags: Array<{ id: string; collection: "tags" }> | null; 30 32 }; 31 33 }, 32 34 ) { ··· 38 40 const aliases = work.metadata.aliases ?? []; 39 41 return [work.id, ...aliases].some(picomatch(term)); 40 42 } 41 - return work.metadata.tags?.some((t) => t.id === term.replace(/^#/, "")); 43 + 44 + const tagToTest = resolveAliased(term.replace(/^#/, ""), tags); 45 + return work.metadata.tags?.some( 46 + (t) => resolveAliased(t.id, tags) === tagToTest, 47 + ); 42 48 }); 43 49 }); 44 50 }
+9 -8
src/pages/tags/[tag].astro
··· 4 4 import StrongHeader from "../../components/StrongHeader.astro"; 5 5 import WorksGrid from "../../components/WorksGrid.astro"; 6 6 import Layout from "../../layouts/Regular.astro"; 7 + import { resolveAliased } from "../../aliases"; 7 8 8 9 export const getStaticPaths = (async () => { 9 10 const tags = await getCollection("tags"); 10 11 return tags.map((t) => ({ params: { tag: t.id } })); 11 12 }) satisfies GetStaticPaths; 13 + 14 + const tags = await getCollection("tags"); 15 + 16 + console.log(tags.find((t) => t.id === "library")); 12 17 13 18 const entry = await getEntry("tags", Astro.params.tag!.toString()); 14 19 if (!entry) return Astro.rewrite("/404"); 15 20 16 - const { 17 - plural, 18 - "learn more at": learnMoreAt, 19 - description, 20 - aliases, 21 - slug, 22 - } = entry.data; 21 + const { plural, "learn more at": learnMoreAt, description, slug } = entry.data; 23 22 24 23 const works = await getCollection("works").then((works) => 25 24 works ··· 27 26 .filter( 28 27 (w) => 29 28 !w.metadata.private && 30 - w.metadata.tags?.some((t) => [slug, ...aliases].includes(t.id)), 29 + w.metadata.tags?.some( 30 + (t) => resolveAliased(t.id, tags) === resolveAliased(entry.id, tags), 31 + ), 31 32 ), 32 33 ); 33 34 ---