Migrate deploy-lftp script to tsx
Also change assets directory to `assets` and add default `.htaccess` file
This commit is contained in:
parent
405ad38f5d
commit
d01594f456
19 changed files with 172 additions and 97 deletions
.gitignore
.vscode
README.mdastro.config.mjsdeploy_lftp.shpackage-lock.jsonpackage.jsonpublic
scripts
src
components
content
layouts
pages
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -7,15 +7,20 @@ dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
npm-debug.log*
|
*.log
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
# environment variables
|
# environment variables
|
||||||
.env
|
.env
|
||||||
.env.production
|
.env.production
|
||||||
|
|
||||||
# macOS-specific files
|
# macOS-specific files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# local project settings
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/settings.json
|
||||||
|
|
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
|
@ -1,4 +1,3 @@
|
||||||
{
|
{
|
||||||
"recommendations": ["astro-build.astro-vscode"],
|
"recommendations": ["astro-build.astro-vscode", "bradlc.vscode-tailwindcss", "esbenp.prettier-vscode"]
|
||||||
"unwantedRecommendations": []
|
|
||||||
}
|
}
|
||||||
|
|
16
.vscode/settings.json
vendored
Normal file
16
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*.css": "tailwindcss"
|
||||||
|
},
|
||||||
|
"[astro]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"prettier.configPath": ".prettierrc.mjs",
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
10
README.md
10
README.md
|
@ -2,14 +2,20 @@
|
||||||
|
|
||||||
Static website built in Astro + Typescript + TailwindCSS.
|
Static website built in Astro + Typescript + TailwindCSS.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Node.js 20+
|
||||||
|
- LFTP, for remote deployment script
|
||||||
|
- LibreOffice, for story export script
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.badmanners.xyz/badmanners/gallery.badmanners.xyz
|
git clone https://git.badmanners.xyz/badmanners/gallery.badmanners.xyz
|
||||||
cd git.badmanners.xyz
|
cd gallery.badmanners.xyz
|
||||||
npm install
|
npm install && npm run astro -- sync
|
||||||
```
|
```
|
||||||
|
|
||||||
### Local development
|
### Local development
|
||||||
|
|
|
@ -16,6 +16,9 @@ export default defineConfig({
|
||||||
markdown: {
|
markdown: {
|
||||||
smartypants: false,
|
smartypants: false,
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
assets: "assets",
|
||||||
|
},
|
||||||
redirects: {
|
redirects: {
|
||||||
"/stories": "/stories/1",
|
"/stories": "/stories/1",
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
#!/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
HOST="${DEPLOY_LFTP_HOST}"
|
|
||||||
if [ -z "$HOST" ]; then
|
|
||||||
echo "ERROR: Missing envvar \$DEPLOY_LFTP_HOST"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
USER="${DEPLOY_LFTP_USER}"
|
|
||||||
if [ -z "$USER" ]; then
|
|
||||||
echo "ERROR: Missing envvar \$DEPLOY_LFTP_USER"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
PASSWORD="${DEPLOY_LFTP_PASSWORD}"
|
|
||||||
if [ -z "$PASSWORD" ]; then
|
|
||||||
echo "ERROR: Missing envvar \$DEPLOY_LFTP_PASSWORD"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
TARGETFOLDER="${DEPLOY_LFTP_TARGETFOLDER}"
|
|
||||||
if [ -z "$TARGETFOLDER" ]; then
|
|
||||||
echo "ERROR: Missing envvar \$DEPLOY_LFTP_TARGETFOLDER"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
SOURCEFOLDER="${DEPLOY_LFTP_SOURCEFOLDER:-dist/}"
|
|
||||||
if [ -z "$SOURCEFOLDER" ]; then
|
|
||||||
echo "ERROR: Missing envvar \$DEPLOY_LFTP_SOURCEFOLDER"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
lftp -f "
|
|
||||||
open -u $USER,$PASSWORD $HOST
|
|
||||||
mirror --reverse --include-glob _astro/* --delete --only-missing --no-perms --verbose $SOURCEFOLDER $TARGETFOLDER
|
|
||||||
mirror --reverse --exclude-glob _astro/* --delete --no-perms --verbose $SOURCEFOLDER $TARGETFOLDER
|
|
||||||
bye
|
|
||||||
"
|
|
||||||
|
|
||||||
echo "Done."
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "gallery-badmanners-xyz",
|
"name": "gallery-badmanners-xyz",
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "gallery-badmanners-xyz",
|
"name": "gallery-badmanners-xyz",
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.5.10",
|
"@astrojs/check": "^0.5.10",
|
||||||
"@astrojs/rss": "^4.0.5",
|
"@astrojs/rss": "^4.0.5",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "gallery-badmanners-xyz",
|
"name": "gallery-badmanners-xyz",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.4.0",
|
"version": "1.4.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro",
|
"astro": "astro",
|
||||||
"prettier": "prettier . --write",
|
"prettier": "prettier . --write",
|
||||||
"deploy-lftp": "dotenv ./deploy_lftp.sh",
|
"deploy-lftp": "dotenv tsx scripts/deploy-lftp.ts --",
|
||||||
"export-story": "tsx scripts/export-story.ts"
|
"export-story": "tsx scripts/export-story.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
1
public/.htaccess
Normal file
1
public/.htaccess
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ErrorDocument 404 /404.html
|
62
scripts/deploy-lftp.ts
Normal file
62
scripts/deploy-lftp.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
|
import { program, Option } from "commander";
|
||||||
|
|
||||||
|
interface DeployLftpOptions {
|
||||||
|
host: string;
|
||||||
|
user: string;
|
||||||
|
password: string;
|
||||||
|
targetFolder: string;
|
||||||
|
sourceFolder: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deployLftp({ host, user, password, targetFolder, sourceFolder }: DeployLftpOptions) {
|
||||||
|
const process = spawn(
|
||||||
|
"lftp",
|
||||||
|
[
|
||||||
|
"-c",
|
||||||
|
[
|
||||||
|
`open -u ${user},${password} ${host}`,
|
||||||
|
`mirror --reverse --include-glob assets/* --delete --only-missing --no-perms --verbose ${sourceFolder} ${targetFolder}`,
|
||||||
|
`mirror --reverse --exclude-glob assets/* --delete --no-perms --verbose ${sourceFolder} ${targetFolder}`,
|
||||||
|
`bye`,
|
||||||
|
].join("\n"),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
stdio: "inherit",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
process.on("close", (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(`deploy-lftp failed with code ${code}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await program
|
||||||
|
.name("deploy-lftp")
|
||||||
|
.description("Deploy files to remote server with LFTP")
|
||||||
|
.addOption(
|
||||||
|
new Option("-h, --host <hostname>", "Hostname of the LFTP (i.e. WebDav, SCP, SFTP...) remote.").env(
|
||||||
|
"DEPLOY_LFTP_HOST",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.addOption(new Option("-u, --user <username>", "Username portion of the LFTP credentials").env("DEPLOY_LFTP_USER"))
|
||||||
|
.addOption(
|
||||||
|
new Option("-p, --password <pass>", "Password portion of the LFTP credentials").env("DEPLOY_LFTP_PASSWORD"),
|
||||||
|
)
|
||||||
|
.addOption(
|
||||||
|
new Option("-t, --target-folder <remoteDir>", "Folder to mirror files to in the LFTP remote").env(
|
||||||
|
"DEPLOY_LFTP_TARGETFOLDER",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.addOption(
|
||||||
|
new Option("-s, --source-folder <localDir>", "Folder to read files from in the local machine")
|
||||||
|
.env("DEPLOY_LFTP_SOURCEFOLDER")
|
||||||
|
.default("dist/"),
|
||||||
|
)
|
||||||
|
.action(deployLftp)
|
||||||
|
.parseAsync();
|
|
@ -165,7 +165,6 @@ const { instance, user, postId } = Astro.props;
|
||||||
commentBoxPostLink.href = comment.url;
|
commentBoxPostLink.href = comment.url;
|
||||||
commentBoxPostLink.target = "_blank";
|
commentBoxPostLink.target = "_blank";
|
||||||
const publishDate = commentBoxPostLink.querySelector<HTMLSpanElement>("[data-publish-date]")!;
|
const publishDate = commentBoxPostLink.querySelector<HTMLSpanElement>("[data-publish-date]")!;
|
||||||
// TO-DO Pretty format date
|
|
||||||
publishDate.innerText = new Date(Date.parse(comment.created_at)).toLocaleString("en-US", {
|
publishDate.innerText = new Date(Date.parse(comment.created_at)).toLocaleString("en-US", {
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
|
|
@ -10,24 +10,50 @@ export const WEBSITE_LIST = [
|
||||||
"weasyl",
|
"weasyl",
|
||||||
"inkbunny",
|
"inkbunny",
|
||||||
"sofurry",
|
"sofurry",
|
||||||
"twitter",
|
|
||||||
"mastodon",
|
"mastodon",
|
||||||
|
"twitter",
|
||||||
"bluesky",
|
"bluesky",
|
||||||
"itaku",
|
"itaku",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const localUrlRegex = /^((\/?\.\.(?=\/))+|(\.(?=\/)))?\/?[a-z0-9_-]+(\/[a-z0-9_-]+)*\/?$/;
|
const localUrlRegex = /^((\/?\.\.(?=\/))+|(\.(?=\/)))?\/?[a-z0-9_-]+(\/[a-z0-9_-]+)*\/?$/;
|
||||||
|
const ekaPostUrlRegex = /^(?:https:\/\/)(?:www\.)?aryion\.com\/g4\/view\/([1-9]\d*)\/?$/;
|
||||||
|
const furaffinityPostUrlRegex = /^(?:https:\/\/)(?:www\.)?furaffinity\.net\/view\/([1-9]\d*)\/?$/;
|
||||||
|
const weasylPostUrlRegex =
|
||||||
|
/^(?:https:\/\/)(?:www\.)?weasyl\.com\/~([a-zA-Z][a-zA-Z0-9_-]+)\/submissions\/([1-9]\d*(?:\/[a-zA-Z0-9_-]+)?)\/?$/;
|
||||||
|
const inkbunnyPostUrlRegex = /^(?:https:\/\/)(?:www\.)?inkbunny\.net\/s\/([1-9]\d*)\/?$/;
|
||||||
|
const sofurryPostUrlRegex = /^(?:https:\/\/)www\.sofurry\.com\/view\/([1-9]\d*)\/?$/;
|
||||||
|
const mastodonPostUrlRegex = /^(?:https:\/\/)((?:[a-zA-Z0-9_-]+\.)+[a-z]+)\/@([a-zA-Z][a-zA-Z0-9_-]+)\/([1-9]\d*)\/?$/;
|
||||||
|
|
||||||
const refineAuthors = (value: { id: any } | any[]) => "id" in value || value.length > 0;
|
const refineAuthors = (value: { id: any } | any[]) => "id" in value || value.length > 0;
|
||||||
const refineCopyrightedCharacters = (value: Record<string, any>) => !("" in value) || Object.keys(value).length == 1;
|
const refineCopyrightedCharacters = (value: Record<string, any>) => !("" in value) || Object.keys(value).length == 1;
|
||||||
|
|
||||||
const lang = z.enum(["eng", "tok"]).default("eng");
|
const lang = z.enum(["eng", "tok"]).default("eng");
|
||||||
const website = z.enum(WEBSITE_LIST);
|
const website = z.enum(WEBSITE_LIST);
|
||||||
const platform = z.enum(["web", "windows", "linux", "macos", "android", "ios"]);
|
const platform = z.enum(["web", "windows", "linux", "macos", "android", "ios"]);
|
||||||
const mastodonPost = z.object({
|
const mastodonPost = z
|
||||||
instance: z.string(),
|
.object({
|
||||||
user: z.string(),
|
instance: z.string(),
|
||||||
postId: z.string(),
|
user: z.string(),
|
||||||
});
|
postId: z.string(),
|
||||||
|
})
|
||||||
|
.or(
|
||||||
|
z.string().transform((mastodonPost, ctx) => {
|
||||||
|
const match = mastodonPostUrlRegex.exec(mastodonPost);
|
||||||
|
if (!match) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: `"mastodonPost" contains an invalid URL`,
|
||||||
|
});
|
||||||
|
return z.NEVER;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
instance: match[1],
|
||||||
|
user: match[2],
|
||||||
|
postId: match[3],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
const copyrightedCharacters = z
|
const copyrightedCharacters = z
|
||||||
.record(z.string(), reference("users"))
|
.record(z.string(), reference("users"))
|
||||||
.default({})
|
.default({})
|
||||||
|
@ -71,6 +97,11 @@ const storiesCollection = defineCollection({
|
||||||
relatedGames: z.array(reference("games")).default([]),
|
relatedGames: z.array(reference("games")).default([]),
|
||||||
posts: z
|
posts: z
|
||||||
.object({
|
.object({
|
||||||
|
eka: z.string().regex(ekaPostUrlRegex).optional(),
|
||||||
|
furaffinity: z.string().regex(furaffinityPostUrlRegex).optional(),
|
||||||
|
weasyl: z.string().regex(weasylPostUrlRegex).optional(),
|
||||||
|
inkbunny: z.string().regex(inkbunnyPostUrlRegex).optional(),
|
||||||
|
sofurry: z.string().regex(sofurryPostUrlRegex).optional(),
|
||||||
mastodon: mastodonPost.optional(),
|
mastodon: mastodonPost.optional(),
|
||||||
})
|
})
|
||||||
.default({}),
|
.default({}),
|
||||||
|
@ -105,6 +136,11 @@ const gamesCollection = defineCollection({
|
||||||
relatedGames: z.array(reference("games")).default([]),
|
relatedGames: z.array(reference("games")).default([]),
|
||||||
posts: z
|
posts: z
|
||||||
.object({
|
.object({
|
||||||
|
eka: z.string().regex(ekaPostUrlRegex).optional(),
|
||||||
|
furaffinity: z.string().regex(furaffinityPostUrlRegex).optional(),
|
||||||
|
weasyl: z.string().regex(weasylPostUrlRegex).optional(),
|
||||||
|
inkbunny: z.string().regex(inkbunnyPostUrlRegex).optional(),
|
||||||
|
sofurry: z.string().regex(sofurryPostUrlRegex).optional(),
|
||||||
mastodon: mastodonPost.optional(),
|
mastodon: mastodonPost.optional(),
|
||||||
})
|
})
|
||||||
.default({}),
|
.default({}),
|
||||||
|
|
|
@ -4,7 +4,7 @@ pubDate: 2024-06-23
|
||||||
authors: bad-manners
|
authors: bad-manners
|
||||||
wordCount: 2600
|
wordCount: 2600
|
||||||
contentWarning: >
|
contentWarning: >
|
||||||
Contains: Non-fatal unbirth and oral vore, with willing anthro maned wolf predator and willing anthro mimic x maned wolf hybrid prey. Also includes gay sex, masturbation, and sleep play.
|
Contains: Non-fatal unbirth and oral vore, with willing anthro male maned wolf predator and willing micro anthro male mimic x maned wolf hybrid prey. Also includes gay sex, masturbation, and sleep play.
|
||||||
thumbnail: /src/assets/thumbnails/bm_19_woofer_exploration.png
|
thumbnail: /src/assets/thumbnails/bm_19_woofer_exploration.png
|
||||||
description: |
|
description: |
|
||||||
The Director wakes up in the middle of the night to a little intruder, and decides to have some fun with him.
|
The Director wakes up in the middle of the night to a little intruder, and decides to have some fun with him.
|
||||||
|
|
|
@ -10,3 +10,4 @@ tags:
|
||||||
- daddy play
|
- daddy play
|
||||||
- BDSM
|
- BDSM
|
||||||
- dubcon
|
- dubcon
|
||||||
|
- plushie
|
||||||
|
|
|
@ -64,7 +64,7 @@ const thumbnail =
|
||||||
</Fragment>
|
</Fragment>
|
||||||
<div
|
<div
|
||||||
id="top"
|
id="top"
|
||||||
class="min-w-screen relative min-h-screen bg-radial from-bm-300 to-bm-600 px-1 pb-16 pt-20 dark:from-green-700 dark:to-green-950 print:bg-none"
|
class="min-w-screen relative min-h-screen bg-radial from-bm-300 to-bm-600 px-1 pb-16 pt-20 dark:from-green-700 dark:to-green-950 print:bg-none print:pb-0 print:pt-0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="toolbox-buttons"
|
id="toolbox-buttons"
|
||||||
|
|
|
@ -77,7 +77,7 @@ const wordCount = props.wordCount ? `${props.wordCount}` : "???";
|
||||||
</Fragment>
|
</Fragment>
|
||||||
<div
|
<div
|
||||||
id="top"
|
id="top"
|
||||||
class="min-w-screen relative min-h-screen bg-radial from-bm-300 to-bm-600 px-1 pb-16 pt-20 dark:from-green-700 dark:to-green-950 print:bg-none"
|
class="min-w-screen relative min-h-screen bg-radial from-bm-300 to-bm-600 px-1 pb-16 pt-20 dark:from-green-700 dark:to-green-950 print:bg-none print:pb-0 print:pt-0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="toolbox-buttons"
|
id="toolbox-buttons"
|
||||||
|
|
|
@ -7,7 +7,7 @@ The Noto Sans and Noto Serif typefaces are copyrighted to the Noto Project Autho
|
||||||
|
|
||||||
The generic SVG icons were created by Font Awesome and are distributed under the CC-BY-4.0 license.
|
The generic SVG icons were created by Font Awesome and are distributed under the CC-BY-4.0 license.
|
||||||
|
|
||||||
All third-party trademarks belong to their respective owners, and I'm not affiliated with any of them.
|
All third-party trademarks and attributed characters belong to their respective owners, and I'm not affiliated with any of them.
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
export const GET: APIRoute = () => {
|
export const GET: APIRoute = () => {
|
||||||
|
|
|
@ -11,49 +11,29 @@ const [stories, games, tagCategories] = await Promise.all([
|
||||||
const tagsSet = new Set<string>();
|
const tagsSet = new Set<string>();
|
||||||
const draftOnlyTagsSet = new Set<string>();
|
const draftOnlyTagsSet = new Set<string>();
|
||||||
const seriesCollection = await getCollection("series");
|
const seriesCollection = await getCollection("series");
|
||||||
stories
|
// Add tags from non-drafts to set; then, add tags only from drafts to separate set
|
||||||
.filter((story) => !story.data.isDraft)
|
[stories, games]
|
||||||
.forEach((story) => {
|
.flat()
|
||||||
story.data.tags.forEach((tag) => {
|
.sort((a, b) => (a.data.isDraft ? 1 : b.data.isDraft ? -1 : 0))
|
||||||
tagsSet.add(tag);
|
.forEach((value) => {
|
||||||
});
|
if (value.data.isDraft) {
|
||||||
|
value.data.tags.forEach((tag) => {
|
||||||
|
if (!tagsSet.has(tag)) {
|
||||||
|
draftOnlyTagsSet.add(tag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
value.data.tags.forEach((tag) => {
|
||||||
|
tagsSet.add(tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
games
|
|
||||||
.filter((game) => !game.data.isDraft)
|
|
||||||
.forEach((game) => {
|
|
||||||
game.data.tags.forEach((tag) => {
|
|
||||||
tagsSet.add(tag);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
stories
|
|
||||||
.filter((story) => story.data.isDraft)
|
|
||||||
.forEach((story) => {
|
|
||||||
story.data.tags.forEach((tag) => {
|
|
||||||
if (!tagsSet.has(tag)) {
|
|
||||||
draftOnlyTagsSet.add(tag);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
games
|
|
||||||
.filter((game) => game.data.isDraft)
|
|
||||||
.forEach((game) => {
|
|
||||||
game.data.tags.forEach((tag) => {
|
|
||||||
if (!tagsSet.has(tag)) {
|
|
||||||
draftOnlyTagsSet.add(tag);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const uncategorizedTagsSet = new Set(tagsSet);
|
|
||||||
|
|
||||||
|
const uncategorizedTagsSet = new Set(tagsSet);
|
||||||
const categorizedTags: Array<[string, string, string[]]> = tagCategories
|
const categorizedTags: Array<[string, string, string[]]> = tagCategories
|
||||||
.sort((a, b) => a.data.index - b.data.index)
|
.sort((a, b) => a.data.index - b.data.index)
|
||||||
.map((category) => {
|
.map((category) => {
|
||||||
const tagList = category.data.tags.map((tag) => {
|
const tagList = category.data.tags.map((tag) => (typeof tag === "string" ? tag : tag["eng"]!));
|
||||||
if (typeof tag === "string") {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
return tag["eng"]!;
|
|
||||||
});
|
|
||||||
tagList.forEach((tag, index) => {
|
tagList.forEach((tag, index) => {
|
||||||
if (index !== tagList.findLastIndex((val) => tag == val)) {
|
if (index !== tagList.findLastIndex((val) => tag == val)) {
|
||||||
throw new Error(`Duplicated tag "${tag}" found in multiple tag-categories`);
|
throw new Error(`Duplicated tag "${tag}" found in multiple tag-categories`);
|
||||||
|
|
|
@ -16,7 +16,11 @@ type Params = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStaticPaths: GetStaticPaths = async () => {
|
export const getStaticPaths: GetStaticPaths = async () => {
|
||||||
const [stories, games, series] = await Promise.all([getCollection("stories"), getCollection("games"), getCollection("series")]);
|
const [stories, games, series] = await Promise.all([
|
||||||
|
getCollection("stories"),
|
||||||
|
getCollection("games"),
|
||||||
|
getCollection("series"),
|
||||||
|
]);
|
||||||
const seriesTags = new Set(series.map((s) => s.data.name));
|
const seriesTags = new Set(series.map((s) => s.data.name));
|
||||||
const tags = new Set<string>();
|
const tags = new Set<string>();
|
||||||
stories.forEach((story) => {
|
stories.forEach((story) => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue