@abreen/tada 1.0.2 → 1.1.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 (120) hide show
  1. package/README.md +29 -33
  2. package/bin/tada.ts +356 -0
  3. package/bin/validators.test.ts +204 -0
  4. package/bin/validators.ts +83 -0
  5. package/{webpack/apply-base-path-plugin.js → build/apply-base-path-plugin.ts} +16 -7
  6. package/build/bundle.ts +117 -0
  7. package/{webpack/code.test.js → build/code.test.ts} +6 -7
  8. package/build/colors.ts +25 -0
  9. package/build/content-watch.ts +107 -0
  10. package/build/copy.ts +118 -0
  11. package/{webpack/deflist-id-plugin.js → build/deflist-id-plugin.ts} +7 -6
  12. package/{webpack/external-links-plugin.js → build/external-links-plugin.ts} +14 -5
  13. package/build/features.ts +11 -0
  14. package/build/generate-content-assets.ts +315 -0
  15. package/build/generate-favicon.ts +165 -0
  16. package/build/generate-fonts.ts +31 -0
  17. package/{webpack/generate-manifest-plugin.js → build/generate-manifest.ts} +29 -36
  18. package/build/globals.test.ts +101 -0
  19. package/{webpack/globals.js → build/globals.ts} +28 -13
  20. package/{webpack/heading-subtitle-plugin.js → build/heading-subtitle-plugin.ts} +4 -2
  21. package/build/json-schema.test.ts +57 -0
  22. package/build/json-schema.ts +33 -0
  23. package/build/log.test.ts +111 -0
  24. package/build/log.ts +167 -0
  25. package/{webpack/markdown-plugins.test.js → build/markdown-plugins.test.ts} +94 -9
  26. package/{webpack/pagefind-plugin.test.js → build/pagefind.test.ts} +74 -13
  27. package/build/pagefind.ts +339 -0
  28. package/{webpack/pdf-text.js → build/pdf-text.ts} +47 -27
  29. package/build/pipeline.ts +93 -0
  30. package/{webpack/reachability.test.js → build/reachability.test.ts} +3 -3
  31. package/{webpack/reachability.js → build/reachability.ts} +77 -34
  32. package/build/serve.ts +112 -0
  33. package/{webpack/site-variables.js → build/site-variables.ts} +22 -15
  34. package/{webpack → build}/site.schema.json +3 -10
  35. package/{webpack/templates.js → build/templates.ts} +35 -33
  36. package/{webpack/text-to-id.js → build/text-to-id.ts} +2 -2
  37. package/build/toc-plugin.test.ts +105 -0
  38. package/{webpack/toc-plugin.js → build/toc-plugin.ts} +32 -13
  39. package/build/types.ts +172 -0
  40. package/build/util.ts +26 -0
  41. package/{webpack/utils/code.js → build/utils/code.ts} +119 -60
  42. package/{webpack/utils/content-files.js → build/utils/content-files.ts} +40 -35
  43. package/build/utils/derive-theme.test.ts +111 -0
  44. package/build/utils/derive-theme.ts +85 -0
  45. package/build/utils/file-types.test.ts +61 -0
  46. package/build/utils/file-types.ts +13 -0
  47. package/build/utils/front-matter.test.ts +80 -0
  48. package/{webpack/utils/front-matter.js → build/utils/front-matter.ts} +22 -9
  49. package/{webpack → build}/utils/jdi-runner/LiterateRunner.java +1 -1
  50. package/{webpack/utils/literate-java.js → build/utils/literate-java.ts} +63 -34
  51. package/{webpack/utils/markdown.js → build/utils/markdown.ts} +94 -49
  52. package/build/utils/paths.test.ts +91 -0
  53. package/{webpack/utils/paths.js → build/utils/paths.ts} +14 -22
  54. package/{webpack/utils/render.js → build/utils/render.ts} +188 -123
  55. package/build/utils/shiki-highlighter.ts +29 -0
  56. package/build/validate-internal-links-plugin.test.ts +106 -0
  57. package/{webpack/validate-internal-links-plugin.js → build/validate-internal-links-plugin.ts} +47 -20
  58. package/{webpack/watch-reachability-state.test.js → build/watch-reachability-state.test.ts} +8 -8
  59. package/{webpack/watch-reachability-state.js → build/watch-reachability-state.ts} +63 -24
  60. package/{webpack/watch-reload-client.js → build/watch-reload-client.ts} +3 -1
  61. package/build/watch.ts +573 -0
  62. package/content/index.md +9 -3
  63. package/content/markdown.md +2 -1
  64. package/content/problem_sets/index.html +14 -0
  65. package/fonts/google-sans-code/woff2/GoogleSansCodeVariable-Italic.woff2 +0 -0
  66. package/fonts/google-sans-code/woff2/GoogleSansCodeVariable.woff2 +0 -0
  67. package/fonts/inter/woff2/InterVariable-Italic.woff2 +0 -0
  68. package/fonts/inter/woff2/InterVariable.woff2 +0 -0
  69. package/package.json +28 -19
  70. package/src/_alerts.scss +92 -0
  71. package/src/_base.scss +106 -0
  72. package/src/{layout.scss → _layout.scss} +0 -2
  73. package/src/anchor/style.scss +1 -9
  74. package/src/code/index.ts +3 -3
  75. package/src/code.scss +1 -1
  76. package/src/critical.scss +5 -0
  77. package/src/header/_base.scss +129 -0
  78. package/src/header/style.scss +3 -131
  79. package/src/index.ts +1 -2
  80. package/src/question/style.scss +1 -1
  81. package/src/search/index.ts +36 -15
  82. package/src/search/style.scss +9 -15
  83. package/src/style.scss +6 -269
  84. package/src/toc/style.scss +5 -39
  85. package/src/util.ts +8 -5
  86. package/templates/_theme.scss +38 -14
  87. package/tsconfig.json +10 -6
  88. package/types/file-system-access.d.ts +5 -0
  89. package/types/markdown-it-plugins.d.ts +11 -0
  90. package/types/untyped-modules.d.ts +40 -0
  91. package/bin/tada.js +0 -361
  92. package/content/problem_sets/index.md +0 -6
  93. package/webpack/build-state.js +0 -97
  94. package/webpack/colors.js +0 -15
  95. package/webpack/config.base.js +0 -151
  96. package/webpack/config.dev.js +0 -23
  97. package/webpack/config.prod.js +0 -32
  98. package/webpack/content-watch-plugin.js +0 -153
  99. package/webpack/features.js +0 -5
  100. package/webpack/generate-content-assets-plugin.js +0 -308
  101. package/webpack/generate-favicon-plugin.js +0 -198
  102. package/webpack/generate-fonts-plugin.js +0 -69
  103. package/webpack/json-schema.js +0 -19
  104. package/webpack/log.js +0 -143
  105. package/webpack/pagefind-plugin.js +0 -379
  106. package/webpack/print-flair-plugin.js +0 -22
  107. package/webpack/serve.js +0 -104
  108. package/webpack/util.js +0 -49
  109. package/webpack/utils/define-plugin.js +0 -20
  110. package/webpack/utils/file-types.js +0 -26
  111. package/webpack/utils/parse-hsl.js +0 -8
  112. package/webpack/utils/shiki-highlighter.js +0 -26
  113. package/webpack/watch.js +0 -166
  114. /package/{webpack → build}/flair.json +0 -0
  115. /package/{webpack → build}/utils/jdi-runner/LiterateRunner.class +0 -0
  116. /package/fonts/google-sans-code/{GoogleSansCodeVariable-Italic.ttf → ttf/GoogleSansCodeVariable-Italic.ttf} +0 -0
  117. /package/fonts/google-sans-code/{GoogleSansCodeVariable.ttf → ttf/GoogleSansCodeVariable.ttf} +0 -0
  118. /package/fonts/inter/{InterVariable-Italic.ttf → ttf/InterVariable-Italic.ttf} +0 -0
  119. /package/fonts/inter/{InterVariable.ttf → ttf/InterVariable.ttf} +0 -0
  120. /package/types/{dev.ts → dev.d.ts} +0 -0
@@ -0,0 +1,29 @@
1
+ import type { HighlighterGeneric, BundledLanguage, BundledTheme } from 'shiki';
2
+ import { makeLogger } from '../log.js';
3
+
4
+ const log = makeLogger(__filename);
5
+
6
+ let highlighter: HighlighterGeneric<BundledLanguage, BundledTheme> | null =
7
+ null;
8
+
9
+ export async function initHighlighter(langs: string[]): Promise<void> {
10
+ if (highlighter) {
11
+ return;
12
+ }
13
+ log.debug`Initializing syntax highlighter`;
14
+ const { createHighlighter } = await import('shiki');
15
+ highlighter = await createHighlighter({
16
+ themes: ['github-light', 'github-dark'],
17
+ langs,
18
+ });
19
+ }
20
+
21
+ export function getHighlighter(): HighlighterGeneric<
22
+ BundledLanguage,
23
+ BundledTheme
24
+ > {
25
+ if (!highlighter) {
26
+ throw new Error('Shiki highlighter not initialized');
27
+ }
28
+ return highlighter;
29
+ }
@@ -0,0 +1,106 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import MarkdownIt from 'markdown-it';
3
+ import validateInternalLinks from './validate-internal-links-plugin.js';
4
+
5
+ function createMd(
6
+ validTargets: string[],
7
+ options: { sourceUrlPath?: string; codeExtensions?: string[] } = {},
8
+ ) {
9
+ const md = new MarkdownIt({ html: true });
10
+ md.use(validateInternalLinks, {
11
+ enabled: true,
12
+ filePath: 'test.md',
13
+ sourceUrlPath: options.sourceUrlPath ?? '/test.html',
14
+ validTargets: new Set(validTargets),
15
+ codeExtensions: options.codeExtensions ?? [],
16
+ });
17
+ return md;
18
+ }
19
+
20
+ describe('validateInternalLinks', () => {
21
+ test('does nothing when disabled', () => {
22
+ const md = new MarkdownIt();
23
+ md.use(validateInternalLinks, { enabled: false });
24
+ expect(() => md.render('[link](/missing.html)')).not.toThrow();
25
+ });
26
+
27
+ test('throws when required options are missing', () => {
28
+ const md = new MarkdownIt();
29
+ expect(() => {
30
+ md.use(validateInternalLinks, { enabled: true });
31
+ md.render('test');
32
+ }).toThrow('requires filePath');
33
+ });
34
+
35
+ test('allows valid internal links', () => {
36
+ const md = createMd(['/about.html']);
37
+ expect(() => md.render('[About](/about.html)')).not.toThrow();
38
+ });
39
+
40
+ test('allows external links', () => {
41
+ const md = createMd([]);
42
+ expect(() => md.render('[Google](https://google.com)')).not.toThrow();
43
+ });
44
+
45
+ test('allows anchor links', () => {
46
+ const md = createMd([]);
47
+ expect(() => md.render('[Section](#top)')).not.toThrow();
48
+ });
49
+
50
+ test('allows mailto links', () => {
51
+ const md = createMd([]);
52
+ expect(() => md.render('[Email](mailto:test@example.com)')).not.toThrow();
53
+ });
54
+
55
+ test('throws for broken internal link', () => {
56
+ const md = createMd(['/about.html']);
57
+ expect(() => md.render('[Missing](/missing.html)')).toThrow(
58
+ 'broken internal link',
59
+ );
60
+ });
61
+
62
+ test('strips query and hash before validating', () => {
63
+ const md = createMd(['/page.html']);
64
+ expect(() => md.render('[Page](/page.html?v=1#section)')).not.toThrow();
65
+ });
66
+
67
+ test('resolves relative links from source path', () => {
68
+ const md = createMd(['/docs/guide.html'], {
69
+ sourceUrlPath: '/docs/index.html',
70
+ });
71
+ expect(() => md.render('[Guide](guide.html)')).not.toThrow();
72
+ });
73
+
74
+ test('throws for broken relative link', () => {
75
+ const md = createMd(['/docs/guide.html'], {
76
+ sourceUrlPath: '/docs/index.html',
77
+ });
78
+ expect(() => md.render('[Missing](missing.html)')).toThrow(
79
+ 'broken internal link',
80
+ );
81
+ });
82
+
83
+ test('rewrites code extensions to .html', () => {
84
+ const md = createMd(['/src/App.html'], { codeExtensions: ['java'] });
85
+ expect(() => md.render('[App](/src/App.java)')).not.toThrow();
86
+ });
87
+
88
+ test('detects directory links that should reference index.html', () => {
89
+ const md = createMd(['/docs/index.html']);
90
+ // The plugin logs the "directory link" message and throws a generic
91
+ // "broken internal link(s)" error, so match the thrown message.
92
+ expect(() => md.render('[Docs](/docs)')).toThrow('broken internal link');
93
+ });
94
+
95
+ test('validates links in raw HTML blocks', () => {
96
+ const md = createMd(['/about.html']);
97
+ expect(() => md.render('<a href="/missing.html">link</a>')).toThrow(
98
+ 'broken internal link',
99
+ );
100
+ });
101
+
102
+ test('allows protocol-relative URLs', () => {
103
+ const md = createMd([]);
104
+ expect(() => md.render('[CDN](//cdn.example.com/lib.js)')).not.toThrow();
105
+ });
106
+ });
@@ -1,13 +1,23 @@
1
- const path = require('path');
2
- const { makeLogger } = require('./log');
1
+ import type MarkdownIt from 'markdown-it';
2
+ import type Token from 'markdown-it/lib/token.mjs';
3
+ import path from 'path';
4
+ import { makeLogger } from './log.js';
3
5
 
4
6
  const log = makeLogger(__filename);
5
7
 
6
- function stripQueryAndHash(href) {
8
+ interface ValidateInternalLinksOptions {
9
+ enabled?: boolean;
10
+ filePath?: string;
11
+ sourceUrlPath?: string;
12
+ validTargets?: Set<string>;
13
+ codeExtensions?: string[];
14
+ }
15
+
16
+ function stripQueryAndHash(href: string): string {
7
17
  return href.split('#')[0].split('?')[0];
8
18
  }
9
19
 
10
- function isExternalOrAnchor(href) {
20
+ function isExternalOrAnchor(href: string): boolean {
11
21
  if (!href || href.startsWith('#') || href.startsWith('//')) {
12
22
  return true;
13
23
  }
@@ -15,7 +25,7 @@ function isExternalOrAnchor(href) {
15
25
  return /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(href);
16
26
  }
17
27
 
18
- function normalizePathname(pathname) {
28
+ function normalizePathname(pathname: string): string {
19
29
  const normalized = path.posix.normalize(pathname);
20
30
  if (normalized === '.') {
21
31
  return '/';
@@ -23,7 +33,7 @@ function normalizePathname(pathname) {
23
33
  return normalized.startsWith('/') ? normalized : `/${normalized}`;
24
34
  }
25
35
 
26
- function createCodeExtPattern(codeExtensions) {
36
+ function createCodeExtPattern(codeExtensions: string[]): RegExp | null {
27
37
  const escaped = codeExtensions.map(ext =>
28
38
  ext.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
29
39
  );
@@ -33,7 +43,10 @@ function createCodeExtPattern(codeExtensions) {
33
43
  return new RegExp(`\\.(${escaped.join('|')})$`, 'i');
34
44
  }
35
45
 
36
- function rewriteCodeLink(pathname, codeExtPattern) {
46
+ function rewriteCodeLink(
47
+ pathname: string,
48
+ codeExtPattern: RegExp | null,
49
+ ): string {
37
50
  if (!codeExtPattern) {
38
51
  return pathname;
39
52
  }
@@ -41,7 +54,11 @@ function rewriteCodeLink(pathname, codeExtPattern) {
41
54
  return pathname.replace(codeExtPattern, '.html');
42
55
  }
43
56
 
44
- function resolveLinkPath(sourceUrlPath, rawHref, codeExtPattern) {
57
+ function resolveLinkPath(
58
+ sourceUrlPath: string,
59
+ rawHref: string,
60
+ codeExtPattern: RegExp | null,
61
+ ): string | null {
45
62
  const hrefPath = stripQueryAndHash(rawHref.trim());
46
63
  if (!hrefPath) {
47
64
  return null;
@@ -55,11 +72,14 @@ function resolveLinkPath(sourceUrlPath, rawHref, codeExtPattern) {
55
72
  return rewriteCodeLink(resolved, codeExtPattern);
56
73
  }
57
74
 
58
- function getDirectoryIndexPath(pathname) {
75
+ function getDirectoryIndexPath(pathname: string): string {
59
76
  return normalizePathname(path.posix.join(pathname, 'index.html'));
60
77
  }
61
78
 
62
- module.exports = function validateInternalLinks(md, options = {}) {
79
+ export default function validateInternalLinks(
80
+ md: MarkdownIt,
81
+ options: ValidateInternalLinksOptions = {},
82
+ ): void {
63
83
  const {
64
84
  enabled = true,
65
85
  filePath,
@@ -79,9 +99,9 @@ module.exports = function validateInternalLinks(md, options = {}) {
79
99
  }
80
100
 
81
101
  const codeExtPattern = createCodeExtPattern(codeExtensions);
82
- const seenErrors = new Set();
102
+ const seenErrors = new Set<string>();
83
103
 
84
- function reportBrokenLink(rawHref, resolvedPath) {
104
+ function reportBrokenLink(rawHref: string, resolvedPath: string): void {
85
105
  const key = `${rawHref}|${resolvedPath}`;
86
106
  if (seenErrors.has(key)) {
87
107
  return;
@@ -91,7 +111,11 @@ module.exports = function validateInternalLinks(md, options = {}) {
91
111
  log.error`${filePath}: broken internal link: "${rawHref}" (resolved to "${resolvedPath}")`;
92
112
  }
93
113
 
94
- function reportDirectoryLink(rawHref, resolvedPath, indexPath) {
114
+ function reportDirectoryLink(
115
+ rawHref: string,
116
+ resolvedPath: string,
117
+ indexPath: string,
118
+ ): void {
95
119
  const key = `${rawHref}|${resolvedPath}|directory`;
96
120
  if (seenErrors.has(key)) {
97
121
  return;
@@ -101,7 +125,7 @@ module.exports = function validateInternalLinks(md, options = {}) {
101
125
  log.error`${filePath}: directory link must reference index.html explicitly: "${rawHref}" (resolved to "${resolvedPath}", expected "${indexPath}")`;
102
126
  }
103
127
 
104
- function validateHref(rawHref) {
128
+ function validateHref(rawHref: string): void {
105
129
  if (!rawHref) {
106
130
  return;
107
131
  }
@@ -111,7 +135,7 @@ module.exports = function validateInternalLinks(md, options = {}) {
111
135
  return;
112
136
  }
113
137
 
114
- const resolvedPath = resolveLinkPath(sourceUrlPath, href, codeExtPattern);
138
+ const resolvedPath = resolveLinkPath(sourceUrlPath!, href, codeExtPattern);
115
139
  if (!resolvedPath) {
116
140
  return;
117
141
  }
@@ -119,20 +143,23 @@ module.exports = function validateInternalLinks(md, options = {}) {
119
143
  const directoryIndexPath = getDirectoryIndexPath(resolvedPath);
120
144
  if (
121
145
  directoryIndexPath !== resolvedPath &&
122
- validTargets.has(directoryIndexPath)
146
+ validTargets!.has(directoryIndexPath)
123
147
  ) {
124
148
  reportDirectoryLink(rawHref, resolvedPath, directoryIndexPath);
125
149
  return;
126
150
  }
127
151
 
128
- if (!validTargets.has(resolvedPath)) {
152
+ if (!validTargets!.has(resolvedPath)) {
129
153
  reportBrokenLink(rawHref, resolvedPath);
130
154
  }
131
155
  }
132
156
 
133
- function validateToken(token) {
157
+ function validateToken(token: Token): void {
134
158
  if (token.type === 'link_open') {
135
- validateHref(token.attrGet('href'));
159
+ const href = token.attrGet('href');
160
+ if (href) {
161
+ validateHref(href);
162
+ }
136
163
  } else if (token.type === 'html_block' || token.type === 'html_inline') {
137
164
  token.content.replace(/<a\b[^>]*\bhref\s*=\s*"([^"]+)"/gi, (_, href) => {
138
165
  validateHref(href);
@@ -152,4 +179,4 @@ module.exports = function validateInternalLinks(md, options = {}) {
152
179
  );
153
180
  }
154
181
  });
155
- };
182
+ }
@@ -1,23 +1,23 @@
1
- const { describe, expect, test } = require('bun:test');
2
- const { collectReachableSiteAssets } = require('./reachability');
3
- const WatchReachabilityState = require('./watch-reachability-state');
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { collectReachableSiteAssets } from './reachability.js';
3
+ import WatchReachabilityState from './watch-reachability-state.js';
4
4
 
5
- function createReader(htmlByPath) {
6
- const calls = [];
5
+ function createReader(htmlByPath: Map<string, string>) {
6
+ const calls: string[] = [];
7
7
 
8
8
  return {
9
9
  calls,
10
- read(assetPath) {
10
+ read(assetPath: string): string {
11
11
  calls.push(assetPath);
12
12
  if (!htmlByPath.has(assetPath)) {
13
13
  throw new Error(`Missing HTML asset: ${assetPath}`);
14
14
  }
15
- return htmlByPath.get(assetPath);
15
+ return htmlByPath.get(assetPath)!;
16
16
  },
17
17
  };
18
18
  }
19
19
 
20
- function createState(htmlEntries) {
20
+ function createState(htmlEntries: Record<string, string>) {
21
21
  const htmlByPath = new Map(Object.entries(htmlEntries));
22
22
  const reader = createReader(htmlByPath);
23
23
  const state = new WatchReachabilityState();
@@ -1,17 +1,40 @@
1
- const { collectDirectSiteAssetLinks } = require('./reachability');
1
+ import { collectDirectSiteAssetLinks } from './reachability.js';
2
2
 
3
- function cloneSet(values) {
3
+ type ReadHtmlForAsset = (assetPath: string) => string;
4
+
5
+ function cloneSet(values: Iterable<string> | undefined): Set<string> {
4
6
  return new Set(values || []);
5
7
  }
6
8
 
9
+ interface IncrementalOptions {
10
+ changedAssetPaths?: Iterable<string>;
11
+ removedAssetPaths?: Iterable<string>;
12
+ readHtmlForAsset: ReadHtmlForAsset;
13
+ }
14
+
7
15
  class WatchReachabilityState {
8
- constructor({ rootPath = 'index.html', basePath = '/' } = {}) {
16
+ private rootPath: string;
17
+ private basePath: string;
18
+ private knownAssets: Set<string>;
19
+ private outgoingBySource: Map<string, Set<string>>;
20
+ private incomingByTarget: Map<string, Set<string>>;
21
+ private reachable: Set<string>;
22
+ private initialized: boolean;
23
+
24
+ constructor({
25
+ rootPath = 'index.html',
26
+ basePath = '/',
27
+ }: { rootPath?: string; basePath?: string } = {}) {
9
28
  this.rootPath = rootPath;
10
29
  this.basePath = basePath;
11
- this.reset();
30
+ this.knownAssets = new Set();
31
+ this.outgoingBySource = new Map();
32
+ this.incomingByTarget = new Map();
33
+ this.reachable = new Set();
34
+ this.initialized = false;
12
35
  }
13
36
 
14
- reset() {
37
+ reset(): void {
15
38
  this.knownAssets = new Set();
16
39
  this.outgoingBySource = new Map();
17
40
  this.incomingByTarget = new Map();
@@ -19,13 +42,13 @@ class WatchReachabilityState {
19
42
  this.initialized = false;
20
43
  }
21
44
 
22
- setKnownAssets(assetPaths) {
45
+ setKnownAssets(assetPaths: Iterable<string>): void {
23
46
  this.knownAssets = cloneSet(assetPaths);
24
47
  this.reachable = new Set(
25
48
  [...this.reachable].filter(assetPath => this.knownAssets.has(assetPath)),
26
49
  );
27
50
 
28
- const nextOutgoingBySource = new Map();
51
+ const nextOutgoingBySource = new Map<string, Set<string>>();
29
52
  for (const [sourcePath, targets] of this.outgoingBySource) {
30
53
  if (!this.knownAssets.has(sourcePath)) {
31
54
  continue;
@@ -43,7 +66,7 @@ class WatchReachabilityState {
43
66
  this.rebuildIncomingByTarget();
44
67
  }
45
68
 
46
- rebuild(readHtmlForAsset) {
69
+ rebuild(readHtmlForAsset: ReadHtmlForAsset): void {
47
70
  if (!this.knownAssets.has(this.rootPath)) {
48
71
  throw new Error(`Pagefind reachability root not found: ${this.rootPath}`);
49
72
  }
@@ -66,20 +89,24 @@ class WatchReachabilityState {
66
89
  this.initialized = true;
67
90
  }
68
91
 
69
- applyIncremental({ changedAssetPaths, removedAssetPaths, readHtmlForAsset }) {
92
+ applyIncremental({
93
+ changedAssetPaths,
94
+ removedAssetPaths,
95
+ readHtmlForAsset,
96
+ }: IncrementalOptions): void {
70
97
  if (!this.initialized) {
71
98
  this.rebuild(readHtmlForAsset);
72
99
  return;
73
100
  }
74
101
 
75
- const normalizedChanged = new Set();
102
+ const normalizedChanged = new Set<string>();
76
103
  for (const assetPath of changedAssetPaths || []) {
77
104
  if (this.knownAssets.has(assetPath)) {
78
105
  normalizedChanged.add(assetPath);
79
106
  }
80
107
  }
81
108
 
82
- const normalizedRemoved = new Set();
109
+ const normalizedRemoved = new Set<string>();
83
110
  for (const assetPath of removedAssetPaths || []) {
84
111
  if (
85
112
  !this.knownAssets.has(assetPath) &&
@@ -89,7 +116,7 @@ class WatchReachabilityState {
89
116
  }
90
117
  }
91
118
 
92
- const previouslyReachableRoots = new Set();
119
+ const previouslyReachableRoots = new Set<string>();
93
120
  for (const assetPath of normalizedChanged) {
94
121
  if (this.reachable.has(assetPath)) {
95
122
  previouslyReachableRoots.add(assetPath);
@@ -130,7 +157,7 @@ class WatchReachabilityState {
130
157
  this.reachable.delete(assetPath);
131
158
  }
132
159
 
133
- const seedPaths = new Set();
160
+ const seedPaths = new Set<string>();
134
161
  for (const assetPath of stillKnownRoots) {
135
162
  if (affected.has(assetPath)) {
136
163
  seedPaths.add(assetPath);
@@ -163,11 +190,14 @@ class WatchReachabilityState {
163
190
  this.initialized = true;
164
191
  }
165
192
 
166
- getReachablePaths() {
193
+ getReachablePaths(): string[] {
167
194
  return [...this.reachable].sort();
168
195
  }
169
196
 
170
- readOutgoingPaths(sourcePath, readHtmlForAsset) {
197
+ private readOutgoingPaths(
198
+ sourcePath: string,
199
+ readHtmlForAsset: ReadHtmlForAsset,
200
+ ): Set<string> {
171
201
  if (!this.knownAssets.has(sourcePath)) {
172
202
  return new Set();
173
203
  }
@@ -182,7 +212,10 @@ class WatchReachabilityState {
182
212
  return new Set(htmlAssetPaths);
183
213
  }
184
214
 
185
- addIncomingEdges(sourcePath, outgoingPaths) {
215
+ private addIncomingEdges(
216
+ sourcePath: string,
217
+ outgoingPaths: Set<string>,
218
+ ): void {
186
219
  for (const targetPath of outgoingPaths) {
187
220
  let incomingPaths = this.incomingByTarget.get(targetPath);
188
221
  if (!incomingPaths) {
@@ -193,14 +226,14 @@ class WatchReachabilityState {
193
226
  }
194
227
  }
195
228
 
196
- rebuildIncomingByTarget() {
229
+ private rebuildIncomingByTarget(): void {
197
230
  this.incomingByTarget = new Map();
198
231
  for (const [sourcePath, outgoingPaths] of this.outgoingBySource) {
199
232
  this.addIncomingEdges(sourcePath, outgoingPaths);
200
233
  }
201
234
  }
202
235
 
203
- removeOutgoingEdges(sourcePath) {
236
+ private removeOutgoingEdges(sourcePath: string): void {
204
237
  const outgoingPaths = this.outgoingBySource.get(sourcePath);
205
238
  if (!outgoingPaths) {
206
239
  return;
@@ -219,7 +252,10 @@ class WatchReachabilityState {
219
252
  }
220
253
  }
221
254
 
222
- replaceOutgoing(sourcePath, outgoingPaths) {
255
+ private replaceOutgoing(
256
+ sourcePath: string,
257
+ outgoingPaths: Set<string>,
258
+ ): void {
223
259
  this.removeOutgoingEdges(sourcePath);
224
260
  if (!this.knownAssets.has(sourcePath)) {
225
261
  this.outgoingBySource.delete(sourcePath);
@@ -230,19 +266,22 @@ class WatchReachabilityState {
230
266
  this.addIncomingEdges(sourcePath, outgoingPaths);
231
267
  }
232
268
 
233
- removeAsset(assetPath) {
269
+ private removeAsset(assetPath: string): void {
234
270
  this.removeOutgoingEdges(assetPath);
235
271
  this.outgoingBySource.delete(assetPath);
236
272
  this.incomingByTarget.delete(assetPath);
237
273
  this.reachable.delete(assetPath);
238
274
  }
239
275
 
240
- collectClosure(rootPaths, allowedPaths = null) {
276
+ private collectClosure(
277
+ rootPaths: Set<string>,
278
+ allowedPaths: Set<string> | null = null,
279
+ ): Set<string> {
241
280
  const pending = [...rootPaths];
242
- const visited = new Set();
281
+ const visited = new Set<string>();
243
282
 
244
283
  while (pending.length > 0) {
245
- const currentPath = pending.pop();
284
+ const currentPath = pending.pop()!;
246
285
  if (visited.has(currentPath)) {
247
286
  continue;
248
287
  }
@@ -270,4 +309,4 @@ class WatchReachabilityState {
270
309
  }
271
310
  }
272
311
 
273
- module.exports = WatchReachabilityState;
312
+ export default WatchReachabilityState;
@@ -1,3 +1,5 @@
1
+ declare const __WEBSOCKET_PORT__: number;
2
+
1
3
  (function () {
2
4
  const style = document.createElement('style');
3
5
  style.textContent = [
@@ -24,7 +26,7 @@
24
26
  ].join('\n');
25
27
  document.head.appendChild(style);
26
28
 
27
- const ws = new WebSocket('ws://localhost:35729');
29
+ const ws = new WebSocket(`ws://localhost:${__WEBSOCKET_PORT__}`);
28
30
 
29
31
  ws.onopen = () => {
30
32
  console.log('[watch-reload] connected to watcher');