hdoc-tools 0.60.1 → 0.62.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.
Files changed (55) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +89 -75
  3. package/hdoc-build-db.js +275 -275
  4. package/hdoc-build-embeddings.js +202 -202
  5. package/hdoc-build-pdf.js +232 -232
  6. package/hdoc-build.js +14 -6
  7. package/hdoc-bump.js +4 -2
  8. package/hdoc-content-routes.js +143 -83
  9. package/hdoc-create.js +110 -108
  10. package/hdoc-db.js +114 -114
  11. package/hdoc-help.js +60 -60
  12. package/hdoc-init.js +103 -68
  13. package/hdoc-install-browser.js +145 -145
  14. package/hdoc-mermaid.js +204 -204
  15. package/hdoc-module.js +1102 -1079
  16. package/hdoc-serve.js +13 -7
  17. package/hdoc-stats.js +9 -9
  18. package/hdoc-validate-config.js +355 -329
  19. package/hdoc-validate-interbook.js +321 -0
  20. package/hdoc-validate.js +1231 -1158
  21. package/hdoc-ver.js +4 -2
  22. package/hdoc.js +12 -11
  23. package/npm-shrinkwrap.json +2 -2
  24. package/package.json +13 -2
  25. package/schemas/hdocbook-project.schema.json +20 -0
  26. package/schemas/hdocbook.schema.json +6 -2
  27. package/templates/doc-header-non-git.html +19 -19
  28. package/templates/doc-header.html +26 -26
  29. package/templates/init/.github/workflows/hdocbuild_onpull.yml +16 -16
  30. package/templates/init/.github/workflows/hdocbuild_onpush.yml +15 -15
  31. package/templates/init/LICENSE +21 -21
  32. package/templates/init/README.md +9 -9
  33. package/templates/init/_hdocbook/index.md +4 -4
  34. package/templates/init/gitignore +8 -8
  35. package/templates/init/resources/README.md +2 -2
  36. package/templates/pdf/css/custom-block.css +90 -90
  37. package/templates/pdf/css/fonts.css +221 -221
  38. package/templates/pdf/css/hdocs-pdf.css +495 -495
  39. package/templates/pdf/css/vars.css +404 -404
  40. package/templates/pdf/template-footer.html +19 -19
  41. package/templates/pdf/template-header.html +37 -37
  42. package/templates/pdf/template.html +20 -20
  43. package/templates/pdf-header-non-git.html +12 -12
  44. package/templates/pdf-header.html +16 -16
  45. package/ui/content/invalid-hdocbook-json.html +6 -6
  46. package/ui/content/invalid-hdocbook-json.md +7 -7
  47. package/ui/css/theme-default/styles/components/content.css +124 -124
  48. package/ui/css/theme-default/styles/components/sidebar.css +182 -182
  49. package/ui/css/theme-default/styles/htldoc.layouts.css +310 -310
  50. package/ui/index.html +419 -419
  51. package/ui/js/doc.hornbill.js +31 -44
  52. package/ui/js/mermaid-theme.json +27 -0
  53. package/hdoc-build-onyx.js +0 -134
  54. package/templates/mermaid-theme.yaml +0 -28
  55. package/templates/pdf/fonts/inter-cyrillic copy.woff2 +0 -0
@@ -1,145 +1,145 @@
1
- // Resilient browser provisioning for hdoc-tools.
2
- //
3
- // Replaces the old `puppeteer browsers install ...` one-liner postinstall.
4
- // Puppeteer's own bundled download (install.mjs) is disabled via
5
- // .puppeteerrc.cjs (skipDownload), so this script is the single, controlled
6
- // place Chrome + chrome-headless-shell are fetched.
7
- //
8
- // Why: on some Windows Server 2019 build agents the Chrome archive extracts
9
- // only partially (Defender quarantining binaries mid-extract, or a truncated
10
- // download behind a proxy). The first failure leaves the version folder on
11
- // disk, after which Puppeteer refuses to re-extract and every later install
12
- // reports "folder exists but executable missing" — a permanent dead end.
13
- //
14
- // This script makes provisioning idempotent and self-healing:
15
- // * skip when a valid executable already exists,
16
- // * delete any stale/partial version folder before (re)installing,
17
- // * retry the download a few times,
18
- // * verify the executable exists afterwards and fail loudly with concrete
19
- // remediation if it still does not.
20
-
21
- (async () => {
22
- const fs = require("node:fs");
23
- const path = require("node:path");
24
- const {
25
- install,
26
- computeExecutablePath,
27
- detectBrowserPlatform,
28
- Browser,
29
- } = require("@puppeteer/browsers");
30
-
31
- // Build id + cache dir come from the shared .puppeteerrc.cjs so install and
32
- // runtime launch can never drift apart.
33
- const puppeteerConfig = require(path.join(__dirname, ".puppeteerrc.cjs"));
34
- const CHROME_BUILD = puppeteerConfig.chromeBuild;
35
- const MAX_ATTEMPTS = 3;
36
-
37
- const RED = "\x1b[31m";
38
- const YELLOW = "\x1b[33m";
39
- const GREEN = "\x1b[32m";
40
- const RESET = "\x1b[0m";
41
-
42
- const log = (msg) => console.log(`[hdoc-tools] ${msg}`);
43
-
44
- // Resolve the cache directory the same way Puppeteer does at runtime:
45
- // PUPPETEER_CACHE_DIR wins, otherwise the fixed cacheDir declared in
46
- // .puppeteerrc.cjs. Reading the shared config (rather than recomputing from
47
- // os.homedir()) guarantees we install to exactly the path the browser is
48
- // later launched from — critical for `sudo npm i -g`, where postinstall runs
49
- // as root but `hdoc` runs as a normal user with a different home directory.
50
- const cacheDir = process.env.PUPPETEER_CACHE_DIR || puppeteerConfig.cacheDir;
51
-
52
- let platform;
53
- try {
54
- platform = detectBrowserPlatform();
55
- } catch (err) {
56
- console.error(
57
- `${RED}Unable to detect browser platform: ${err.message}${RESET}`,
58
- );
59
- process.exit(1);
60
- }
61
-
62
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
63
-
64
- const rmrf = (target) => {
65
- try {
66
- fs.rmSync(target, { recursive: true, force: true });
67
- } catch {
68
- /* best effort */
69
- }
70
- };
71
-
72
- // A usable install is one where the executable exists and is non-empty.
73
- const isUsable = (exePath) => {
74
- try {
75
- return fs.statSync(exePath).size > 0;
76
- } catch {
77
- return false;
78
- }
79
- };
80
-
81
- const provision = async (browser, label) => {
82
- const exePath = computeExecutablePath({
83
- browser,
84
- buildId: CHROME_BUILD,
85
- cacheDir,
86
- platform,
87
- });
88
- // chrome.exe -> chrome-win64 -> win64-<build>; nuke the whole build folder
89
- // so a partial extraction can never block a clean re-extract.
90
- const versionFolder = path.dirname(path.dirname(exePath));
91
-
92
- if (isUsable(exePath)) {
93
- log(`${label} ${CHROME_BUILD} already present, skipping download.`);
94
- return true;
95
- }
96
-
97
- for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
98
- rmrf(versionFolder); // clear any stale/partial extract first
99
- try {
100
- log(
101
- `Installing ${label} ${CHROME_BUILD} (attempt ${attempt}/${MAX_ATTEMPTS})...`,
102
- );
103
- await install({
104
- browser,
105
- buildId: CHROME_BUILD,
106
- cacheDir,
107
- platform,
108
- unpack: true,
109
- });
110
- } catch (err) {
111
- console.error(
112
- `${YELLOW} attempt ${attempt} failed: ${err.message}${RESET}`,
113
- );
114
- }
115
-
116
- if (isUsable(exePath)) {
117
- log(`${GREEN}${label} ${CHROME_BUILD} ready.${RESET}`);
118
- return true;
119
- }
120
-
121
- if (attempt < MAX_ATTEMPTS) await sleep(2000);
122
- }
123
-
124
- return false;
125
- };
126
-
127
- const chromeOk = await provision(Browser.CHROME, "Chrome");
128
- const shellOk = await provision(
129
- Browser.CHROMEHEADLESSSHELL,
130
- "chrome-headless-shell",
131
- );
132
-
133
- if (chromeOk && shellOk) process.exit(0);
134
-
135
- console.error(
136
- `\n${RED}Failed to provision a complete browser into:${RESET}\n ${cacheDir}\n\n` +
137
- "This is almost always one of:\n" +
138
- " 1. Antivirus (e.g. Windows Defender) quarantining Chrome files mid-extract.\n" +
139
- ` Fix: Add-MpPreference -ExclusionPath "${cacheDir}"\n` +
140
- " 2. A stale/partial cache folder. Fix: delete the chrome / chrome-headless-shell\n" +
141
- ` sub-folders under "${cacheDir}" and reinstall.\n` +
142
- " 3. A truncated download behind a proxy / TLS inspection. Check npm/HTTPS proxy config.\n",
143
- );
144
- process.exit(1);
145
- })();
1
+ // Resilient browser provisioning for hdoc-tools.
2
+ //
3
+ // Replaces the old `puppeteer browsers install ...` one-liner postinstall.
4
+ // Puppeteer's own bundled download (install.mjs) is disabled via
5
+ // .puppeteerrc.cjs (skipDownload), so this script is the single, controlled
6
+ // place Chrome + chrome-headless-shell are fetched.
7
+ //
8
+ // Why: on some Windows Server 2019 build agents the Chrome archive extracts
9
+ // only partially (Defender quarantining binaries mid-extract, or a truncated
10
+ // download behind a proxy). The first failure leaves the version folder on
11
+ // disk, after which Puppeteer refuses to re-extract and every later install
12
+ // reports "folder exists but executable missing" — a permanent dead end.
13
+ //
14
+ // This script makes provisioning idempotent and self-healing:
15
+ // * skip when a valid executable already exists,
16
+ // * delete any stale/partial version folder before (re)installing,
17
+ // * retry the download a few times,
18
+ // * verify the executable exists afterwards and fail loudly with concrete
19
+ // remediation if it still does not.
20
+
21
+ (async () => {
22
+ const fs = require("node:fs");
23
+ const path = require("node:path");
24
+ const {
25
+ install,
26
+ computeExecutablePath,
27
+ detectBrowserPlatform,
28
+ Browser,
29
+ } = require("@puppeteer/browsers");
30
+
31
+ // Build id + cache dir come from the shared .puppeteerrc.cjs so install and
32
+ // runtime launch can never drift apart.
33
+ const puppeteerConfig = require(path.join(__dirname, ".puppeteerrc.cjs"));
34
+ const CHROME_BUILD = puppeteerConfig.chromeBuild;
35
+ const MAX_ATTEMPTS = 3;
36
+
37
+ const RED = "\x1b[31m";
38
+ const YELLOW = "\x1b[33m";
39
+ const GREEN = "\x1b[32m";
40
+ const RESET = "\x1b[0m";
41
+
42
+ const log = (msg) => console.log(`[hdoc-tools] ${msg}`);
43
+
44
+ // Resolve the cache directory the same way Puppeteer does at runtime:
45
+ // PUPPETEER_CACHE_DIR wins, otherwise the fixed cacheDir declared in
46
+ // .puppeteerrc.cjs. Reading the shared config (rather than recomputing from
47
+ // os.homedir()) guarantees we install to exactly the path the browser is
48
+ // later launched from — critical for `sudo npm i -g`, where postinstall runs
49
+ // as root but `hdoc` runs as a normal user with a different home directory.
50
+ const cacheDir = process.env.PUPPETEER_CACHE_DIR || puppeteerConfig.cacheDir;
51
+
52
+ let platform;
53
+ try {
54
+ platform = detectBrowserPlatform();
55
+ } catch (err) {
56
+ console.error(
57
+ `${RED}Unable to detect browser platform: ${err.message}${RESET}`,
58
+ );
59
+ process.exit(1);
60
+ }
61
+
62
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
63
+
64
+ const rmrf = (target) => {
65
+ try {
66
+ fs.rmSync(target, { recursive: true, force: true });
67
+ } catch {
68
+ /* best effort */
69
+ }
70
+ };
71
+
72
+ // A usable install is one where the executable exists and is non-empty.
73
+ const isUsable = (exePath) => {
74
+ try {
75
+ return fs.statSync(exePath).size > 0;
76
+ } catch {
77
+ return false;
78
+ }
79
+ };
80
+
81
+ const provision = async (browser, label) => {
82
+ const exePath = computeExecutablePath({
83
+ browser,
84
+ buildId: CHROME_BUILD,
85
+ cacheDir,
86
+ platform,
87
+ });
88
+ // chrome.exe -> chrome-win64 -> win64-<build>; nuke the whole build folder
89
+ // so a partial extraction can never block a clean re-extract.
90
+ const versionFolder = path.dirname(path.dirname(exePath));
91
+
92
+ if (isUsable(exePath)) {
93
+ log(`${label} ${CHROME_BUILD} already present, skipping download.`);
94
+ return true;
95
+ }
96
+
97
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
98
+ rmrf(versionFolder); // clear any stale/partial extract first
99
+ try {
100
+ log(
101
+ `Installing ${label} ${CHROME_BUILD} (attempt ${attempt}/${MAX_ATTEMPTS})...`,
102
+ );
103
+ await install({
104
+ browser,
105
+ buildId: CHROME_BUILD,
106
+ cacheDir,
107
+ platform,
108
+ unpack: true,
109
+ });
110
+ } catch (err) {
111
+ console.error(
112
+ `${YELLOW} attempt ${attempt} failed: ${err.message}${RESET}`,
113
+ );
114
+ }
115
+
116
+ if (isUsable(exePath)) {
117
+ log(`${GREEN}${label} ${CHROME_BUILD} ready.${RESET}`);
118
+ return true;
119
+ }
120
+
121
+ if (attempt < MAX_ATTEMPTS) await sleep(2000);
122
+ }
123
+
124
+ return false;
125
+ };
126
+
127
+ const chromeOk = await provision(Browser.CHROME, "Chrome");
128
+ const shellOk = await provision(
129
+ Browser.CHROMEHEADLESSSHELL,
130
+ "chrome-headless-shell",
131
+ );
132
+
133
+ if (chromeOk && shellOk) process.exit(0);
134
+
135
+ console.error(
136
+ `\n${RED}Failed to provision a complete browser into:${RESET}\n ${cacheDir}\n\n` +
137
+ "This is almost always one of:\n" +
138
+ " 1. Antivirus (e.g. Windows Defender) quarantining Chrome files mid-extract.\n" +
139
+ ` Fix: Add-MpPreference -ExclusionPath "${cacheDir}"\n` +
140
+ " 2. A stale/partial cache folder. Fix: delete the chrome / chrome-headless-shell\n" +
141
+ ` sub-folders under "${cacheDir}" and reinstall.\n` +
142
+ " 3. A truncated download behind a proxy / TLS inspection. Check npm/HTTPS proxy config.\n",
143
+ );
144
+ process.exit(1);
145
+ })();