Fix Docker build by splitting client-safe gua utils from fs-based content loader.
Client components were importing zhouyi.ts which pulled in fs/promises; move pure helpers to gua-utils.ts and mark zhouyi.ts server-only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Sparkles } from "lucide-react";
|
import { Sparkles } from "lucide-react";
|
||||||
import { guaNumFromMark } from "@/lib/content/zhouyi";
|
import { guaNumFromMark } from "@/lib/content/gua-utils";
|
||||||
|
|
||||||
export default function GuaFooter({
|
export default function GuaFooter({
|
||||||
guaMark,
|
guaMark,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import type { LearnVariant } from "@/lib/content/zhouyi";
|
import type { LearnVariant } from "@/lib/content/gua-utils";
|
||||||
|
|
||||||
function resolveLearnHref(
|
function resolveLearnHref(
|
||||||
href: string | undefined,
|
href: string | undefined,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { guaNumFromMark } from "@/lib/content/zhouyi";
|
import { guaNumFromMark } from "@/lib/content/gua-utils";
|
||||||
|
|
||||||
export interface ResultObj {
|
export interface ResultObj {
|
||||||
guaTitle: string;
|
guaTitle: string;
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/** 客户端/服务端均可用的卦名工具(不含 fs) */
|
||||||
|
|
||||||
|
export type LearnVariant = "traditional" | "simplified";
|
||||||
|
|
||||||
|
export function guaNumFromMark(guaMark: string): string {
|
||||||
|
return guaMark.split(".")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGuaNumber(guaMark: string): number {
|
||||||
|
return parseInt(guaMark.split(".")[0], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGuaName(guaMark: string): string {
|
||||||
|
return guaMark.split(".").slice(1).join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stripFrontmatter(content: string): string {
|
||||||
|
if (!content.startsWith("---")) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
const end = content.indexOf("---", 3);
|
||||||
|
if (end === -1) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return content.slice(end + 3).trimStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractZhangMingRen(guaDetail: string): string | undefined {
|
||||||
|
return guaDetail
|
||||||
|
.match(/(\*\*台灣張銘仁[\s\S]*?)(?=周易第\d+卦)/)?.[1]
|
||||||
|
?.replaceAll("\n\n", "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractChangeDetails(
|
||||||
|
guaDetail: string,
|
||||||
|
guaChange: string,
|
||||||
|
guaTitle: string,
|
||||||
|
): string[] {
|
||||||
|
const changeList: string[] = [];
|
||||||
|
if (guaChange === "无变爻") {
|
||||||
|
return changeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
guaChange
|
||||||
|
.split(":")[1]
|
||||||
|
.trim()
|
||||||
|
.split(",")
|
||||||
|
.forEach((change) => {
|
||||||
|
const detail = guaDetail
|
||||||
|
.match(`(\\*\\*${change}變卦[\\s\\S]*?)(?=${guaTitle}|$)`)?.[1]
|
||||||
|
?.replaceAll("\n\n", "\n");
|
||||||
|
if (detail) {
|
||||||
|
changeList.push(detail.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return changeList;
|
||||||
|
}
|
||||||
+12
-58
@@ -1,11 +1,21 @@
|
|||||||
|
import "server-only";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import type { LearnVariant } from "@/lib/content/gua-utils";
|
||||||
|
|
||||||
|
export type { LearnVariant } from "@/lib/content/gua-utils";
|
||||||
|
export {
|
||||||
|
extractChangeDetails,
|
||||||
|
extractZhangMingRen,
|
||||||
|
getGuaName,
|
||||||
|
getGuaNumber,
|
||||||
|
guaNumFromMark,
|
||||||
|
stripFrontmatter,
|
||||||
|
} from "@/lib/content/gua-utils";
|
||||||
|
|
||||||
const DOCS_ROOT = path.join(process.cwd(), "content", "zhouyi", "docs");
|
const DOCS_ROOT = path.join(process.cwd(), "content", "zhouyi", "docs");
|
||||||
const OTHER_ROOT = path.join(DOCS_ROOT, "other");
|
const OTHER_ROOT = path.join(DOCS_ROOT, "other");
|
||||||
|
|
||||||
export type LearnVariant = "traditional" | "simplified";
|
|
||||||
|
|
||||||
function getVariantRoot(variant: LearnVariant): string {
|
function getVariantRoot(variant: LearnVariant): string {
|
||||||
return variant === "traditional" ? DOCS_ROOT : OTHER_ROOT;
|
return variant === "traditional" ? DOCS_ROOT : OTHER_ROOT;
|
||||||
}
|
}
|
||||||
@@ -21,11 +31,6 @@ export async function listGuaMarks(
|
|||||||
.sort();
|
.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 序号 slug,如 "35" */
|
|
||||||
export function guaNumFromMark(guaMark: string): string {
|
|
||||||
return guaMark.split(".")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 由序号解析目录名,如 "35" → "35.火地晋" */
|
/** 由序号解析目录名,如 "35" → "35.火地晋" */
|
||||||
export async function markFromNum(
|
export async function markFromNum(
|
||||||
num: string,
|
num: string,
|
||||||
@@ -52,54 +57,3 @@ export async function readLearnMarkdown(
|
|||||||
: path.join(root, guaMark, "index.md");
|
: path.join(root, guaMark, "index.md");
|
||||||
return fs.readFile(filePath, "utf-8");
|
return fs.readFile(filePath, "utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stripFrontmatter(content: string): string {
|
|
||||||
if (!content.startsWith("---")) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
const end = content.indexOf("---", 3);
|
|
||||||
if (end === -1) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
return content.slice(end + 3).trimStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extractZhangMingRen(guaDetail: string): string | undefined {
|
|
||||||
return guaDetail
|
|
||||||
.match(/(\*\*台灣張銘仁[\s\S]*?)(?=周易第\d+卦)/)?.[1]
|
|
||||||
?.replaceAll("\n\n", "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extractChangeDetails(
|
|
||||||
guaDetail: string,
|
|
||||||
guaChange: string,
|
|
||||||
guaTitle: string,
|
|
||||||
): string[] {
|
|
||||||
const changeList: string[] = [];
|
|
||||||
if (guaChange === "无变爻") {
|
|
||||||
return changeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
guaChange
|
|
||||||
.split(":")[1]
|
|
||||||
.trim()
|
|
||||||
.split(",")
|
|
||||||
.forEach((change) => {
|
|
||||||
const detail = guaDetail
|
|
||||||
.match(`(\\*\\*${change}變卦[\\s\\S]*?)(?=${guaTitle}|$)`)?.[1]
|
|
||||||
?.replaceAll("\n\n", "\n");
|
|
||||||
if (detail) {
|
|
||||||
changeList.push(detail.trim());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return changeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuaNumber(guaMark: string): number {
|
|
||||||
return parseInt(guaMark.split(".")[0], 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getGuaName(guaMark: string): string {
|
|
||||||
return guaMark.split(".").slice(1).join(".");
|
|
||||||
}
|
|
||||||
|
|||||||
Generated
+7
@@ -22,6 +22,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-markdown": "^9.0.3",
|
"react-markdown": "^9.0.3",
|
||||||
|
"server-only": "^0.0.1",
|
||||||
"tailwind-merge": "^2.6.0"
|
"tailwind-merge": "^2.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -7369,6 +7370,12 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/server-only": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/server-only/-/server-only-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-markdown": "^9.0.3",
|
"react-markdown": "^9.0.3",
|
||||||
|
"server-only": "^0.0.1",
|
||||||
"tailwind-merge": "^2.6.0"
|
"tailwind-merge": "^2.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user