@stati/core 1.24.3 → 1.24.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/search/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAuBtE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA0CtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,QAAQ,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,MAAM,EAAE,YAAY,GACnB,cAAc,EAAE,CAiGlB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAsBhF;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,oEAAoE;IACpE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,kDAAkD;IAClD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,cAAc,EAAE,EACjC,MAAM,EAAE,YAAY,GACnB,WAAW,CA6Bb;AAiBD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAqC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAczF"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/search/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,WAAW,EACX,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3D;AAqBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA2BtE;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8DtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,QAAQ,EAAE,EACf,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACnC,MAAM,EAAE,YAAY,GACnB,cAAc,EAAE,CAiGlB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAsBhF;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,oEAAoE;IACpE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,kDAAkD;IAClD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,cAAc,EAAE,EACjC,MAAM,EAAE,YAAY,GACnB,WAAW,CA6Bb;AAiBD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,UAAQ,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAqC9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAczF"}
@@ -17,6 +17,24 @@ import { SEARCH_INDEX_VERSION, SEARCH_DEFAULTS } from './constants.js';
17
17
  export function generateContentHash(content) {
18
18
  return createHash('md5').update(content).digest('hex').substring(0, 8);
19
19
  }
20
+ /**
21
+ * URL segments that read as acronyms and stay uppercase in breadcrumbs.
22
+ */
23
+ const BREADCRUMB_ACRONYMS = new Set([
24
+ 'ai',
25
+ 'api',
26
+ 'cli',
27
+ 'css',
28
+ 'faq',
29
+ 'html',
30
+ 'isg',
31
+ 'json',
32
+ 'rss',
33
+ 'seo',
34
+ 'ui',
35
+ 'url',
36
+ 'xml',
37
+ ]);
20
38
  /**
21
39
  * Builds a breadcrumb string from a URL path.
22
40
  *
@@ -32,15 +50,19 @@ export function buildBreadcrumb(url, pageTitle) {
32
50
  if (segments.length === 0) {
33
51
  return pageTitle;
34
52
  }
35
- // Convert slug segments to title case
53
+ // Convert slug segments to title case, keeping known acronyms uppercase
36
54
  const crumbs = segments.slice(0, -1).map((segment) => segment
37
55
  .split('-')
38
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
56
+ .map((word) => BREADCRUMB_ACRONYMS.has(word.toLowerCase())
57
+ ? word.toUpperCase()
58
+ : word.charAt(0).toUpperCase() + word.slice(1))
39
59
  .join(' '));
40
60
  // Add the page title as the last crumb
41
61
  crumbs.push(pageTitle);
42
62
  return crumbs.join(' > ');
43
63
  }
64
+ /** Private-use character that brackets inline code placeholders in stripMarkdown. */
65
+ const CODE_MARK = '';
44
66
  /**
45
67
  * Strips Markdown syntax to extract plain text for search indexing.
46
68
  * Handles code blocks, links, images, emphasis, etc.
@@ -50,9 +72,21 @@ export function buildBreadcrumb(url, pageTitle) {
50
72
  */
51
73
  export function stripMarkdown(markdown) {
52
74
  let text = markdown;
53
- // Remove code blocks (fenced and indented)
54
- text = text.replace(/```[\s\S]*?```/g, ' ');
55
- text = text.replace(/`[^`]+`/g, ' ');
75
+ // Remove fenced code blocks. Fences sit at the start of a line, so inline
76
+ // spans written with three or more backticks are left for the step below.
77
+ text = text.replace(/^[ \t>]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t>]*\1[`~]*[ \t]*$/gm, ' ');
78
+ // Keep inline code text (identifiers are what people search for), shielded
79
+ // from the emphasis and HTML stripping below and restored at the end.
80
+ // A span closes on a backtick run of the same length as its opener, so
81
+ // ``a `b` c`` is one span; one padding space on each side is dropped.
82
+ const inlineCode = [];
83
+ text = text.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g, (_match, _fence, code) => {
84
+ inlineCode.push(/^ [\s\S]* $/.test(code) && code.trim() ? code.slice(1, -1) : code);
85
+ return `${CODE_MARK}${inlineCode.length - 1}${CODE_MARK}`;
86
+ });
87
+ // Remove table delimiter rows and cell pipes
88
+ text = text.replace(/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/gm, '');
89
+ text = text.replace(/\s*\|\s*/g, ' ');
56
90
  // Remove images ![alt](url)
57
91
  text = text.replace(/!\[[^\]]*\]\([^)]*\)/g, ' ');
58
92
  // Convert links [text](url) to just text
@@ -74,6 +108,8 @@ export function stripMarkdown(markdown) {
74
108
  // Remove list markers
75
109
  text = text.replace(/^[\s]*[-*+]\s+/gm, '');
76
110
  text = text.replace(/^[\s]*\d+\.\s+/gm, '');
111
+ // Restore inline code
112
+ text = text.replace(new RegExp(`${CODE_MARK}(\\d+)${CODE_MARK}`, 'g'), (_match, index) => inlineCode[Number(index)] ?? '');
77
113
  // Normalize whitespace
78
114
  text = text.replace(/\s+/g, ' ').trim();
79
115
  return text;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stati/core",
3
- "version": "1.24.3",
3
+ "version": "1.24.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -38,15 +38,15 @@
38
38
  "test": "vitest run"
39
39
  },
40
40
  "dependencies": {
41
- "chokidar": "^3.6.0",
42
- "esbuild": "^0.28.1",
41
+ "chokidar": "^5.0.0",
42
+ "esbuild": "^0.28.2",
43
43
  "eta": "^3.5.0",
44
44
  "fast-glob": "^3.3.3",
45
- "fs-extra": "^11.2.0",
45
+ "fs-extra": "^11.4.1",
46
46
  "gray-matter": "^4.0.3",
47
- "markdown-it": "^14.1.0",
48
- "open": "^10.1.0",
49
- "ws": "^8.18.0"
47
+ "markdown-it": "^14.3.2",
48
+ "open": "^11.0.4",
49
+ "ws": "^8.22.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/fs-extra": "^11.0.4",