hdoc-tools 0.57.5 → 0.59.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Hornbill Docs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/hdoc-build-db.js CHANGED
@@ -39,6 +39,16 @@
39
39
  "location_url",
40
40
  "http_code INTEGER UNINDEXED",
41
41
  ],
42
+ // Semantic search embeddings, generated by hdoc-build-embeddings.js.
43
+ // The docs service merges these into its sqlite-vec index at publish
44
+ hdoc_embeddings: [
45
+ "resource_url",
46
+ "chunk_seq INTEGER",
47
+ "chunk_text",
48
+ "embedding BLOB",
49
+ "model",
50
+ "dims INTEGER",
51
+ ],
42
52
  };
43
53
 
44
54
  exports.create_table = (db, table, cols, virtual, fts5) => {
@@ -0,0 +1,202 @@
1
+ (() => {
2
+ const path = require("node:path");
3
+ const fs = require("node:fs");
4
+ const crypto = require("node:crypto");
5
+
6
+ // Model identity - must match what the docs service query embedder uses
7
+ // (see embedder.h in esp-docs-service). The service refuses embeddings
8
+ // built with a different model or dimension count.
9
+ const MODEL_ID = "all-MiniLM-L6-v2";
10
+ const HF_MODEL = "Xenova/all-MiniLM-L6-v2";
11
+ const DIMS = 384;
12
+
13
+ // SHA-256 of onnx/model.onnx as downloaded from Hugging Face. Matches the
14
+ // vendored copy the docs service runs (search_model/model.onnx). Verified
15
+ // after download, before any embeddings are generated - a hash mismatch
16
+ // (tampered/corrupt/re-published model) aborts the embedding step.
17
+ const MODEL_SHA256 = "759c3cd2b7fe7e93933ad23c4c9181b7396442a2ed746ec7c1d46192c469c46e";
18
+
19
+ // Chunking parameters (word based). MiniLM attends to ~256 wordpiece
20
+ // tokens; 180 words plus the title prefix stays comfortably inside that.
21
+ const CHUNK_WORDS = 180;
22
+ const CHUNK_OVERLAP_WORDS = 30;
23
+ const MAX_CHUNKS_PER_DOC = 40;
24
+ const EMBED_BATCH_SIZE = 16;
25
+
26
+ // Maximum characters of chunk text stored for use as a result snippet
27
+ const STORED_CHUNK_CHARS = 500;
28
+
29
+ // Split document plain text into overlapping word-window chunks
30
+ const chunk_text = (text) => {
31
+ const words = text.split(/\s+/).filter((w) => w.length > 0);
32
+ if (words.length === 0) return [];
33
+
34
+ const chunks = [];
35
+ let start = 0;
36
+ while (start < words.length && chunks.length < MAX_CHUNKS_PER_DOC) {
37
+ const end = Math.min(start + CHUNK_WORDS, words.length);
38
+ chunks.push(words.slice(start, end).join(" "));
39
+ if (end >= words.length) break;
40
+ start = end - CHUNK_OVERLAP_WORDS;
41
+ }
42
+ return chunks;
43
+ };
44
+
45
+ // Compute the resource_url for an index record. Must stay in step with
46
+ // the equivalent logic in populate_index (hdoc-build-db.js), as the docs
47
+ // service joins hdoc_embeddings to hdoc_index on this value.
48
+ const resource_url_for = (file) => {
49
+ let index_path_name = file.relative_path.replaceAll("\\", "/");
50
+ if (
51
+ index_path_name.endsWith("/index.md") ||
52
+ index_path_name.endsWith("/index.html") ||
53
+ index_path_name.endsWith("/index.htm")
54
+ ) {
55
+ index_path_name = index_path_name.substring(0, index_path_name.lastIndexOf("/"));
56
+ }
57
+ index_path_name = `/${index_path_name.replace(path.extname(file.relative_path), "")}`;
58
+ return file.index_html.id !== null
59
+ ? `${index_path_name}#${file.index_html.id}`
60
+ : index_path_name;
61
+ };
62
+
63
+ // Generate semantic search embeddings for every indexed document and
64
+ // write them to the hdoc_embeddings table. Non-fatal on failure - a book
65
+ // without embeddings remains fully searchable via keyword search.
66
+ exports.populate_embeddings = async (db, doc_id, index_records, verbose = false) => {
67
+ const response = {
68
+ success: false,
69
+ error: "",
70
+ doc_count: 0,
71
+ chunk_count: 0,
72
+ };
73
+
74
+ let transformers = null;
75
+ try {
76
+ // @huggingface/transformers is ESM-only, load it dynamically
77
+ transformers = await import("@huggingface/transformers");
78
+ } catch (e) {
79
+ response.error = `Could not load @huggingface/transformers: ${e.message}`;
80
+ return response;
81
+ }
82
+
83
+ // Keep the model cache inside the package, like the puppeteer browser
84
+ transformers.env.cacheDir = path.join(__dirname, ".model-cache");
85
+
86
+ console.log(`\nGenerating semantic search embeddings (${MODEL_ID})...`);
87
+
88
+ let extractor = null;
89
+ try {
90
+ // fp32 to match the model the docs service runs at query time
91
+ extractor = await transformers.pipeline("feature-extraction", HF_MODEL, {
92
+ dtype: "fp32",
93
+ });
94
+ } catch (e) {
95
+ response.error = `Could not load embedding model ${HF_MODEL}: ${e.message}`;
96
+ return response;
97
+ }
98
+
99
+ // Verify the downloaded/cached model file against the pinned hash
100
+ // before generating any embeddings
101
+ const model_file = path.join(transformers.env.cacheDir, HF_MODEL, "onnx", "model.onnx");
102
+ try {
103
+ const hash = crypto
104
+ .createHash("sha256")
105
+ .update(fs.readFileSync(model_file))
106
+ .digest("hex");
107
+ if (hash !== MODEL_SHA256) {
108
+ response.error = `Embedding model checksum mismatch for ${model_file}: got ${hash}, expected ${MODEL_SHA256}. Delete the .model-cache folder to re-download, or update MODEL_SHA256 if the pinned model has intentionally changed.`;
109
+ return response;
110
+ }
111
+ } catch (e) {
112
+ response.error = `Could not verify embedding model checksum: ${e.message}`;
113
+ return response;
114
+ }
115
+
116
+ // Build the chunk list across all documents
117
+ const chunks = [];
118
+ for (const file of index_records) {
119
+ if (file.inline) continue;
120
+ if (file.status === "draft") continue;
121
+ if (!file.index_html || !file.index_html.text) continue;
122
+
123
+ const url = resource_url_for(file);
124
+ const title = (file.index_html.fm_props && file.index_html.fm_props.title) || "";
125
+
126
+ const doc_chunks = chunk_text(file.index_html.text);
127
+ if (doc_chunks.length === 0) continue;
128
+
129
+ response.doc_count++;
130
+ for (let seq = 0; seq < doc_chunks.length; seq++) {
131
+ chunks.push({
132
+ url: url,
133
+ seq: seq,
134
+ text: doc_chunks[seq],
135
+ // Title provides context for the embedding but is not
136
+ // duplicated into the stored snippet text
137
+ embed_input: title ? `${title}\n${doc_chunks[seq]}` : doc_chunks[seq],
138
+ });
139
+ }
140
+ }
141
+
142
+ if (chunks.length === 0) {
143
+ response.success = true;
144
+ console.log("No documents to embed.");
145
+ return response;
146
+ }
147
+
148
+ const stmt = db.prepare(
149
+ "INSERT INTO hdoc_embeddings (resource_url, chunk_seq, chunk_text, embedding, model, dims) VALUES (?, ?, ?, ?, ?, ?)",
150
+ );
151
+ const insert_batch = db.transaction((rows) => {
152
+ for (const row of rows) {
153
+ stmt.run(row.url, row.seq, row.text.substring(0, STORED_CHUNK_CHARS), row.embedding, MODEL_ID, DIMS);
154
+ }
155
+ });
156
+
157
+ const start_time = Date.now();
158
+
159
+ try {
160
+ for (let i = 0; i < chunks.length; i += EMBED_BATCH_SIZE) {
161
+ const batch = chunks.slice(i, i + EMBED_BATCH_SIZE);
162
+
163
+ const output = await extractor(
164
+ batch.map((c) => c.embed_input),
165
+ { pooling: "mean", normalize: true },
166
+ );
167
+
168
+ // output is a [batch, DIMS] tensor over one flat Float32Array
169
+ const data = output.data;
170
+ const dims = output.dims[output.dims.length - 1];
171
+ if (dims !== DIMS) {
172
+ response.error = `Embedding model returned ${dims} dimensions, expected ${DIMS}`;
173
+ return response;
174
+ }
175
+
176
+ for (let j = 0; j < batch.length; j++) {
177
+ // Copy the row out of the shared tensor buffer
178
+ const vec = new Float32Array(data.slice(j * DIMS, (j + 1) * DIMS));
179
+ batch[j].embedding = Buffer.from(vec.buffer, 0, DIMS * 4);
180
+ }
181
+
182
+ insert_batch(batch);
183
+ response.chunk_count += batch.length;
184
+
185
+ if (verbose || i % (EMBED_BATCH_SIZE * 10) === 0) {
186
+ process.stdout.write(`\rEmbedding chunks: ${Math.min(i + EMBED_BATCH_SIZE, chunks.length)}/${chunks.length}`);
187
+ }
188
+ }
189
+ } catch (e) {
190
+ response.error = `Embedding generation failed: ${e.message}`;
191
+ return response;
192
+ }
193
+
194
+ const secs = Math.round((Date.now() - start_time) / 1000);
195
+ console.log(
196
+ `\rEmbedding Build Complete: ${response.chunk_count} chunks across ${response.doc_count} documents in ${secs}s.`,
197
+ );
198
+
199
+ response.success = true;
200
+ return response;
201
+ };
202
+ })();
package/hdoc-build.js CHANGED
@@ -8,6 +8,7 @@
8
8
  const hdoc_validate_config = require(path.join(__dirname, "hdoc-validate-config.js"));
9
9
  const hdoc = require(path.join(__dirname, "hdoc-module.js"));
10
10
  const hdoc_build_db = require(path.join(__dirname, "hdoc-build-db.js"));
11
+ const hdoc_build_embeddings = require(path.join(__dirname, "hdoc-build-embeddings.js"));
11
12
  const hdoc_build_onyx = require(path.join(__dirname, "hdoc-build-onyx.js"));
12
13
  const hdoc_build_pdf = require(path.join(__dirname, "hdoc-build-pdf.js"));
13
14
  const hdoc_index = require(path.join(__dirname, "hdoc-db.js"));
@@ -1041,6 +1042,7 @@
1041
1042
  gen_exclude,
1042
1043
  build_version = "",
1043
1044
  output_links = true,
1045
+ build_embeddings = true,
1044
1046
  ) => {
1045
1047
  if (github_api_token !== "") {
1046
1048
  git_token = github_api_token;
@@ -1533,6 +1535,21 @@
1533
1535
  }
1534
1536
  }
1535
1537
 
1538
+ // Generate semantic search embeddings into the index. A failure here is
1539
+ // not fatal - the book simply ships without semantic search coverage
1540
+ if (!validate && build_embeddings) {
1541
+ const embeddings = await hdoc_build_embeddings.populate_embeddings(
1542
+ db.db,
1543
+ doc_id,
1544
+ index_records,
1545
+ verbose,
1546
+ );
1547
+ if (!embeddings.success) {
1548
+ console.error(`\nWARNING: Semantic embeddings not generated: ${embeddings.error}`);
1549
+ console.error("The book will be keyword-searchable only. Use --no-embeddings to silence this warning.");
1550
+ }
1551
+ }
1552
+
1536
1553
  if (!validate) {
1537
1554
  try {
1538
1555
  const zip_path = path.join(work_path, `${doc_id}.zip`);
package/hdoc-bump.js CHANGED
@@ -1,123 +1,123 @@
1
- (() => {
2
- const fs = require("node:fs");
3
- const path = require("node:path");
4
-
5
- exports.run = (source_path, bump_type) => {
6
- if (
7
- bump_type !== "patch" &&
8
- bump_type !== "minor" &&
9
- bump_type !== "major"
10
- ) {
11
- console.error(`Unsupported bump type: ${bump_type}`);
12
- process.exit(1);
13
- }
14
- console.log(`Bumping ${bump_type} book version...\n`);
15
-
16
- // Get document ID
17
- const hdocbook_project_config_path = path.join(
18
- source_path,
19
- "hdocbook-project.json",
20
- );
21
- let hdocbook_project;
22
- try {
23
- hdocbook_project = require(hdocbook_project_config_path);
24
- } catch (e) {
25
- console.error("File not found: hdocbook-project.json:");
26
- console.error(e, "\n");
27
- console.error("hdoc bump needs to be run in the root of a HDoc Book.\n");
28
- process.exit(1);
29
- }
30
- const doc_id = hdocbook_project.docId;
31
-
32
- const book_path = path.join(source_path, doc_id);
33
- const hdocbook_path = path.join(book_path, "hdocbook.json");
34
-
35
- let hdocbook_config;
36
- try {
37
- hdocbook_config = require(hdocbook_path);
38
- } catch (e) {
39
- console.error("File not found: hdocbook.json");
40
- console.error(e, "\n");
41
- console.error("hdoc bump needs to be run in the root of a HDoc Book.\n");
42
- process.exit(1);
43
- }
44
- const initial_version = hdocbook_config.version;
45
- const hdocbook_version = hdocbook_config.version.split(".");
46
- if (hdocbook_version.length !== 3) {
47
- console.error(
48
- `Book version does not appear to be in a semantic versioning format: ${initial_version}`,
49
- );
50
- process.exit(1);
51
- }
52
-
53
- if (Number.isNaN(hdocbook_version[0])) {
54
- console.error(
55
- `Existing major version is not a number: ${hdocbook_version[0]}`,
56
- );
57
- process.exit(1);
58
- }
59
- if (Number.isNaN(hdocbook_version[1])) {
60
- console.error(
61
- `Existing minor version is not a number: ${hdocbook_version[1]}`,
62
- );
63
- process.exit(1);
64
- }
65
- if (Number.isNaN(hdocbook_version[2])) {
66
- console.error(
67
- `Existing patch version is not a number: ${hdocbook_version[2]}`,
68
- );
69
- process.exit(1);
70
- }
71
-
72
- switch (bump_type) {
73
- case "major":
74
- try {
75
- hdocbook_version[0] = Number.parseInt(hdocbook_version[0], 10) + 1;
76
- hdocbook_version[1] = 0;
77
- hdocbook_version[2] = 0;
78
- } catch (e) {
79
- console.error("Failed to update major version:");
80
- console.error(e);
81
- process.exit(1);
82
- }
83
- break;
84
- case "minor":
85
- try {
86
- hdocbook_version[0] = Number.parseInt(hdocbook_version[0], 10);
87
- hdocbook_version[1] = Number.parseInt(hdocbook_version[1], 10) + 1;
88
- hdocbook_version[2] = 0;
89
- } catch (e) {
90
- console.error("Failed to update minor version:");
91
- console.error(e);
92
- process.exit(1);
93
- }
94
- break;
95
- default:
96
- //case "patch" catered for with this default
97
- try {
98
- hdocbook_version[0] = Number.parseInt(hdocbook_version[0], 10);
99
- hdocbook_version[1] = Number.parseInt(hdocbook_version[1], 10);
100
- hdocbook_version[2] = Number.parseInt(hdocbook_version[2], 10) + 1;
101
- } catch (e) {
102
- console.error("Failed to update patch version:");
103
- console.error(e);
104
- process.exit(1);
105
- }
106
- break;
107
- }
108
-
109
- hdocbook_config.version = hdocbook_version.join(".");
110
-
111
- try {
112
- fs.writeFileSync(hdocbook_path, JSON.stringify(hdocbook_config, null, 2));
113
- } catch (e) {
114
- console.error("Error writing bumped version to book config:", e);
115
- process.exit(1);
116
- }
117
-
118
- console.log(
119
- `Book version updated from ${initial_version} to ${hdocbook_config.version}\n`,
120
- );
121
- return true;
122
- };
123
- })();
1
+ (() => {
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+
5
+ exports.run = (source_path, bump_type) => {
6
+ if (
7
+ bump_type !== "patch" &&
8
+ bump_type !== "minor" &&
9
+ bump_type !== "major"
10
+ ) {
11
+ console.error(`Unsupported bump type: ${bump_type}`);
12
+ process.exit(1);
13
+ }
14
+ console.log(`Bumping ${bump_type} book version...\n`);
15
+
16
+ // Get document ID
17
+ const hdocbook_project_config_path = path.join(
18
+ source_path,
19
+ "hdocbook-project.json",
20
+ );
21
+ let hdocbook_project;
22
+ try {
23
+ hdocbook_project = require(hdocbook_project_config_path);
24
+ } catch (e) {
25
+ console.error("File not found: hdocbook-project.json:");
26
+ console.error(e, "\n");
27
+ console.error("hdoc bump needs to be run in the root of a HDoc Book.\n");
28
+ process.exit(1);
29
+ }
30
+ const doc_id = hdocbook_project.docId;
31
+
32
+ const book_path = path.join(source_path, doc_id);
33
+ const hdocbook_path = path.join(book_path, "hdocbook.json");
34
+
35
+ let hdocbook_config;
36
+ try {
37
+ hdocbook_config = require(hdocbook_path);
38
+ } catch (e) {
39
+ console.error("File not found: hdocbook.json");
40
+ console.error(e, "\n");
41
+ console.error("hdoc bump needs to be run in the root of a HDoc Book.\n");
42
+ process.exit(1);
43
+ }
44
+ const initial_version = hdocbook_config.version;
45
+ const hdocbook_version = hdocbook_config.version.split(".");
46
+ if (hdocbook_version.length !== 3) {
47
+ console.error(
48
+ `Book version does not appear to be in a semantic versioning format: ${initial_version}`,
49
+ );
50
+ process.exit(1);
51
+ }
52
+
53
+ if (Number.isNaN(hdocbook_version[0])) {
54
+ console.error(
55
+ `Existing major version is not a number: ${hdocbook_version[0]}`,
56
+ );
57
+ process.exit(1);
58
+ }
59
+ if (Number.isNaN(hdocbook_version[1])) {
60
+ console.error(
61
+ `Existing minor version is not a number: ${hdocbook_version[1]}`,
62
+ );
63
+ process.exit(1);
64
+ }
65
+ if (Number.isNaN(hdocbook_version[2])) {
66
+ console.error(
67
+ `Existing patch version is not a number: ${hdocbook_version[2]}`,
68
+ );
69
+ process.exit(1);
70
+ }
71
+
72
+ switch (bump_type) {
73
+ case "major":
74
+ try {
75
+ hdocbook_version[0] = Number.parseInt(hdocbook_version[0], 10) + 1;
76
+ hdocbook_version[1] = 0;
77
+ hdocbook_version[2] = 0;
78
+ } catch (e) {
79
+ console.error("Failed to update major version:");
80
+ console.error(e);
81
+ process.exit(1);
82
+ }
83
+ break;
84
+ case "minor":
85
+ try {
86
+ hdocbook_version[0] = Number.parseInt(hdocbook_version[0], 10);
87
+ hdocbook_version[1] = Number.parseInt(hdocbook_version[1], 10) + 1;
88
+ hdocbook_version[2] = 0;
89
+ } catch (e) {
90
+ console.error("Failed to update minor version:");
91
+ console.error(e);
92
+ process.exit(1);
93
+ }
94
+ break;
95
+ default:
96
+ //case "patch" catered for with this default
97
+ try {
98
+ hdocbook_version[0] = Number.parseInt(hdocbook_version[0], 10);
99
+ hdocbook_version[1] = Number.parseInt(hdocbook_version[1], 10);
100
+ hdocbook_version[2] = Number.parseInt(hdocbook_version[2], 10) + 1;
101
+ } catch (e) {
102
+ console.error("Failed to update patch version:");
103
+ console.error(e);
104
+ process.exit(1);
105
+ }
106
+ break;
107
+ }
108
+
109
+ hdocbook_config.version = hdocbook_version.join(".");
110
+
111
+ try {
112
+ fs.writeFileSync(hdocbook_path, JSON.stringify(hdocbook_config, null, 2));
113
+ } catch (e) {
114
+ console.error("Error writing bumped version to book config:", e);
115
+ process.exit(1);
116
+ }
117
+
118
+ console.log(
119
+ `Book version updated from ${initial_version} to ${hdocbook_config.version}\n`,
120
+ );
121
+ return true;
122
+ };
123
+ })();
package/hdoc-db.js CHANGED
@@ -94,6 +94,13 @@
94
94
  }
95
95
  }
96
96
 
97
+ // Page chrome - the document-header block (breadcrumb bar, edit link,
98
+ // title, "Article / date / N minutes to read / N contributors") is
99
+ // rendered into every page and would otherwise pollute search index
100
+ // term frequencies and embedding chunks. The title is indexed
101
+ // separately from frontmatter, so nothing of value is lost.
102
+ $(".document-header").remove();
103
+
97
104
  // Full-document plain text for search indexing
98
105
  const text = $("body").text();
99
106
 
package/hdoc-help.js CHANGED
@@ -13,6 +13,7 @@ Commands
13
13
  - Use the '--set-version 1.2.3' argument to set the version number of the built book.
14
14
  - Use the '--no-color' argument to remove any color control characters from the output.
15
15
  - Use the '--no-links' argument to skip link output to CLI during validation.
16
+ - Use the '--no-embeddings' argument to skip semantic search embedding generation.
16
17
 
17
18
  - createDocs
18
19
  Creates folder structure and markdown documents as defined in the HDocBook navigation item links
package/hdoc-ver.js CHANGED
@@ -1,43 +1,43 @@
1
- (() => {
2
- const fs = require("node:fs");
3
- const path = require("node:path");
4
-
5
- exports.run = (source_path) => {
6
- console.log("Retrieving book version...\n");
7
-
8
- // Get document ID
9
- const hdocbook_project_config_path = path.join(
10
- source_path,
11
- "hdocbook-project.json",
12
- );
13
- let hdocbook_project;
14
- try {
15
- hdocbook_project = require(hdocbook_project_config_path);
16
- } catch (e) {
17
- console.error("File not found: hdocbook-project.json:");
18
- console.log(e, "\n");
19
- console.error("hdoc ver needs to be run in the root of a HDoc Book.\n");
20
- process.exit(1);
21
- }
22
- const doc_id = hdocbook_project.docId;
23
-
24
- const book_path = path.join(source_path, doc_id);
25
- const hdocbook_path = path.join(book_path, "hdocbook.json");
26
-
27
- let hdocbook_config;
28
- try {
29
- hdocbook_config = require(hdocbook_path);
30
- } catch (e) {
31
- console.error("File not found: hdocbook.json");
32
- console.log(e, "\n");
33
- console.error("hdoc ver needs to be run in the root of a HDoc Book.\n");
34
- process.exit(1);
35
- }
36
- if (hdocbook_config.version && hdocbook_config.version !== "") {
37
- console.log(`Book version: ${hdocbook_config.version}\n`);
38
- } else {
39
- console.error("Error - this book has no version defined.\n");
40
- process.exit(1);
41
- }
42
- };
43
- })();
1
+ (() => {
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+
5
+ exports.run = (source_path) => {
6
+ console.log("Retrieving book version...\n");
7
+
8
+ // Get document ID
9
+ const hdocbook_project_config_path = path.join(
10
+ source_path,
11
+ "hdocbook-project.json",
12
+ );
13
+ let hdocbook_project;
14
+ try {
15
+ hdocbook_project = require(hdocbook_project_config_path);
16
+ } catch (e) {
17
+ console.error("File not found: hdocbook-project.json:");
18
+ console.log(e, "\n");
19
+ console.error("hdoc ver needs to be run in the root of a HDoc Book.\n");
20
+ process.exit(1);
21
+ }
22
+ const doc_id = hdocbook_project.docId;
23
+
24
+ const book_path = path.join(source_path, doc_id);
25
+ const hdocbook_path = path.join(book_path, "hdocbook.json");
26
+
27
+ let hdocbook_config;
28
+ try {
29
+ hdocbook_config = require(hdocbook_path);
30
+ } catch (e) {
31
+ console.error("File not found: hdocbook.json");
32
+ console.log(e, "\n");
33
+ console.error("hdoc ver needs to be run in the root of a HDoc Book.\n");
34
+ process.exit(1);
35
+ }
36
+ if (hdocbook_config.version && hdocbook_config.version !== "") {
37
+ console.log(`Book version: ${hdocbook_config.version}\n`);
38
+ } else {
39
+ console.error("Error - this book has no version defined.\n");
40
+ process.exit(1);
41
+ }
42
+ };
43
+ })();
package/hdoc.js CHANGED
@@ -163,6 +163,7 @@
163
163
  let bump_type = "patch"; // To generate spellcheck exclusions for all files
164
164
  let output_links = true;
165
165
  let onyx_index = false;
166
+ let build_embeddings = true;
166
167
 
167
168
  // Get options from command args
168
169
  for (let x = 0; x < process.argv.length; x++) {
@@ -212,6 +213,8 @@
212
213
  output_links = false;
213
214
  } else if (process.argv[x].toLowerCase() === "--onyx") {
214
215
  onyx_index = true;
216
+ } else if (process.argv[x].toLowerCase() === "--no-embeddings") {
217
+ build_embeddings = false;
215
218
  } else if (process.argv[x].toLowerCase() === "--quiet") {
216
219
  // handled at startup via _quietMode
217
220
  }
@@ -270,7 +273,7 @@
270
273
  gen_exclude,
271
274
  build_version,
272
275
  output_links,
273
- onyx_index,
276
+ build_embeddings,
274
277
  );
275
278
  } else if (command.toLowerCase() === "createdocs") {
276
279
  const creator = require(path.join(__dirname, "hdoc-create.js"));
@@ -285,7 +288,7 @@
285
288
  gen_exclude,
286
289
  build_version,
287
290
  output_links,
288
- onyx_index,
291
+ build_embeddings,
289
292
  );
290
293
  } else if (command.toLowerCase() === "stats") {
291
294
  const stats = require(path.join(__dirname, "hdoc-stats.js"));