From c966aa514a51b67fc80faf4cd033aaf4edfa6121 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 22:41:57 +0900 Subject: [PATCH 01/26] =?UTF-8?q?refactor:=20generate=E3=82=B9=E3=82=AF?= =?UTF-8?q?=E3=83=AA=E3=83=97=E3=83=88=E3=81=AETS=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 10 +-- eslint.config.mjs | 1 - generateBibliography.js | 84 ----------------------- generatePosts.js | 100 ---------------------------- generateSitemap.js | 66 ------------------ index.js | 8 +-- package-lock.json | 64 ++++++++++++++++++ package.json | 4 +- src/scripts/generateBibliography.ts | 99 +++++++++++++++++++++++++++ src/scripts/generatePosts.ts | 77 +++++++++++++++++++++ src/scripts/generateSitemap.ts | 67 +++++++++++++++++++ tsconfig.checkjs.json | 14 ---- 12 files changed, 319 insertions(+), 275 deletions(-) delete mode 100644 generateBibliography.js delete mode 100644 generatePosts.js delete mode 100644 generateSitemap.js create mode 100644 src/scripts/generateBibliography.ts create mode 100644 src/scripts/generatePosts.ts create mode 100644 src/scripts/generateSitemap.ts delete mode 100644 tsconfig.checkjs.json diff --git a/AGENTS.md b/AGENTS.md index 3b9e9ee..0b9dfd9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,9 +14,9 @@ - `src/route.ts`: URL状態から表示ページを決めて描画する。 - `src/reroute.ts`: `popstate`、検索フォーム、内部リンククリックを監視して再ルーティングする。 - `src/ssg.ts`: `post.template.html` と `posts.json` から `posts/*.html` を生成する。 -- `generatePosts.js`: `markdown/*.md` から `posts.json` を更新する。 -- `generateBibliography.js`: `works.json` に文献表記を付与する。 -- `generateSitemap.js`: `sitemap.xml` を生成する。 +- `src/scripts/generatePosts.ts`: `markdown/*.md` から `posts.json` を更新する。 +- `src/scripts/generateBibliography.ts`: `works.json` に文献表記を付与する。 +- `src/scripts/generateSitemap.ts`: `sitemap.xml` を生成する。 ## HTMLエントリー @@ -32,7 +32,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 - 研究業績ページ用の独立エントリー。 - `./works.json` を読み、`src/works/Works.ts` でHTMLを生成して `renderRoot()` に渡す。 - ブログSPAの `route.ts` は使わない。 -- 文献表記は `generateBibliography.js` が `works.json` に事前生成する。 +- 文献表記は `src/scripts/generateBibliography.ts` が `works.json` に事前生成する。 ### `posts/[id].html` -> `src/hydrate.ts` @@ -59,7 +59,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 - `npm run lint-css`: CSS lint。 - `npm run fmt`: Prettier整形。 -`tsc` と `tsc-js` は `-w` 付きなので、単発確認では `./node_modules/.bin/tsc --noEmit` などを使う。 +`tsc` は `-w` 付きなので、単発確認では `npm run tsc:once` などを使う。 ## Code App / iOS 環境 diff --git a/eslint.config.mjs b/eslint.config.mjs index a31a0a2..2ecb91d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -3,7 +3,6 @@ import js from '@eslint/js'; import eslintConfigPrettier from 'eslint-config-prettier'; import { defineConfig } from 'eslint/config'; import importPlugin from 'eslint-plugin-import'; -import { fileURLToPath } from 'node:url'; import tseslint from 'typescript-eslint'; export default defineConfig( diff --git a/generateBibliography.js b/generateBibliography.js deleted file mode 100644 index 891661d..0000000 --- a/generateBibliography.js +++ /dev/null @@ -1,84 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const CSL = require('citeproc'); -const prettier = require('prettier'); - -const worksPath = path.resolve('works.json'); -const stylePath = path.resolve('assets', 'citeproc', 'sist02modified.csl'); -const localePath = path.resolve('assets', 'citeproc', 'locales-ja-JP.xml'); - -/** - * @param {object} obj - * @return {object} - */ -function removeNullProperties(obj) { - return Object.entries(obj) - .filter(([_k, v]) => v !== null) - .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}); -} - -/** - * @param {import('csl-json').Data[]} data - * @param {string} style - * @param {string} locale - * @return {string[]} - */ -function citeproc(data, style, locale) { - const items = data.map((item) => ({ - ...removeNullProperties(item), - id: item.id.toString(), - })); - const sys = { - retrieveLocale: (_lang) => locale, - retrieveItem: (id) => items.find((item) => id === item.id), - }; - const citeproc = new CSL.Engine(sys, style); - citeproc.setOutputFormat('text'); - - citeproc.updateItems(items.map((item) => item.id)); - const bib = citeproc.makeBibliography(); - if (bib === false) return []; - - return bib[1].map((text) => text.replace(/\n$/, '')); -} - -/** - * @param {import('csl-json').Data[]} items - * @return {import('./src/works/citation').Citation[]} - */ -function AppendBibliopraphy(items) { - const bibTexts = citeproc( - items, - fs.readFileSync(stylePath, 'utf-8'), - fs.readFileSync(localePath, 'utf-8'), - ); - return items.map((item, i) => ({ - ...item, - _bibliographyText: bibTexts[i], - })); -} - -/** - * @return {Promise} - */ -async function generateBibliography() { - const works = JSON.parse(fs.readFileSync(worksPath, 'utf8')); - const newWorks = AppendBibliopraphy(works); - fs.writeFileSync( - worksPath, - await prettier.format(JSON.stringify(newWorks), { - semi: false, - parser: 'json', - }), - ); - console.log('bibliography generated.'); -} - -module.exports = generateBibliography; - -if (require.main === module) { - generateBibliography().catch((e) => { - console.error(e); - process.exitCode = 1; - }); -} diff --git a/generatePosts.js b/generatePosts.js deleted file mode 100644 index b7cb1bc..0000000 --- a/generatePosts.js +++ /dev/null @@ -1,100 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const prettier = require('prettier'); -const MarkdownIt = require('markdown-it'); -const MarkdownItFootnote = require('markdown-it-footnote'); -const matter = require('gray-matter'); - -const md = new MarkdownIt(); -md.use(MarkdownItFootnote); - -const jsonPath = path.resolve(__dirname, 'posts.json'); -const mdPath = path.resolve(__dirname, 'markdown'); - -/** - * @param dir string - * @return string[] - */ -function listFiles(dir) { - return fs - .readdirSync(dir, { withFileTypes: true }) - .flatMap((dirent) => { - if (/^\..*/.test(dirent.name)) return; // exclude '.icloud' file - return dirent.isFile() - ? [`${dir}/${dirent.name}`] - : listFiles(`${dir}/${dirent.name}`); - }) - .filter((v) => v !== undefined); -} - -/** - * @param paths string[] - * @return {import('./src/post').JSONPost[]} - */ -function readPostsMarkdown(paths) { - return paths - .map((path) => fs.readFileSync(path, 'utf-8')) - .map((string) => matter(string)) - .map((matter) => ({ - ...matter.data, - text: md - .render(matter.content) - .replace(/\n/g, '') - .replace(/>/g, '>') - .replace(/</g, '<') - // 漢字《ふりがな》 - .replace(/|(.+?)《(.+?)》/g, '$1$2') - .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') - .replace(/([一-龠]+)《(.+?)》/g, '$1$2'), - })); -} - -/** - * @param posts {import('./src/post').JSONPost[]} - * @return {import('./src/post').JSONPost[]} - */ -function uniquePosts(posts) { - const uniquePosts = Array.from( - new Map(posts.map((post) => [post.id, post])).values(), - ); - return uniquePosts.sort(function (a, b) { - if (a.id < b.id) return 1; - if (a.id > b.id) return -1; - return 0; - }); -} - -/** - * @param posts {import('./src/post').JSONPost[]} - * @param mdPosts {import('./src/post').JSONPost[]} - * @return {Promise} - */ -async function writePostsJson(posts, mdPosts) { - fs.writeFileSync( - jsonPath, - await prettier.format(JSON.stringify(uniquePosts([...mdPosts, ...posts])), { - semi: false, - parser: 'json', - }), - ); -} - -/** - * @return {Promise} - */ -async function generatePosts() { - await writePostsJson( - readPostsMarkdown(listFiles(mdPath)), - JSON.parse(fs.readFileSync(jsonPath, 'utf-8')), - ); - console.log('posts genarated.'); -} - -module.exports = generatePosts; - -if (require.main === module) { - generatePosts().catch((e) => { - console.error(e); - process.exitCode = 1; - }); -} diff --git a/generateSitemap.js b/generateSitemap.js deleted file mode 100644 index eab3ea2..0000000 --- a/generateSitemap.js +++ /dev/null @@ -1,66 +0,0 @@ -const { writeFileSync } = require('fs'); -const { SitemapStream, streamToPromise } = require('sitemap'); -/** @type {(xml: string, options?: object) => string} */ -const format = /** @type {any} */ (require('xml-formatter')); -const { updatedAt } = require('./about.json'); - -const sitemap = new SitemapStream({ hostname: 'https://kyonenya.github.io/' }); - -/** - * @param {import('./src/post').JSONPost[]} posts - * @return {string} - */ -function getLatestModifiedAt(posts) { - return posts - .map((post) => post.modifiedAt) - .sort((a, b) => new Date(b).getTime() - new Date(a).getTime())[0]; -} - -/** - * @param {import('./src/post').JSONPost[]} posts - * @return { {tag: string, modifiedAt: string}[] } - */ -function tagHistory(posts) { - const tags = [...new Set(posts.map((post) => post.tags).flat())]; // uniq - return tags.map((tag) => { - return { - tag, - modifiedAt: getLatestModifiedAt( - posts.filter((post) => post.tags.includes(tag)), - ), - }; - }); -} - -/** - * @param {import('./src/post').JSONPost[]} posts - * @return {void} - */ -function generateSitemap(posts) { - sitemap.write({ url: '', lastmod: getLatestModifiedAt(posts) }); - sitemap.write({ url: 'works', lastmod: updatedAt }); - sitemap.write({ url: 'about', lastmod: updatedAt }); - posts.forEach((post) => - sitemap.write({ url: `/posts/${post.id}`, lastmod: post.modifiedAt }), - ); - tagHistory(posts).forEach(({ tag, modifiedAt }) => - sitemap.write({ url: `?tag=${tag}`, lastmod: modifiedAt }), - ); - sitemap.end(); - - streamToPromise(sitemap) - .then((sm) => - writeFileSync( - './sitemap.xml', - format(sm.toString(), { indentation: ' ', collapseContent: true }), - ), - ) - .then(() => console.log('sitemap generated.')); -} - -module.exports = generateSitemap; - -if (require.main === module) { - const posts = require('./posts.json'); - generateSitemap(posts); -} diff --git a/index.js b/index.js index 6b10266..4c6d317 100644 --- a/index.js +++ b/index.js @@ -4,11 +4,11 @@ const path = require('path'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const config = require('./webpack.dev.config.js'); -const generatePosts = require('./generatePosts'); -const generateSitemap = require('./generateSitemap'); -const generateBibliography = require('./generateBibliography'); const { createJiti } = require('jiti'); const jiti = createJiti(__filename); +const { generatePosts } = jiti('./src/scripts/generatePosts.ts'); +const { generateSitemap } = jiti('./src/scripts/generateSitemap.ts'); +const { generateBibliography } = jiti('./src/scripts/generateBibliography.ts'); const { generateStaticHTML } = jiti('./src/ssg.ts'); const rootDir = __dirname; @@ -43,6 +43,6 @@ express() const posts = JSON.parse( fs.readFileSync(path.resolve(__dirname, 'posts.json'), 'utf8'), ); - generateSitemap(posts); + await generateSitemap(posts); generateStaticHTML(); })().catch((e) => console.error(e)); diff --git a/package-lock.json b/package-lock.json index 9982cd5..4b472b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ }, "devDependencies": { "@eslint/js": "^9.39.4", + "@types/markdown-it": "^14.1.2", + "@types/markdown-it-footnote": "^3.0.4", "@types/node": "18.19.130", "citeproc": "^2.4.63", "csl-json": "^0.1.0", @@ -567,6 +569,37 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/markdown-it-footnote": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/markdown-it-footnote/-/markdown-it-footnote-3.0.4.tgz", + "integrity": "sha512-XJ6n+v+2u+2gmYLSHcxyoNT/YrgrKvHuDJQlykFjuxCQCr86P2/fx1V6/0lcKxv5cSIlCaJ6sUcNS3zDI7I+LA==", + "dev": true, + "dependencies": { + "@types/markdown-it": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, "node_modules/@types/node": { "version": "18.19.130", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", @@ -7889,6 +7922,37 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "requires": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "@types/markdown-it-footnote": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/markdown-it-footnote/-/markdown-it-footnote-3.0.4.tgz", + "integrity": "sha512-XJ6n+v+2u+2gmYLSHcxyoNT/YrgrKvHuDJQlykFjuxCQCr86P2/fx1V6/0lcKxv5cSIlCaJ6sUcNS3zDI7I+LA==", + "dev": true, + "requires": { + "@types/markdown-it": "*" + } + }, + "@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, "@types/node": { "version": "18.19.130", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", diff --git a/package.json b/package.json index 86c2ba8..bcdf514 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "dev": "node index.js", "tsc": "tsc -w --noEmit", - "tsc-js": "tsc -w --noEmit --resolveJsonModule --p tsconfig.checkjs.json", + "tsc:once": "tsc --noEmit", "build": "node ./node_modules/webpack/bin/webpack.js", "build-css": "postcss src/css/index.css -o dist/css/bundle.css --verbose", "fmt": "prettier --write **/*.{ts,js,mjs,json,css}", @@ -17,6 +17,8 @@ }, "devDependencies": { "@eslint/js": "^9.39.4", + "@types/markdown-it": "^14.1.2", + "@types/markdown-it-footnote": "^3.0.4", "@types/node": "18.19.130", "citeproc": "^2.4.63", "csl-json": "^0.1.0", diff --git a/src/scripts/generateBibliography.ts b/src/scripts/generateBibliography.ts new file mode 100644 index 0000000..61ebc0e --- /dev/null +++ b/src/scripts/generateBibliography.ts @@ -0,0 +1,99 @@ +import { readFileSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +// eslint-disable-next-line import/no-unresolved +import { Data } from 'csl-json'; +import { format } from 'prettier'; +import { Citation } from '../works/citation'; + +type CslEngine = { + setOutputFormat: (format: 'text') => void; + updateItems: (ids: string[]) => void; + makeBibliography: () => false | [unknown, string[]]; +}; + +type Csl = { + Engine: new ( + sys: { + retrieveLocale: (lang: string) => string; + retrieveItem: ( + id: string, + ) => (Partial & { id: string }) | undefined; + }, + style: string, + ) => CslEngine; +}; + +const require = createRequire(__filename); +const CSL = require('citeproc') as Csl; +const rootDir = path.resolve(__dirname, '../..'); +const worksPath = path.resolve(rootDir, 'works.json'); +const stylePath = path.resolve( + rootDir, + 'assets', + 'citeproc', + 'sist02modified.csl', +); +const localePath = path.resolve( + rootDir, + 'assets', + 'citeproc', + 'locales-ja-JP.xml', +); + +function removeNullProperties(obj: T): Partial { + return Object.fromEntries( + Object.entries(obj).filter(([, v]) => v !== null), + ) as Partial; +} + +function citeproc(data: Data[], style: string, locale: string): string[] { + const items = data.map((item) => ({ + ...removeNullProperties(item), + id: item.id.toString(), + })); + const sys = { + retrieveLocale: () => locale, + retrieveItem: (id: string) => items.find((item) => id === item.id), + }; + const citeproc = new CSL.Engine(sys, style); + citeproc.setOutputFormat('text'); + + citeproc.updateItems(items.map((item) => item.id)); + const bib = citeproc.makeBibliography(); + if (bib === false) return []; + + return bib[1].map((text) => text.replace(/\n$/, '')); +} + +function appendBibliography(items: Data[]): Citation[] { + const bibTexts = citeproc( + items, + readFileSync(stylePath, 'utf-8'), + readFileSync(localePath, 'utf-8'), + ); + return items.map((item, i) => ({ + ...item, + _bibliographyText: bibTexts[i], + })); +} + +export async function generateBibliography(): Promise { + const works = JSON.parse(readFileSync(worksPath, 'utf8')) as Data[]; + const newWorks = appendBibliography(works); + writeFileSync( + worksPath, + await format(JSON.stringify(newWorks), { + semi: false, + parser: 'json', + }), + ); + console.log('bibliography generated.'); +} + +if (path.resolve(process.argv[1] ?? '') === __filename) { + generateBibliography().catch((e: unknown) => { + console.error(e); + process.exitCode = 1; + }); +} diff --git a/src/scripts/generatePosts.ts b/src/scripts/generatePosts.ts new file mode 100644 index 0000000..315439f --- /dev/null +++ b/src/scripts/generatePosts.ts @@ -0,0 +1,77 @@ +import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import matter from 'gray-matter'; +import MarkdownIt from 'markdown-it'; +import markdownItFootnote from 'markdown-it-footnote'; +import { format } from 'prettier'; +import { JSONPost } from '../post'; + +const md = new MarkdownIt(); +md.use(markdownItFootnote); + +const rootDir = path.resolve(__dirname, '../..'); +const jsonPath = path.resolve(rootDir, 'posts.json'); +const mdPath = path.resolve(rootDir, 'markdown'); + +function listFiles(dir: string): string[] { + return readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => { + if (/^\..*/.test(dirent.name)) return []; // exclude '.icloud' file + const filePath = path.resolve(dir, dirent.name); + return dirent.isFile() ? [filePath] : listFiles(filePath); + }); +} + +function readPostsMarkdown(paths: string[]): JSONPost[] { + return paths + .map((path) => readFileSync(path, 'utf-8')) + .map((string) => matter(string)) + .map( + (matter) => + ({ + ...matter.data, + text: md + .render(matter.content) + .replace(/\n/g, '') + .replace(/>/g, '>') + .replace(/</g, '<') + // 漢字《ふりがな》 + .replace(/|(.+?)《(.+?)》/g, '$1$2') + .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') + .replace(/([一-龠]+)《(.+?)》/g, '$1$2'), + }) as JSONPost, + ); +} + +function uniquePosts(posts: JSONPost[]): JSONPost[] { + return Array.from( + new Map(posts.map((post) => [post.id, post])).values(), + ).sort((a, b) => b.id - a.id); +} + +async function writePostsJson( + posts: JSONPost[], + mdPosts: JSONPost[], +): Promise { + writeFileSync( + jsonPath, + await format(JSON.stringify(uniquePosts([...mdPosts, ...posts])), { + semi: false, + parser: 'json', + }), + ); +} + +export async function generatePosts(): Promise { + await writePostsJson( + readPostsMarkdown(listFiles(mdPath)), + JSON.parse(readFileSync(jsonPath, 'utf-8')) as JSONPost[], + ); + console.log('posts genarated.'); +} + +if (path.resolve(process.argv[1] ?? '') === __filename) { + generatePosts().catch((e: unknown) => { + console.error(e); + process.exitCode = 1; + }); +} diff --git a/src/scripts/generateSitemap.ts b/src/scripts/generateSitemap.ts new file mode 100644 index 0000000..5ef627b --- /dev/null +++ b/src/scripts/generateSitemap.ts @@ -0,0 +1,67 @@ +import { readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { SitemapStream, streamToPromise } from 'sitemap'; +import format from 'xml-formatter'; +import { JSONPost } from '../post'; + +type About = { + updatedAt: string; +}; + +const rootDir = path.resolve(__dirname, '../..'); +const sitemapPath = path.resolve(rootDir, 'sitemap.xml'); + +function getLatestModifiedAt(posts: JSONPost[]): string { + return ( + posts + .map((post) => post.modifiedAt) + .sort((a, b) => new Date(b).getTime() - new Date(a).getTime())[0] ?? '' + ); +} + +function tagHistory(posts: JSONPost[]): { tag: string; modifiedAt: string }[] { + const tags = [...new Set(posts.flatMap((post) => post.tags))]; + return tags.map((tag) => ({ + tag, + modifiedAt: getLatestModifiedAt( + posts.filter((post) => post.tags.includes(tag)), + ), + })); +} + +export async function generateSitemap(posts: JSONPost[]): Promise { + const { updatedAt } = JSON.parse( + readFileSync(path.resolve(rootDir, 'about.json'), 'utf8'), + ) as About; + const sitemap = new SitemapStream({ + hostname: 'https://kyonenya.github.io/', + }); + + sitemap.write({ url: '', lastmod: getLatestModifiedAt(posts) }); + sitemap.write({ url: 'works', lastmod: updatedAt }); + sitemap.write({ url: 'about', lastmod: updatedAt }); + posts.forEach((post) => + sitemap.write({ url: `/posts/${post.id}`, lastmod: post.modifiedAt }), + ); + tagHistory(posts).forEach(({ tag, modifiedAt }) => + sitemap.write({ url: `?tag=${tag}`, lastmod: modifiedAt }), + ); + sitemap.end(); + + const sm = await streamToPromise(sitemap); + writeFileSync( + sitemapPath, + format(sm.toString(), { indentation: ' ', collapseContent: true }), + ); + console.log('sitemap generated.'); +} + +if (path.resolve(process.argv[1] ?? '') === __filename) { + const posts = JSON.parse( + readFileSync(path.resolve(rootDir, 'posts.json'), 'utf8'), + ) as JSONPost[]; + generateSitemap(posts).catch((e: unknown) => { + console.error(e); + process.exitCode = 1; + }); +} diff --git a/tsconfig.checkjs.json b/tsconfig.checkjs.json deleted file mode 100644 index 0974eb7..0000000 --- a/tsconfig.checkjs.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "rootDir": "./", - "allowJs": true, - "checkJs": true, - "noImplicitAny": false - }, - "include": [ - "generateBibliography.js", - "generatePosts.js", - "generateSitemap.js" - ] -} From a90f1fc06585a19ebb499d8c3178b2018bc411d1 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 22:42:26 +0900 Subject: [PATCH 02/26] =?UTF-8?q?refacotor:=20=5F=5Fdirname,=20=5F=5Ffilen?= =?UTF-8?q?ame=E3=82=92=E4=BD=BF=E3=82=8F=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/scripts/generateBibliography.ts | 9 ++++++--- src/scripts/generatePosts.ts | 7 +++++-- src/scripts/generateSitemap.ts | 7 +++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/scripts/generateBibliography.ts b/src/scripts/generateBibliography.ts index 61ebc0e..bb81c58 100644 --- a/src/scripts/generateBibliography.ts +++ b/src/scripts/generateBibliography.ts @@ -1,6 +1,7 @@ import { readFileSync, writeFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; // eslint-disable-next-line import/no-unresolved import { Data } from 'csl-json'; import { format } from 'prettier'; @@ -24,9 +25,11 @@ type Csl = { ) => CslEngine; }; -const require = createRequire(__filename); +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); +const require = createRequire(import.meta.url); const CSL = require('citeproc') as Csl; -const rootDir = path.resolve(__dirname, '../..'); +const rootDir = path.resolve(dirname, '../..'); const worksPath = path.resolve(rootDir, 'works.json'); const stylePath = path.resolve( rootDir, @@ -91,7 +94,7 @@ export async function generateBibliography(): Promise { console.log('bibliography generated.'); } -if (path.resolve(process.argv[1] ?? '') === __filename) { +if (path.resolve(process.argv[1] ?? '') === filename) { generateBibliography().catch((e: unknown) => { console.error(e); process.exitCode = 1; diff --git a/src/scripts/generatePosts.ts b/src/scripts/generatePosts.ts index 315439f..cc98bd2 100644 --- a/src/scripts/generatePosts.ts +++ b/src/scripts/generatePosts.ts @@ -1,5 +1,6 @@ import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import matter from 'gray-matter'; import MarkdownIt from 'markdown-it'; import markdownItFootnote from 'markdown-it-footnote'; @@ -9,7 +10,9 @@ import { JSONPost } from '../post'; const md = new MarkdownIt(); md.use(markdownItFootnote); -const rootDir = path.resolve(__dirname, '../..'); +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); +const rootDir = path.resolve(dirname, '../..'); const jsonPath = path.resolve(rootDir, 'posts.json'); const mdPath = path.resolve(rootDir, 'markdown'); @@ -69,7 +72,7 @@ export async function generatePosts(): Promise { console.log('posts genarated.'); } -if (path.resolve(process.argv[1] ?? '') === __filename) { +if (path.resolve(process.argv[1] ?? '') === filename) { generatePosts().catch((e: unknown) => { console.error(e); process.exitCode = 1; diff --git a/src/scripts/generateSitemap.ts b/src/scripts/generateSitemap.ts index 5ef627b..3520bef 100644 --- a/src/scripts/generateSitemap.ts +++ b/src/scripts/generateSitemap.ts @@ -1,5 +1,6 @@ import { readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { SitemapStream, streamToPromise } from 'sitemap'; import format from 'xml-formatter'; import { JSONPost } from '../post'; @@ -8,7 +9,9 @@ type About = { updatedAt: string; }; -const rootDir = path.resolve(__dirname, '../..'); +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); +const rootDir = path.resolve(dirname, '../..'); const sitemapPath = path.resolve(rootDir, 'sitemap.xml'); function getLatestModifiedAt(posts: JSONPost[]): string { @@ -56,7 +59,7 @@ export async function generateSitemap(posts: JSONPost[]): Promise { console.log('sitemap generated.'); } -if (path.resolve(process.argv[1] ?? '') === __filename) { +if (path.resolve(process.argv[1] ?? '') === filename) { const posts = JSON.parse( readFileSync(path.resolve(rootDir, 'posts.json'), 'utf8'), ) as JSONPost[]; From 2eb0950264acda436f0496d1fd3c2621ace532d5 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 22:58:31 +0900 Subject: [PATCH 03/26] refactor: rename --- AGENTS.md | 6 +++--- index.js | 8 ++++---- src/scripts/{generatePosts.ts => generatePostsJson.ts} | 4 ++-- src/scripts/generateSitemap.ts | 7 ++----- src/scripts/{generateBibliography.ts => generateWorks.ts} | 6 +++--- 5 files changed, 14 insertions(+), 17 deletions(-) rename src/scripts/{generatePosts.ts => generatePostsJson.ts} (95%) rename src/scripts/{generateBibliography.ts => generateWorks.ts} (94%) diff --git a/AGENTS.md b/AGENTS.md index 0b9dfd9..cbd6763 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,8 +14,8 @@ - `src/route.ts`: URL状態から表示ページを決めて描画する。 - `src/reroute.ts`: `popstate`、検索フォーム、内部リンククリックを監視して再ルーティングする。 - `src/ssg.ts`: `post.template.html` と `posts.json` から `posts/*.html` を生成する。 -- `src/scripts/generatePosts.ts`: `markdown/*.md` から `posts.json` を更新する。 -- `src/scripts/generateBibliography.ts`: `works.json` に文献表記を付与する。 +- `src/scripts/generatePostsJson.ts`: `markdown/*.md` から `posts.json` を更新する。 +- `src/scripts/generateWorks.ts`: `works.json` に文献表記を付与する。 - `src/scripts/generateSitemap.ts`: `sitemap.xml` を生成する。 ## HTMLエントリー @@ -32,7 +32,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 - 研究業績ページ用の独立エントリー。 - `./works.json` を読み、`src/works/Works.ts` でHTMLを生成して `renderRoot()` に渡す。 - ブログSPAの `route.ts` は使わない。 -- 文献表記は `src/scripts/generateBibliography.ts` が `works.json` に事前生成する。 +- 文献表記は `src/scripts/generateWorks.ts` が `works.json` に事前生成する。 ### `posts/[id].html` -> `src/hydrate.ts` diff --git a/index.js b/index.js index 4c6d317..78958ed 100644 --- a/index.js +++ b/index.js @@ -6,9 +6,9 @@ const webpackDevMiddleware = require('webpack-dev-middleware'); const config = require('./webpack.dev.config.js'); const { createJiti } = require('jiti'); const jiti = createJiti(__filename); -const { generatePosts } = jiti('./src/scripts/generatePosts.ts'); +const { generatePostsJson } = jiti('./src/scripts/generatePostsJson.ts'); const { generateSitemap } = jiti('./src/scripts/generateSitemap.ts'); -const { generateBibliography } = jiti('./src/scripts/generateBibliography.ts'); +const { generateWorks } = jiti('./src/scripts/generateWorks.ts'); const { generateStaticHTML } = jiti('./src/ssg.ts'); const rootDir = __dirname; @@ -38,8 +38,8 @@ express() ); (async () => { - await generatePosts(); - await generateBibliography(); + await generatePostsJson(); + await generateWorks(); const posts = JSON.parse( fs.readFileSync(path.resolve(__dirname, 'posts.json'), 'utf8'), ); diff --git a/src/scripts/generatePosts.ts b/src/scripts/generatePostsJson.ts similarity index 95% rename from src/scripts/generatePosts.ts rename to src/scripts/generatePostsJson.ts index cc98bd2..1e36af1 100644 --- a/src/scripts/generatePosts.ts +++ b/src/scripts/generatePostsJson.ts @@ -64,7 +64,7 @@ async function writePostsJson( ); } -export async function generatePosts(): Promise { +export async function generatePostsJson(): Promise { await writePostsJson( readPostsMarkdown(listFiles(mdPath)), JSON.parse(readFileSync(jsonPath, 'utf-8')) as JSONPost[], @@ -73,7 +73,7 @@ export async function generatePosts(): Promise { } if (path.resolve(process.argv[1] ?? '') === filename) { - generatePosts().catch((e: unknown) => { + generatePostsJson().catch((e: unknown) => { console.error(e); process.exitCode = 1; }); diff --git a/src/scripts/generateSitemap.ts b/src/scripts/generateSitemap.ts index 3520bef..83623f8 100644 --- a/src/scripts/generateSitemap.ts +++ b/src/scripts/generateSitemap.ts @@ -3,12 +3,9 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { SitemapStream, streamToPromise } from 'sitemap'; import format from 'xml-formatter'; +import { Update } from '../notify'; import { JSONPost } from '../post'; -type About = { - updatedAt: string; -}; - const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); const rootDir = path.resolve(dirname, '../..'); @@ -35,7 +32,7 @@ function tagHistory(posts: JSONPost[]): { tag: string; modifiedAt: string }[] { export async function generateSitemap(posts: JSONPost[]): Promise { const { updatedAt } = JSON.parse( readFileSync(path.resolve(rootDir, 'about.json'), 'utf8'), - ) as About; + ) as Update; const sitemap = new SitemapStream({ hostname: 'https://kyonenya.github.io/', }); diff --git a/src/scripts/generateBibliography.ts b/src/scripts/generateWorks.ts similarity index 94% rename from src/scripts/generateBibliography.ts rename to src/scripts/generateWorks.ts index bb81c58..f750eaf 100644 --- a/src/scripts/generateBibliography.ts +++ b/src/scripts/generateWorks.ts @@ -81,7 +81,7 @@ function appendBibliography(items: Data[]): Citation[] { })); } -export async function generateBibliography(): Promise { +export async function generateWorks(): Promise { const works = JSON.parse(readFileSync(worksPath, 'utf8')) as Data[]; const newWorks = appendBibliography(works); writeFileSync( @@ -91,11 +91,11 @@ export async function generateBibliography(): Promise { parser: 'json', }), ); - console.log('bibliography generated.'); + console.log('works generated.'); } if (path.resolve(process.argv[1] ?? '') === filename) { - generateBibliography().catch((e: unknown) => { + generateWorks().catch((e: unknown) => { console.error(e); process.exitCode = 1; }); From 9e2d5d2688ce0347d4c822bf2f5a79adeffe096a Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 23:02:16 +0900 Subject: [PATCH 04/26] =?UTF-8?q?refactor:=20=E5=BC=95=E6=95=B0=E6=B8=A1?= =?UTF-8?q?=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 14 ++++++-------- src/scripts/generatePostsJson.ts | 11 +++++++---- src/ssg.ts | 10 ++++------ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 78958ed..ff40746 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,4 @@ const express = require('express'); -const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); @@ -38,11 +37,10 @@ express() ); (async () => { - await generatePostsJson(); - await generateWorks(); - const posts = JSON.parse( - fs.readFileSync(path.resolve(__dirname, 'posts.json'), 'utf8'), - ); - await generateSitemap(posts); - generateStaticHTML(); + const posts = await generatePostsJson(); + await Promise.all([ + generateWorks(), + generateSitemap(posts), + Promise.resolve().then(() => generateStaticHTML(posts)), + ]); })().catch((e) => console.error(e)); diff --git a/src/scripts/generatePostsJson.ts b/src/scripts/generatePostsJson.ts index 1e36af1..757ed17 100644 --- a/src/scripts/generatePostsJson.ts +++ b/src/scripts/generatePostsJson.ts @@ -54,22 +54,25 @@ function uniquePosts(posts: JSONPost[]): JSONPost[] { async function writePostsJson( posts: JSONPost[], mdPosts: JSONPost[], -): Promise { +): Promise { + const newPosts = uniquePosts([...mdPosts, ...posts]); writeFileSync( jsonPath, - await format(JSON.stringify(uniquePosts([...mdPosts, ...posts])), { + await format(JSON.stringify(newPosts), { semi: false, parser: 'json', }), ); + return newPosts; } -export async function generatePostsJson(): Promise { - await writePostsJson( +export async function generatePostsJson(): Promise { + const posts = await writePostsJson( readPostsMarkdown(listFiles(mdPath)), JSON.parse(readFileSync(jsonPath, 'utf-8')) as JSONPost[], ); console.log('posts genarated.'); + return posts; } if (path.resolve(process.argv[1] ?? '') === filename) { diff --git a/src/ssg.ts b/src/ssg.ts index 5a8dfde..65a7d11 100644 --- a/src/ssg.ts +++ b/src/ssg.ts @@ -3,10 +3,10 @@ import { readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; import { articlePage } from './Article'; import { BlogCard } from './BlogCard'; -import { jsonToPosts, Post, JSONPost } from './post'; +import { jsonToPosts } from './post'; +import type { JSONPost, Post } from './post'; const templatePath = path.resolve(__dirname, '..', 'post.template.html'); -const jsonPath = path.resolve(__dirname, '..', 'posts.json'); const distPath = path.resolve(__dirname, '..', 'posts'); function createTemplateValues(post: Post): Record { @@ -35,11 +35,9 @@ function embedTemplate(post: Post, template: string, posts: Post[]): string { }); } -export function generateStaticHTML(): void { +export function generateStaticHTML(jsonPosts: JSONPost[]): void { const template = readFileSync(templatePath, 'utf8'); - const posts = jsonToPosts( - JSON.parse(readFileSync(jsonPath, 'utf8')) as JSONPost[], - ); + const posts = jsonToPosts(jsonPosts); posts.forEach((post) => writeFileSync( path.resolve(distPath, `${post.id}.html`), From 96365ba348a590468976dabd09d68d48cb510bf4 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 23:16:25 +0900 Subject: [PATCH 05/26] =?UTF-8?q?refacotor:=20=E9=9D=9E=E5=90=8C=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 18 +++++--- src/scripts/generatePostsJson.ts | 78 ++++++++++++++++++-------------- src/scripts/generateSitemap.ts | 28 +++++++----- src/scripts/generateWorks.ts | 37 ++++++++------- src/ssg.ts | 18 ++++---- 5 files changed, 104 insertions(+), 75 deletions(-) diff --git a/index.js b/index.js index ff40746..5cc3590 100644 --- a/index.js +++ b/index.js @@ -37,10 +37,14 @@ express() ); (async () => { - const posts = await generatePostsJson(); - await Promise.all([ - generateWorks(), - generateSitemap(posts), - Promise.resolve().then(() => generateStaticHTML(posts)), - ]); -})().catch((e) => console.error(e)); + try { + const posts = await generatePostsJson(); + await Promise.all([ + generateWorks(), + generateSitemap(posts), + generateStaticHTML(posts), + ]); + } catch (e) { + console.error(e); + } +})(); diff --git a/src/scripts/generatePostsJson.ts b/src/scripts/generatePostsJson.ts index 757ed17..46acc8a 100644 --- a/src/scripts/generatePostsJson.ts +++ b/src/scripts/generatePostsJson.ts @@ -1,11 +1,11 @@ -import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { readdir, readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import matter from 'gray-matter'; import MarkdownIt from 'markdown-it'; import markdownItFootnote from 'markdown-it-footnote'; import { format } from 'prettier'; -import { JSONPost } from '../post'; +import type { JSONPost } from '../post'; const md = new MarkdownIt(); md.use(markdownItFootnote); @@ -16,33 +16,35 @@ const rootDir = path.resolve(dirname, '../..'); const jsonPath = path.resolve(rootDir, 'posts.json'); const mdPath = path.resolve(rootDir, 'markdown'); -function listFiles(dir: string): string[] { - return readdirSync(dir, { withFileTypes: true }).flatMap((dirent) => { - if (/^\..*/.test(dirent.name)) return []; // exclude '.icloud' file - const filePath = path.resolve(dir, dirent.name); - return dirent.isFile() ? [filePath] : listFiles(filePath); - }); +async function listFiles(dir: string): Promise { + const paths = await Promise.all( + (await readdir(dir, { withFileTypes: true })).map(async (dirent) => { + if (/^\..*/.test(dirent.name)) return []; // exclude '.icloud' file + const filePath = path.resolve(dir, dirent.name); + return dirent.isFile() ? [filePath] : await listFiles(filePath); + }), + ); + return paths.flat(); } -function readPostsMarkdown(paths: string[]): JSONPost[] { - return paths - .map((path) => readFileSync(path, 'utf-8')) - .map((string) => matter(string)) - .map( - (matter) => - ({ - ...matter.data, - text: md - .render(matter.content) - .replace(/\n/g, '') - .replace(/>/g, '>') - .replace(/</g, '<') - // 漢字《ふりがな》 - .replace(/|(.+?)《(.+?)》/g, '$1$2') - .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') - .replace(/([一-龠]+)《(.+?)》/g, '$1$2'), - }) as JSONPost, - ); +async function readPostsMarkdown(paths: string[]): Promise { + return await Promise.all( + paths.map(async (path) => { + const { data, content } = matter(await readFile(path, 'utf-8')); + return { + ...data, + text: md + .render(content) + .replace(/\n/g, '') + .replace(/>/g, '>') + .replace(/</g, '<') + // 漢字《ふりがな》 + .replace(/|(.+?)《(.+?)》/g, '$1$2') + .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') + .replace(/([一-龠]+)《(.+?)》/g, '$1$2'), + } as JSONPost; + }), + ); } function uniquePosts(posts: JSONPost[]): JSONPost[] { @@ -56,7 +58,7 @@ async function writePostsJson( mdPosts: JSONPost[], ): Promise { const newPosts = uniquePosts([...mdPosts, ...posts]); - writeFileSync( + await writeFile( jsonPath, await format(JSON.stringify(newPosts), { semi: false, @@ -67,17 +69,27 @@ async function writePostsJson( } export async function generatePostsJson(): Promise { + const [mdPaths, postsJson] = await Promise.all([ + listFiles(mdPath), + readFile(jsonPath, 'utf-8'), + ]); const posts = await writePostsJson( - readPostsMarkdown(listFiles(mdPath)), - JSON.parse(readFileSync(jsonPath, 'utf-8')) as JSONPost[], + JSON.parse(postsJson) as JSONPost[], + await readPostsMarkdown(mdPaths), ); console.log('posts genarated.'); return posts; } -if (path.resolve(process.argv[1] ?? '') === filename) { - generatePostsJson().catch((e: unknown) => { +async function main(): Promise { + try { + await generatePostsJson(); + } catch (e) { console.error(e); process.exitCode = 1; - }); + } +} + +if (path.resolve(process.argv[1] ?? '') === filename) { + void main(); } diff --git a/src/scripts/generateSitemap.ts b/src/scripts/generateSitemap.ts index 83623f8..b5627ae 100644 --- a/src/scripts/generateSitemap.ts +++ b/src/scripts/generateSitemap.ts @@ -1,10 +1,10 @@ -import { readFileSync, writeFileSync } from 'node:fs'; +import { readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { SitemapStream, streamToPromise } from 'sitemap'; import format from 'xml-formatter'; -import { Update } from '../notify'; -import { JSONPost } from '../post'; +import type { Update } from '../notify'; +import type { JSONPost } from '../post'; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -31,7 +31,7 @@ function tagHistory(posts: JSONPost[]): { tag: string; modifiedAt: string }[] { export async function generateSitemap(posts: JSONPost[]): Promise { const { updatedAt } = JSON.parse( - readFileSync(path.resolve(rootDir, 'about.json'), 'utf8'), + await readFile(path.resolve(rootDir, 'about.json'), 'utf8'), ) as Update; const sitemap = new SitemapStream({ hostname: 'https://kyonenya.github.io/', @@ -49,19 +49,25 @@ export async function generateSitemap(posts: JSONPost[]): Promise { sitemap.end(); const sm = await streamToPromise(sitemap); - writeFileSync( + await writeFile( sitemapPath, format(sm.toString(), { indentation: ' ', collapseContent: true }), ); console.log('sitemap generated.'); } -if (path.resolve(process.argv[1] ?? '') === filename) { - const posts = JSON.parse( - readFileSync(path.resolve(rootDir, 'posts.json'), 'utf8'), - ) as JSONPost[]; - generateSitemap(posts).catch((e: unknown) => { +async function main(): Promise { + try { + const posts = JSON.parse( + await readFile(path.resolve(rootDir, 'posts.json'), 'utf8'), + ) as JSONPost[]; + await generateSitemap(posts); + } catch (e) { console.error(e); process.exitCode = 1; - }); + } +} + +if (path.resolve(process.argv[1] ?? '') === filename) { + void main(); } diff --git a/src/scripts/generateWorks.ts b/src/scripts/generateWorks.ts index f750eaf..134d4bd 100644 --- a/src/scripts/generateWorks.ts +++ b/src/scripts/generateWorks.ts @@ -1,11 +1,10 @@ -import { readFileSync, writeFileSync } from 'node:fs'; +import { readFile, writeFile } from 'node:fs/promises'; import { createRequire } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -// eslint-disable-next-line import/no-unresolved -import { Data } from 'csl-json'; +import type { Data } from 'csl-json'; import { format } from 'prettier'; -import { Citation } from '../works/citation'; +import type { Citation } from '../works/citation'; type CslEngine = { setOutputFormat: (format: 'text') => void; @@ -69,12 +68,12 @@ function citeproc(data: Data[], style: string, locale: string): string[] { return bib[1].map((text) => text.replace(/\n$/, '')); } -function appendBibliography(items: Data[]): Citation[] { - const bibTexts = citeproc( - items, - readFileSync(stylePath, 'utf-8'), - readFileSync(localePath, 'utf-8'), - ); +async function appendBibliography(items: Data[]): Promise { + const [style, locale] = await Promise.all([ + readFile(stylePath, 'utf-8'), + readFile(localePath, 'utf-8'), + ]); + const bibTexts = citeproc(items, style, locale); return items.map((item, i) => ({ ...item, _bibliographyText: bibTexts[i], @@ -82,9 +81,9 @@ function appendBibliography(items: Data[]): Citation[] { } export async function generateWorks(): Promise { - const works = JSON.parse(readFileSync(worksPath, 'utf8')) as Data[]; - const newWorks = appendBibliography(works); - writeFileSync( + const works = JSON.parse(await readFile(worksPath, 'utf8')) as Data[]; + const newWorks = await appendBibliography(works); + await writeFile( worksPath, await format(JSON.stringify(newWorks), { semi: false, @@ -94,9 +93,15 @@ export async function generateWorks(): Promise { console.log('works generated.'); } -if (path.resolve(process.argv[1] ?? '') === filename) { - generateWorks().catch((e: unknown) => { +async function main(): Promise { + try { + await generateWorks(); + } catch (e) { console.error(e); process.exitCode = 1; - }); + } +} + +if (path.resolve(process.argv[1] ?? '') === filename) { + void main(); } diff --git a/src/ssg.ts b/src/ssg.ts index 65a7d11..9d86ff7 100644 --- a/src/ssg.ts +++ b/src/ssg.ts @@ -1,5 +1,5 @@ /* server-side only: do not import client-side code */ -import { readFileSync, writeFileSync } from 'node:fs'; +import { readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { articlePage } from './Article'; import { BlogCard } from './BlogCard'; @@ -35,14 +35,16 @@ function embedTemplate(post: Post, template: string, posts: Post[]): string { }); } -export function generateStaticHTML(jsonPosts: JSONPost[]): void { - const template = readFileSync(templatePath, 'utf8'); +export async function generateStaticHTML(jsonPosts: JSONPost[]): Promise { + const template = await readFile(templatePath, 'utf8'); const posts = jsonToPosts(jsonPosts); - posts.forEach((post) => - writeFileSync( - path.resolve(distPath, `${post.id}.html`), - embedTemplate(post, template, posts), - 'utf8', + await Promise.all( + posts.map((post) => + writeFile( + path.resolve(distPath, `${post.id}.html`), + embedTemplate(post, template, posts), + 'utf8', + ), ), ); console.log('static html generated.'); From aedd89d90cc03c66557d5d2dcbc1a8e32a62b80e Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 23:33:25 +0900 Subject: [PATCH 06/26] =?UTF-8?q?refactor:=20remove=20regacy=20&=20?= =?UTF-8?q?=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=97=E3=83=88=E5=AE=9F=E8=A1=8C?= =?UTF-8?q?=E3=82=92=E5=8D=98=E7=B4=94=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/scripts/generatePostsJson.ts | 10 +--------- src/scripts/generateSitemap.ts | 17 ++++------------- src/scripts/generateWorks.ts | 18 +++++------------- src/ssg.ts | 15 +++++++++++++-- 4 files changed, 23 insertions(+), 37 deletions(-) diff --git a/src/scripts/generatePostsJson.ts b/src/scripts/generatePostsJson.ts index 46acc8a..bd05598 100644 --- a/src/scripts/generatePostsJson.ts +++ b/src/scripts/generatePostsJson.ts @@ -81,15 +81,7 @@ export async function generatePostsJson(): Promise { return posts; } -async function main(): Promise { - try { - await generatePostsJson(); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} if (path.resolve(process.argv[1] ?? '') === filename) { - void main(); + void generatePostsJson(); } diff --git a/src/scripts/generateSitemap.ts b/src/scripts/generateSitemap.ts index b5627ae..efc6d5d 100644 --- a/src/scripts/generateSitemap.ts +++ b/src/scripts/generateSitemap.ts @@ -56,18 +56,9 @@ export async function generateSitemap(posts: JSONPost[]): Promise { console.log('sitemap generated.'); } -async function main(): Promise { - try { - const posts = JSON.parse( - await readFile(path.resolve(rootDir, 'posts.json'), 'utf8'), - ) as JSONPost[]; - await generateSitemap(posts); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} - if (path.resolve(process.argv[1] ?? '') === filename) { - void main(); + const posts = JSON.parse( + await readFile(path.resolve(rootDir, 'posts.json'), 'utf8'), + ) as JSONPost[]; + void generateSitemap(posts); } diff --git a/src/scripts/generateWorks.ts b/src/scripts/generateWorks.ts index 134d4bd..5ad5dae 100644 --- a/src/scripts/generateWorks.ts +++ b/src/scripts/generateWorks.ts @@ -1,7 +1,9 @@ import { readFile, writeFile } from 'node:fs/promises'; -import { createRequire } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore citeproc does not publish TypeScript declarations. +import Citeproc from 'citeproc'; import type { Data } from 'csl-json'; import { format } from 'prettier'; import type { Citation } from '../works/citation'; @@ -12,7 +14,7 @@ type CslEngine = { makeBibliography: () => false | [unknown, string[]]; }; -type Csl = { +const CSL = Citeproc as { Engine: new ( sys: { retrieveLocale: (lang: string) => string; @@ -26,8 +28,6 @@ type Csl = { const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); -const require = createRequire(import.meta.url); -const CSL = require('citeproc') as Csl; const rootDir = path.resolve(dirname, '../..'); const worksPath = path.resolve(rootDir, 'works.json'); const stylePath = path.resolve( @@ -93,15 +93,7 @@ export async function generateWorks(): Promise { console.log('works generated.'); } -async function main(): Promise { - try { - await generateWorks(); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} if (path.resolve(process.argv[1] ?? '') === filename) { - void main(); + void generateWorks(); } diff --git a/src/ssg.ts b/src/ssg.ts index 9d86ff7..76ab190 100644 --- a/src/ssg.ts +++ b/src/ssg.ts @@ -1,13 +1,17 @@ /* server-side only: do not import client-side code */ import { readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { articlePage } from './Article'; import { BlogCard } from './BlogCard'; import { jsonToPosts } from './post'; import type { JSONPost, Post } from './post'; -const templatePath = path.resolve(__dirname, '..', 'post.template.html'); -const distPath = path.resolve(__dirname, '..', 'posts'); +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); +const rootDir = path.resolve(dirname, '..'); +const templatePath = path.resolve(rootDir, 'post.template.html'); +const distPath = path.resolve(rootDir, 'posts'); function createTemplateValues(post: Post): Record { const page = articlePage(post, true); @@ -49,3 +53,10 @@ export async function generateStaticHTML(jsonPosts: JSONPost[]): Promise { ); console.log('static html generated.'); } + +if (path.resolve(process.argv[1] ?? '') === filename) { + const posts = JSON.parse( + await readFile(path.resolve(rootDir, 'posts.json'), 'utf8'), + ) as JSONPost[]; + void generateStaticHTML(posts); +} From aedc06921b49feb756520a11f74440e88c01744e Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 23:45:46 +0900 Subject: [PATCH 07/26] =?UTF-8?q?refactor:=20index.js=20->=20mjs=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 +- README.md | 2 +- index.js => index.mjs | 35 +++++++++---------- package.json | 4 +-- src/scripts/generatePostsJson.ts | 1 - src/scripts/generateWorks.ts | 1 - webpack.config.js | 27 -------------- webpack.config.mjs | 32 +++++++++++++++++ ...ck.dev.config.js => webpack.dev.config.mjs | 6 ++-- 9 files changed, 56 insertions(+), 54 deletions(-) rename index.js => index.mjs (67%) delete mode 100644 webpack.config.js create mode 100644 webpack.config.mjs rename webpack.dev.config.js => webpack.dev.config.mjs (78%) diff --git a/AGENTS.md b/AGENTS.md index cbd6763..00ef332 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,7 +65,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 - iOSの[Code App](https://code.thebaselab.com/)でも開発サーバーを動かすことがある。公式ドキュメント上も、Code AppはiOSの制約を受け、ネイティブコンポーネントを含むモジュールの追加やsubprocess起動ができない。 - Code AppのNode.js v18.19.0では、`process.setUncaughtExceptionCaptureCallback()` が有効な状態で起動することがある。この環境では `domain` モジュールと共存できず、`ts-node/register` は `ERR_DOMAIN_CALLBACK_NOT_AVAILABLE` で落ちる。サーバー側TSの読み込みには `jiti` を使う。 -- Code AppのNode環境では `WebAssembly` が未定義になることがある。webpackの既定 `md4` ハッシュ経路は `webpack/lib/util/hash/md4.js` に入り `ReferenceError: WebAssembly is not defined` になるため、`webpack.config.js` の `output.hashFunction: 'sha256'` を外さない。 +- Code AppのNode環境では `WebAssembly` が未定義になることがある。webpackの既定 `md4` ハッシュ経路は `webpack/lib/util/hash/md4.js` に入り `ReferenceError: WebAssembly is not defined` になるため、`webpack.config.mjs` の `output.hashFunction: 'sha256'` を外さない。 - Node 18 / OpenSSL 3 では古いwebpackの `md4` ハッシュが `digital envelope routines` / `ERR_OSSL_EVP_UNSUPPORTED` で落ちる。webpack系依存を古い版へ戻す場合はCode Appで `npm run dev` を再検証する。 - Code Appでは、Nodeの `child_process.spawn()` から子プロセスを作る処理が `spawn EPERM` / `Operation not permitted` で落ちることがある。内蔵ターミナルがコマンドを起動できても、その中のNodeプロセスからさらに別プロセスを起動できるとは限らない。Code Appで動かすツールは、外部CLIや複数コマンドを内部から起動しない構成を優先する。 diff --git a/README.md b/README.md index 78e105a..3b5d7c9 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ - 依存パッケージ0、ビルドサイズを10KB未満まで削減 - webpack・PostCSS([v1.1.0](https://github.com/kyonenya/kyonenya.github.io/releases/tag/v1.1.0)より) - TypeScript([v2.0.0](https://github.com/kyonenya/kyonenya.github.io/releases/tag/v2.0.0)より) -- [Codesandbox for iOS](https://codesandbox.io/codesandbox-for-ios) というiPadでNode.jsが動かせるアプリで開発 +- [Code App](https://code.thebaselab.com/) というiPadでNode.jsが動かせるアプリで開発 ## なぜそんな設計で作るのですか(v0.0.0) diff --git a/index.js b/index.mjs similarity index 67% rename from index.js rename to index.mjs index 5cc3590..0031487 100644 --- a/index.js +++ b/index.mjs @@ -1,16 +1,19 @@ -const express = require('express'); -const path = require('path'); -const webpack = require('webpack'); -const webpackDevMiddleware = require('webpack-dev-middleware'); -const config = require('./webpack.dev.config.js'); -const { createJiti } = require('jiti'); -const jiti = createJiti(__filename); +import express from 'express'; +import { createJiti } from 'jiti'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import webpack from 'webpack'; +import webpackDevMiddleware from 'webpack-dev-middleware'; +import config from './webpack.dev.config.mjs'; + +const filename = fileURLToPath(import.meta.url); +const rootDir = path.dirname(filename); +const jiti = createJiti(import.meta.url); const { generatePostsJson } = jiti('./src/scripts/generatePostsJson.ts'); const { generateSitemap } = jiti('./src/scripts/generateSitemap.ts'); const { generateWorks } = jiti('./src/scripts/generateWorks.ts'); const { generateStaticHTML } = jiti('./src/ssg.ts'); -const rootDir = __dirname; const port = process.env['WEB_APP_PORT'] ?? 3100; express() @@ -37,14 +40,10 @@ express() ); (async () => { - try { - const posts = await generatePostsJson(); - await Promise.all([ - generateWorks(), - generateSitemap(posts), - generateStaticHTML(posts), - ]); - } catch (e) { - console.error(e); - } + const posts = await generatePostsJson(); + await Promise.all([ + generateWorks(), + generateSitemap(posts), + generateStaticHTML(posts), + ]); })(); diff --git a/package.json b/package.json index bcdf514..7684624 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "kyonenya.github.io", "version": "2.5.0", "scripts": { - "dev": "node index.js", + "dev": "node index.mjs", "tsc": "tsc -w --noEmit", "tsc:once": "tsc --noEmit", - "build": "node ./node_modules/webpack/bin/webpack.js", + "build": "node ./node_modules/webpack/bin/webpack.js --config webpack.config.mjs", "build-css": "postcss src/css/index.css -o dist/css/bundle.css --verbose", "fmt": "prettier --write **/*.{ts,js,mjs,json,css}", "lint": "eslint --fix src/**/*.ts", diff --git a/src/scripts/generatePostsJson.ts b/src/scripts/generatePostsJson.ts index bd05598..707a19e 100644 --- a/src/scripts/generatePostsJson.ts +++ b/src/scripts/generatePostsJson.ts @@ -81,7 +81,6 @@ export async function generatePostsJson(): Promise { return posts; } - if (path.resolve(process.argv[1] ?? '') === filename) { void generatePostsJson(); } diff --git a/src/scripts/generateWorks.ts b/src/scripts/generateWorks.ts index 5ad5dae..4cac75d 100644 --- a/src/scripts/generateWorks.ts +++ b/src/scripts/generateWorks.ts @@ -93,7 +93,6 @@ export async function generateWorks(): Promise { console.log('works generated.'); } - if (path.resolve(process.argv[1] ?? '') === filename) { void generateWorks(); } diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 742e15e..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,27 +0,0 @@ -const path = require('path'); - -module.exports = { - mode: 'production', - entry: { - index: path.join(__dirname, 'src', 'index.ts'), - works: path.join(__dirname, 'src', 'works', 'index.ts'), - hydrate: path.join(__dirname, 'src', 'hydrate.ts'), - }, - output: { - path: path.join(__dirname, 'dist'), - filename: '[name].js', - hashFunction: 'sha256', - }, - module: { - rules: [ - { - test: /\.ts$/, - exclude: /node_modules/, - use: [{ loader: 'ts-loader' }], - }, - ], - }, - resolve: { - extensions: ['.ts', '.js'], - }, -}; diff --git a/webpack.config.mjs b/webpack.config.mjs new file mode 100644 index 0000000..d2baf14 --- /dev/null +++ b/webpack.config.mjs @@ -0,0 +1,32 @@ +// @ts-check +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); + +export default { + mode: 'production', + entry: { + index: path.join(dirname, 'src', 'index.ts'), + works: path.join(dirname, 'src', 'works', 'index.ts'), + hydrate: path.join(dirname, 'src', 'hydrate.ts'), + }, + output: { + path: path.join(dirname, 'dist'), + filename: '[name].js', + hashFunction: 'sha256', + }, + module: { + rules: [ + { + test: /\.ts$/, + exclude: /node_modules/, + use: [{ loader: 'ts-loader' }], + }, + ], + }, + resolve: { + extensions: ['.ts', '.js'], + }, +}; diff --git a/webpack.dev.config.js b/webpack.dev.config.mjs similarity index 78% rename from webpack.dev.config.js rename to webpack.dev.config.mjs index c80da8c..55b7548 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.mjs @@ -1,7 +1,7 @@ -const path = require('path'); -const productionConfig = require('./webpack.config'); +// @ts-check +import productionConfig from './webpack.config.mjs'; -module.exports = { +export default { ...productionConfig, mode: 'development', output: { From 1db89bcac21fc2616a0fddc254aebff817f07879 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Wed, 6 May 2026 23:52:55 +0900 Subject: [PATCH 08/26] =?UTF-8?q?chore:=20npm=20scripts=E3=82=92=E7=B0=A1?= =?UTF-8?q?=E6=BD=94=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7684624..69fea59 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dev": "node index.mjs", "tsc": "tsc -w --noEmit", "tsc:once": "tsc --noEmit", - "build": "node ./node_modules/webpack/bin/webpack.js --config webpack.config.mjs", + "build": "node ./node_modules/webpack/bin/webpack.js", "build-css": "postcss src/css/index.css -o dist/css/bundle.css --verbose", "fmt": "prettier --write **/*.{ts,js,mjs,json,css}", "lint": "eslint --fix src/**/*.ts", From e9d6758654ee60fbc825c715cbbe4b3097cf3aab Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 01:01:08 +0900 Subject: [PATCH 09/26] refactor: rename --- AGENTS.md | 8 ++++---- index.mjs | 6 +++--- src/scripts/{generatePostsJson.ts => postsJson.ts} | 0 src/scripts/{generateSitemap.ts => sitemap.ts} | 0 src/scripts/{generateWorks.ts => worksJson.ts} | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename src/scripts/{generatePostsJson.ts => postsJson.ts} (100%) rename src/scripts/{generateSitemap.ts => sitemap.ts} (100%) rename src/scripts/{generateWorks.ts => worksJson.ts} (100%) diff --git a/AGENTS.md b/AGENTS.md index 00ef332..f63d280 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,9 +14,9 @@ - `src/route.ts`: URL状態から表示ページを決めて描画する。 - `src/reroute.ts`: `popstate`、検索フォーム、内部リンククリックを監視して再ルーティングする。 - `src/ssg.ts`: `post.template.html` と `posts.json` から `posts/*.html` を生成する。 -- `src/scripts/generatePostsJson.ts`: `markdown/*.md` から `posts.json` を更新する。 -- `src/scripts/generateWorks.ts`: `works.json` に文献表記を付与する。 -- `src/scripts/generateSitemap.ts`: `sitemap.xml` を生成する。 +- `src/scripts/postsJson.ts`: `markdown/*.md` から `posts.json` を更新する。 +- `src/scripts/worksJson.ts`: `works.json` に文献表記を付与する。 +- `src/scripts/sitemap.ts`: `sitemap.xml` を生成する。 ## HTMLエントリー @@ -32,7 +32,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 - 研究業績ページ用の独立エントリー。 - `./works.json` を読み、`src/works/Works.ts` でHTMLを生成して `renderRoot()` に渡す。 - ブログSPAの `route.ts` は使わない。 -- 文献表記は `src/scripts/generateWorks.ts` が `works.json` に事前生成する。 +- 文献表記は `src/scripts/worksJson.ts` が `works.json` に事前生成する。 ### `posts/[id].html` -> `src/hydrate.ts` diff --git a/index.mjs b/index.mjs index 0031487..efaef39 100644 --- a/index.mjs +++ b/index.mjs @@ -9,9 +9,9 @@ import config from './webpack.dev.config.mjs'; const filename = fileURLToPath(import.meta.url); const rootDir = path.dirname(filename); const jiti = createJiti(import.meta.url); -const { generatePostsJson } = jiti('./src/scripts/generatePostsJson.ts'); -const { generateSitemap } = jiti('./src/scripts/generateSitemap.ts'); -const { generateWorks } = jiti('./src/scripts/generateWorks.ts'); +const { generatePostsJson } = jiti('./src/scripts/postsJson.ts'); +const { generateSitemap } = jiti('./src/scripts/sitemap.ts'); +const { generateWorks } = jiti('./src/scripts/worksJson.ts'); const { generateStaticHTML } = jiti('./src/ssg.ts'); const port = process.env['WEB_APP_PORT'] ?? 3100; diff --git a/src/scripts/generatePostsJson.ts b/src/scripts/postsJson.ts similarity index 100% rename from src/scripts/generatePostsJson.ts rename to src/scripts/postsJson.ts diff --git a/src/scripts/generateSitemap.ts b/src/scripts/sitemap.ts similarity index 100% rename from src/scripts/generateSitemap.ts rename to src/scripts/sitemap.ts diff --git a/src/scripts/generateWorks.ts b/src/scripts/worksJson.ts similarity index 100% rename from src/scripts/generateWorks.ts rename to src/scripts/worksJson.ts From 18c7ba44ec7ec2868a24fcf2bb3481e7aee553b7 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 01:20:25 +0900 Subject: [PATCH 10/26] refactor: rename --- index.mjs | 8 ++++---- src/scripts/postsJson.ts | 6 ++++-- src/scripts/worksJson.ts | 8 ++++---- src/ssg.ts | 8 ++++---- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/index.mjs b/index.mjs index efaef39..5a75f5d 100644 --- a/index.mjs +++ b/index.mjs @@ -11,8 +11,8 @@ const rootDir = path.dirname(filename); const jiti = createJiti(import.meta.url); const { generatePostsJson } = jiti('./src/scripts/postsJson.ts'); const { generateSitemap } = jiti('./src/scripts/sitemap.ts'); -const { generateWorks } = jiti('./src/scripts/worksJson.ts'); -const { generateStaticHTML } = jiti('./src/ssg.ts'); +const { generateWorksJson } = jiti('./src/scripts/worksJson.ts'); +const { generateStaticHtml } = jiti('./src/ssg.ts'); const port = process.env['WEB_APP_PORT'] ?? 3100; @@ -42,8 +42,8 @@ express() (async () => { const posts = await generatePostsJson(); await Promise.all([ - generateWorks(), + generateWorksJson(), generateSitemap(posts), - generateStaticHTML(posts), + generateStaticHtml(posts), ]); })(); diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index 707a19e..e690286 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -17,11 +17,13 @@ const jsonPath = path.resolve(rootDir, 'posts.json'); const mdPath = path.resolve(rootDir, 'markdown'); async function listFiles(dir: string): Promise { + const dirents = await readdir(dir, { withFileTypes: true }); const paths = await Promise.all( - (await readdir(dir, { withFileTypes: true })).map(async (dirent) => { + dirents.map(async (dirent) => { if (/^\..*/.test(dirent.name)) return []; // exclude '.icloud' file const filePath = path.resolve(dir, dirent.name); - return dirent.isFile() ? [filePath] : await listFiles(filePath); + if (dirent.isFile()) return [filePath]; + return await listFiles(filePath); }), ); return paths.flat(); diff --git a/src/scripts/worksJson.ts b/src/scripts/worksJson.ts index 4cac75d..68d8bb0 100644 --- a/src/scripts/worksJson.ts +++ b/src/scripts/worksJson.ts @@ -68,7 +68,7 @@ function citeproc(data: Data[], style: string, locale: string): string[] { return bib[1].map((text) => text.replace(/\n$/, '')); } -async function appendBibliography(items: Data[]): Promise { +async function appendCitations(items: Data[]): Promise { const [style, locale] = await Promise.all([ readFile(stylePath, 'utf-8'), readFile(localePath, 'utf-8'), @@ -80,9 +80,9 @@ async function appendBibliography(items: Data[]): Promise { })); } -export async function generateWorks(): Promise { +export async function generateWorksJson(): Promise { const works = JSON.parse(await readFile(worksPath, 'utf8')) as Data[]; - const newWorks = await appendBibliography(works); + const newWorks = await appendCitations(works); await writeFile( worksPath, await format(JSON.stringify(newWorks), { @@ -94,5 +94,5 @@ export async function generateWorks(): Promise { } if (path.resolve(process.argv[1] ?? '') === filename) { - void generateWorks(); + void generateWorksJson(); } diff --git a/src/ssg.ts b/src/ssg.ts index 76ab190..7307e93 100644 --- a/src/ssg.ts +++ b/src/ssg.ts @@ -11,7 +11,7 @@ const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); const rootDir = path.resolve(dirname, '..'); const templatePath = path.resolve(rootDir, 'post.template.html'); -const distPath = path.resolve(rootDir, 'posts'); +const distDir = path.resolve(rootDir, 'posts'); function createTemplateValues(post: Post): Record { const page = articlePage(post, true); @@ -39,13 +39,13 @@ function embedTemplate(post: Post, template: string, posts: Post[]): string { }); } -export async function generateStaticHTML(jsonPosts: JSONPost[]): Promise { +export async function generateStaticHtml(jsonPosts: JSONPost[]): Promise { const template = await readFile(templatePath, 'utf8'); const posts = jsonToPosts(jsonPosts); await Promise.all( posts.map((post) => writeFile( - path.resolve(distPath, `${post.id}.html`), + path.resolve(distDir, `${post.id}.html`), embedTemplate(post, template, posts), 'utf8', ), @@ -58,5 +58,5 @@ if (path.resolve(process.argv[1] ?? '') === filename) { const posts = JSON.parse( await readFile(path.resolve(rootDir, 'posts.json'), 'utf8'), ) as JSONPost[]; - void generateStaticHTML(posts); + void generateStaticHtml(posts); } From 0c58c60e3947ae60ea5a27c1205a82af1d99814f Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 01:24:59 +0900 Subject: [PATCH 11/26] =?UTF-8?q?fix:=20posts.json=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=99=82=E3=81=AFmd=E3=82=92json=E3=82=88=E3=82=8A=E5=84=AA?= =?UTF-8?q?=E5=85=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/scripts/postsJson.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index e690286..9ca497c 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -59,11 +59,10 @@ async function writePostsJson( posts: JSONPost[], mdPosts: JSONPost[], ): Promise { - const newPosts = uniquePosts([...mdPosts, ...posts]); + const newPosts = uniquePosts([...posts, ...mdPosts]); await writeFile( jsonPath, await format(JSON.stringify(newPosts), { - semi: false, parser: 'json', }), ); From 3977753af1a5ab7298b3a3a36a7d163644691899 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 01:35:41 +0900 Subject: [PATCH 12/26] refactor: rename; mdPosts > jsonPosts --- src/scripts/postsJson.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index 9ca497c..2e6886c 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -56,15 +56,13 @@ function uniquePosts(posts: JSONPost[]): JSONPost[] { } async function writePostsJson( - posts: JSONPost[], + jsonPosts: JSONPost[], mdPosts: JSONPost[], ): Promise { - const newPosts = uniquePosts([...posts, ...mdPosts]); + const newPosts = uniquePosts([...jsonPosts, ...mdPosts]); // mdPosts > jsonPosts await writeFile( jsonPath, - await format(JSON.stringify(newPosts), { - parser: 'json', - }), + await format(JSON.stringify(newPosts), { parser: 'json' }), ); return newPosts; } @@ -78,7 +76,7 @@ export async function generatePostsJson(): Promise { JSON.parse(postsJson) as JSONPost[], await readPostsMarkdown(mdPaths), ); - console.log('posts genarated.'); + console.log('posts generated.'); return posts; } From ba0018d3a5efb3171f8e7d6e762ee266c20d0020 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 01:39:20 +0900 Subject: [PATCH 13/26] refactor: simplify path --- src/scripts/worksJson.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/scripts/worksJson.ts b/src/scripts/worksJson.ts index 68d8bb0..381e566 100644 --- a/src/scripts/worksJson.ts +++ b/src/scripts/worksJson.ts @@ -30,18 +30,9 @@ const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); const rootDir = path.resolve(dirname, '../..'); const worksPath = path.resolve(rootDir, 'works.json'); -const stylePath = path.resolve( - rootDir, - 'assets', - 'citeproc', - 'sist02modified.csl', -); -const localePath = path.resolve( - rootDir, - 'assets', - 'citeproc', - 'locales-ja-JP.xml', -); +const citeprocDir = path.resolve(rootDir, 'assets', 'citeproc'); +const stylePath = path.resolve(citeprocDir, 'sist02modified.csl'); +const localePath = path.resolve(citeprocDir, 'locales-ja-JP.xml'); function removeNullProperties(obj: T): Partial { return Object.fromEntries( From 1e85b1c2aa11948ce094775d991bb974f993efa2 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 07:21:55 +0900 Subject: [PATCH 14/26] =?UTF-8?q?refactor:=20render=20=E3=82=92=20/src=20?= =?UTF-8?q?=E7=9B=B4=E4=B8=8B=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +++ package.json | 2 +- src/Article.ts | 2 +- src/{lib => }/render.ts | 2 +- src/reroute.ts | 2 +- src/route.ts | 2 +- src/works/index.ts | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 .vscode/settings.json rename src/{lib => }/render.ts (97%) diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dc1a02a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "biome.enabled": false +} diff --git a/package.json b/package.json index 69fea59..8cba025 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "node ./node_modules/webpack/bin/webpack.js", "build-css": "postcss src/css/index.css -o dist/css/bundle.css --verbose", "fmt": "prettier --write **/*.{ts,js,mjs,json,css}", - "lint": "eslint --fix src/**/*.ts", + "lint": "eslint --fix src/*.ts src/**/*.ts", "lint-css": "node ./node_modules/stylelint/bin/stylelint.mjs src/**/*.css", "lint-css:fix": "node ./node_modules/stylelint/bin/stylelint.mjs src/**/*.css --fix" }, diff --git a/src/Article.ts b/src/Article.ts index 0015a35..e6b2d42 100644 --- a/src/Article.ts +++ b/src/Article.ts @@ -2,8 +2,8 @@ import { TagList } from './TagList'; import { toExternalLink } from './lib/ExternalLink'; import { MarkupText, kerningDoubleDash } from './lib/MarkupText'; import { formatYMDHm, formatYMD, fromNow } from './lib/dateUtils'; -import { baseUrl, Page } from './lib/render'; import { Post } from './post'; +import { baseUrl, Page } from './render'; const Article = (post: Post, ssg?: boolean): string => `
diff --git a/src/lib/render.ts b/src/render.ts similarity index 97% rename from src/lib/render.ts rename to src/render.ts index bab4119..b6fad27 100644 --- a/src/lib/render.ts +++ b/src/render.ts @@ -1,4 +1,4 @@ -import { isDevelopment } from './utils'; +import { isDevelopment } from './lib/utils'; export type Page = { body: string; diff --git a/src/reroute.ts b/src/reroute.ts index 3bf0e3a..489d213 100644 --- a/src/reroute.ts +++ b/src/reroute.ts @@ -1,4 +1,4 @@ -import { scrollToId } from './lib/render'; +import { scrollToId } from './render'; import { toState } from './state'; function watchPopState(reroute: () => void): void { diff --git a/src/route.ts b/src/route.ts index 0b94168..8a7b028 100644 --- a/src/route.ts +++ b/src/route.ts @@ -1,8 +1,8 @@ import { articlePage } from './Article'; import { PostList, TaggedPostList, SearchedPostList } from './PostList'; -import { renderPage, baseUrl } from './lib/render'; import { isDevelopment } from './lib/utils'; import { Post, excludeReserved } from './post'; +import { renderPage, baseUrl } from './render'; import { toState } from './state'; const searchInputElement = diff --git a/src/works/index.ts b/src/works/index.ts index de32421..a822360 100644 --- a/src/works/index.ts +++ b/src/works/index.ts @@ -1,6 +1,6 @@ -import { renderRoot } from '../lib/render'; import { fetcher } from '../lib/utils'; import { notifyUpdate, Update } from '../notify'; +import { renderRoot } from '../render'; import { Works } from './Works'; import { Citation } from './citation'; From c16f5b50b18ec787e22c2e020e25f856a023452e Mon Sep 17 00:00:00 2001 From: Takashi Kyonenya Date: Thu, 7 May 2026 07:34:42 +0900 Subject: [PATCH 15/26] chore: rename npm scripts --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8cba025..f42e15b 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,10 @@ "build": "node ./node_modules/webpack/bin/webpack.js", "build-css": "postcss src/css/index.css -o dist/css/bundle.css --verbose", "fmt": "prettier --write **/*.{ts,js,mjs,json,css}", - "lint": "eslint --fix src/*.ts src/**/*.ts", - "lint-css": "node ./node_modules/stylelint/bin/stylelint.mjs src/**/*.css", - "lint-css:fix": "node ./node_modules/stylelint/bin/stylelint.mjs src/**/*.css --fix" + "lint": "eslint src/*.ts src/**/*.ts --debug", + "lint:fix": "eslint src/*.ts src/**/*.ts --fix --debug", + "lint-css": "node ./node_modules/stylelint/bin/stylelint.mjs src/**/*.css --formatter verbose", + "lint-css:fix": "node ./node_modules/stylelint/bin/stylelint.mjs src/**/*.css --fix --formatter verbose" }, "dependencies": { "search-summary": "^1.1.2" From 9a23aef7e686f9567e78b55101667aee1715bdb9 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 07:39:31 +0900 Subject: [PATCH 16/26] =?UTF-8?q?refactor:=20AGENTS.md=E3=81=AE=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f63d280..d1b0030 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,9 @@ - `src/app.ts`: ブログSPAの共通起動処理。 - `src/route.ts`: URL状態から表示ページを決めて描画する。 - `src/reroute.ts`: `popstate`、検索フォーム、内部リンククリックを監視して再ルーティングする。 +- `src/state.ts`: `pathname` / `search` / `hash` から記事ID・タグ・検索語を取り出す。 +- `src/render.ts`: `#root` 描画、canonical更新、開発時の内部リンク化、スクロールを扱う。 +- `src/notify.ts`: `about.json` の更新情報から通知表示を扱う。 - `src/ssg.ts`: `post.template.html` と `posts.json` から `posts/*.html` を生成する。 - `src/scripts/postsJson.ts`: `markdown/*.md` から `posts.json` を更新する。 - `src/scripts/worksJson.ts`: `works.json` に文献表記を付与する。 @@ -25,13 +28,14 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 ### `index.html` -> `src/index.ts` - ブログ本体のSPAエントリー。 -- `src/index.ts` は `app()` を呼ぶだけ。`app(true)` は `./posts.json` / `./about.json` を読み、初回に `route(posts)` で `#root` を描画する。 +- `src/index.ts` は `app()` を呼ぶだけ。`app()` は `./posts.json` / `./about.json` を読み、初回に `route(posts)` で `#root` を描画する。 ### `works.html` -> `src/works/index.ts` - 研究業績ページ用の独立エントリー。 - `./works.json` を読み、`src/works/Works.ts` でHTMLを生成して `renderRoot()` に渡す。 - ブログSPAの `route.ts` は使わない。 +- `about.json` も読み、`notifyUpdate()` で更新通知を表示する。 - 文献表記は `src/scripts/worksJson.ts` が `works.json` に事前生成する。 ### `posts/[id].html` -> `src/hydrate.ts` @@ -43,6 +47,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 ## 生成物 - `dist/*.js` と `dist/css/bundle.css` は公開対象のビルド成果物。 +- 開発サーバーではwebpack-dev-middlewareが `dist/dev/*.js` 相当を配信し、`/dist/*.js` からリダイレクトする。 - `posts/*.html` はSSG成果物。 - `posts.json`、`works.json`、`sitemap.xml` も生成スクリプトで更新されることがある。 - 生成物の差分が大量に出る場合は、変更理由を確認してから扱う。無関係なら巻き戻さない。 @@ -51,15 +56,18 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 `package.json` のscripts: -- `npm run dev`: 開発サーバー起動。起動時に記事・サイトマップ・文献・静的記事HTMLも生成する。 +- `npm run dev`: Express開発サーバー起動。起動時に記事・サイトマップ・文献・静的記事HTMLも生成する。 - `npm run tsc`: TypeScript型チェックのwatch。 +- `npm run tsc:once`: TypeScript型チェックの単発実行。 - `npm run build`: webpackで `dist/index.js`、`dist/hydrate.js`、`dist/works.js` を生成。 - `npm run build-css`: CSSを `dist/css/bundle.css` に生成。 -- `npm run lint`: TypeScriptのESLint修正。 +- `npm run lint`: TypeScriptのESLint確認。 +- `npm run lint:fix`: TypeScriptのESLint自動修正。 - `npm run lint-css`: CSS lint。 +- `npm run lint-css:fix`: CSS lintの自動修正。 - `npm run fmt`: Prettier整形。 -`tsc` は `-w` 付きなので、単発確認では `npm run tsc:once` などを使う。 +`tsc` は `-w` 付きなので、単発確認では `npm run tsc:once` を使う。 ## Code App / iOS 環境 @@ -80,6 +88,7 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 - `src/ssg.ts` はサーバーサイド専用。クライアント専用APIに依存するコードをimportしない。 - 予約投稿は本番相当では `excludeReserved` で除外される。開発時はlocalhost判定で表示される。 - `blog-card` はCustom Element。SSGではカードHTMLを事前埋め込みし、SPAでは `defineBlogCard` が定義する。 +- ESLint設定は `eslint.config.mjs` に集約されている。`import/order` はwarnで有効。 ## 編集方針 From 78f9fa793e2455c2f21b796194ffc5785b6626ac Mon Sep 17 00:00:00 2001 From: Takashi Kyonenya Date: Thu, 7 May 2026 07:43:13 +0900 Subject: [PATCH 17/26] chore: build --- dist/works.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/works.js b/dist/works.js index 8534f4e..01fe339 100644 --- a/dist/works.js +++ b/dist/works.js @@ -1 +1 @@ -(()=>{"use strict";async function e(e){const t=await fetch(e);if(!t.ok)throw new Error(t.statusText);return await t.json()}const t="https://kyonenya.github.io/";const n=e=>{document.getElementById(e)?.scrollIntoView({behavior:"smooth"})},o=864e5,a=30*o,r={seconds:1e3,minutes:6e4,hours:36e5,day:o,month:a,year:12*a};Object.keys(r),new Intl.RelativeTimeFormat("ja-JP",{style:"narrow"});const s=["書籍","学位論文","論文","発表","翻訳"];const c=e=>function(e){return e.replace(/\[(.+?)\]\((.+?)\)/g,(e,t,n)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({content:t,href:n}))}((e=>e.replace(/——(?![^(]*\))/g,'——'))(e)),i=e=>`${c(e)}`,l=(e,t,n)=>{const o=e.id.toString()===n,a=o?`${t}.`:`${t}.`,r=function(e){const t=e.issued?.["date-parts"]?.[0]??e["event-date"]?.["date-parts"]?.[0];if(t)return new Date\n ${a}\n ${(o?i:c)(e._bibliographyText)}${r}\n \n `};!async function(){const o=await e("./works.json"),a=decodeURIComponent(window.location.hash.replace("#",""));var r,c;r=((e,t)=>{const n=(e=>Object.fromEntries(s.map(t=>[t,e.filter(e=>function(e){switch(e.type){case"book":return"書籍";case"article-journal":return e.translator?"翻訳":"論文";case"paper-conference":return"発表";case"thesis":return"学位論文";default:return}}(e)===t)])))(e);return`\n
\n
\n

業績一覧

\n ${s.map(e=>((e,t,n)=>e&&0!==e.length?`\n

${t}

\n
    \n ${e.map((e,t)=>l(e,t+1,n)).join("")}\n
`:"")(n[e],e,t)).join("")}\n
\n
`})(o,a),document.getElementById("root").innerHTML=r,n(window.location.hash.replace("#","")),c=window.location.href,/localhost:\d+/.test(c)&&document.querySelectorAll(`a[href^="${t}"]`).forEach(e=>{e.href="/"+e.href.replace(t,"")}),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),function(e,t){return e.getTime(){localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await e("./about.json"))}()})(); \ No newline at end of file +(()=>{"use strict";async function e(e){const t=await fetch(e);if(!t.ok)throw new Error(t.statusText);return await t.json()}const t=864e5,n=30*t,o={seconds:1e3,minutes:6e4,hours:36e5,day:t,month:n,year:12*n};Object.keys(o),new Intl.RelativeTimeFormat("ja-JP",{style:"narrow"});const a="https://kyonenya.github.io/";const r=e=>{document.getElementById(e)?.scrollIntoView({behavior:"smooth"})},s=["書籍","学位論文","論文","発表","翻訳"];const c=e=>function(e){return e.replace(/\[(.+?)\]\((.+?)\)/g,(e,t,n)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({content:t,href:n}))}((e=>e.replace(/——(?![^(]*\))/g,'——'))(e)),i=e=>`${c(e)}`,l=(e,t,n)=>{const o=e.id.toString()===n,a=o?`${t}.`:`${t}.`,r=function(e){const t=e.issued?.["date-parts"]?.[0]??e["event-date"]?.["date-parts"]?.[0];if(t)return new Date\n ${a}\n ${(o?i:c)(e._bibliographyText)}${r}\n \n `};!async function(){const t=await e("./works.json"),n=decodeURIComponent(window.location.hash.replace("#",""));var o,c;o=((e,t)=>{const n=(e=>Object.fromEntries(s.map(t=>[t,e.filter(e=>function(e){switch(e.type){case"book":return"書籍";case"article-journal":return e.translator?"翻訳":"論文";case"paper-conference":return"発表";case"thesis":return"学位論文";default:return}}(e)===t)])))(e);return`\n
\n
\n

業績一覧

\n ${s.map(e=>((e,t,n)=>e&&0!==e.length?`\n

${t}

\n
    \n ${e.map((e,t)=>l(e,t+1,n)).join("")}\n
`:"")(n[e],e,t)).join("")}\n
\n
`})(t,n),document.getElementById("root").innerHTML=o,r(window.location.hash.replace("#","")),c=window.location.href,/localhost:\d+/.test(c)&&document.querySelectorAll(`a[href^="${a}"]`).forEach(e=>{e.href="/"+e.href.replace(a,"")}),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),function(e,t){return e.getTime(){localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await e("./about.json"))}()})(); \ No newline at end of file From 235169d6038993bd21ce24b7d76fe1202d20f862 Mon Sep 17 00:00:00 2001 From: "openai-code-agent[bot]" <242516109+Codex@users.noreply.github.com> Date: Wed, 6 May 2026 23:09:09 +0000 Subject: [PATCH 18/26] =?UTF-8?q?fix:=20WEB=5FAPP=5FPORT=E3=82=92=E6=95=B0?= =?UTF-8?q?=E5=80=A4=E3=81=A8=E3=81=97=E3=81=A6=E6=89=B1=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kyonenya <62150154+kyonenya@users.noreply.github.com> --- index.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.mjs b/index.mjs index 5a75f5d..8f85ac2 100644 --- a/index.mjs +++ b/index.mjs @@ -14,7 +14,11 @@ const { generateSitemap } = jiti('./src/scripts/sitemap.ts'); const { generateWorksJson } = jiti('./src/scripts/worksJson.ts'); const { generateStaticHtml } = jiti('./src/ssg.ts'); -const port = process.env['WEB_APP_PORT'] ?? 3100; +const port = (() => { + const raw = process.env['WEB_APP_PORT']; + const parsed = raw === undefined ? 3100 : Number(raw); + return Number.isNaN(parsed) ? 3100 : parsed; +})(); express() .use(webpackDevMiddleware(webpack(config))) From 365077b542645dd668a79c1ede07bf762ccdfb5a Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 09:05:51 +0900 Subject: [PATCH 19/26] =?UTF-8?q?fix:=20=E3=83=AC=E3=83=93=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 ++ index.mjs | 8 ++------ src/scripts/postsJson.ts | 28 ++++++++++------------------ src/scripts/sitemap.ts | 18 ++++++++++-------- src/scripts/worksJson.ts | 10 +++++----- 5 files changed, 29 insertions(+), 37 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d1b0030..d7be851 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,6 +101,8 @@ webpackのentryは `index`、`works`、`hydrate` の3種類。 ## コードレビューの基本方針 +- GitHub PR上でレビューする場合は、まとめてコメントするだけで済ませず、当該の変更行へGitHubのPR上でインラインコメントを残してください。 +- レビュー依頼では、断りなくコミット・pushを行わないでください。レビューはレビューだけに留め、修正案はコメントとして提示してください。 - ベストプラクティスの具体例を提示してください - 学習リソースの提案を積極的に行ってください - 以下のプレフィックスを使用してレビューコメントを分類してください: diff --git a/index.mjs b/index.mjs index 8f85ac2..37de492 100644 --- a/index.mjs +++ b/index.mjs @@ -14,11 +14,7 @@ const { generateSitemap } = jiti('./src/scripts/sitemap.ts'); const { generateWorksJson } = jiti('./src/scripts/worksJson.ts'); const { generateStaticHtml } = jiti('./src/ssg.ts'); -const port = (() => { - const raw = process.env['WEB_APP_PORT']; - const parsed = raw === undefined ? 3100 : Number(raw); - return Number.isNaN(parsed) ? 3100 : parsed; -})(); +const port = 3100; express() .use(webpackDevMiddleware(webpack(config))) @@ -50,4 +46,4 @@ express() generateSitemap(posts), generateStaticHtml(posts), ]); -})(); +})().catch((e) => console.error(e)); diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index 2e6886c..6bd71fb 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -31,8 +31,8 @@ async function listFiles(dir: string): Promise { async function readPostsMarkdown(paths: string[]): Promise { return await Promise.all( - paths.map(async (path) => { - const { data, content } = matter(await readFile(path, 'utf-8')); + paths.map(async (filePath) => { + const { data, content } = matter(await readFile(filePath, 'utf-8')); return { ...data, text: md @@ -55,29 +55,21 @@ function uniquePosts(posts: JSONPost[]): JSONPost[] { ).sort((a, b) => b.id - a.id); } -async function writePostsJson( - jsonPosts: JSONPost[], - mdPosts: JSONPost[], -): Promise { - const newPosts = uniquePosts([...jsonPosts, ...mdPosts]); // mdPosts > jsonPosts - await writeFile( - jsonPath, - await format(JSON.stringify(newPosts), { parser: 'json' }), - ); - return newPosts; -} - export async function generatePostsJson(): Promise { const [mdPaths, postsJson] = await Promise.all([ listFiles(mdPath), readFile(jsonPath, 'utf-8'), ]); - const posts = await writePostsJson( - JSON.parse(postsJson) as JSONPost[], - await readPostsMarkdown(mdPaths), + const jsonPosts = JSON.parse(postsJson) as JSONPost[]; + const mdPosts = await readPostsMarkdown(mdPaths); + const newPosts = uniquePosts([...jsonPosts, ...mdPosts]); // mdPosts > jsonPosts + await writeFile( + jsonPath, + await format(JSON.stringify(newPosts), { parser: 'json' }), ); console.log('posts generated.'); - return posts; + + return newPosts; } if (path.resolve(process.argv[1] ?? '') === filename) { diff --git a/src/scripts/sitemap.ts b/src/scripts/sitemap.ts index efc6d5d..c45b338 100644 --- a/src/scripts/sitemap.ts +++ b/src/scripts/sitemap.ts @@ -11,11 +11,13 @@ const dirname = path.dirname(filename); const rootDir = path.resolve(dirname, '../..'); const sitemapPath = path.resolve(rootDir, 'sitemap.xml'); -function getLatestModifiedAt(posts: JSONPost[]): string { +function getLatestModifiedAt(posts: JSONPost[]): string | null { + const latest = Math.max( + ...posts.map((post) => new Date(post.modifiedAt).getTime()), + ); return ( - posts - .map((post) => post.modifiedAt) - .sort((a, b) => new Date(b).getTime() - new Date(a).getTime())[0] ?? '' + posts.find((post) => new Date(post.modifiedAt).getTime() === latest) + ?.modifiedAt ?? null ); } @@ -23,9 +25,9 @@ function tagHistory(posts: JSONPost[]): { tag: string; modifiedAt: string }[] { const tags = [...new Set(posts.flatMap((post) => post.tags))]; return tags.map((tag) => ({ tag, - modifiedAt: getLatestModifiedAt( - posts.filter((post) => post.tags.includes(tag)), - ), + modifiedAt: + getLatestModifiedAt(posts.filter((post) => post.tags.includes(tag))) ?? + '', })); } @@ -37,7 +39,7 @@ export async function generateSitemap(posts: JSONPost[]): Promise { hostname: 'https://kyonenya.github.io/', }); - sitemap.write({ url: '', lastmod: getLatestModifiedAt(posts) }); + sitemap.write({ url: '', lastmod: getLatestModifiedAt(posts) ?? '' }); sitemap.write({ url: 'works', lastmod: updatedAt }); sitemap.write({ url: 'about', lastmod: updatedAt }); posts.forEach((post) => diff --git a/src/scripts/worksJson.ts b/src/scripts/worksJson.ts index 381e566..20641cd 100644 --- a/src/scripts/worksJson.ts +++ b/src/scripts/worksJson.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore citeproc does not publish TypeScript declarations. import Citeproc from 'citeproc'; -import type { Data } from 'csl-json'; +import type { Data as CSLJSON } from 'csl-json'; import { format } from 'prettier'; import type { Citation } from '../works/citation'; @@ -20,7 +20,7 @@ const CSL = Citeproc as { retrieveLocale: (lang: string) => string; retrieveItem: ( id: string, - ) => (Partial & { id: string }) | undefined; + ) => (Partial) | undefined; }, style: string, ) => CslEngine; @@ -40,7 +40,7 @@ function removeNullProperties(obj: T): Partial { ) as Partial; } -function citeproc(data: Data[], style: string, locale: string): string[] { +function citeproc(data: CSLJSON[], style: string, locale: string): string[] { const items = data.map((item) => ({ ...removeNullProperties(item), id: item.id.toString(), @@ -59,7 +59,7 @@ function citeproc(data: Data[], style: string, locale: string): string[] { return bib[1].map((text) => text.replace(/\n$/, '')); } -async function appendCitations(items: Data[]): Promise { +async function appendCitations(items: CSLJSON[]): Promise { const [style, locale] = await Promise.all([ readFile(stylePath, 'utf-8'), readFile(localePath, 'utf-8'), @@ -72,7 +72,7 @@ async function appendCitations(items: Data[]): Promise { } export async function generateWorksJson(): Promise { - const works = JSON.parse(await readFile(worksPath, 'utf8')) as Data[]; + const works = JSON.parse(await readFile(worksPath, 'utf8')) as CSLJSON[]; const newWorks = await appendCitations(works); await writeFile( worksPath, From 55a023483dee3d2d537dabe007a85fa1c425c7ca Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 09:18:49 +0900 Subject: [PATCH 20/26] =?UTF-8?q?refacotor:=20citeproc=E3=81=AE=E5=9E=8B?= =?UTF-8?q?=E4=BB=98=E3=81=91=E3=82=92AI=E3=81=AB=E3=82=84=E3=82=89?= =?UTF-8?q?=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/scripts/citeproc.d.ts | 40 +++++++++++++++++++++++++++++++++++++++ src/scripts/postsJson.ts | 21 +++++++++++--------- src/scripts/worksJson.ts | 28 ++++++--------------------- 3 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 src/scripts/citeproc.d.ts diff --git a/src/scripts/citeproc.d.ts b/src/scripts/citeproc.d.ts new file mode 100644 index 0000000..5233757 --- /dev/null +++ b/src/scripts/citeproc.d.ts @@ -0,0 +1,40 @@ +declare module 'citeproc' { + export type Item = Partial & { + id: string; + }; + + export type Bibliography = [ + params: Record, + entries: string[], + ]; + + export type Sys = { + retrieveLocale: (lang: string) => string; + retrieveItem: (id: string) => Item; + variableWrapper?: (...args: unknown[]) => unknown; + stringCompare?: (a: string, b: string) => number; + }; + + export class Engine { + constructor( + sys: Sys, + style: string, + lang?: string, + forceLang?: boolean, + ); + setOutputFormat: (mode: string) => void; + updateItems: ( + idList: string[], + nosort?: boolean, + rerunAmbigs?: boolean, + implicitUpdate?: boolean, + ) => void; + makeBibliography: (bibsection?: Record) => false | Bibliography; + } + + const Citeproc: { + Engine: typeof Engine; + }; + + export default Citeproc; +} diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index 6bd71fb..0432ff4 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -29,21 +29,24 @@ async function listFiles(dir: string): Promise { return paths.flat(); } +function replacePostHtml(html: string): string { + return html + .replace(/\n/g, '') + .replace(/>/g, '>') + .replace(/</g, '<') + // 漢字《ふりがな》 + .replace(/|(.+?)《(.+?)》/g, '$1$2') + .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') + .replace(/([一-龠]+)《(.+?)》/g, '$1$2'); +} + async function readPostsMarkdown(paths: string[]): Promise { return await Promise.all( paths.map(async (filePath) => { const { data, content } = matter(await readFile(filePath, 'utf-8')); return { ...data, - text: md - .render(content) - .replace(/\n/g, '') - .replace(/>/g, '>') - .replace(/</g, '<') - // 漢字《ふりがな》 - .replace(/|(.+?)《(.+?)》/g, '$1$2') - .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') - .replace(/([一-龠]+)《(.+?)》/g, '$1$2'), + text: replacePostHtml(md.render(content)), } as JSONPost; }), ); diff --git a/src/scripts/worksJson.ts b/src/scripts/worksJson.ts index 20641cd..5812906 100644 --- a/src/scripts/worksJson.ts +++ b/src/scripts/worksJson.ts @@ -1,31 +1,11 @@ import { readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore citeproc does not publish TypeScript declarations. import Citeproc from 'citeproc'; import type { Data as CSLJSON } from 'csl-json'; import { format } from 'prettier'; import type { Citation } from '../works/citation'; -type CslEngine = { - setOutputFormat: (format: 'text') => void; - updateItems: (ids: string[]) => void; - makeBibliography: () => false | [unknown, string[]]; -}; - -const CSL = Citeproc as { - Engine: new ( - sys: { - retrieveLocale: (lang: string) => string; - retrieveItem: ( - id: string, - ) => (Partial) | undefined; - }, - style: string, - ) => CslEngine; -}; - const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); const rootDir = path.resolve(dirname, '../..'); @@ -47,9 +27,13 @@ function citeproc(data: CSLJSON[], style: string, locale: string): string[] { })); const sys = { retrieveLocale: () => locale, - retrieveItem: (id: string) => items.find((item) => id === item.id), + retrieveItem: (id: string) => { + const item = items.find((item) => id === item.id); + if (!item) throw new Error(`citeproc item not found: ${id}`); + return item; + }, }; - const citeproc = new CSL.Engine(sys, style); + const citeproc = new Citeproc.Engine(sys, style); citeproc.setOutputFormat('text'); citeproc.updateItems(items.map((item) => item.id)); From ef7542f91b17a5bbfabb2dcaf4b7e8d8d43d3e72 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 09:53:34 +0900 Subject: [PATCH 21/26] =?UTF-8?q?fix:=20=E3=83=AB=E3=83=BC=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=83=B3=E3=82=B0=E3=81=8C=E4=BA=8C=E9=87=8D=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=A6=E3=82=8B=E5=95=8F=E9=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/render.ts | 14 +++++++------- src/reroute.ts | 2 -- src/route.ts | 25 ++++++++++++++----------- src/works/citation.ts | 4 ---- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/render.ts b/src/render.ts index b6fad27..2490b7c 100644 --- a/src/render.ts +++ b/src/render.ts @@ -13,7 +13,13 @@ export const baseUrl = 'https://kyonenya.github.io/'; export function renderRoot(html: string): void { const rootElement = document.getElementById('root'); rootElement.innerHTML = html; - scrollToId(window.location.hash.replace('#', '')); + + const target = document.getElementById(window.location.hash.replace('#', '')); + if (target) { + target.scrollIntoView({ behavior: 'smooth' }); + } else { + window.scrollTo(0, 0); + } if (isDevelopment(window.location.href)) { // overwrite to internal link @@ -49,9 +55,3 @@ export function renderPage(page: Page): void { document.head.appendChild(link); } } - -export const scrollToId = (id: string): void => { - document.getElementById(id)?.scrollIntoView({ - behavior: 'smooth', - }); -}; diff --git a/src/reroute.ts b/src/reroute.ts index 489d213..c021ab7 100644 --- a/src/reroute.ts +++ b/src/reroute.ts @@ -1,4 +1,3 @@ -import { scrollToId } from './render'; import { toState } from './state'; function watchPopState(reroute: () => void): void { @@ -36,7 +35,6 @@ function watchInternalLinkClicks(reroute: () => void): void { e.preventDefault(); window.history.pushState(undefined, '', a.href); reroute(); - scrollToId(a.hash.replace('#', '')); } }); } diff --git a/src/route.ts b/src/route.ts index 8a7b028..eb59814 100644 --- a/src/route.ts +++ b/src/route.ts @@ -11,30 +11,33 @@ const searchInputElement = const routeMap = { article: (post: Post): void => { renderPage(articlePage(post)); - if (!searchInputElement) return; - searchInputElement.style.display = 'none'; // disable search form + // disable search form + if (searchInputElement) searchInputElement.style.display = 'none'; }, - postList: (posts: Post[]): void => + postList: (posts: Post[]): void => { renderPage({ body: PostList(posts), title: 'placet experiri', href: baseUrl, - }), - taggedPostList: (posts: Post[], tag: string): void => + }); + if (searchInputElement) searchInputElement.style.display = 'block'; + }, + taggedPostList: (posts: Post[], tag: string): void => { renderPage({ body: TaggedPostList(posts, tag), title: `#${tag}|placet experiri`, href: `${baseUrl}?tag=${tag}`, - }), - searchedPostList: (posts: Post[], keyword: string, tag?: string): void => + }); + if (searchInputElement) searchInputElement.style.display = 'block'; + }, + searchedPostList: (posts: Post[], keyword: string, tag?: string): void => { renderPage({ body: SearchedPostList(posts, keyword, tag), title: `「${keyword}」|placet experiri`, - }), + }); + if (searchInputElement) searchInputElement.style.display = 'block'; + }, beforeEach: (legacyId: number | undefined): void => { - window.scrollTo(0, 0); - if (!searchInputElement) return; - searchInputElement.style.display = 'block'; if (legacyId) { // for backward compatibility window.history.replaceState(undefined, '', `/posts/${legacyId}`); diff --git a/src/works/citation.ts b/src/works/citation.ts index 2d334c4..b21560e 100644 --- a/src/works/citation.ts +++ b/src/works/citation.ts @@ -13,17 +13,13 @@ function detectGenre(item: Citation): Genre | undefined { switch (item.type) { case 'book': return '書籍'; - break; case 'article-journal': if (item.translator) return '翻訳'; return '論文'; - break; case 'paper-conference': return '発表'; - break; case 'thesis': return '学位論文'; - break; default: return undefined; } From 437c169e791831539e661afc9de1ffbbe87f8081 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 10:01:59 +0900 Subject: [PATCH 22/26] =?UTF-8?q?refactor:=20state=E3=81=8C=E7=A9=BA?= =?UTF-8?q?=E3=81=AE=E3=81=A8=E3=81=8D=E3=81=AFnull=E3=82=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/route.ts | 12 ++++++------ src/scripts/citeproc.d.ts | 11 ++++------- src/scripts/postsJson.ts | 18 ++++++++++-------- src/state.ts | 24 +++++++++++++----------- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/route.ts b/src/route.ts index eb59814..80c3477 100644 --- a/src/route.ts +++ b/src/route.ts @@ -37,8 +37,8 @@ const routeMap = { }); if (searchInputElement) searchInputElement.style.display = 'block'; }, - beforeEach: (legacyId: number | undefined): void => { - if (legacyId) { + beforeEach: (legacyId: number | null): void => { + if (legacyId !== null) { // for backward compatibility window.history.replaceState(undefined, '', `/posts/${legacyId}`); } @@ -58,13 +58,13 @@ export function route(rawPosts: Post[]): void { routeMap.beforeEach(legacyId); - if (id !== undefined) { + if (id !== null) { const post = posts.find((post) => post.id === id); if (!post) return; // TODO: 404 routeMap.article(post); - } else if (keyword !== undefined) { - routeMap.searchedPostList(posts, keyword, tag); - } else if (tag !== undefined) { + } else if (keyword !== null) { + routeMap.searchedPostList(posts, keyword, tag ?? undefined); + } else if (tag !== null) { routeMap.taggedPostList(posts, tag); } else { routeMap.postList(posts); diff --git a/src/scripts/citeproc.d.ts b/src/scripts/citeproc.d.ts index 5233757..02c9c70 100644 --- a/src/scripts/citeproc.d.ts +++ b/src/scripts/citeproc.d.ts @@ -16,12 +16,7 @@ declare module 'citeproc' { }; export class Engine { - constructor( - sys: Sys, - style: string, - lang?: string, - forceLang?: boolean, - ); + constructor(sys: Sys, style: string, lang?: string, forceLang?: boolean); setOutputFormat: (mode: string) => void; updateItems: ( idList: string[], @@ -29,7 +24,9 @@ declare module 'citeproc' { rerunAmbigs?: boolean, implicitUpdate?: boolean, ) => void; - makeBibliography: (bibsection?: Record) => false | Bibliography; + makeBibliography: ( + bibsection?: Record, + ) => false | Bibliography; } const Citeproc: { diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index 0432ff4..46557ad 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -30,14 +30,16 @@ async function listFiles(dir: string): Promise { } function replacePostHtml(html: string): string { - return html - .replace(/\n/g, '') - .replace(/>/g, '>') - .replace(/</g, '<') - // 漢字《ふりがな》 - .replace(/|(.+?)《(.+?)》/g, '$1$2') - .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') - .replace(/([一-龠]+)《(.+?)》/g, '$1$2'); + return ( + html + .replace(/\n/g, '') + .replace(/>/g, '>') + .replace(/</g, '<') + // 漢字《ふりがな》 + .replace(/|(.+?)《(.+?)》/g, '$1$2') + .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') + .replace(/([一-龠]+)《(.+?)》/g, '$1$2') + ); } async function readPostsMarkdown(paths: string[]): Promise { diff --git a/src/state.ts b/src/state.ts index 2176c44..3885962 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,8 +1,8 @@ type State = { - id: number | undefined; - legacyId: number | undefined; - tag: string | undefined; - keyword: string | undefined; + id: number | null; + legacyId: number | null; + tag: string | null; + keyword: string | null; }; export function toState( @@ -11,21 +11,23 @@ export function toState( locationHash?: string, ): State { const idStr = /\/posts\/(\d+)/.exec(locationPathname)?.[1]; - const id = idStr ? Number(idStr) : undefined; + const id = idStr ? Number(idStr) : null; const searchParams = new URLSearchParams(locationSearch); const tag = searchParams.get('tag'); const legacyIdStr = searchParams.get('id'); const legacyId = - legacyIdStr && /^\d+$/.test(legacyIdStr) ? Number(legacyIdStr) : undefined; + legacyIdStr && /^\d+$/.test(legacyIdStr) ? Number(legacyIdStr) : null; + + const keyword = + locationHash === undefined || locationHash === '' + ? null + : decodeURIComponent(locationHash.replace('#', '')); return { id: id ?? legacyId, legacyId, - tag: tag ?? undefined, - keyword: - locationHash === undefined || locationHash === '' - ? undefined - : decodeURIComponent(locationHash.replace('#', '')), + tag, + keyword, }; } From 02bc1161aacc6d75a4bf65771d2c4040db96f218 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 10:02:40 +0900 Subject: [PATCH 23/26] chore: build --- dist/hydrate.js | 2 +- dist/index.js | 2 +- dist/works.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/hydrate.js b/dist/hydrate.js index 26c8de8..e28c32d 100644 --- a/dist/hydrate.js +++ b/dist/hydrate.js @@ -1 +1 @@ -(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:o=50,beforeLength:r=20}=null!=e?e:{},s=t.indexOf(n);if(-1===s)return;const a=s-r<=0?0:s-r,i=s+n.length,l=a+o;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,s),keyword:t.substring(s,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(o)(e,n),t.Ks=e=>(n,o)=>{const r=(0,t.xq)(n,o,e);if(void 0===r)return;const{ellipsisToken:s="...",keywordModifier:a=e=>e}=null!=e?e:{};return(r.isBeforeEllipsed?s:"")+r.beforeText+a(r.keyword)+r.afterText+(r.isAfterEllipsed?s:"")},t.ur=(e,n,o)=>(0,t.Ks)(o)(e,n)}},t={};const n=e=>`#${e}`,o=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,r=(e,t)=>`\n
      \n ${e.map(e=>o(e,t)).join("")}\n
    \n`,s=864e5,a=30*s,i={seconds:1e3,minutes:6e4,hours:36e5,day:s,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:o}=d(e);return`${t}-${u(n)}-${u(o)}`}function h(e,t){return e.getTime()Math.abs(c(t,e)){e.href="/"+e.href.replace(y,"")}),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const v=e=>{document.getElementById(e)?.scrollIntoView({behavior:"smooth"})};function x(e,t,n){const o=/\/posts\/(\d+)/.exec(t)?.[1],r=o?Number(o):void 0,s=new URLSearchParams(e),a=s.get("tag"),i=s.get("id"),l=i&&/^\d+$/.test(i)?Number(i):void 0;return{id:r??l,legacyId:l,tag:a??void 0,keyword:void 0===n||""===n?void 0:decodeURIComponent(n.replace("#",""))}}const L=e=>e.replace(/——(?![^(]*\))/g,'——'),k=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${L(e.title)}

    `:""}\n ${(e=>e.replace(/——/g,"──").replace(/([!?]) /g,(e,t)=>`${t} `).replace(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replace(/(.+?)<\/a>/g,(e,t,n,o)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({href:t,attributes:n,content:o}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${r(e.tags)}\n
    \n
    \n
    `;var n};var S=function n(o){var r=t[o];if(void 0!==r)return r.exports;var s=t[o]={exports:{}};return e[o](s,s.exports,n),s.exports}(224);const T=e=>`${e}`,A=e=>{const{post:t,tag:n,keyword:o}=e,s=(0,S.ur)(t.plainText,o,{maxLength:200,beforeLength:48,keywordModifier:T});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${L(((e,t)=>t?e.replace(t,T(t)):e)(e,t))}\n

    `)(t.title,o):""}\n ${s?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(s):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${r(t.tags,n)}\n
    \n `;var a},E=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,D=e=>E(e.map((t,n)=>A({post:e[n]})).reverse().join("")),q=(e,t)=>E(e.map((n,o)=>n.tags.includes(t)?A({post:e[o],tag:t}):"").reverse().join(""),`#${t}`),I=(e,t,n)=>E(e.map((o,r)=>void 0===n||o.tags.includes(n)?A({post:e[r],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),j=document.querySelector(".el_search_input");function C(e){const{id:t,legacyId:n,tag:o,keyword:r}=x(window.location.search,window.location.pathname,window.location.hash),s=b(window.location.href)?e:function(e){return e.filter(e=>h(e.createdAt,new Date))}(e);if((e=>{window.scrollTo(0,0),j&&(j.style.display="block",e&&window.history.replaceState(void 0,"",`/posts/${e}`))})(n),void 0!==t){const e=s.find(e=>e.id===t);if(!e)return;(e=>{w((e=>({body:k(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${y}posts/${e.id}`}))(e)),j&&(j.style.display="none")})(e)}else void 0!==r?((e,t,n)=>{w({body:I(e,t,n),title:`「${t}」|placet experiri`})})(s,r,o):void 0!==o?((e,t)=>{w({body:q(e,t),title:`#${t}|placet experiri`,href:`${y}?tag=${t}`})})(s,o):(e=>{w({body:D(e),title:"placet experiri",href:y})})(s)}document.querySelectorAll(".bl_posts_dateago").forEach(e=>{const t=e.getAttribute("data-date");t&&(e.textContent=m(new Date(t)))}),async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,r=`${t}/about.json`,s=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replace(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replace(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replace(/——/g,"──").replace(/
    (.+?)<\/div>/g,"").replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:_(e.createdAt),modifiedAt:_(e.modifiedAt)}}(e))}(await $(n));var a;e&&C(s),function(e){window.addEventListener("popstate",()=>e())}(a=()=>C(s)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(x(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e(),v(n.hash.replace("#","")))})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(s),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),h(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(r))}(!1)})(); \ No newline at end of file +(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:s=50,beforeLength:o=20}=null!=e?e:{},r=t.indexOf(n);if(-1===r)return;const a=r-o<=0?0:r-o,i=r+n.length,l=a+s;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,r),keyword:t.substring(r,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(s)(e,n),t.Ks=e=>(n,s)=>{const o=(0,t.xq)(n,s,e);if(void 0===o)return;const{ellipsisToken:r="...",keywordModifier:a=e=>e}=null!=e?e:{};return(o.isBeforeEllipsed?r:"")+o.beforeText+a(o.keyword)+o.afterText+(o.isAfterEllipsed?r:"")},t.ur=(e,n,s)=>(0,t.Ks)(s)(e,n)}},t={};const n=e=>`#${e}`,s=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,o=(e,t)=>`\n
      \n ${e.map(e=>s(e,t)).join("")}\n
    \n`,r=864e5,a=30*r,i={seconds:1e3,minutes:6e4,hours:36e5,day:r,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:s}=d(e);return`${t}-${u(n)}-${u(s)}`}function f(e,t){return e.getTime()Math.abs(c(t,e))e.replace(/——(?![^(]*\))/g,'——'),v="https://kyonenya.github.io/";function x(e){const t=document.querySelector(".el_logo_suffix"),n=document.querySelector("meta[name=description]");if(function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),b(window.location.href)&&document.querySelectorAll(`a[href^="${v}"]`).forEach(e=>{e.href="/"+e.href.replace(v,"")})}(e.body),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const k=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${w(e.title)}

    `:""}\n ${(e=>e.replace(/——/g,"──").replace(/([!?]) /g,(e,t)=>`${t} `).replace(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replace(/(.+?)<\/a>/g,(e,t,n,s)=>(e=>{const{href:t,content:n,attributes:s=""}=e;return`${n}`})({href:t,attributes:n,content:s}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${o(e.tags)}\n
    \n
    \n
    `;var n};var L=function n(s){var o=t[s];if(void 0!==o)return o.exports;var r=t[s]={exports:{}};return e[s](r,r.exports,n),r.exports}(224);const S=e=>`${e}`,T=e=>{const{post:t,tag:n,keyword:s}=e,r=(0,L.ur)(t.plainText,s,{maxLength:200,beforeLength:48,keywordModifier:S});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${w(((e,t)=>t?e.replace(t,S(t)):e)(e,t))}\n

    `)(t.title,s):""}\n ${r?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(r):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${o(t.tags,n)}\n
    \n `;var a},A=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,E=e=>A(e.map((t,n)=>T({post:e[n]})).reverse().join("")),D=(e,t)=>A(e.map((n,s)=>n.tags.includes(t)?T({post:e[s],tag:t}):"").reverse().join(""),`#${t}`),q=(e,t,n)=>A(e.map((s,o)=>void 0===n||s.tags.includes(n)?T({post:e[o],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),I=document.querySelector(".el_search_input");function j(e){const{id:t,legacyId:n,tag:s,keyword:o}=_(window.location.search,window.location.pathname,window.location.hash),r=b(window.location.href)?e:function(e){return e.filter(e=>f(e.createdAt,new Date))}(e);if((e=>{null!==e&&window.history.replaceState(void 0,"",`/posts/${e}`)})(n),null!==t){const e=r.find(e=>e.id===t);if(!e)return;(e=>{x((e=>({body:k(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${v}posts/${e.id}`}))(e)),I&&(I.style.display="none")})(e)}else null!==o?((e,t,n)=>{x({body:q(e,t,n),title:`「${t}」|placet experiri`}),I&&(I.style.display="block")})(r,o,s??void 0):null!==s?((e,t)=>{x({body:D(e,t),title:`#${t}|placet experiri`,href:`${v}?tag=${t}`}),I&&(I.style.display="block")})(r,s):(e=>{x({body:E(e),title:"placet experiri",href:v}),I&&(I.style.display="block")})(r)}document.querySelectorAll(".bl_posts_dateago").forEach(e=>{const t=e.getAttribute("data-date");t&&(e.textContent=m(new Date(t)))}),async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,o=`${t}/about.json`,r=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replace(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replace(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replace(/——/g,"──").replace(/
    (.+?)<\/div>/g,"").replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:y(e.createdAt),modifiedAt:y(e.modifiedAt)}}(e))}(await $(n));var a;e&&j(r),function(e){window.addEventListener("popstate",()=>e())}(a=()=>j(r)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(_(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e())})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(r),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),f(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(o))}(!1)})(); \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index ea104d0..18b7ef5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1 +1 @@ -(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:r=50,beforeLength:s=20}=null!=e?e:{},o=t.indexOf(n);if(-1===o)return;const i=o-s<=0?0:o-s,a=o+n.length,l=i+r;return{isBeforeEllipsed:0!==i,beforeText:t.substring(i,o),keyword:t.substring(o,a),afterText:t.substring(a,l),isAfterEllipsed:l(0,t.xS)(r)(e,n),t.Ks=e=>(n,r)=>{const s=(0,t.xq)(n,r,e);if(void 0===s)return;const{ellipsisToken:o="...",keywordModifier:i=e=>e}=null!=e?e:{};return(s.isBeforeEllipsed?o:"")+s.beforeText+i(s.keyword)+s.afterText+(s.isAfterEllipsed?o:"")},t.ur=(e,n,r)=>(0,t.Ks)(r)(e,n)}},t={};const n=e=>`#${e}`,r=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,s=(e,t)=>`\n
      \n ${e.map(e=>r(e,t)).join("")}\n
    \n`,o=864e5,i=30*o,a={seconds:1e3,minutes:6e4,hours:36e5,day:o,month:i,year:12*i},l=Object.keys(a);function c(e,t){return e/a[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:r}=d(e);return`${t}-${u(n)}-${u(r)}`}function h(e,t){return e.getTime()Math.abs(c(t,e)){e.href="/"+e.href.replace(y,"")}),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const w=e=>{document.getElementById(e)?.scrollIntoView({behavior:"smooth"})};function x(e,t,n){const r=/\/posts\/(\d+)/.exec(t)?.[1],s=r?Number(r):void 0,o=new URLSearchParams(e),i=o.get("tag"),a=o.get("id"),l=a&&/^\d+$/.test(a)?Number(a):void 0;return{id:s??l,legacyId:l,tag:i??void 0,keyword:void 0===n||""===n?void 0:decodeURIComponent(n.replace("#",""))}}const L=e=>e.replace(/——(?![^(]*\))/g,'——'),k=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${L(e.title)}

    `:""}\n ${(e=>e.replace(/——/g,"──").replace(/([!?]) /g,(e,t)=>`${t} `).replace(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replace(/(.+?)<\/a>/g,(e,t,n,r)=>(e=>{const{href:t,content:n,attributes:r=""}=e;return`${n}`})({href:t,attributes:n,content:r}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${s(e.tags)}\n
    \n
    \n
    `;var n};var S=function n(r){var s=t[r];if(void 0!==s)return s.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}(224);const T=e=>`${e}`,A=e=>{const{post:t,tag:n,keyword:r}=e,o=(0,S.ur)(t.plainText,r,{maxLength:200,beforeLength:48,keywordModifier:T});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${L(((e,t)=>t?e.replace(t,T(t)):e)(e,t))}\n

    `)(t.title,r):""}\n ${o?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(o):(i=t.plainText,`\n
    \n

    \n ${i.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${s(t.tags,n)}\n
    \n `;var i},E=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,D=e=>E(e.map((t,n)=>A({post:e[n]})).reverse().join("")),q=(e,t)=>E(e.map((n,r)=>n.tags.includes(t)?A({post:e[r],tag:t}):"").reverse().join(""),`#${t}`),I=(e,t,n)=>E(e.map((r,s)=>void 0===n||r.tags.includes(n)?A({post:e[s],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),j=document.querySelector(".el_search_input");function C(e){const{id:t,legacyId:n,tag:r,keyword:s}=x(window.location.search,window.location.pathname,window.location.hash),o=b(window.location.href)?e:function(e){return e.filter(e=>h(e.createdAt,new Date))}(e);if((e=>{window.scrollTo(0,0),j&&(j.style.display="block",e&&window.history.replaceState(void 0,"",`/posts/${e}`))})(n),void 0!==t){const e=o.find(e=>e.id===t);if(!e)return;(e=>{v((e=>({body:k(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${y}posts/${e.id}`}))(e)),j&&(j.style.display="none")})(e)}else void 0!==s?((e,t,n)=>{v({body:I(e,t,n),title:`「${t}」|placet experiri`})})(o,s,r):void 0!==r?((e,t)=>{v({body:q(e,t),title:`#${t}|placet experiri`,href:`${y}?tag=${t}`})})(o,r):(e=>{v({body:D(e),title:"placet experiri",href:y})})(o)}!async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,s=`${t}/about.json`,o=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replace(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replace(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replace(/——/g,"──").replace(/
    (.+?)<\/div>/g,"").replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:_(e.createdAt),modifiedAt:_(e.modifiedAt)}}(e))}(await $(n));var i;e&&C(o),function(e){window.addEventListener("popstate",()=>e())}(i=()=>C(o)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(x(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(i),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e(),w(n.hash.replace("#","")))})}(i),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(o),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),h(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(s))}()})(); \ No newline at end of file +(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:s=50,beforeLength:r=20}=null!=e?e:{},o=t.indexOf(n);if(-1===o)return;const a=o-r<=0?0:o-r,i=o+n.length,l=a+s;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,o),keyword:t.substring(o,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(s)(e,n),t.Ks=e=>(n,s)=>{const r=(0,t.xq)(n,s,e);if(void 0===r)return;const{ellipsisToken:o="...",keywordModifier:a=e=>e}=null!=e?e:{};return(r.isBeforeEllipsed?o:"")+r.beforeText+a(r.keyword)+r.afterText+(r.isAfterEllipsed?o:"")},t.ur=(e,n,s)=>(0,t.Ks)(s)(e,n)}},t={};const n=e=>`#${e}`,s=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,r=(e,t)=>`\n
      \n ${e.map(e=>s(e,t)).join("")}\n
    \n`,o=864e5,a=30*o,i={seconds:1e3,minutes:6e4,hours:36e5,day:o,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:s}=d(e);return`${t}-${u(n)}-${u(s)}`}function f(e,t){return e.getTime()Math.abs(c(t,e))e.replace(/——(?![^(]*\))/g,'——'),v="https://kyonenya.github.io/";function x(e){const t=document.querySelector(".el_logo_suffix"),n=document.querySelector("meta[name=description]");if(function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),b(window.location.href)&&document.querySelectorAll(`a[href^="${v}"]`).forEach(e=>{e.href="/"+e.href.replace(v,"")})}(e.body),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const k=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${w(e.title)}

    `:""}\n ${(e=>e.replace(/——/g,"──").replace(/([!?]) /g,(e,t)=>`${t} `).replace(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replace(/(.+?)<\/a>/g,(e,t,n,s)=>(e=>{const{href:t,content:n,attributes:s=""}=e;return`${n}`})({href:t,attributes:n,content:s}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${r(e.tags)}\n
    \n
    \n
    `;var n};var L=function n(s){var r=t[s];if(void 0!==r)return r.exports;var o=t[s]={exports:{}};return e[s](o,o.exports,n),o.exports}(224);const S=e=>`${e}`,T=e=>{const{post:t,tag:n,keyword:s}=e,o=(0,L.ur)(t.plainText,s,{maxLength:200,beforeLength:48,keywordModifier:S});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${w(((e,t)=>t?e.replace(t,S(t)):e)(e,t))}\n

    `)(t.title,s):""}\n ${o?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(o):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${r(t.tags,n)}\n
    \n `;var a},A=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,E=e=>A(e.map((t,n)=>T({post:e[n]})).reverse().join("")),D=(e,t)=>A(e.map((n,s)=>n.tags.includes(t)?T({post:e[s],tag:t}):"").reverse().join(""),`#${t}`),q=(e,t,n)=>A(e.map((s,r)=>void 0===n||s.tags.includes(n)?T({post:e[r],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),I=document.querySelector(".el_search_input");function j(e){const{id:t,legacyId:n,tag:s,keyword:r}=_(window.location.search,window.location.pathname,window.location.hash),o=b(window.location.href)?e:function(e){return e.filter(e=>f(e.createdAt,new Date))}(e);if((e=>{null!==e&&window.history.replaceState(void 0,"",`/posts/${e}`)})(n),null!==t){const e=o.find(e=>e.id===t);if(!e)return;(e=>{x((e=>({body:k(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${v}posts/${e.id}`}))(e)),I&&(I.style.display="none")})(e)}else null!==r?((e,t,n)=>{x({body:q(e,t,n),title:`「${t}」|placet experiri`}),I&&(I.style.display="block")})(o,r,s??void 0):null!==s?((e,t)=>{x({body:D(e,t),title:`#${t}|placet experiri`,href:`${v}?tag=${t}`}),I&&(I.style.display="block")})(o,s):(e=>{x({body:E(e),title:"placet experiri",href:v}),I&&(I.style.display="block")})(o)}!async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,r=`${t}/about.json`,o=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replace(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replace(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replace(/——/g,"──").replace(/
    (.+?)<\/div>/g,"").replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:y(e.createdAt),modifiedAt:y(e.modifiedAt)}}(e))}(await $(n));var a;e&&j(o),function(e){window.addEventListener("popstate",()=>e())}(a=()=>j(o)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(_(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e())})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(o),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),f(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(r))}()})(); \ No newline at end of file diff --git a/dist/works.js b/dist/works.js index 01fe339..9f447a3 100644 --- a/dist/works.js +++ b/dist/works.js @@ -1 +1 @@ -(()=>{"use strict";async function e(e){const t=await fetch(e);if(!t.ok)throw new Error(t.statusText);return await t.json()}const t=864e5,n=30*t,o={seconds:1e3,minutes:6e4,hours:36e5,day:t,month:n,year:12*n};Object.keys(o),new Intl.RelativeTimeFormat("ja-JP",{style:"narrow"});const a="https://kyonenya.github.io/";const r=e=>{document.getElementById(e)?.scrollIntoView({behavior:"smooth"})},s=["書籍","学位論文","論文","発表","翻訳"];const c=e=>function(e){return e.replace(/\[(.+?)\]\((.+?)\)/g,(e,t,n)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({content:t,href:n}))}((e=>e.replace(/——(?![^(]*\))/g,'——'))(e)),i=e=>`${c(e)}`,l=(e,t,n)=>{const o=e.id.toString()===n,a=o?`${t}.`:`${t}.`,r=function(e){const t=e.issued?.["date-parts"]?.[0]??e["event-date"]?.["date-parts"]?.[0];if(t)return new Date\n ${a}\n ${(o?i:c)(e._bibliographyText)}${r}\n \n `};!async function(){const t=await e("./works.json"),n=decodeURIComponent(window.location.hash.replace("#",""));var o,c;o=((e,t)=>{const n=(e=>Object.fromEntries(s.map(t=>[t,e.filter(e=>function(e){switch(e.type){case"book":return"書籍";case"article-journal":return e.translator?"翻訳":"論文";case"paper-conference":return"発表";case"thesis":return"学位論文";default:return}}(e)===t)])))(e);return`\n
    \n
    \n

    業績一覧

    \n ${s.map(e=>((e,t,n)=>e&&0!==e.length?`\n

    ${t}

    \n
      \n ${e.map((e,t)=>l(e,t+1,n)).join("")}\n
    `:"")(n[e],e,t)).join("")}\n
    \n
    `})(t,n),document.getElementById("root").innerHTML=o,r(window.location.hash.replace("#","")),c=window.location.href,/localhost:\d+/.test(c)&&document.querySelectorAll(`a[href^="${a}"]`).forEach(e=>{e.href="/"+e.href.replace(a,"")}),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),function(e,t){return e.getTime(){localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await e("./about.json"))}()})(); \ No newline at end of file +(()=>{"use strict";async function e(e){const t=await fetch(e);if(!t.ok)throw new Error(t.statusText);return await t.json()}const t=864e5,n=30*t,o={seconds:1e3,minutes:6e4,hours:36e5,day:t,month:n,year:12*n};Object.keys(o),new Intl.RelativeTimeFormat("ja-JP",{style:"narrow"});const a="https://kyonenya.github.io/";const r=["書籍","学位論文","論文","発表","翻訳"];const s=e=>function(e){return e.replace(/\[(.+?)\]\((.+?)\)/g,(e,t,n)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({content:t,href:n}))}((e=>e.replace(/——(?![^(]*\))/g,'——'))(e)),c=e=>`${s(e)}`,i=(e,t,n)=>{const o=e.id.toString()===n,a=o?`${t}.`:`${t}.`,r=function(e){const t=e.issued?.["date-parts"]?.[0]??e["event-date"]?.["date-parts"]?.[0];if(t)return new Date\n ${a}\n ${(o?c:s)(e._bibliographyText)}${r}\n \n `};!async function(){!function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));var n;t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),n=window.location.href,/localhost:\d+/.test(n)&&document.querySelectorAll(`a[href^="${a}"]`).forEach(e=>{e.href="/"+e.href.replace(a,"")})}(((e,t)=>{const n=(e=>Object.fromEntries(r.map(t=>[t,e.filter(e=>function(e){switch(e.type){case"book":return"書籍";case"article-journal":return e.translator?"翻訳":"論文";case"paper-conference":return"発表";case"thesis":return"学位論文";default:return}}(e)===t)])))(e);return`\n
    \n
    \n

    業績一覧

    \n ${r.map(e=>((e,t,n)=>e&&0!==e.length?`\n

    ${t}

    \n
      \n ${e.map((e,t)=>i(e,t+1,n)).join("")}\n
    `:"")(n[e],e,t)).join("")}\n
    \n
    `})(await e("./works.json"),decodeURIComponent(window.location.hash.replace("#","")))),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),function(e,t){return e.getTime(){localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await e("./about.json"))}()})(); \ No newline at end of file From 3a26d9ccd127f0044b283c09fd59f3decb423e33 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 10:23:18 +0900 Subject: [PATCH 24/26] refactor: type import --- eslint.config.mjs | 7 +++++++ src/Article.ts | 4 ++-- src/BlogCard.ts | 2 +- src/PostList.ts | 2 +- src/PostListItem.ts | 2 +- src/app.ts | 4 ++-- src/lib/dateUtils.ts | 2 +- src/route.ts | 2 +- src/works/Works.ts | 2 +- src/works/citation.ts | 5 ++--- src/works/citationDate.ts | 2 +- src/works/index.ts | 4 ++-- 12 files changed, 22 insertions(+), 16 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 2ecb91d..76c5b05 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -27,6 +27,13 @@ export default defineConfig( alphabetize: { order: 'asc' }, }, ], + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + disallowTypeAnnotations: false, // allow `import('foo').Bar` + }, + ], }, }, diff --git a/src/Article.ts b/src/Article.ts index e6b2d42..b7d8b60 100644 --- a/src/Article.ts +++ b/src/Article.ts @@ -2,8 +2,8 @@ import { TagList } from './TagList'; import { toExternalLink } from './lib/ExternalLink'; import { MarkupText, kerningDoubleDash } from './lib/MarkupText'; import { formatYMDHm, formatYMD, fromNow } from './lib/dateUtils'; -import { Post } from './post'; -import { baseUrl, Page } from './render'; +import type { Post } from './post'; +import { baseUrl, type Page } from './render'; const Article = (post: Post, ssg?: boolean): string => `
    diff --git a/src/BlogCard.ts b/src/BlogCard.ts index 2f2f39b..e235d06 100644 --- a/src/BlogCard.ts +++ b/src/BlogCard.ts @@ -1,6 +1,6 @@ import { TagListItem } from './TagList'; import { formatYMD } from './lib/dateUtils'; -import { Post } from './post'; +import type { Post } from './post'; const Summary = (plainText: string) => `

    diff --git a/src/PostList.ts b/src/PostList.ts index 7b025c9..21a8e7e 100644 --- a/src/PostList.ts +++ b/src/PostList.ts @@ -1,5 +1,5 @@ import { PostListItem } from './PostListItem'; -import { Post } from './post'; +import type { Post } from './post'; const Container = (listItems: string, archiveHeaderText?: string) => `

    diff --git a/src/PostListItem.ts b/src/PostListItem.ts index ee595b0..eafbe53 100644 --- a/src/PostListItem.ts +++ b/src/PostListItem.ts @@ -2,7 +2,7 @@ import { generateSummary } from 'search-summary'; import { TagList } from './TagList'; import { kerningDoubleDash } from './lib/MarkupText'; import { formatYMD, fromNow } from './lib/dateUtils'; -import { Post } from './post'; +import type { Post } from './post'; const Keyword = (keyword: string) => `${keyword}`; diff --git a/src/app.ts b/src/app.ts index f349e9b..296a042 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,7 +1,7 @@ import { defineBlogCard } from './BlogCard'; import { fetcher } from './lib/utils'; -import { notifyUpdate, Update } from './notify'; -import { jsonToPosts, JSONPost } from './post'; +import { notifyUpdate, type Update } from './notify'; +import { jsonToPosts, type JSONPost } from './post'; import { registerRerouter } from './reroute'; import { route } from './route'; diff --git a/src/lib/dateUtils.ts b/src/lib/dateUtils.ts index d2b86c5..19fd6f5 100644 --- a/src/lib/dateUtils.ts +++ b/src/lib/dateUtils.ts @@ -1,4 +1,4 @@ -import { toUnitTime, units, getDateParts, DateParts } from './dateConst'; +import { toUnitTime, units, getDateParts, type DateParts } from './dateConst'; function pad2(num: number): string { return String(num).padStart(2, '0'); diff --git a/src/route.ts b/src/route.ts index 80c3477..b369451 100644 --- a/src/route.ts +++ b/src/route.ts @@ -1,7 +1,7 @@ import { articlePage } from './Article'; import { PostList, TaggedPostList, SearchedPostList } from './PostList'; import { isDevelopment } from './lib/utils'; -import { Post, excludeReserved } from './post'; +import { excludeReserved, type Post } from './post'; import { renderPage, baseUrl } from './render'; import { toState } from './state'; diff --git a/src/works/Works.ts b/src/works/Works.ts index 05290f6..591b132 100644 --- a/src/works/Works.ts +++ b/src/works/Works.ts @@ -1,6 +1,6 @@ import { parseMarkdownLink } from '../lib/ExternalLink'; import { kerningDoubleDash } from '../lib/MarkupText'; -import { Citation, toCitationMap, Genre } from './citation'; +import { toCitationMap, type Citation, Genre } from './citation'; import { isUnpublished } from './citationDate'; const Text = (text: string): string => diff --git a/src/works/citation.ts b/src/works/citation.ts index b21560e..a398fff 100644 --- a/src/works/citation.ts +++ b/src/works/citation.ts @@ -1,7 +1,6 @@ -// eslint-disable-next-line import/no-unresolved -import { Data } from 'csl-json'; +import type { Data as CSLJSON } from 'csl-json'; -export type Citation = Omit & { +export type Citation = Omit & { id: string | number; _bibliographyText: string; }; diff --git a/src/works/citationDate.ts b/src/works/citationDate.ts index fbd4ab3..b94b493 100644 --- a/src/works/citationDate.ts +++ b/src/works/citationDate.ts @@ -1,4 +1,4 @@ -import { Citation } from './citation'; +import type { Citation } from './citation'; type DateParts = [number, number, number]; diff --git a/src/works/index.ts b/src/works/index.ts index a822360..f5488b6 100644 --- a/src/works/index.ts +++ b/src/works/index.ts @@ -1,8 +1,8 @@ import { fetcher } from '../lib/utils'; -import { notifyUpdate, Update } from '../notify'; +import { notifyUpdate, type Update } from '../notify'; import { renderRoot } from '../render'; import { Works } from './Works'; -import { Citation } from './citation'; +import type { Citation } from './citation'; const worksPath = './works.json'; const aboutPath = './about.json'; From 2fe7eac3d7d5f2b1778e6027be0c78ce1fd36c7a Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 10:28:50 +0900 Subject: [PATCH 25/26] refactor: replace->replaceAll --- src/lib/ExternalLink.ts | 4 ++-- src/lib/MarkupText.ts | 8 ++++---- src/post.ts | 10 +++++----- src/scripts/postsJson.ts | 12 ++++++------ src/ssg.ts | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lib/ExternalLink.ts b/src/lib/ExternalLink.ts index 4dbd8bb..7dd06d1 100644 --- a/src/lib/ExternalLink.ts +++ b/src/lib/ExternalLink.ts @@ -8,7 +8,7 @@ const ExternalLink = (props: { }; export function toExternalLink(html: string): string { - return html.replace( + return html.replaceAll( /(.+?)<\/a>/g, // href start with '?' or '/' (_, href: string, attributes: string, content: string) => ExternalLink({ href, attributes, content }), @@ -16,7 +16,7 @@ export function toExternalLink(html: string): string { } export function parseMarkdownLink(text: string): string { - return text.replace( + return text.replaceAll( /\[(.+?)\]\((.+?)\)/g, (_, content: string, href: string) => ExternalLink({ content, href }), ); diff --git a/src/lib/MarkupText.ts b/src/lib/MarkupText.ts index 5f5c4a3..de3f46a 100644 --- a/src/lib/MarkupText.ts +++ b/src/lib/MarkupText.ts @@ -2,14 +2,14 @@ export const MarkupText = (html: string): string => html // double dash -> double ruled line - .replace(/——/g, '──') + .replaceAll('——', '──') // full-width space -> half-width space after (!|?) - .replace(/([!?]) /g, (_, token: string) => `${token} `) + .replaceAll(/([!?]) /g, (_, token: string) => `${token} `) // unset paragraph indent start with brackets - .replace( + .replaceAll( /

    ([「『(].+?)<\/p>/g, (_, content: string) => `

    ${content}

    `, ); export const kerningDoubleDash = (text: string): string => - text.replace(/——(?![^(]*\))/g, '——'); // kerning except link href; + text.replaceAll(/——(?![^(]*\))/g, '——'); // kerning except link href; diff --git a/src/post.ts b/src/post.ts index 0b61ea8..249d581 100644 --- a/src/post.ts +++ b/src/post.ts @@ -31,14 +31,14 @@ export function jsonToPost(post: JSONPost): Post { ...post, title: post.title === null || post.title === '' ? undefined : post.title, plainText: post.text - .replace( + .replaceAll( /
    (.+?)<\/blockquote>/g, (_, text: string) => `> ${text}`, ) - .replace(/

    (.+?)<\/h2>/g, (_, text: string) => `## ${text}`) - .replace(/——/g, '──') - .replace(/
    (.+?)<\/div>/g, '') - .replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, ''), + .replaceAll(/

    (.+?)<\/h2>/g, (_, text: string) => `## ${text}`) + .replaceAll('——', '──') + .replaceAll(/
    (.+?)<\/div>/g, '') + .replaceAll(/<("[^"]*"|'[^']*'|[^'">])*>/g, ''), createdAt: parseDate(post.createdAt), modifiedAt: parseDate(post.modifiedAt), }; diff --git a/src/scripts/postsJson.ts b/src/scripts/postsJson.ts index 46557ad..4bd8ef7 100644 --- a/src/scripts/postsJson.ts +++ b/src/scripts/postsJson.ts @@ -32,13 +32,13 @@ async function listFiles(dir: string): Promise { function replacePostHtml(html: string): string { return ( html - .replace(/\n/g, '') - .replace(/>/g, '>') - .replace(/</g, '<') + .replaceAll('\n', '') + .replaceAll('>', '>') + .replaceAll('<', '<') // 漢字《ふりがな》 - .replace(/|(.+?)《(.+?)》/g, '$1$2') - .replace(/\{(.+?)\|(.+?)\}/g, '$1$2') - .replace(/([一-龠]+)《(.+?)》/g, '$1$2') + .replaceAll(/|(.+?)《(.+?)》/g, '$1$2') + .replaceAll(/\{(.+?)\|(.+?)\}/g, '$1$2') + .replaceAll(/([一-龠]+)《(.+?)》/g, '$1$2') ); } diff --git a/src/ssg.ts b/src/ssg.ts index 7307e93..6d01ddc 100644 --- a/src/ssg.ts +++ b/src/ssg.ts @@ -27,12 +27,12 @@ function createTemplateValues(post: Post): Record { function embedTemplate(post: Post, template: string, posts: Post[]): string { const values = createTemplateValues(post); return template - .replace(/\{\{(\w+)\}\}/g, (_, key: string) => { + .replaceAll(/\{\{(\w+)\}\}/g, (_, key: string) => { const value = values[key]; if (value === undefined) console.log(`Missing template var: ${key}`); return value; }) - .replace(/<\/blog-card>/g, (_, id: string) => { + .replaceAll(/<\/blog-card>/g, (_, id: string) => { const post = posts.find((post) => id === post.id.toString()); if (!post) return ''; return `${BlogCard(post)}`; From 6375994d9a16524cdee3260dfe6861a56e34e3f5 Mon Sep 17 00:00:00 2001 From: kyonenya Date: Thu, 7 May 2026 10:29:36 +0900 Subject: [PATCH 26/26] chore: build --- dist/hydrate.js | 2 +- dist/index.js | 2 +- dist/works.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/hydrate.js b/dist/hydrate.js index e28c32d..24935a7 100644 --- a/dist/hydrate.js +++ b/dist/hydrate.js @@ -1 +1 @@ -(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:s=50,beforeLength:o=20}=null!=e?e:{},r=t.indexOf(n);if(-1===r)return;const a=r-o<=0?0:r-o,i=r+n.length,l=a+s;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,r),keyword:t.substring(r,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(s)(e,n),t.Ks=e=>(n,s)=>{const o=(0,t.xq)(n,s,e);if(void 0===o)return;const{ellipsisToken:r="...",keywordModifier:a=e=>e}=null!=e?e:{};return(o.isBeforeEllipsed?r:"")+o.beforeText+a(o.keyword)+o.afterText+(o.isAfterEllipsed?r:"")},t.ur=(e,n,s)=>(0,t.Ks)(s)(e,n)}},t={};const n=e=>`#${e}`,s=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,o=(e,t)=>`\n
      \n ${e.map(e=>s(e,t)).join("")}\n
    \n`,r=864e5,a=30*r,i={seconds:1e3,minutes:6e4,hours:36e5,day:r,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:s}=d(e);return`${t}-${u(n)}-${u(s)}`}function f(e,t){return e.getTime()Math.abs(c(t,e))e.replace(/——(?![^(]*\))/g,'——'),v="https://kyonenya.github.io/";function x(e){const t=document.querySelector(".el_logo_suffix"),n=document.querySelector("meta[name=description]");if(function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),b(window.location.href)&&document.querySelectorAll(`a[href^="${v}"]`).forEach(e=>{e.href="/"+e.href.replace(v,"")})}(e.body),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const k=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${w(e.title)}

    `:""}\n ${(e=>e.replace(/——/g,"──").replace(/([!?]) /g,(e,t)=>`${t} `).replace(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replace(/(.+?)<\/a>/g,(e,t,n,s)=>(e=>{const{href:t,content:n,attributes:s=""}=e;return`${n}`})({href:t,attributes:n,content:s}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${o(e.tags)}\n
    \n
    \n
    `;var n};var L=function n(s){var o=t[s];if(void 0!==o)return o.exports;var r=t[s]={exports:{}};return e[s](r,r.exports,n),r.exports}(224);const S=e=>`${e}`,T=e=>{const{post:t,tag:n,keyword:s}=e,r=(0,L.ur)(t.plainText,s,{maxLength:200,beforeLength:48,keywordModifier:S});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${w(((e,t)=>t?e.replace(t,S(t)):e)(e,t))}\n

    `)(t.title,s):""}\n ${r?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(r):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${o(t.tags,n)}\n
    \n `;var a},A=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,E=e=>A(e.map((t,n)=>T({post:e[n]})).reverse().join("")),D=(e,t)=>A(e.map((n,s)=>n.tags.includes(t)?T({post:e[s],tag:t}):"").reverse().join(""),`#${t}`),q=(e,t,n)=>A(e.map((s,o)=>void 0===n||s.tags.includes(n)?T({post:e[o],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),I=document.querySelector(".el_search_input");function j(e){const{id:t,legacyId:n,tag:s,keyword:o}=_(window.location.search,window.location.pathname,window.location.hash),r=b(window.location.href)?e:function(e){return e.filter(e=>f(e.createdAt,new Date))}(e);if((e=>{null!==e&&window.history.replaceState(void 0,"",`/posts/${e}`)})(n),null!==t){const e=r.find(e=>e.id===t);if(!e)return;(e=>{x((e=>({body:k(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${v}posts/${e.id}`}))(e)),I&&(I.style.display="none")})(e)}else null!==o?((e,t,n)=>{x({body:q(e,t,n),title:`「${t}」|placet experiri`}),I&&(I.style.display="block")})(r,o,s??void 0):null!==s?((e,t)=>{x({body:D(e,t),title:`#${t}|placet experiri`,href:`${v}?tag=${t}`}),I&&(I.style.display="block")})(r,s):(e=>{x({body:E(e),title:"placet experiri",href:v}),I&&(I.style.display="block")})(r)}document.querySelectorAll(".bl_posts_dateago").forEach(e=>{const t=e.getAttribute("data-date");t&&(e.textContent=m(new Date(t)))}),async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,o=`${t}/about.json`,r=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replace(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replace(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replace(/——/g,"──").replace(/
    (.+?)<\/div>/g,"").replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:y(e.createdAt),modifiedAt:y(e.modifiedAt)}}(e))}(await $(n));var a;e&&j(r),function(e){window.addEventListener("popstate",()=>e())}(a=()=>j(r)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(_(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e())})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(r),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),f(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(o))}(!1)})(); \ No newline at end of file +(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:s=50,beforeLength:o=20}=null!=e?e:{},r=t.indexOf(n);if(-1===r)return;const a=r-o<=0?0:r-o,i=r+n.length,l=a+s;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,r),keyword:t.substring(r,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(s)(e,n),t.Ks=e=>(n,s)=>{const o=(0,t.xq)(n,s,e);if(void 0===o)return;const{ellipsisToken:r="...",keywordModifier:a=e=>e}=null!=e?e:{};return(o.isBeforeEllipsed?r:"")+o.beforeText+a(o.keyword)+o.afterText+(o.isAfterEllipsed?r:"")},t.ur=(e,n,s)=>(0,t.Ks)(s)(e,n)}},t={};const n=e=>`#${e}`,s=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,o=(e,t)=>`\n
      \n ${e.map(e=>s(e,t)).join("")}\n
    \n`,r=864e5,a=30*r,i={seconds:1e3,minutes:6e4,hours:36e5,day:r,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:s}=d(e);return`${t}-${u(n)}-${u(s)}`}function f(e,t){return e.getTime()Math.abs(c(t,e))e.replaceAll(/——(?![^(]*\))/g,'——'),v="https://kyonenya.github.io/";function x(e){const t=document.querySelector(".el_logo_suffix"),n=document.querySelector("meta[name=description]");if(function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),b(window.location.href)&&document.querySelectorAll(`a[href^="${v}"]`).forEach(e=>{e.href="/"+e.href.replace(v,"")})}(e.body),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const A=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${w(e.title)}

    `:""}\n ${(e=>e.replaceAll("——","──").replaceAll(/([!?]) /g,(e,t)=>`${t} `).replaceAll(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replaceAll(/(.+?)<\/a>/g,(e,t,n,s)=>(e=>{const{href:t,content:n,attributes:s=""}=e;return`${n}`})({href:t,attributes:n,content:s}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${o(e.tags)}\n
    \n
    \n
    `;var n};var k=function n(s){var o=t[s];if(void 0!==o)return o.exports;var r=t[s]={exports:{}};return e[s](r,r.exports,n),r.exports}(224);const L=e=>`${e}`,S=e=>{const{post:t,tag:n,keyword:s}=e,r=(0,k.ur)(t.plainText,s,{maxLength:200,beforeLength:48,keywordModifier:L});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${w(((e,t)=>t?e.replace(t,L(t)):e)(e,t))}\n

    `)(t.title,s):""}\n ${r?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(r):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${o(t.tags,n)}\n
    \n `;var a},T=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,E=e=>T(e.map((t,n)=>S({post:e[n]})).reverse().join("")),D=(e,t)=>T(e.map((n,s)=>n.tags.includes(t)?S({post:e[s],tag:t}):"").reverse().join(""),`#${t}`),q=(e,t,n)=>T(e.map((s,o)=>void 0===n||s.tags.includes(n)?S({post:e[o],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),I=document.querySelector(".el_search_input");function j(e){const{id:t,legacyId:n,tag:s,keyword:o}=_(window.location.search,window.location.pathname,window.location.hash),r=b(window.location.href)?e:function(e){return e.filter(e=>f(e.createdAt,new Date))}(e);if((e=>{null!==e&&window.history.replaceState(void 0,"",`/posts/${e}`)})(n),null!==t){const e=r.find(e=>e.id===t);if(!e)return;(e=>{x((e=>({body:A(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${v}posts/${e.id}`}))(e)),I&&(I.style.display="none")})(e)}else null!==o?((e,t,n)=>{x({body:q(e,t,n),title:`「${t}」|placet experiri`}),I&&(I.style.display="block")})(r,o,s??void 0):null!==s?((e,t)=>{x({body:D(e,t),title:`#${t}|placet experiri`,href:`${v}?tag=${t}`}),I&&(I.style.display="block")})(r,s):(e=>{x({body:E(e),title:"placet experiri",href:v}),I&&(I.style.display="block")})(r)}document.querySelectorAll(".bl_posts_dateago").forEach(e=>{const t=e.getAttribute("data-date");t&&(e.textContent=m(new Date(t)))}),async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,o=`${t}/about.json`,r=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replaceAll(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replaceAll(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replaceAll("——","──").replaceAll(/
    (.+?)<\/div>/g,"").replaceAll(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:y(e.createdAt),modifiedAt:y(e.modifiedAt)}}(e))}(await $(n));var a;e&&j(r),function(e){window.addEventListener("popstate",()=>e())}(a=()=>j(r)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(_(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e())})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(r),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),f(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(o))}(!1)})(); \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 18b7ef5..c2d0185 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1 +1 @@ -(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:s=50,beforeLength:r=20}=null!=e?e:{},o=t.indexOf(n);if(-1===o)return;const a=o-r<=0?0:o-r,i=o+n.length,l=a+s;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,o),keyword:t.substring(o,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(s)(e,n),t.Ks=e=>(n,s)=>{const r=(0,t.xq)(n,s,e);if(void 0===r)return;const{ellipsisToken:o="...",keywordModifier:a=e=>e}=null!=e?e:{};return(r.isBeforeEllipsed?o:"")+r.beforeText+a(r.keyword)+r.afterText+(r.isAfterEllipsed?o:"")},t.ur=(e,n,s)=>(0,t.Ks)(s)(e,n)}},t={};const n=e=>`#${e}`,s=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,r=(e,t)=>`\n
      \n ${e.map(e=>s(e,t)).join("")}\n
    \n`,o=864e5,a=30*o,i={seconds:1e3,minutes:6e4,hours:36e5,day:o,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:s}=d(e);return`${t}-${u(n)}-${u(s)}`}function f(e,t){return e.getTime()Math.abs(c(t,e))e.replace(/——(?![^(]*\))/g,'——'),v="https://kyonenya.github.io/";function x(e){const t=document.querySelector(".el_logo_suffix"),n=document.querySelector("meta[name=description]");if(function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),b(window.location.href)&&document.querySelectorAll(`a[href^="${v}"]`).forEach(e=>{e.href="/"+e.href.replace(v,"")})}(e.body),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const k=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${w(e.title)}

    `:""}\n ${(e=>e.replace(/——/g,"──").replace(/([!?]) /g,(e,t)=>`${t} `).replace(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replace(/(.+?)<\/a>/g,(e,t,n,s)=>(e=>{const{href:t,content:n,attributes:s=""}=e;return`${n}`})({href:t,attributes:n,content:s}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${r(e.tags)}\n
    \n
    \n
    `;var n};var L=function n(s){var r=t[s];if(void 0!==r)return r.exports;var o=t[s]={exports:{}};return e[s](o,o.exports,n),o.exports}(224);const S=e=>`${e}`,T=e=>{const{post:t,tag:n,keyword:s}=e,o=(0,L.ur)(t.plainText,s,{maxLength:200,beforeLength:48,keywordModifier:S});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${w(((e,t)=>t?e.replace(t,S(t)):e)(e,t))}\n

    `)(t.title,s):""}\n ${o?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(o):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${r(t.tags,n)}\n
    \n `;var a},A=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,E=e=>A(e.map((t,n)=>T({post:e[n]})).reverse().join("")),D=(e,t)=>A(e.map((n,s)=>n.tags.includes(t)?T({post:e[s],tag:t}):"").reverse().join(""),`#${t}`),q=(e,t,n)=>A(e.map((s,r)=>void 0===n||s.tags.includes(n)?T({post:e[r],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),I=document.querySelector(".el_search_input");function j(e){const{id:t,legacyId:n,tag:s,keyword:r}=_(window.location.search,window.location.pathname,window.location.hash),o=b(window.location.href)?e:function(e){return e.filter(e=>f(e.createdAt,new Date))}(e);if((e=>{null!==e&&window.history.replaceState(void 0,"",`/posts/${e}`)})(n),null!==t){const e=o.find(e=>e.id===t);if(!e)return;(e=>{x((e=>({body:k(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${v}posts/${e.id}`}))(e)),I&&(I.style.display="none")})(e)}else null!==r?((e,t,n)=>{x({body:q(e,t,n),title:`「${t}」|placet experiri`}),I&&(I.style.display="block")})(o,r,s??void 0):null!==s?((e,t)=>{x({body:D(e,t),title:`#${t}|placet experiri`,href:`${v}?tag=${t}`}),I&&(I.style.display="block")})(o,s):(e=>{x({body:E(e),title:"placet experiri",href:v}),I&&(I.style.display="block")})(o)}!async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,r=`${t}/about.json`,o=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replace(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replace(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replace(/——/g,"──").replace(/
    (.+?)<\/div>/g,"").replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:y(e.createdAt),modifiedAt:y(e.modifiedAt)}}(e))}(await $(n));var a;e&&j(o),function(e){window.addEventListener("popstate",()=>e())}(a=()=>j(o)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(_(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e())})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(o),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),f(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(r))}()})(); \ No newline at end of file +(()=>{"use strict";var e={224(e,t){t.ur=t.Ks=t.xq=t.xS=void 0,t.xS=e=>(t,n)=>{if(void 0===n)return;const{maxLength:s=50,beforeLength:r=20}=null!=e?e:{},o=t.indexOf(n);if(-1===o)return;const a=o-r<=0?0:o-r,i=o+n.length,l=a+s;return{isBeforeEllipsed:0!==a,beforeText:t.substring(a,o),keyword:t.substring(o,i),afterText:t.substring(i,l),isAfterEllipsed:l(0,t.xS)(s)(e,n),t.Ks=e=>(n,s)=>{const r=(0,t.xq)(n,s,e);if(void 0===r)return;const{ellipsisToken:o="...",keywordModifier:a=e=>e}=null!=e?e:{};return(r.isBeforeEllipsed?o:"")+r.beforeText+a(r.keyword)+r.afterText+(r.isAfterEllipsed?o:"")},t.ur=(e,n,s)=>(0,t.Ks)(s)(e,n)}},t={};const n=e=>`#${e}`,s=(e,t)=>`\n
  • \n \n ${e===t?`${n(e)}`:n(e)}\n \n
  • `,r=(e,t)=>`\n
      \n ${e.map(e=>s(e,t)).join("")}\n
    \n`,o=864e5,a=30*o,i={seconds:1e3,minutes:6e4,hours:36e5,day:o,month:a,year:12*a},l=Object.keys(i);function c(e,t){return e/i[t]}function d(e){return{seconds:e.getSeconds(),minutes:e.getMinutes(),hours:e.getHours(),day:e.getDate(),month:e.getMonth()+1,year:e.getFullYear()}}function u(e){return String(e).padStart(2,"0")}function p(e){const{year:t,month:n,day:s}=d(e);return`${t}-${u(n)}-${u(s)}`}function f(e,t){return e.getTime()Math.abs(c(t,e))e.replaceAll(/——(?![^(]*\))/g,'——'),v="https://kyonenya.github.io/";function x(e){const t=document.querySelector(".el_logo_suffix"),n=document.querySelector("meta[name=description]");if(function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),b(window.location.href)&&document.querySelectorAll(`a[href^="${v}"]`).forEach(e=>{e.href="/"+e.href.replace(v,"")})}(e.body),document.title=e.title,t.innerText=e.suffix??"",e.description&&(n.content=e.description),e.href){document.querySelectorAll('link[rel="canonical"]').forEach(e=>e.remove());const t=document.createElement("link");t.rel="canonical",t.href=e.href,document.head.appendChild(t)}}const A=(e,t)=>{return`\n
    \n
    \n
    \n \n
    \n
    \n ${e.title?`

    ${w(e.title)}

    `:""}\n ${(e=>e.replaceAll("——","──").replaceAll(/([!?]) /g,(e,t)=>`${t} `).replaceAll(/

    ([「『(].+?)<\/p>/g,(e,t)=>`

    ${t}

    `))((n=e.text,n.replaceAll(/(.+?)<\/a>/g,(e,t,n,s)=>(e=>{const{href:t,content:n,attributes:s=""}=e;return`${n}`})({href:t,attributes:n,content:s}))))}\n
    \n
    \n \n ${t?p(e.createdAt):m(e.createdAt)}\n \n ${r(e.tags)}\n
    \n
    \n
    `;var n};var k=function n(s){var r=t[s];if(void 0!==r)return r.exports;var o=t[s]={exports:{}};return e[s](o,o.exports,n),o.exports}(224);const L=e=>`${e}`,S=e=>{const{post:t,tag:n,keyword:s}=e,o=(0,k.ur)(t.plainText,s,{maxLength:200,beforeLength:48,keywordModifier:L});return`\n \n \n
    \n \n
    \n ${t.title?((e,t)=>`\n

    \n ${w(((e,t)=>t?e.replace(t,L(t)):e)(e,t))}\n

    `)(t.title,s):""}\n ${o?(e=>`\n
    \n

    \n ${e}\n

    \n
    `)(o):(a=t.plainText,`\n
    \n

    \n ${a.substring(0,300)}\n

    \n
    `)}\n
    \n
    \n \n ${m(t.createdAt)}\n \n ${r(t.tags,n)}\n
    \n `;var a},T=(e,t)=>`\n
    \n ${t?`

    ${t}

    `:""}\n
      \n ${e}\n
    \n
    `,E=e=>T(e.map((t,n)=>S({post:e[n]})).reverse().join("")),D=(e,t)=>T(e.map((n,s)=>n.tags.includes(t)?S({post:e[s],tag:t}):"").reverse().join(""),`#${t}`),q=(e,t,n)=>T(e.map((s,r)=>void 0===n||s.tags.includes(n)?S({post:e[r],tag:n,keyword:t}):"").reverse().join(""),`「${t}」`),I=document.querySelector(".el_search_input");function j(e){const{id:t,legacyId:n,tag:s,keyword:r}=_(window.location.search,window.location.pathname,window.location.hash),o=b(window.location.href)?e:function(e){return e.filter(e=>f(e.createdAt,new Date))}(e);if((e=>{null!==e&&window.history.replaceState(void 0,"",`/posts/${e}`)})(n),null!==t){const e=o.find(e=>e.id===t);if(!e)return;(e=>{x((e=>({body:A(e,void 0),title:e.title?`${e.title}|placet experiri :: ${e.id}`:`placet experiri :: ${e.id}`,suffix:` :: ${e.id}`,description:`${e.plainText.substring(0,110)}…`,href:`${v}posts/${e.id}`}))(e)),I&&(I.style.display="none")})(e)}else null!==r?((e,t,n)=>{x({body:q(e,t,n),title:`「${t}」|placet experiri`}),I&&(I.style.display="block")})(o,r,s??void 0):null!==s?((e,t)=>{x({body:D(e,t),title:`#${t}|placet experiri`,href:`${v}?tag=${t}`}),I&&(I.style.display="block")})(o,s):(e=>{x({body:E(e),title:"placet experiri",href:v}),I&&(I.style.display="block")})(o)}!async function(e=!0){const t=e?".":"..",n=`${t}/posts.json`,r=`${t}/about.json`,o=function(e){return[...e].reverse().map(e=>function(e){return{...e,title:null===e.title||""===e.title?void 0:e.title,plainText:e.text.replaceAll(/
    (.+?)<\/blockquote>/g,(e,t)=>`> ${t}`).replaceAll(/

    (.+?)<\/h2>/g,(e,t)=>`## ${t}`).replaceAll("——","──").replaceAll(/
    (.+?)<\/div>/g,"").replaceAll(/<("[^"]*"|'[^']*'|[^'">])*>/g,""),createdAt:y(e.createdAt),modifiedAt:y(e.modifiedAt)}}(e))}(await $(n));var a;e&&j(o),function(e){window.addEventListener("popstate",()=>e())}(a=()=>j(o)),function(e){const t=document.querySelector(".el_search_form"),n=document.querySelector(".el_search_input");t?.addEventListener("submit",t=>{t.preventDefault(),window.history.pushState(_(window.location.search,window.location.pathname,`#${n.value}`),"",`${window.location.search}#${n.value}`),e()})}(a),function(e){document.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const n=t.target.closest('a[href^="#"], a[href^="/?"], a[href="/"], a[href^="/posts/"]');n&&(t.preventDefault(),window.history.pushState(void 0,"",n.href),e())})}(a),function(e){class t extends HTMLElement{constructor(){super();const t=this.getAttribute("id");if(!t)return;const n=e.find(e=>e.id===parseInt(t,10));n&&(this.innerHTML=(e=>{return`\n `;var t})(n))}}window.customElements.define("blog-card",t)}(o),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),f(new Date,n)}(new Date(e),t)||t<=0||(n?.addEventListener("click",()=>{localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await $(r))}()})(); \ No newline at end of file diff --git a/dist/works.js b/dist/works.js index 9f447a3..0f16265 100644 --- a/dist/works.js +++ b/dist/works.js @@ -1 +1 @@ -(()=>{"use strict";async function e(e){const t=await fetch(e);if(!t.ok)throw new Error(t.statusText);return await t.json()}const t=864e5,n=30*t,o={seconds:1e3,minutes:6e4,hours:36e5,day:t,month:n,year:12*n};Object.keys(o),new Intl.RelativeTimeFormat("ja-JP",{style:"narrow"});const a="https://kyonenya.github.io/";const r=["書籍","学位論文","論文","発表","翻訳"];const s=e=>function(e){return e.replace(/\[(.+?)\]\((.+?)\)/g,(e,t,n)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({content:t,href:n}))}((e=>e.replace(/——(?![^(]*\))/g,'——'))(e)),c=e=>`${s(e)}`,i=(e,t,n)=>{const o=e.id.toString()===n,a=o?`${t}.`:`${t}.`,r=function(e){const t=e.issued?.["date-parts"]?.[0]??e["event-date"]?.["date-parts"]?.[0];if(t)return new Date\n ${a}\n ${(o?c:s)(e._bibliographyText)}${r}\n \n `};!async function(){!function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));var n;t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),n=window.location.href,/localhost:\d+/.test(n)&&document.querySelectorAll(`a[href^="${a}"]`).forEach(e=>{e.href="/"+e.href.replace(a,"")})}(((e,t)=>{const n=(e=>Object.fromEntries(r.map(t=>[t,e.filter(e=>function(e){switch(e.type){case"book":return"書籍";case"article-journal":return e.translator?"翻訳":"論文";case"paper-conference":return"発表";case"thesis":return"学位論文";default:return}}(e)===t)])))(e);return`\n
    \n
    \n

    業績一覧

    \n ${r.map(e=>((e,t,n)=>e&&0!==e.length?`\n

    ${t}

    \n
      \n ${e.map((e,t)=>i(e,t+1,n)).join("")}\n
    `:"")(n[e],e,t)).join("")}\n
    \n
    `})(await e("./works.json"),decodeURIComponent(window.location.hash.replace("#","")))),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),function(e,t){return e.getTime(){localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await e("./about.json"))}()})(); \ No newline at end of file +(()=>{"use strict";async function e(e){const t=await fetch(e);if(!t.ok)throw new Error(t.statusText);return await t.json()}const t=864e5,n=30*t,o={seconds:1e3,minutes:6e4,hours:36e5,day:t,month:n,year:12*n};Object.keys(o),new Intl.RelativeTimeFormat("ja-JP",{style:"narrow"});const a="https://kyonenya.github.io/";const r=["書籍","学位論文","論文","発表","翻訳"];const s=e=>function(e){return e.replaceAll(/\[(.+?)\]\((.+?)\)/g,(e,t,n)=>(e=>{const{href:t,content:n,attributes:o=""}=e;return`${n}`})({content:t,href:n}))}((e=>e.replaceAll(/——(?![^(]*\))/g,'——'))(e)),c=e=>`${s(e)}`,i=(e,t,n)=>{const o=e.id.toString()===n,a=o?`${t}.`:`${t}.`,r=function(e){const t=e.issued?.["date-parts"]?.[0]??e["event-date"]?.["date-parts"]?.[0];if(t)return new Date\n ${a}\n ${(o?c:s)(e._bibliographyText)}${r}\n \n `};!async function(){!function(e){document.getElementById("root").innerHTML=e;const t=document.getElementById(window.location.hash.replace("#",""));var n;t?t.scrollIntoView({behavior:"smooth"}):window.scrollTo(0,0),n=window.location.href,/localhost:\d+/.test(n)&&document.querySelectorAll(`a[href^="${a}"]`).forEach(e=>{e.href="/"+e.href.replace(a,"")})}(((e,t)=>{const n=(e=>Object.fromEntries(r.map(t=>[t,e.filter(e=>function(e){switch(e.type){case"book":return"書籍";case"article-journal":return e.translator?"翻訳":"論文";case"paper-conference":return"発表";case"thesis":return"学位論文";default:return}}(e)===t)])))(e);return`\n
    \n
    \n

    業績一覧

    \n ${r.map(e=>((e,t,n)=>e&&0!==e.length?`\n

    ${t}

    \n
      \n ${e.map((e,t)=>i(e,t+1,n)).join("")}\n
    `:"")(n[e],e,t)).join("")}\n
    \n
    `})(await e("./works.json"),decodeURIComponent(window.location.hash.replace("#","")))),function({updatedAt:e,newDays:t}){const n=document.getElementById("about");!function(e,t){const n=new Date(e);return n.setDate(e.getDate()+t),function(e,t){return e.getTime(){localStorage.setItem(e,"true")}),"true"!==localStorage.getItem(e)&&n?.classList.add("el_badge"))}(await e("./about.json"))}()})(); \ No newline at end of file