refactor: replace webpack context loading with dev server proxy

Remove the webpack-based file indexing strategy in favor of a proxy
approach. The dev server now proxies content requests to a local CLI
server, simplifying the data loading logic and removing webpack-specific
types and loaders.
This commit is contained in:
2026-07-11 09:19:41 +08:00
parent 52563fd3ef
commit c661d2a1d2
3 changed files with 22 additions and 61 deletions
+9 -13
View File
@@ -18,15 +18,6 @@ export default defineConfig({
}, },
}, },
rspack: { rspack: {
module: {
rules: [
{
test: /\.md|\.yarn|\.csv$/,
exclude: /\.schema\.csv$/,
type: "asset/source",
},
],
},
plugins: [ plugins: [
process.env.ANALYZE ? new BundleAnalyzerPlugin() : undefined, process.env.ANALYZE ? new BundleAnalyzerPlugin() : undefined,
].filter(Boolean), ].filter(Boolean),
@@ -40,13 +31,18 @@ export default defineConfig({
index: "./src/main.tsx", index: "./src/main.tsx",
}, },
}, },
server: {
proxy: {
// Proxy content API requests to the CLI dev server (npm run tttk)
"/__CONTENT_INDEX.json": "http://localhost:3000",
"/__COMPLETIONS.json": "http://localhost:3000",
"/content": "http://localhost:3000",
"/journal": "http://localhost:3000",
},
},
output: { output: {
distPath: { distPath: {
root: "dist/web", root: "dist/web",
}, },
copy:
process.env.NODE_ENV === "development"
? [{ from: "./content", to: "content" }]
: [],
}, },
}); });
+13 -33
View File
@@ -1,10 +1,14 @@
/** /**
* 文件索引管理器 * 文件索引管理器
* 支持种文件索引加载方式: * 支持种文件索引加载方式:
* 1. CLI 环境:从 /__CONTENT_INDEX.json 加载 * 1. CLI / 代理环境:从 /__CONTENT_INDEX.json 加载
* 2. Dev 环境:使用 webpackContext 仅加载 .md 文件(仅在 dev 构建中生效) * 2. 浏览器环境:用户选择本地文件夹,扫描文件索引
* 3. 浏览器环境:用户选择本地文件夹,扫描文件索引
* - 支持 IndexedDB 持久化目录句柄,刷新页面无需重新选择 * - 支持 IndexedDB 持久化目录句柄,刷新页面无需重新选择
*
* Dev 工作流:运行 CLI 服务器并在 rsbuild 配置中设置代理
* > npm run tttk serve ./content
* > npm run dev
* rsbuild 会将 /__CONTENT_INDEX.json、/__COMPLETIONS.json、/content/ 代理到 CLI 服务器
*/ */
import { import {
@@ -18,20 +22,20 @@ type FileIndex = Record<string, string>;
let fileIndex: FileIndex | null = null; let fileIndex: FileIndex | null = null;
let indexLoadPromise: Promise<void> | null = null; let indexLoadPromise: Promise<void> | null = null;
let activeSource: "cli" | "webpack" | "folder" | null = null; let activeSource: "cli" | "folder" | null = null;
/** Currently active directory handle (if folder source) */ /** Currently active directory handle (if folder source) */
let activeDirHandle: FileSystemDirectoryHandle | null = null; let activeDirHandle: FileSystemDirectoryHandle | null = null;
/** /**
* 加载文件索引(只加载一次) * 加载文件索引(只加载一次)
* 尝试顺序:CLI JSON → webpack context (dev only) → 已持久化的目录句柄 * 尝试顺序:CLI JSON → 已持久化的目录句柄
*/ */
function ensureIndexLoaded(): Promise<void> { function ensureIndexLoaded(): Promise<void> {
if (indexLoadPromise) return indexLoadPromise; if (indexLoadPromise) return indexLoadPromise;
indexLoadPromise = (async () => { indexLoadPromise = (async () => {
// 策略 1: CLI 环境 — 从 /__CONTENT_INDEX.json 加载 // 策略 1: CLI / 代理环境 — 从 /__CONTENT_INDEX.json 加载
try { try {
const response = await fetch("/__CONTENT_INDEX.json"); const response = await fetch("/__CONTENT_INDEX.json");
if (response.ok) { if (response.ok) {
@@ -44,31 +48,7 @@ function ensureIndexLoaded(): Promise<void> {
// CLI 索引不可用时尝试下一个策略 // CLI 索引不可用时尝试下一个策略
} }
// 策略 2: Dev 环境 — webpackContext + raw loader(仅 dev 构建时生效) // 策略 2: 浏览器 — 尝试从 IndexedDB 恢复保存的目录句柄
// 生产构建时 process.env.NODE_ENV 被替换为 'production',整个分支会被 tree-shake 掉
if (process.env.NODE_ENV === "development") {
try {
const context = import.meta.webpackContext("../../content", {
recursive: true,
regExp: /\.md|\.yarn|\.csv|\.svg$/i,
});
const keys = context.keys();
const index: FileIndex = {};
for (const key of keys) {
const module = context(key) as { default?: string } | string;
const content =
typeof module === "string" ? module : (module.default ?? "");
const normalizedPath = "/content" + key.slice(1);
index[normalizedPath] = content;
}
fileIndex = { ...fileIndex, ...index };
activeSource = "webpack";
} catch (e) {
// webpackContext 不可用时忽略
}
}
// 策略 3: 浏览器 — 尝试从 IndexedDB 恢复保存的目录句柄
if (!activeSource) { if (!activeSource) {
try { try {
const restored = await restoreSavedHandle(); const restored = await restoreSavedHandle();
@@ -176,7 +156,7 @@ export async function loadFromUserFolder(): Promise<string[] | null> {
/** /**
* 移除已保存的目录句柄,并清除索引 * 移除已保存的目录句柄,并清除索引
* 之后确保索引加载时回退到 CLI/webpack 策略 * 之后确保索引加载时回退到 CLI 策略
*/ */
export async function switchToBuiltInSource(): Promise<void> { export async function switchToBuiltInSource(): Promise<void> {
await removeHandle(); await removeHandle();
-15
View File
@@ -35,21 +35,6 @@ interface Window {
): Promise<FileSystemDirectoryHandle>; ): Promise<FileSystemDirectoryHandle>;
} }
interface WebpackContext {
(path: string): { default?: string } | string;
keys(): string[];
}
interface ImportMeta {
webpackContext(
directory: string,
options: {
recursive?: boolean;
regExp?: RegExp;
},
): WebpackContext;
}
declare module "*.md" { declare module "*.md" {
const content: string; const content: string;
export default content; export default content;