From f66940feac87a1935aaf51584382c72b38ac86cd Mon Sep 17 00:00:00 2001 From: S-Riku-tus Date: Fri, 20 Mar 2026 17:55:37 +0900 Subject: [PATCH 01/19] =?UTF-8?q?gitignore=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c9fdd6f0b0..ab2810b1e8 100644 --- a/.gitignore +++ b/.gitignore @@ -220,3 +220,6 @@ $RECYCLE.BIN/ application/upload/** !application/upload/**/ !application/upload/**/.gitkeep + +# ローカル Lighthouse 計測の出力(任意) +scoring-tool/lighthouse*.json From 82c168913e4651a9048da33fab63ca44af02f2f2 Mon Sep 17 00:00:00 2001 From: S-Riku-tus Date: Fri, 20 Mar 2026 18:04:35 +0900 Subject: [PATCH 02/19] 1 --- Dockerfile | 2 +- application/README.md | 6 +- application/client/babel.config.js | 9 +- application/client/package.json | 5 +- application/client/postcss.config.js | 8 +- .../direct_message/DirectMessageListPage.tsx | 4 +- .../direct_message/DirectMessagePage.tsx | 4 +- .../components/foundation/CoveredImage.tsx | 3 + .../new_post_modal/NewPostModalPage.tsx | 42 ++- .../src/components/post/CommentItem.tsx | 6 +- .../client/src/components/post/PostItem.tsx | 6 +- .../src/components/post/TranslatableText.tsx | 5 +- .../src/components/timeline/TimelineItem.tsx | 6 +- .../user_profile/UserProfileHeader.tsx | 6 +- .../client/src/containers/AppContainer.tsx | 133 +++++-- .../src/containers/NewPostModalContainer.tsx | 39 ++- application/client/src/hooks/use_fetch.ts | 29 +- .../client/src/hooks/use_infinite_fetch.ts | 39 ++- application/client/src/index.css | 172 ++++++++- application/client/src/index.html | 168 +-------- application/client/src/utils/date_utils.ts | 36 ++ application/client/src/utils/fetchers.ts | 22 ++ application/client/webpack.config.js | 36 +- application/pnpm-lock.yaml | 325 +++++++++++++++++- application/server/src/index.ts | 26 +- application/server/src/routes/static.ts | 81 ++++- .../calculate_crok_chat_flow_action.ts | 12 +- .../scoring/calculate_dm_chat_flow_action.ts | 14 +- .../src/scoring/calculate_post_flow_action.ts | 16 +- .../calculate_search_post_flow_action.ts | 8 +- .../calculate_user_auth_flow_action.ts | 42 ++- 31 files changed, 969 insertions(+), 341 deletions(-) create mode 100644 application/client/src/utils/date_utils.ts diff --git a/Dockerfile b/Dockerfile index 2c95811428..4c48a1d373 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN --mount=type=cache,target=/pnpm/store pnpm install --frozen-lockfile COPY ./application . -RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm build +RUN NODE_OPTIONS="--max-old-space-size=4096" NODE_ENV=production pnpm build RUN --mount=type=cache,target=/pnpm/store CI=true pnpm install --frozen-lockfile --prod --filter @web-speed-hackathon-2026/server diff --git a/application/README.md b/application/README.md index f03680422d..9758dfa1af 100644 --- a/application/README.md +++ b/application/README.md @@ -14,10 +14,14 @@ CaX のアプリケーションコードです。 ### ビルド・起動 -1. アプリケーションをビルドします +1. アプリケーションをビルドします(本番相当の最適化。Docker ビルドと同じ `NODE_ENV=production` です) - ```bash pnpm run build ``` + - 以前の「非最小化・`mode: none`」に近いビルドが必要な場合のみ(デバッグ用): + - ```bash + pnpm --filter @web-speed-hackathon-2026/client run build:dev + ``` 2. サーバーを起動します - ```bash pnpm run start diff --git a/application/client/babel.config.js b/application/client/babel.config.js index c3c574591a..e2d3b43018 100644 --- a/application/client/babel.config.js +++ b/application/client/babel.config.js @@ -1,19 +1,22 @@ +const isProduction = process.env.NODE_ENV === "production"; + module.exports = { presets: [ ["@babel/preset-typescript"], [ "@babel/preset-env", { - targets: "ie 11", + // 本番はレギュレーションどおり最新 Chrome 想定。modules:false で webpack が import() 分割できるようにする + targets: isProduction ? { chrome: "120" } : "ie 11", corejs: "3", - modules: "commonjs", + modules: isProduction ? false : "commonjs", useBuiltIns: false, }, ], [ "@babel/preset-react", { - development: true, + development: !isProduction, runtime: "automatic", }, ], diff --git a/application/client/package.json b/application/client/package.json index 9f8e80a6a8..2014e72189 100644 --- a/application/client/package.json +++ b/application/client/package.json @@ -5,7 +5,8 @@ "license": "MPL-2.0", "author": "CyberAgent, Inc.", "scripts": { - "build": "NODE_ENV=development webpack", + "build": "NODE_ENV=production webpack", + "build:dev": "NODE_ENV=development webpack", "typecheck": "tsc" }, "dependencies": { @@ -57,6 +58,7 @@ "@babel/preset-env": "7.28.3", "@babel/preset-react": "7.27.1", "@babel/preset-typescript": "7.27.1", + "@tailwindcss/postcss": "4.2.2", "@tsconfig/strictest": "2.0.8", "@types/bluebird": "3.5.42", "@types/common-tags": "1.8.4", @@ -83,6 +85,7 @@ "postcss-loader": "8.2.0", "postcss-preset-env": "10.4.0", "react-markdown": "10.1.0", + "tailwindcss": "4.2.2", "typescript": "5.9.3", "webpack": "5.102.1", "webpack-cli": "6.0.1", diff --git a/application/client/postcss.config.js b/application/client/postcss.config.js index d7ee920b94..21c397a6ff 100644 --- a/application/client/postcss.config.js +++ b/application/client/postcss.config.js @@ -1,11 +1,5 @@ -const postcssImport = require("postcss-import"); -const postcssPresetEnv = require("postcss-preset-env"); - module.exports = { plugins: [ - postcssImport(), - postcssPresetEnv({ - stage: 3, - }), + require("@tailwindcss/postcss")(), ], }; diff --git a/application/client/src/components/direct_message/DirectMessageListPage.tsx b/application/client/src/components/direct_message/DirectMessageListPage.tsx index 5a373e918e..3f63b51217 100644 --- a/application/client/src/components/direct_message/DirectMessageListPage.tsx +++ b/application/client/src/components/direct_message/DirectMessageListPage.tsx @@ -1,4 +1,4 @@ -import moment from "moment"; +import { fromNow } from "@web-speed-hackathon-2026/client/src/utils/date_utils"; import { useCallback, useEffect, useState } from "react"; import { Button } from "@web-speed-hackathon-2026/client/src/components/foundation/Button"; @@ -100,7 +100,7 @@ export const DirectMessageListPage = ({ activeUser, newDmModalId }: Props) => { className="text-cax-text-subtle text-xs" dateTime={lastMessage.createdAt} > - {moment(lastMessage.createdAt).locale("ja").fromNow()} + {fromNow(lastMessage.createdAt)} )} diff --git a/application/client/src/components/direct_message/DirectMessagePage.tsx b/application/client/src/components/direct_message/DirectMessagePage.tsx index 098c7d2894..bdea3a0af4 100644 --- a/application/client/src/components/direct_message/DirectMessagePage.tsx +++ b/application/client/src/components/direct_message/DirectMessagePage.tsx @@ -1,5 +1,5 @@ import classNames from "classnames"; -import moment from "moment"; +import { formatTime } from "@web-speed-hackathon-2026/client/src/utils/date_utils"; import { ChangeEvent, useCallback, @@ -141,7 +141,7 @@ export const DirectMessagePage = ({

{isActiveUserSend && message.isRead && ( 既読 diff --git a/application/client/src/components/foundation/CoveredImage.tsx b/application/client/src/components/foundation/CoveredImage.tsx index 8ad9cc1f7d..59b885d9a7 100644 --- a/application/client/src/components/foundation/CoveredImage.tsx +++ b/application/client/src/components/foundation/CoveredImage.tsx @@ -65,6 +65,9 @@ export const CoveredImage = ({ src }: Props) => { }, )} src={blobUrl} + loading="lazy" + decoding="async" + fetchPriority="low" />

-

diff --git a/application/client/src/components/post/PostItem.tsx b/application/client/src/components/post/PostItem.tsx index 5fa904c91a..e59867989e 100644 --- a/application/client/src/components/post/PostItem.tsx +++ b/application/client/src/components/post/PostItem.tsx @@ -1,4 +1,4 @@ -import moment from "moment"; +import { formatDate, toISOString } from "@web-speed-hackathon-2026/client/src/utils/date_utils"; import { Link } from "@web-speed-hackathon-2026/client/src/components/foundation/Link"; import { ImageArea } from "@web-speed-hackathon-2026/client/src/components/post/ImageArea"; @@ -67,8 +67,8 @@ export const PostItem = ({ post }: Props) => { ) : null}

-

diff --git a/application/client/src/components/post/TranslatableText.tsx b/application/client/src/components/post/TranslatableText.tsx index d772529d92..b099174f0a 100644 --- a/application/client/src/components/post/TranslatableText.tsx +++ b/application/client/src/components/post/TranslatableText.tsx @@ -1,7 +1,5 @@ import { useCallback, useState } from "react"; -import { createTranslator } from "@web-speed-hackathon-2026/client/src/utils/create_translator"; - type State = | { type: "idle"; text: string } | { type: "loading" } @@ -20,6 +18,9 @@ export const TranslatableText = ({ text }: Props) => { (async () => { updateState({ type: "loading" }); try { + const { createTranslator } = await import( + "@web-speed-hackathon-2026/client/src/utils/create_translator" + ); using translator = await createTranslator({ sourceLanguage: "ja", targetLanguage: "en", diff --git a/application/client/src/components/timeline/TimelineItem.tsx b/application/client/src/components/timeline/TimelineItem.tsx index 21b88980f8..8790478668 100644 --- a/application/client/src/components/timeline/TimelineItem.tsx +++ b/application/client/src/components/timeline/TimelineItem.tsx @@ -1,4 +1,4 @@ -import moment from "moment"; +import { formatDate, toISOString } from "@web-speed-hackathon-2026/client/src/utils/date_utils"; import { MouseEventHandler, useCallback } from "react"; import { Link, useNavigate } from "react-router"; @@ -76,8 +76,8 @@ export const TimelineItem = ({ post }: Props) => { - -