@mirascript/help 0.1.80 → 0.1.82

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": "@mirascript/help",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "author": "CloudPSS",
5
5
  "license": "MIT",
6
6
  "description": "Help documentation for Mirascript core language.",
@@ -28,7 +28,7 @@
28
28
  "js-yaml": "^5.2.3"
29
29
  },
30
30
  "scripts": {
31
- "build": "pnpm clean && node ./scripts/build.js",
31
+ "build": "pnpm clean && node ./scripts/build.ts",
32
32
  "clean": "rimraf dist"
33
33
  }
34
34
  }
@@ -16,67 +16,90 @@ const distRoot = path.join(packageRoot, 'dist');
16
16
  * ---
17
17
  */
18
18
 
19
- /**
20
- * Read a markdown file under `src/`.
21
- * @param {string} relativePath
22
- * @returns {Promise<string>}
23
- */
24
- async function readMarkdown(relativePath) {
19
+ /** Read a markdown file under `src/`. */
20
+ async function readMarkdown(relativePath: string): Promise<string> {
25
21
  const fullPath = path.join(srcRoot, relativePath);
26
22
  return await readFile(fullPath, 'utf8');
27
23
  }
28
24
 
29
25
  const FRONT_MATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/;
30
26
 
31
- /**
32
- * Extract front-matter and strip it from markdown.
33
- * @param {string} markdown
34
- * @param {string} relativePath
35
- * @returns {{ attributes: Record<string, unknown>, body: string }}
36
- */
37
- function splitFrontMatter(markdown, relativePath) {
27
+ /** Front-matter attributes of a markdown doc. */
28
+ interface FrontMatter {
29
+ /** The token(s) mapped to the doc body. */
30
+ token?: unknown;
31
+ /** Fallback token when `token` is missing. */
32
+ title?: unknown;
33
+ /** Whether the token is reserved and must be skipped. */
34
+ reserved?: unknown;
35
+ }
36
+
37
+ /** A doc item before the token is normalized to a string. */
38
+ interface RawDocItem {
39
+ /** The token, possibly an array of tokens. */
40
+ token: string | string[];
41
+ /** The markdown body. */
42
+ body: string;
43
+ /** Whether the token is reserved. */
44
+ reserved: boolean;
45
+ /** The source file relative path. */
46
+ file: string;
47
+ }
48
+
49
+ /** A doc item with a normalized string token. */
50
+ interface DocItem {
51
+ /** The token. */
52
+ token: string;
53
+ /** The markdown body. */
54
+ body: string;
55
+ /** Whether the token is reserved. */
56
+ reserved: boolean;
57
+ /** The source file relative path. */
58
+ file: string;
59
+ }
60
+
61
+ /** Extract front-matter and strip it from markdown. */
62
+ function splitFrontMatter(markdown: string, relativePath: string): { attributes: FrontMatter; body: string } {
38
63
  const m = FRONT_MATTER_RE.exec(markdown);
39
64
  if (!m) {
40
65
  throw new Error(`Missing front-matter in ${relativePath}. Add token/order mapping.`);
41
66
  }
42
67
 
43
- const attributes = load(m[1]);
44
- let body = markdown.slice(m[0].length);
68
+ // The regex guarantees both groups exist when `m` is truthy.
69
+ const yaml = m[1];
70
+ const fullMatch = m[0];
71
+ if (yaml === undefined || fullMatch === undefined) {
72
+ throw new Error(`Missing front-matter in ${relativePath}. Add token/order mapping.`);
73
+ }
74
+ const attributes = load(yaml) as FrontMatter;
75
+ let body = markdown.slice(fullMatch.length);
45
76
  // Trim leading newlines and tailing newlines
46
77
  body = body.replace(/^\r?\n+/, '').replace(/\r?\n+$/, '') + '\n';
47
78
  return { attributes, body };
48
79
  }
49
80
 
50
- /**
51
- * Load docs under a folder like `keyword` or `operator`.
52
- * @param {string} folder
53
- * @returns {Promise<Array<[string, string]>>}
54
- */
55
- async function loadDocsFromFolder(folder) {
81
+ /** Load docs under a folder like `keyword` or `operator`. */
82
+ async function loadDocsFromFolder(folder: string): Promise<Array<[string, string]>> {
56
83
  const dirPath = path.join(srcRoot, folder);
57
84
  const dirents = await readdir(dirPath, { withFileTypes: true });
58
85
 
59
- /** @type {Array<{ token: string; order: number; body: string; file: string }>} */
60
- const items = [];
86
+ const items: DocItem[] = [];
61
87
 
62
- /**
63
- * Check and add an item.
64
- * @param {{ token: string | string[]; body: string; reserved: boolean; file: string }} item
65
- */
66
- function putItem(item) {
88
+ /** Check and add an item. */
89
+ function putItem(item: RawDocItem): void {
67
90
  if (Array.isArray(item.token)) {
68
91
  for (const token of item.token) {
69
92
  items.push({ token, body: item.body, reserved: item.reserved, file: item.file });
70
93
  }
71
94
  return;
72
95
  }
73
- if (typeof item.token != 'string' || !item.token.length) {
96
+ if (!item.token.length) {
74
97
  throw new TypeError(`Invalid front-matter field 'token' in ${item.file}`);
75
98
  }
76
99
  if (item.reserved) {
77
100
  return; // skip reserved tokens
78
101
  }
79
- items.push(item);
102
+ items.push({ token: item.token, body: item.body, reserved: item.reserved, file: item.file });
80
103
  }
81
104
 
82
105
  for (const dirent of dirents) {
@@ -86,13 +109,15 @@ async function loadDocsFromFolder(folder) {
86
109
  const relativePath = path.posix.join(folder, dirent.name);
87
110
  const markdown = await readMarkdown(relativePath);
88
111
  const { attributes, body } = splitFrontMatter(markdown, relativePath);
89
- const { token, title, reserved } = attributes;
90
- putItem({ token: token ?? title, body, reserved: Boolean(reserved), file: relativePath });
112
+ const token = attributes.token ?? attributes.title;
113
+ if (typeof token !== 'string' && !Array.isArray(token)) {
114
+ throw new TypeError(`Invalid front-matter field 'token' in ${relativePath}`);
115
+ }
116
+ putItem({ token, body, reserved: Boolean(attributes.reserved), file: relativePath });
91
117
  }
92
118
 
93
- const seenToken = new Set();
94
- /** @type {Array<[string, string]>} */
95
- const entries = [];
119
+ const seenToken = new Set<string>();
120
+ const entries: Array<[string, string]> = [];
96
121
  for (const item of items) {
97
122
  if (seenToken.has(item.token)) {
98
123
  throw new Error(`Duplicate token '${item.token}' (found in ${item.file})`);
@@ -104,32 +129,21 @@ async function loadDocsFromFolder(folder) {
104
129
  return entries;
105
130
  }
106
131
 
107
- /**
108
- * Render an object literal with string keys and raw markdown values.
109
- * @param {Array<[string, string]>} entries
110
- * @returns {string}
111
- */
112
- function renderObjectLiteral(entries) {
132
+ /** Render an object literal with string keys and raw markdown values. */
133
+ function renderObjectLiteral(entries: Array<[string, string]>): string {
113
134
  const lines = entries.map(([k, v]) => ` ${JSON.stringify(k)}: ${JSON.stringify(v)},`);
114
135
  lines.unshift(' __proto__: null,');
115
136
  return `Object.freeze({\n${lines.join('\n')}\n})`;
116
137
  }
117
138
 
118
- /**
119
- * Render a `.d.ts` object type with explicit string-literal keys.
120
- * @param {Array<[string, string]>} entries
121
- * @returns {string}
122
- */
123
- function renderDtsObjectType(entries) {
139
+ /** Render a `.d.ts` object type with explicit string-literal keys. */
140
+ function renderDtsObjectType(entries: Array<[string, string]>): string {
124
141
  const lines = entries.map(([k]) => ` readonly ${JSON.stringify(k)}: string;`);
125
142
  return `{\n${lines.join('\n')}\n}`;
126
143
  }
127
144
 
128
- /**
129
- * Build `dist/index.js` and `dist/index.d.ts`.
130
- * @returns {Promise<void>}
131
- */
132
- async function main() {
145
+ /** Build `dist/index.js` and `dist/index.d.ts`. */
146
+ async function main(): Promise<void> {
133
147
  const keywordEntries = await loadDocsFromFolder('keyword');
134
148
  const operatorEntries = await loadDocsFromFolder('operator');
135
149
 
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../tsconfig.scripts.json"
3
+ }