diff --git a/cms-data-fetcher/src/parse.ts b/cms-data-fetcher/src/parse.ts index eb0e06c..d534461 100644 --- a/cms-data-fetcher/src/parse.ts +++ b/cms-data-fetcher/src/parse.ts @@ -40,35 +40,44 @@ export function parse(content: string) { } function getPOption(text: string) { - let pOpt = 'normal' - if (isImage(text)) { - pOpt = 'image' - } else if (isPhoto(text)) { - pOpt = 'photo' - } else if (isComicPage(text)) { - pOpt = 'comic' - } else if (isYouTube(text)) { - pOpt = 'youtube' - } else if (isTwitter(text)) { - pOpt = 'twitter' - } else if (isAmazon(text)) { - pOpt = 'amazon' - } else if (isBlog(text)) { - pOpt = 'blog' - } else if (isArtifact(text)) { - pOpt = 'artifact' - } else if (isURL(text)) { - pOpt = 'url' - } else if (text === '') { - pOpt = 'empty' - } else if (isCommand(text)) { - pOpt = text.replaceAll('/', '') + // URLではない場合 + if (!URL.canParse(text)) { + if (text === '') { + return 'empty' + } else if (isCommand(text)) { + return text.replaceAll('/', '') + } + return 'normal' + } + + // URLの場合 + const textURL = new URL(text) + if (isImage(textURL.hostname, text)) { + return 'image' + } else if (isPhoto(textURL.hostname, text)) { + return 'photo' + } else if (isComicPage(textURL.hostname, textURL.pathname)) { + return 'comic' + } else if (isYouTube(textURL.hostname)) { + return 'youtube' + } else if (isTwitter(textURL.hostname)) { + return 'twitter' + } else if (isAmazon(textURL.hostname)) { + return 'amazon' + } else if (isBlog(textURL.hostname, textURL.pathname)) { + return 'blog' + } else if (isArtifact(textURL.hostname, textURL.pathname)) { + return 'artifact' + } else if (isURL(textURL)) { + return 'url' + } else { + // URLとしてパースできたが、http/https以外のプロトコルの場合は通常のテキストとして扱う + return 'normal' } - return pOpt } -function isImage(text: string) { - if (text.indexOf('https://r2.maretol.xyz/') === 0) { +function isImage(hostname: string, text: string) { + if (hostname === 'r2.maretol.xyz') { const ext = text.split('.').pop() || '' if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) { return true @@ -77,51 +86,47 @@ function isImage(text: string) { } } -function isPhoto(text: string) { - if (text.indexOf('https://photos.maretol.xyz') === 0) { - const photoURL = text.split('@@')[0] // 画像URL。@@以降はキャプション +function isPhoto(hostname: string, text: string) { + const photoDomain = ['photos.maretol.xyz', 'capture.maretol.xyz'] + if (photoDomain.includes(hostname)) { + const photoURL = text.split('@@')[0] // 画像URL。@@以降はタイトルやキャプション const ext = photoURL.split('.').pop() || '' if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) { return true } } - return + return false } -function isComicPage(text: string) { - return text.indexOf('https://www.maretol.xyz/comics/') === 0 || text.indexOf('https://maretol.xyz/comics/') === 0 +function isComicPage(hostname: string, pathname: string) { + const comicDomain = ['maretol.xyz', 'www.maretol.xyz'] + return comicDomain.includes(hostname) && pathname.indexOf('/comics/') === 0 } -function isYouTube(text: string) { - return text.indexOf('https://youtu.be/') === 0 || text.indexOf('https://www.youtube.com/') === 0 +function isYouTube(hostname: string) { + return ['youtu.be', 'www.youtube.com'].includes(hostname) } -function isTwitter(text: string) { - return ( - text.indexOf('https://twitter.com/') === 0 || - text.indexOf('https://www.twitter.com/') === 0 || - text.indexOf('https://x.com/') === 0 - ) +function isTwitter(hostname: string) { + return ['twitter.com', 'www.twitter.com', 'x.com'].includes(hostname) } -function isAmazon(text: string) { - return text.indexOf('https://www.amazon.co.jp/') === 0 || text.indexOf('https://amzn.to/') === 0 +function isAmazon(hostname: string) { + return ['www.amazon.co.jp', 'amzn.to'].includes(hostname) } // ブログ記事のリンクの場合 -function isBlog(text: string) { - return text.indexOf('https://www.maretol.xyz/blog/') === 0 || text.indexOf('https://maretol.xyz/blog/') === 0 +function isBlog(hostname: string, pathname: string) { + return ['maretol.xyz', 'www.maretol.xyz'].includes(hostname) && pathname.indexOf('/blog/') === 0 } // artifactのリンクの場合 -function isArtifact(text: string) { - return ( - text.indexOf('https://www.maretol.xyz/artifacts/') === 0 || text.indexOf('https://maretol.xyz/artifacts/') === 0 - ) +function isArtifact(hostname: string, pathname: string) { + return ['maretol.xyz', 'www.maretol.xyz'].includes(hostname) && pathname.indexOf('/artifacts/') === 0 } -function isURL(text: string) { - return text.indexOf('https://') === 0 +function isURL(url: URL) { + return url.protocol === 'http:' || url.protocol === 'https:' } function isCommand(text: string) { @@ -150,3 +155,5 @@ function getSubText(text: string) { } return null } + +export { getPOption } diff --git a/cms-data-fetcher/test/index.spec.ts b/cms-data-fetcher/test/index.spec.ts index fbee335..ac15930 100644 --- a/cms-data-fetcher/test/index.spec.ts +++ b/cms-data-fetcher/test/index.spec.ts @@ -1,25 +1,85 @@ -// test/index.spec.ts -import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test'; -import { describe, it, expect } from 'vitest'; -import worker from '../src/index'; +import { describe, it, expect } from 'vitest' +import { getPOption } from '../src/parse' -// For now, you'll need to do something like this to get a correctly-typed -// `Request` to pass to `worker.fetch()`. -const IncomingRequest = Request; - -describe('Hello World worker', () => { - it('responds with Hello World! (unit style)', async () => { - const request = new IncomingRequest('http://example.com'); - // Create an empty context to pass to `worker.fetch()`. - const ctx = createExecutionContext(); - const response = await worker.fetch(request, env, ctx); - // Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions - await waitOnExecutionContext(ctx); - expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); - }); - - it('responds with Hello World! (integration style)', async () => { - const response = await SELF.fetch('https://example.com'); - expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); - }); -}); +describe('parse_getPOptionのテスト', () => { + it('通常テキストの場合', () => { + const text = 'test text' + const result = getPOption(text) + expect(result).toBe('normal') + }) + it('空文字の場合', () => { + const text = '' + const result = getPOption(text) + expect(result).toBe('empty') + }) + it('画像の場合', () => { + const text = 'https://r2.maretol.xyz/test.png' + const result = getPOption(text) + expect(result).toBe('image') + }) + it('写真の場合', () => { + const text = ['https://photos.maretol.xyz/test.jpg', 'https://capture.maretol.xyz/test.jpg'] + text.forEach((t) => { + const result = getPOption(t) + expect(result).toBe('photo') + }) + }) + it('写真でサブテキストがあった場合', () => { + const text = 'https://photos.maretol.xyz/test.jpg@@subtext_key::subtext_value@@subtext::サブテキスト' + const result = getPOption(text) + expect(result).toBe('photo') + }) + it('漫画リンクの場合', () => { + const text = 'https://www.maretol.xyz/comics/test' + const result = getPOption(text) + expect(result).toBe('comic') + }) + it('YouTubeリンクの場合', () => { + const text = ['https://www.youtube.com/watch?v=test', 'https://youtu.be/test'] + text.forEach((t) => { + const result = getPOption(t) + expect(result).toBe('youtube') + }) + }), + it('Twitterリンクの場合', () => { + const text = ['https://twitter.com/test', 'https://www.twitter.com/test', 'https://x.com/test'] + text.forEach((t) => { + const result = getPOption(t) + expect(result).toBe('twitter') + }) + }) + it('Amazonリンクの場合', () => { + const text = ['https://www.amazon.co.jp/test', 'https://amzn.to/test'] + text.forEach((t) => { + const result = getPOption(t) + expect(result).toBe('amazon') + }) + }) + it('ブログリンクの場合', () => { + const text = 'https://www.maretol.xyz/blog/test' + const result = getPOption(text) + expect(result).toBe('blog') + }) + it('artifactリンクの場合', () => { + const text = 'https://www.maretol.xyz/artifacts/test' + const result = getPOption(text) + expect(result).toBe('artifact') + }) + it('その他のURLの場合', () => { + const text = 'https://example.com' + const result = getPOption(text) + expect(result).toBe('url') + }) + it('コマンド入力の場合', () => { + const text = '/test_command' + const result = getPOption(text) + expect(result).toBe('test_command') + }) + it('特殊なテキストの場合', () => { + const text = ['text://hogehoge', '途中にURLが入っている場合。https://example.comみたいな'] + text.forEach((t) => { + const result = getPOption(t) + expect(result).toBe('normal') + }) + }) +}) diff --git a/package.json b/package.json index 71bc861..bb57ce4 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "deploy:page": "npm run --workspace=pages deploy-prd", "deploy-stg:page": "npm run --workspace=pages deploy-stg", "build:page": "npm run --workspace=pages pages:build", - "lint:page": "npm run --workspace=pages lint" + "lint:page": "npm run --workspace=pages lint", + "test:cms": "npm run --workspace=cms-data-fetcher test" }, "author": "maretol", "private": true, diff --git a/pages/app/artifacts/layout.tsx b/pages/app/artifacts/layout.tsx new file mode 100644 index 0000000..cc4240b --- /dev/null +++ b/pages/app/artifacts/layout.tsx @@ -0,0 +1,5 @@ +import BaseLayout from '@/components/large/base_layout' + +export default function ArtifactsLayout({ children }: { children: React.ReactNode }) { + return {children} +} diff --git a/pages/app/blog/[article_id]/@modal/(.)image/[src]/modal.tsx b/pages/app/blog/[article_id]/@modal/(.)image/[src]/modal.tsx index 04d3db5..4096537 100644 --- a/pages/app/blog/[article_id]/@modal/(.)image/[src]/modal.tsx +++ b/pages/app/blog/[article_id]/@modal/(.)image/[src]/modal.tsx @@ -26,7 +26,7 @@ export default function Modal({ imageSrc }: { imageSrc: string }) { alt="" width={1000} height={1000} - className="mb-5 mt-0 w-full max-w-fit h-full max-h-fit shadow-lg" + className="my-10 w-full max-w-fit h-full max-h-fit shadow-lg" /> diff --git a/pages/components/middle/comicbook.tsx b/pages/components/middle/comicbook.tsx index 53f8949..311d751 100644 --- a/pages/components/middle/comicbook.tsx +++ b/pages/components/middle/comicbook.tsx @@ -95,7 +95,7 @@ export default function ComicBook(props: ComicBookProps) { } return ( -
+
- +
) case 'pair': diff --git a/pages/components/ui/dialog.tsx b/pages/components/ui/dialog.tsx index ad6f62f..3c86f94 100644 --- a/pages/components/ui/dialog.tsx +++ b/pages/components/ui/dialog.tsx @@ -31,7 +31,7 @@ const DialogContent = React.forwardRef<