@abreen/tada 1.0.2 → 1.2.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 (146) hide show
  1. package/README.md +96 -48
  2. package/bin/tada.ts +621 -0
  3. package/bin/validators.test.ts +205 -0
  4. package/bin/validators.ts +85 -0
  5. package/{webpack/apply-base-path-plugin.js → build/apply-base-path-plugin.ts} +37 -17
  6. package/build/build-manifest.test.ts +349 -0
  7. package/build/build-manifest.ts +165 -0
  8. package/build/bundle.ts +116 -0
  9. package/{webpack/code.test.js → build/code.test.ts} +10 -7
  10. package/build/colors.ts +25 -0
  11. package/build/content-watch.ts +107 -0
  12. package/build/copy.ts +118 -0
  13. package/{webpack/deflist-id-plugin.js → build/deflist-id-plugin.ts} +7 -6
  14. package/{webpack/external-links-plugin.js → build/external-links-plugin.ts} +14 -5
  15. package/build/features.ts +11 -0
  16. package/build/generate-content-assets.ts +320 -0
  17. package/build/generate-favicon.ts +172 -0
  18. package/build/generate-fonts.ts +31 -0
  19. package/build/generate-katex-assets.ts +35 -0
  20. package/{webpack/generate-manifest-plugin.js → build/generate-manifest.ts} +29 -36
  21. package/build/globals.test.ts +99 -0
  22. package/build/globals.ts +88 -0
  23. package/{webpack/heading-subtitle-plugin.js → build/heading-subtitle-plugin.ts} +4 -2
  24. package/build/json-schema.test.ts +57 -0
  25. package/build/json-schema.ts +33 -0
  26. package/build/log.test.ts +111 -0
  27. package/build/log.ts +167 -0
  28. package/build/markdown-plugins.test.ts +396 -0
  29. package/{webpack/pagefind-plugin.test.js → build/pagefind.test.ts} +74 -13
  30. package/build/pagefind.ts +339 -0
  31. package/build/pdf-text.test.ts +52 -0
  32. package/{webpack/pdf-text.js → build/pdf-text.ts} +47 -27
  33. package/build/pipeline.ts +120 -0
  34. package/{webpack/reachability.test.js → build/reachability.test.ts} +3 -3
  35. package/{webpack/reachability.js → build/reachability.ts} +77 -34
  36. package/build/serve.ts +112 -0
  37. package/build/site-variables.ts +68 -0
  38. package/{webpack/templates.js → build/templates.ts} +38 -35
  39. package/{webpack/text-to-id.js → build/text-to-id.ts} +2 -2
  40. package/build/toc-plugin.test.ts +105 -0
  41. package/{webpack/toc-plugin.js → build/toc-plugin.ts} +32 -13
  42. package/build/types.ts +181 -0
  43. package/build/util.ts +26 -0
  44. package/{webpack/utils/code.js → build/utils/code.ts} +119 -60
  45. package/{webpack/utils/content-files.js → build/utils/content-files.ts} +40 -35
  46. package/build/utils/derive-theme.test.ts +111 -0
  47. package/build/utils/derive-theme.ts +85 -0
  48. package/build/utils/file-types.test.ts +61 -0
  49. package/build/utils/file-types.ts +13 -0
  50. package/build/utils/front-matter.test.ts +77 -0
  51. package/{webpack/utils/front-matter.js → build/utils/front-matter.ts} +22 -9
  52. package/{webpack → build}/utils/jdi-runner/LiterateRunner.java +1 -1
  53. package/{webpack/utils/literate-java.js → build/utils/literate-java.ts} +68 -38
  54. package/{webpack/utils/markdown.js → build/utils/markdown.ts} +126 -49
  55. package/build/utils/paths.test.ts +91 -0
  56. package/{webpack/utils/paths.js → build/utils/paths.ts} +18 -22
  57. package/{webpack/utils/render.js → build/utils/render.ts} +208 -130
  58. package/build/utils/shiki-highlighter.ts +29 -0
  59. package/build/validate-internal-links-plugin.test.ts +106 -0
  60. package/{webpack/validate-internal-links-plugin.js → build/validate-internal-links-plugin.ts} +47 -20
  61. package/{webpack/watch-reachability-state.test.js → build/watch-reachability-state.test.ts} +8 -8
  62. package/{webpack/watch-reachability-state.js → build/watch-reachability-state.ts} +63 -24
  63. package/{webpack/watch-reload-client.js → build/watch-reload-client.ts} +3 -1
  64. package/build/watch.ts +549 -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/{content → init/content}/index.md +9 -3
  70. package/{content → init/content}/lectures/index.md +1 -1
  71. package/{content → init/content}/markdown.md +28 -8
  72. package/init/content/problem_sets/index.html +14 -0
  73. package/package.json +33 -22
  74. package/{templates → schema}/nav.schema.json +1 -2
  75. package/{webpack → schema}/site.schema.json +4 -12
  76. package/src/_alerts.scss +96 -0
  77. package/src/_base.scss +145 -0
  78. package/src/{layout.scss → _layout.scss} +36 -1
  79. package/src/anchor/index.ts +0 -1
  80. package/src/anchor/style.scss +1 -9
  81. package/src/code/index.ts +3 -3
  82. package/src/code.scss +1 -1
  83. package/src/critical.scss +5 -0
  84. package/src/header/_base.scss +129 -0
  85. package/src/header/style.scss +3 -131
  86. package/src/index.ts +4 -5
  87. package/src/question/style.scss +1 -1
  88. package/src/search/index.test.ts +203 -0
  89. package/src/search/index.ts +38 -93
  90. package/src/search/pdf-utils.ts +79 -0
  91. package/src/search/style.scss +8 -28
  92. package/src/style.scss +15 -326
  93. package/src/timezone/index.ts +5 -6
  94. package/src/toc/style.scss +5 -39
  95. package/src/util.ts +9 -6
  96. package/templates/_theme.scss +38 -14
  97. package/templates/_top.html +1 -1
  98. package/tsconfig.json +10 -6
  99. package/types/dev.d.ts +1 -0
  100. package/types/file-system-access.d.ts +5 -0
  101. package/types/markdown-it-plugins.d.ts +11 -0
  102. package/types/site-variables.d.ts +6 -10
  103. package/types/untyped-modules.d.ts +47 -0
  104. package/bin/tada.js +0 -361
  105. package/config/authors.json +0 -1
  106. package/config/nav.json +0 -28
  107. package/content/problem_sets/index.md +0 -6
  108. package/types/dev.ts +0 -3
  109. package/webpack/build-state.js +0 -97
  110. package/webpack/colors.js +0 -15
  111. package/webpack/config.base.js +0 -151
  112. package/webpack/config.dev.js +0 -23
  113. package/webpack/config.prod.js +0 -32
  114. package/webpack/content-watch-plugin.js +0 -153
  115. package/webpack/features.js +0 -5
  116. package/webpack/generate-content-assets-plugin.js +0 -308
  117. package/webpack/generate-favicon-plugin.js +0 -198
  118. package/webpack/generate-fonts-plugin.js +0 -69
  119. package/webpack/globals.js +0 -74
  120. package/webpack/json-schema.js +0 -19
  121. package/webpack/log.js +0 -143
  122. package/webpack/markdown-plugins.test.js +0 -203
  123. package/webpack/pagefind-plugin.js +0 -379
  124. package/webpack/print-flair-plugin.js +0 -22
  125. package/webpack/serve.js +0 -104
  126. package/webpack/site-variables.js +0 -53
  127. package/webpack/util.js +0 -49
  128. package/webpack/utils/define-plugin.js +0 -20
  129. package/webpack/utils/file-types.js +0 -26
  130. package/webpack/utils/parse-hsl.js +0 -8
  131. package/webpack/utils/shiki-highlighter.js +0 -26
  132. package/webpack/watch.js +0 -166
  133. /package/{webpack → build}/flair.json +0 -0
  134. /package/{webpack → build}/utils/jdi-runner/LiterateRunner.class +0 -0
  135. /package/fonts/google-sans-code/{GoogleSansCodeVariable-Italic.ttf → ttf/GoogleSansCodeVariable-Italic.ttf} +0 -0
  136. /package/fonts/google-sans-code/{GoogleSansCodeVariable.ttf → ttf/GoogleSansCodeVariable.ttf} +0 -0
  137. /package/fonts/inter/{InterVariable-Italic.ttf → ttf/InterVariable-Italic.ttf} +0 -0
  138. /package/fonts/inter/{InterVariable.ttf → ttf/InterVariable.ttf} +0 -0
  139. /package/{content → init/content}/lectures/01/Pair.java.md +0 -0
  140. /package/{content → init/content}/lectures/01/Rectangle.java +0 -0
  141. /package/{content → init/content}/lectures/01/demo.py +0 -0
  142. /package/{content → init/content}/lectures/01/index.md +0 -0
  143. /package/{content → init/content}/lectures/01/lecture1.pdf +0 -0
  144. /package/{public → init/public}/avatars/alex.jpg +0 -0
  145. /package/{public → init/public}/test.txt +0 -0
  146. /package/{templates → schema}/authors.schema.json +0 -0
package/webpack/log.js DELETED
@@ -1,143 +0,0 @@
1
- const { inspect } = require('node:util');
2
- const path = require('path');
3
- const { G, R, P, Y, L } = require('./colors');
4
- const FLAIR_STRINGS = require('./flair.json');
5
-
6
- const LEVELS = ['debug', 'info', 'warn', 'error'];
7
-
8
- const ENV_LOG_LEVEL = process.env.TADA_LOG_LEVEL;
9
-
10
- if (ENV_LOG_LEVEL && !LEVELS.includes(ENV_LOG_LEVEL)) {
11
- throw new Error(
12
- `Invalid TADA_LOG_LEVEL "${ENV_LOG_LEVEL}", must be one of: ${LEVELS.join(', ')}`,
13
- );
14
- }
15
-
16
- function shouldLog(loggerLevel, level) {
17
- return LEVELS.indexOf(level) >= LEVELS.indexOf(loggerLevel);
18
- }
19
-
20
- function validateLevel(level) {
21
- if (!LEVELS.includes(level)) {
22
- throw new Error(
23
- `Invalid log level "${level}", must be one of: ${LEVELS.join(', ')}`,
24
- );
25
- }
26
- }
27
-
28
- function print(strings, stream = 'stdout', end = '\n') {
29
- for (const s of strings) {
30
- process[stream].write(s);
31
- }
32
- process[stream].write(end);
33
- }
34
-
35
- function makeLogger(name, logLevel = 'info') {
36
- validateLevel(logLevel);
37
-
38
- if (ENV_LOG_LEVEL) {
39
- logLevel = ENV_LOG_LEVEL;
40
- }
41
-
42
- if (!name) {
43
- name = '';
44
- } else {
45
- // Allow for passing __filename
46
- name = path.basename(name);
47
- }
48
-
49
- const logger = {
50
- /** Don't log if the level is < minLogLevel */
51
- setMinLogLevel(minLogLevel) {
52
- this.minLogLevel = minLogLevel;
53
- },
54
- getArgs(level, strings, args, colorFn) {
55
- const params = [];
56
- params.push(colorFn`${level}` + '\t');
57
- params.push(format(strings, ...args));
58
- return params;
59
- },
60
- debug(strings, ...args) {
61
- if (shouldLog(this.minLogLevel, 'debug')) {
62
- print(this.getArgs('debug', strings, args, L), 'stderr');
63
- }
64
- },
65
- info(strings, ...args) {
66
- if (shouldLog(this.minLogLevel, 'info')) {
67
- print(this.getArgs('info', strings, args, L));
68
- }
69
- },
70
- warn(strings, ...args) {
71
- if (shouldLog(this.minLogLevel, 'warn')) {
72
- print(this.getArgs('warn', strings, args, Y));
73
- }
74
- },
75
- error(strings, ...args) {
76
- if (shouldLog(this.minLogLevel, 'error')) {
77
- print(this.getArgs('error', strings, args, R));
78
- }
79
- },
80
- event(strings, ...args) {
81
- print(this.getArgs('event', strings, args, G));
82
- },
83
- followup(strings) {
84
- print(strings);
85
- },
86
- };
87
-
88
- logger.setMinLogLevel(logLevel);
89
- return logger;
90
- }
91
-
92
- function format(strings, ...args) {
93
- // Called as template tag: first arg is an array-like with .raw
94
- if (strings && typeof strings === 'object' && 'raw' in strings) {
95
- try {
96
- return String.raw(strings, ...args.map(toString));
97
- } catch (e) {
98
- // fallback to safe join
99
- }
100
- } else {
101
- if (Array.isArray(strings)) {
102
- args.unshift(...strings);
103
- } else {
104
- args.unshift(strings);
105
- }
106
-
107
- return args.map(toString).join(' ');
108
- }
109
- }
110
-
111
- function toString(item) {
112
- if (item === undefined) {
113
- return 'undefined';
114
- }
115
- if (item === null) {
116
- return 'null';
117
- }
118
- if (typeof item === 'string') {
119
- return item;
120
- }
121
-
122
- try {
123
- if (typeof item === 'object') {
124
- return inspect(item, {
125
- compact: true,
126
- depth: 2,
127
- breakLength: 80,
128
- maxStringLength: 250,
129
- colors: true,
130
- });
131
- }
132
- throw new Error('not an object');
133
- } catch (e) {
134
- return String(item);
135
- }
136
- }
137
-
138
- function getFlair() {
139
- const i = Math.floor(Math.random() * FLAIR_STRINGS.length);
140
- return P`${FLAIR_STRINGS[i]}!` + ' 🎉';
141
- }
142
-
143
- module.exports = { makeLogger, getFlair };
@@ -1,203 +0,0 @@
1
- const { describe, expect, test } = require('bun:test');
2
- const MarkdownIt = require('markdown-it');
3
- const deflist = require('markdown-it-deflist');
4
- const applyBasePathPlugin = require('./apply-base-path-plugin');
5
- const deflistIdPlugin = require('./deflist-id-plugin');
6
- const externalLinksPlugin = require('./external-links-plugin');
7
- const headingSubtitlePlugin = require('./heading-subtitle-plugin');
8
- const { createMarkdown } = require('./utils/markdown');
9
-
10
- describe('apply-base-path-plugin', () => {
11
- test('rewrites internal links, images, and raw html image sources', () => {
12
- const md = new MarkdownIt({ html: true });
13
- md.use(applyBasePathPlugin, {
14
- basePath: '/course/',
15
- codeLanguages: { java: 'java' },
16
- features: { code: true },
17
- });
18
-
19
- const html = md.render(
20
- [
21
- '[Code](/src/example.java?view=1#top)',
22
- '',
23
- '![Image](/images/pic.png)',
24
- '',
25
- '<img src="/images/raw.png" alt="Raw">',
26
- ].join('\n'),
27
- );
28
-
29
- expect(html).toContain('href="/course/src/example.html?view=1#top"');
30
- expect(html).toContain('src="/course/images/pic.png"');
31
- expect(html).toContain('src="/course/images/raw.png"');
32
- });
33
-
34
- test('rewrites relative code file links without applying base path', () => {
35
- const md = new MarkdownIt();
36
- md.use(applyBasePathPlugin, {
37
- basePath: '/course/',
38
- codeLanguages: { java: 'java' },
39
- features: { code: true },
40
- });
41
-
42
- const html = md.render('[App](./App.java)');
43
-
44
- expect(html).toContain('href="./App.html"');
45
- expect(html).not.toContain('href="./App.java"');
46
- });
47
-
48
- test('keeps code file extensions when the code feature is disabled', () => {
49
- const md = new MarkdownIt();
50
- md.use(applyBasePathPlugin, {
51
- basePath: '/course',
52
- codeLanguages: { java: 'java' },
53
- features: { code: false },
54
- });
55
-
56
- const html = md.render('[Code](/src/example.java#L1)');
57
-
58
- expect(html).toContain('href="/course/src/example.java#L1"');
59
- expect(html).not.toContain('href="/course/src/example.html#L1"');
60
- });
61
- });
62
-
63
- describe('external-links-plugin', () => {
64
- test('marks external http links without changing internal or non-http links', () => {
65
- const md = new MarkdownIt();
66
- md.use(externalLinksPlugin, { internalDomains: ['example.com'] });
67
-
68
- const html = md.render(
69
- [
70
- '[External](https://outside.example/docs)',
71
- '[Internal](https://example.com/docs)',
72
- '[Mail](mailto:test@example.com)',
73
- ].join(' '),
74
- );
75
-
76
- expect(html).toContain(
77
- '<a href="https://outside.example/docs" class="external" target="_blank">External</a>',
78
- );
79
- expect(html).toContain('<a href="https://example.com/docs">Internal</a>');
80
- expect(html).toContain('<a href="mailto:test@example.com">Mail</a>');
81
- });
82
- });
83
-
84
- describe('heading-subtitle-plugin', () => {
85
- test('wraps heading subtitles while preserving inline formatting', () => {
86
- const md = new MarkdownIt();
87
- md.use(headingSubtitlePlugin);
88
-
89
- const html = md.render('## Title # *Subtitle*').trim();
90
-
91
- expect(html).toBe(
92
- '<h2 class="has-subtitle">Title <span class="heading-subtitle"><em>Subtitle</em></span></h2>',
93
- );
94
- });
95
- });
96
-
97
- describe('deflist-id-plugin', () => {
98
- test('injects unique ids for terms and falls back when a term slug is empty', () => {
99
- const md = new MarkdownIt();
100
- md.use(deflist);
101
- md.use(deflistIdPlugin);
102
-
103
- const html = md.render(
104
- ['Term 1', ': One', '', 'Term 1', ': Two', '', '!!!', ': Three'].join(
105
- '\n',
106
- ),
107
- );
108
-
109
- expect(html).toContain('<a id="term-1"></a>Term 1');
110
- expect(html).toContain('<a id="term-1-2"></a>Term 1');
111
- expect(html).toContain('<a id="term"></a>!!!');
112
- });
113
- });
114
-
115
- describe('custom markdown containers', () => {
116
- function createProjectMarkdown() {
117
- return createMarkdown(
118
- { basePath: '/', internalDomains: [], codeLanguages: {}, features: {} },
119
- { validatorOptions: { enabled: false } },
120
- );
121
- }
122
-
123
- test('renders collapsible details blocks', () => {
124
- const md = createProjectMarkdown();
125
-
126
- const html = md.render(
127
- ['<<< details More info', 'Hello', '<<<'].join('\n'),
128
- );
129
-
130
- expect(html).toContain('<details><summary>More info</summary>');
131
- expect(html).toContain('<div class="content">');
132
- expect(html).toContain('<p>Hello</p>');
133
- expect(html).toContain('</div></details>');
134
- });
135
-
136
- test('renders collapsible details blocks with inline Markdown in summary', () => {
137
- const md = createProjectMarkdown();
138
-
139
- const html = md.render(
140
- ['<<< details More **info**', 'Hello', '<<<'].join('\n'),
141
- );
142
-
143
- expect(html).toContain(
144
- '<details><summary>More <strong>info</strong></summary>',
145
- );
146
- });
147
-
148
- test('renders section containers as section elements', () => {
149
- const md = createProjectMarkdown();
150
-
151
- const html = md.render(['::: section', 'Body', ':::'].join('\n'));
152
-
153
- expect(html).toContain('<section>');
154
- expect(html).toContain('<p>Body</p>');
155
- expect(html).toContain('</section>');
156
- });
157
-
158
- test('renders questions with question and spoiler answer', () => {
159
- const md = createProjectMarkdown();
160
-
161
- const html = md.render(
162
- [
163
- '??? question What is a data structure?',
164
- 'An organized collection.',
165
- '???',
166
- ].join('\n'),
167
- );
168
-
169
- expect(html).toContain('<div class="question">');
170
- expect(html).toContain(
171
- '<p class="question-q"><span class="question-label">Q.</span><span>What is a data structure?</span></p>',
172
- );
173
- expect(html).toContain('<p class="question-a-label">A.</p>');
174
- expect(html).toContain(
175
- '<div class="question-a-body" data-pagefind-ignore>',
176
- );
177
- expect(html).toContain('<p>An organized collection.</p>');
178
- });
179
-
180
- test('renders alert containers with custom and default titles', () => {
181
- const md = createProjectMarkdown();
182
-
183
- const html = md.render(
184
- [
185
- '!!! note "Read this"',
186
- 'Body',
187
- '!!!',
188
- '',
189
- '!!! warning',
190
- 'Careful',
191
- '!!!',
192
- ].join('\n'),
193
- );
194
-
195
- expect(html).toContain('<div class="alert note">');
196
- expect(html).toContain('<p class="title" id="read-this">');
197
- expect(html).toContain(
198
- '<div class="alert warning"><p class="title">Warning</p>',
199
- );
200
- expect(html).toContain('<p>Body</p>');
201
- expect(html).toContain('<p>Careful</p>');
202
- });
203
- });