@jxsuite/parser 2.0.1 → 4.0.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/parser",
3
- "version": "2.0.1",
3
+ "version": "4.0.0",
4
4
  "description": "Jx markdown parser and external class integration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,8 +48,8 @@
48
48
  "upgrade": "bun update --latest && bun install"
49
49
  },
50
50
  "dependencies": {
51
- "@jxsuite/markup": "^0.4.12",
52
- "@jxsuite/schema": "^3.0.0",
51
+ "@jxsuite/markup": "^1.0.0",
52
+ "@jxsuite/schema": "^4.0.0",
53
53
  "@shikijs/langs": "^4.5.0",
54
54
  "@shikijs/themes": "^4.5.0",
55
55
  "glob": "^13.0.6",
@@ -66,8 +66,9 @@
66
66
  "yaml": "^2.9.1"
67
67
  },
68
68
  "devDependencies": {
69
- "@happy-dom/global-registrator": "^20.14.6",
70
- "@jxsuite/runtime": "^4.1.0",
69
+ "@happy-dom/global-registrator": "^21.0.3",
70
+ "@jxsuite/runtime": "^5.0.0",
71
+ "@jxsuite/test-support": "^0.0.0",
71
72
  "@types/mdast": "^4.0.4",
72
73
  "ajv": "^8.20.0"
73
74
  },
@@ -18,9 +18,11 @@
18
18
  "settings": {
19
19
  "icon": "grid-four",
20
20
  "label": "Content Types",
21
+ "intro": "Each content type is a collection, such as blog posts, team members or events: a folder of entries that share the fields you define here.",
21
22
  "order": 50,
22
23
  "layout": "map",
23
24
  "entry": {
25
+ "noun": "content type",
24
26
  "ui": { "schema": { "control": "schema-builder" } },
25
27
  "newEntry": {
26
28
  "source": "./content/${key}/",
package/src/alerts.ts CHANGED
@@ -21,10 +21,25 @@
21
21
  * @docs framework/site/jx-markdown
22
22
  */
23
23
 
24
+ import { isJsonObject, isString } from "@jxsuite/schema/guards";
25
+ import type { JsonValue } from "@jxsuite/schema/types";
26
+
24
27
  import type { MdastNode } from "./types.ts";
25
28
 
29
+ /**
30
+ * Default titles by lower-case alert type: GitHub's five are always present, and the renderer looks
31
+ * up whatever type an author wrote, so a type a project switched on itself reads as absent.
32
+ */
33
+ export interface AlertTitles extends Readonly<Record<string, string>> {
34
+ readonly caution: string;
35
+ readonly important: string;
36
+ readonly note: string;
37
+ readonly tip: string;
38
+ readonly warning: string;
39
+ }
40
+
26
41
  /** The five alert types GitHub defines, and the title each shows by default. */
27
- export const DEFAULT_ALERT_TITLES: Readonly<Record<string, string>> = {
42
+ export const DEFAULT_ALERT_TITLES: AlertTitles = {
28
43
  caution: "Caution",
29
44
  important: "Important",
30
45
  note: "Note",
@@ -60,7 +75,7 @@ const TAG_NAME = /^[A-Za-z][\w.-]*$/;
60
75
  * Throws on a value that is not a tag name so a typo in `project.json` fails the build where it was
61
76
  * written rather than rendering a callout as an unknown element.
62
77
  */
63
- export function normalizeAlerts(config?: unknown, where = "alerts"): AlertSettings | null {
78
+ export function normalizeAlerts(config?: JsonValue, where = "alerts"): AlertSettings | null {
64
79
  if (config === false) {
65
80
  return null;
66
81
  }
@@ -71,7 +86,7 @@ export function normalizeAlerts(config?: unknown, where = "alerts"): AlertSettin
71
86
  if (config === undefined || config === true) {
72
87
  return { disabled, types };
73
88
  }
74
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
89
+ if (!isJsonObject(config)) {
75
90
  throw new TypeError(
76
91
  `${where}: expected an object mapping alert types to element names, or false`,
77
92
  );
@@ -86,7 +101,7 @@ export function normalizeAlerts(config?: unknown, where = "alerts"): AlertSettin
86
101
  disabled.add(key);
87
102
  } else if (element === true) {
88
103
  types.set(key, null);
89
- } else if (typeof element === "string" && TAG_NAME.test(element)) {
104
+ } else if (isString(element) && TAG_NAME.test(element)) {
90
105
  types.set(key, element);
91
106
  } else {
92
107
  throw new TypeError(
@@ -114,7 +129,7 @@ const MARKER = /^\[!([A-Za-z][\w-]*)\][+-]?/;
114
129
  export function inlineText(nodes: readonly MdastNode[]): string {
115
130
  let out = "";
116
131
  for (const node of nodes) {
117
- if (typeof node.value === "string") {
132
+ if (node.value !== undefined) {
118
133
  out += node.value;
119
134
  } else if (node.children) {
120
135
  out += inlineText(node.children);
@@ -139,7 +154,7 @@ const LINE_END = /\r?\n/;
139
154
  */
140
155
  function markerOf(paragraph: MdastNode): { type: string; rest: string } | null {
141
156
  const [first] = paragraph.children ?? [];
142
- if (first?.type !== "text" || typeof first.value !== "string") {
157
+ if (first?.type !== "text" || first.value === undefined) {
143
158
  return null;
144
159
  }
145
160
  const match = MARKER.exec(first.value);
@@ -178,7 +193,7 @@ function splitMarker(
178
193
  const take = (node: MdastNode) => {
179
194
  if (inTitle && node.type === "break") {
180
195
  inTitle = false;
181
- } else if (inTitle && node.type === "text" && typeof node.value === "string") {
196
+ } else if (inTitle && node.type === "text" && node.value !== undefined) {
182
197
  const lineEnd = LINE_END.exec(node.value);
183
198
  if (lineEnd === null) {
184
199
  title.push(node);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Content-data — how a reader gets entries out of the `content` section's loaded data.
3
+ *
4
+ * The section's data is a map of type name to entries, which the content loader's `ContentStore`
5
+ * may fill on demand (specs/extensions.md §8). This module is the reading side alone, with no Node
6
+ * imports, because `ContentCollection` and `ContentEntry` (content.ts) are bundled on their own and
7
+ * must not pull the loader in with them.
8
+ *
9
+ * @module @jxsuite/parser/content-data
10
+ * @docs extending/extensions/project-sections
11
+ */
12
+
13
+ import type { ContentLoaderEntry } from "./types.ts";
14
+
15
+ /**
16
+ * The `content` section's loaded data as a reader sees it: a map of type name to entries. `load`
17
+ * and `materialize` are optional so a plain map (one a host or a test built) reads the same way.
18
+ */
19
+ export interface ContentSectionData extends ReadonlyMap<string, ContentLoaderEntry[]> {
20
+ /** One type's entries, loading the type first if it has not been. */
21
+ load?: (name: string) => Promise<ContentLoaderEntry[] | undefined>;
22
+ /** Give these entries (and the entries they relate to) their bodies before they are returned. */
23
+ materialize?: (entries: readonly (ContentLoaderEntry | null | undefined)[]) => Promise<void>;
24
+ /** Load every type and every body, so a synchronous `get` sees the whole section. */
25
+ complete?: () => Promise<void>;
26
+ }
27
+
28
+ /**
29
+ * One type's entries from a section's data, loading the type first when the data loads lazily.
30
+ *
31
+ * @param {ContentSectionData | undefined} data - The section's loaded data
32
+ * @param {string} name - Content type name
33
+ * @returns {Promise<ContentLoaderEntry[] | undefined>} The entries, or undefined for no such type
34
+ */
35
+ export async function entriesOf(
36
+ data: ContentSectionData | undefined,
37
+ name: string,
38
+ ): Promise<ContentLoaderEntry[] | undefined> {
39
+ return (await data?.load?.(name)) ?? data?.get(name);
40
+ }
@@ -30,6 +30,7 @@
30
30
  * @docs framework/site/content-collections
31
31
  */
32
32
 
33
+ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
33
34
  import type { JxElement } from "@jxsuite/schema/types";
34
35
  import { routeHref } from "./content-routes.ts";
35
36
  import type { ContentNodes } from "./inert.ts";
@@ -102,8 +103,15 @@ function fragmentFor(raw: string): string {
102
103
  }
103
104
  }
104
105
 
106
+ /** The three parts of an href; `query` and `hash` keep their delimiter and are `""` when absent. */
107
+ interface HrefParts {
108
+ path: string;
109
+ query: string;
110
+ hash: string;
111
+ }
112
+
105
113
  /** Split `path?query#fragment` into its three parts (query and fragment keep their delimiter). */
106
- function splitHref(href: string): { path: string; query: string; hash: string } {
114
+ function splitHref(href: string): HrefParts {
107
115
  const hashAt = href.indexOf("#");
108
116
  const beforeHash = hashAt === -1 ? href : href.slice(0, hashAt);
109
117
  const hash = hashAt === -1 ? "" : href.slice(hashAt);
@@ -288,13 +296,13 @@ function resolveHref(href: string, from: string, index: LinkIndex): Resolution {
288
296
  /** What a removed anchor leaves behind: its own content, as inline nodes. */
289
297
  function contentOf(anchor: JxElement): ContentNodes {
290
298
  if (Array.isArray(anchor.children)) {
291
- return anchor.children as ContentNodes;
299
+ return anchor.children;
292
300
  }
293
- if (typeof anchor.innerHTML === "string") {
301
+ if (anchor.innerHTML !== undefined) {
294
302
  // Text that holds `${` was made inert as escaped `innerHTML` (inert.ts); it keeps that form.
295
303
  return [{ innerHTML: anchor.innerHTML, tagName: "span" }];
296
304
  }
297
- return typeof anchor.textContent === "string" ? [anchor.textContent] : [];
305
+ return isString(anchor.textContent) ? [anchor.textContent] : [];
298
306
  }
299
307
 
300
308
  /**
@@ -318,11 +326,11 @@ export function rewriteLinks(
318
326
  rewriteLinks(node, from, index, report);
319
327
  continue;
320
328
  }
321
- if (typeof node !== "object" || node === null) {
329
+ if (!isNonNullObject(node)) {
322
330
  continue;
323
331
  }
324
332
  const href = node.tagName === "a" ? node.attributes?.href : undefined;
325
- if (typeof href === "string") {
333
+ if (isString(href)) {
326
334
  const resolution = resolveHref(href, from, index);
327
335
  if ("problem" in resolution) {
328
336
  report({ href, reason: resolution.problem });
@@ -337,7 +345,7 @@ export function rewriteLinks(
337
345
  }
338
346
  }
339
347
  if (Array.isArray(node.children)) {
340
- rewriteLinks(node.children as ContentNodes, from, index, report);
348
+ rewriteLinks(node.children, from, index, report);
341
349
  }
342
350
  }
343
351
  }