czon 0.9.8 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@ const path_1 = __importDefault(require("path"));
16
16
  const findEntries_1 = require("../findEntries");
17
17
  const paths_1 = require("../paths");
18
18
  const isExists_1 = require("../utils/isExists");
19
+ const isFile_1 = require("../utils/isFile");
19
20
  /**
20
21
  * 递归遍历 marked token 树,收集所有真实的 link 和 image token。
21
22
  * 自动跳过 code(代码块)和 codespan(行内代码)中的内容。
@@ -192,7 +193,9 @@ async function checkLinks() {
192
193
  }
193
194
  // 再检查文件系统
194
195
  const resolvedFullPath = path_1.default.join(paths_1.INPUT_DIR, resolvedRelative);
195
- if (!(await (0, isExists_1.isExists)(resolvedFullPath))) {
196
+ const targetExists = await (0, isExists_1.isExists)(resolvedFullPath);
197
+ const targetIsFile = targetExists ? await (0, isFile_1.isFile)(resolvedFullPath) : false;
198
+ if (!targetExists || !targetIsFile) {
196
199
  // 通过 basename 模糊匹配,生成候选建议(最多 3 个)
197
200
  const suggestions = [];
198
201
  const targetBasename = path_1.default.basename(hrefWithoutHash);
@@ -209,7 +212,7 @@ async function checkLinks() {
209
212
  raw: link.raw,
210
213
  href: link.href,
211
214
  type: 'dead-link',
212
- message: '目标文件不存在',
215
+ message: targetExists ? '目标是目录,不是文件' : '目标文件不存在',
213
216
  suggestions,
214
217
  });
215
218
  }
@@ -9,6 +9,8 @@ const path_1 = __importDefault(require("path"));
9
9
  const findEntries_1 = require("../findEntries");
10
10
  const metadata_1 = require("../metadata");
11
11
  const paths_1 = require("../paths");
12
+ const isExists_1 = require("../utils/isExists");
13
+ const isFile_1 = require("../utils/isFile");
12
14
  const extractLinksFromMarkdown = (content) => {
13
15
  const linkRegex = /\[.*?\]\((.*?)\)/g;
14
16
  const links = [];
@@ -43,11 +45,14 @@ async function scanSourceFiles() {
43
45
  if (isVisited.has(fullPath))
44
46
  continue;
45
47
  isVisited.add(fullPath);
46
- const isExists = await (0, promises_1.access)(fullPath).then(() => true, () => false);
47
- if (!isExists) {
48
+ if (!(await (0, isExists_1.isExists)(fullPath))) {
48
49
  console.warn(`⚠️ File does not exist: ${fullPath}, skipping.`);
49
50
  continue;
50
51
  }
52
+ if (!(await (0, isFile_1.isFile)(fullPath))) {
53
+ console.warn(`⚠️ Path is not a file: ${fullPath}, skipping.`);
54
+ continue;
55
+ }
51
56
  const contentBuffer = await (0, promises_1.readFile)(fullPath);
52
57
  paths.add(relativePath);
53
58
  let meta = metadata_1.MetaData.files.find(f => f.path === relativePath);
@@ -65,10 +70,21 @@ async function scanSourceFiles() {
65
70
  for (const link of links) {
66
71
  if (URL.canParse(link))
67
72
  continue;
68
- const resolvedPath = path_1.default.resolve(path_1.default.dirname(fullPath), link);
69
- const relativePath = path_1.default.relative(paths_1.INPUT_DIR, resolvedPath);
70
- if (!isVisited.has(relativePath)) {
71
- queue.push(relativePath);
73
+ const hrefWithoutHash = link.split('#')[0];
74
+ const hrefWithoutQuery = hrefWithoutHash.split('?')[0];
75
+ if (!hrefWithoutQuery)
76
+ continue;
77
+ const resolvedPath = path_1.default.resolve(path_1.default.dirname(fullPath), hrefWithoutQuery);
78
+ if ((await (0, isExists_1.isExists)(resolvedPath)) && !(await (0, isFile_1.isFile)(resolvedPath))) {
79
+ console.warn(`⚠️ Link target is a directory: ${link} in ${relativePath}, skipping.`);
80
+ continue;
81
+ }
82
+ const resolvedRelativePath = path_1.default.relative(paths_1.INPUT_DIR, resolvedPath);
83
+ if (resolvedRelativePath.startsWith('..') || path_1.default.isAbsolute(resolvedRelativePath))
84
+ continue;
85
+ const resolvedFullPath = path_1.default.join(paths_1.INPUT_DIR, resolvedRelativePath);
86
+ if (!isVisited.has(resolvedFullPath)) {
87
+ queue.push(resolvedRelativePath);
72
88
  }
73
89
  }
74
90
  }
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.IndexPage = void 0;
6
+ exports.IndexPage = exports.buildIndexPageTitle = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const sortBy_1 = require("../utils/sortBy");
9
9
  const getCategoryDisplayName_1 = require("./utils/getCategoryDisplayName");
@@ -15,6 +15,14 @@ const LanguageSwitcher_1 = require("./components/LanguageSwitcher");
15
15
  const PageLayout_1 = require("./layouts/PageLayout");
16
16
  const resourceMap_1 = require("./resourceMap");
17
17
  const style_1 = require("./style");
18
+ const buildIndexPageTitle = (params) => {
19
+ const safeSiteTitle = params.siteTitle?.trim() || 'CZON';
20
+ if (params.categoryDisplayName) {
21
+ return `${safeSiteTitle} - ${params.categoryDisplayName}`;
22
+ }
23
+ return `${safeSiteTitle} - Index of ${params.lang}`;
24
+ };
25
+ exports.buildIndexPageTitle = buildIndexPageTitle;
18
26
  const IndexPage = props => {
19
27
  const contents = (0, sortBy_1.toSortedBy)(props.ctx.site.files.filter(f => f.metadata && (!props.category || f.category === props.category)), [
20
28
  // 无日期的排前面
@@ -25,10 +33,19 @@ const IndexPage = props => {
25
33
  const allCategories = Array.from(new Set([undefined].concat(props.ctx.site.files.map(f => f.category))));
26
34
  const faviconUrl = (0, resourceMap_1.getFaviconUrlFrom)(props.ctx.path);
27
35
  const customStyleUrl = props.ctx.hasCustomStyle ? (0, resourceMap_1.getCustomStyleUrlFrom)(props.ctx.path) : null;
36
+ const siteTitle = props.ctx.site.options.site?.title;
37
+ const categoryDisplayName = props.category
38
+ ? (0, getCategoryDisplayName_1.getCategoryDisplayName)(props.ctx.site, props.category, props.lang)
39
+ : undefined;
40
+ const pageTitle = (0, exports.buildIndexPageTitle)({
41
+ siteTitle,
42
+ lang: props.lang,
43
+ categoryDisplayName,
44
+ });
28
45
  return (react_1.default.createElement("html", null,
29
46
  react_1.default.createElement("head", null,
30
47
  react_1.default.createElement("meta", { charSet: "UTF-8" }),
31
- react_1.default.createElement("title", null, `Index of ${props.lang.toString()}`),
48
+ react_1.default.createElement("title", null, pageTitle),
32
49
  react_1.default.createElement("link", { rel: "icon", href: faviconUrl, type: "image/x-icon" }),
33
50
  react_1.default.createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }),
34
51
  react_1.default.createElement("meta", { name: "description", content: `Index page for language ${props.lang}` }),
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const strict_1 = __importDefault(require("node:assert/strict"));
7
+ const node_test_1 = __importDefault(require("node:test"));
8
+ const IndexPage_1 = require("./IndexPage");
9
+ (0, node_test_1.default)('uses site title with language on index page', () => {
10
+ const title = (0, IndexPage_1.buildIndexPageTitle)({
11
+ siteTitle: 'My Docs',
12
+ lang: 'en',
13
+ });
14
+ strict_1.default.equal(title, 'My Docs - Index of en');
15
+ });
16
+ (0, node_test_1.default)('uses category title when category page is rendered', () => {
17
+ const title = (0, IndexPage_1.buildIndexPageTitle)({
18
+ siteTitle: 'My Docs',
19
+ lang: 'en',
20
+ categoryDisplayName: 'Guides',
21
+ });
22
+ strict_1.default.equal(title, 'My Docs - Guides');
23
+ });
24
+ (0, node_test_1.default)('falls back to CZON when site title is missing', () => {
25
+ const title = (0, IndexPage_1.buildIndexPageTitle)({
26
+ siteTitle: '',
27
+ lang: 'zh-Hans',
28
+ });
29
+ strict_1.default.equal(title, 'CZON - Index of zh-Hans');
30
+ });
31
+ //# sourceMappingURL=IndexPage.test.js.map
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFile = void 0;
4
+ const promises_1 = require("fs/promises");
5
+ /**
6
+ * 检查路径是否为文件
7
+ * @param path 要检查的路径
8
+ * @returns Promise<boolean> 路径是否是文件
9
+ */
10
+ const isFile = async (path) => {
11
+ return (0, promises_1.stat)(path)
12
+ .then(stats => stats.isFile())
13
+ .catch(() => false);
14
+ };
15
+ exports.isFile = isFile;
16
+ //# sourceMappingURL=isFile.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "czon",
3
- "version": "0.9.8",
3
+ "version": "0.9.10",
4
4
  "description": "CZON - AI enhanced Markdown content engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",