@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
@@ -1,308 +0,0 @@
1
- const path = require('path');
2
- const { RawSource } = require('webpack').sources;
3
- const { makeLogger } = require('./log');
4
- const { isFeatureEnabled } = require('./features');
5
- const { initHighlighter } = require('./utils/shiki-highlighter');
6
- const {
7
- getBuildContentFiles,
8
- getContentDir,
9
- getValidInternalTargets,
10
- renderCodePageAsset,
11
- renderCopiedContentAsset,
12
- renderLiterateJavaPageAsset,
13
- renderPlainTextPageAsset,
14
- } = require('./util');
15
- const { isLiterateJava } = require('./utils/file-types');
16
- const { getWatchState, setBuildDelta } = require('./build-state');
17
-
18
- const log = makeLogger(__filename);
19
-
20
- function normalizePaths(paths) {
21
- return [...paths];
22
- }
23
-
24
- class GenerateContentAssetsPlugin {
25
- constructor(siteVariables) {
26
- this.siteVariables = siteVariables || {};
27
- this.loggedCodeDisabled = false;
28
- this.sourceFileCache = new Map();
29
- this.lastBuildFiles = new Set();
30
- }
31
-
32
- getBuildContentFiles(compiler) {
33
- const watchState = getWatchState(compiler);
34
- if (watchState?.buildContentFiles?.size) {
35
- return normalizePaths(watchState.buildContentFiles);
36
- }
37
-
38
- return getBuildContentFiles(
39
- getContentDir(),
40
- Object.keys(this.siteVariables.codeLanguages || {}),
41
- );
42
- }
43
-
44
- getDirtySourceFiles(compiler, buildContentFiles) {
45
- const watchState = getWatchState(compiler);
46
- const isWatch = !!compiler.watching;
47
-
48
- if (
49
- !isWatch ||
50
- this.lastBuildFiles.size === 0 ||
51
- !watchState ||
52
- watchState.templatesChanged
53
- ) {
54
- return new Set(buildContentFiles);
55
- }
56
-
57
- const buildFileSet = new Set(buildContentFiles);
58
- const dirtySourceFiles = new Set();
59
- for (const filePath of watchState.changedContentFiles) {
60
- if (buildFileSet.has(filePath)) {
61
- dirtySourceFiles.add(filePath);
62
- }
63
- }
64
- return dirtySourceFiles;
65
- }
66
-
67
- isCopiedAssetSource(filePath) {
68
- const ext = path.extname(filePath).toLowerCase();
69
- const codeExtensions = this.siteVariables.codeLanguages || {};
70
- return (
71
- ext === '.pdf' ||
72
- Object.prototype.hasOwnProperty.call(codeExtensions, ext.slice(1))
73
- );
74
- }
75
-
76
- getDirtyCopiedSourceFiles(compiler, buildContentFiles) {
77
- const watchState = getWatchState(compiler);
78
- const isWatch = !!compiler.watching;
79
-
80
- if (!isWatch || this.lastBuildFiles.size === 0 || !watchState) {
81
- return buildContentFiles.filter(filePath =>
82
- this.isCopiedAssetSource(filePath),
83
- );
84
- }
85
-
86
- const buildFileSet = new Set(buildContentFiles);
87
- const dirtyCopiedSourceFiles = [];
88
- for (const filePath of watchState.changedContentFiles) {
89
- if (!buildFileSet.has(filePath) || !this.isCopiedAssetSource(filePath)) {
90
- continue;
91
- }
92
- dirtyCopiedSourceFiles.push(filePath);
93
- }
94
-
95
- return dirtyCopiedSourceFiles;
96
- }
97
-
98
- renderSourceAssets(filePath, contentDir, validInternalTargets, compilation) {
99
- const ext = path.extname(filePath).toLowerCase();
100
- const assets = [];
101
-
102
- if (isLiterateJava(filePath)) {
103
- if (isFeatureEnabled(this.siteVariables, 'literateJava')) {
104
- assets.push(
105
- ...renderLiterateJavaPageAsset({
106
- filePath,
107
- contentDir,
108
- siteVariables: this.siteVariables,
109
- compilation,
110
- }),
111
- );
112
- }
113
- return assets;
114
- }
115
-
116
- if (ext === '.html' || ext === '.md' || ext === '.markdown') {
117
- assets.push(
118
- ...renderPlainTextPageAsset({
119
- filePath,
120
- contentDir,
121
- siteVariables: this.siteVariables,
122
- validInternalTargets,
123
- compilation,
124
- }),
125
- );
126
- return assets;
127
- }
128
-
129
- if (ext === '.pdf') {
130
- return assets;
131
- }
132
-
133
- const codeExtensions = this.siteVariables.codeLanguages || {};
134
- if (ext.slice(1) in codeExtensions) {
135
- if (isFeatureEnabled(this.siteVariables, 'code')) {
136
- assets.push(
137
- ...renderCodePageAsset({
138
- filePath,
139
- contentDir,
140
- siteVariables: this.siteVariables,
141
- compilation,
142
- }),
143
- );
144
- } else if (!this.loggedCodeDisabled) {
145
- log.info`Not generating source code pages due to site.features.code = false`;
146
- this.loggedCodeDisabled = true;
147
- }
148
- }
149
-
150
- return assets;
151
- }
152
-
153
- emitUncachedAssets(compilation, copiedSourceFiles, contentDir) {
154
- for (const filePath of copiedSourceFiles) {
155
- for (const asset of renderCopiedContentAsset({ filePath, contentDir })) {
156
- const source = new RawSource(asset.content);
157
- if (compilation.getAsset(asset.assetPath)) {
158
- compilation.updateAsset(asset.assetPath, source);
159
- } else {
160
- compilation.emitAsset(asset.assetPath, source);
161
- }
162
- }
163
- }
164
- }
165
-
166
- updateSourceCache(
167
- filePath,
168
- assets,
169
- changedHtmlAssetPaths,
170
- removedHtmlAssetPaths,
171
- ) {
172
- const previousAssets = this.sourceFileCache.get(filePath) || [];
173
- const nextAssetPaths = new Set(assets.map(asset => asset.assetPath));
174
-
175
- for (const asset of previousAssets) {
176
- if (nextAssetPaths.has(asset.assetPath)) {
177
- continue;
178
- }
179
- if (asset.assetPath.endsWith('.html')) {
180
- removedHtmlAssetPaths.add(asset.assetPath);
181
- }
182
- }
183
-
184
- for (const asset of assets) {
185
- if (asset.assetPath.endsWith('.html')) {
186
- changedHtmlAssetPaths.add(asset.assetPath);
187
- }
188
- }
189
-
190
- this.sourceFileCache.set(filePath, assets);
191
- }
192
-
193
- emitCachedAssets(compilation, buildContentFiles) {
194
- for (const filePath of buildContentFiles) {
195
- const assets = this.sourceFileCache.get(filePath) || [];
196
- for (const asset of assets) {
197
- const source = new RawSource(asset.content);
198
- if (compilation.getAsset(asset.assetPath)) {
199
- compilation.updateAsset(asset.assetPath, source);
200
- } else {
201
- compilation.emitAsset(asset.assetPath, source);
202
- }
203
- }
204
- }
205
- }
206
-
207
- pruneRemovedSources(buildFileSet, removedHtmlAssetPaths) {
208
- for (const filePath of [...this.lastBuildFiles]) {
209
- if (buildFileSet.has(filePath)) {
210
- continue;
211
- }
212
-
213
- const previousAssets = this.sourceFileCache.get(filePath) || [];
214
- for (const asset of previousAssets) {
215
- if (asset.assetPath.endsWith('.html')) {
216
- removedHtmlAssetPaths.add(asset.assetPath);
217
- }
218
- }
219
- this.sourceFileCache.delete(filePath);
220
- }
221
- }
222
-
223
- apply(compiler) {
224
- const pluginName = 'GenerateContentAssetsPlugin';
225
-
226
- compiler.hooks.beforeCompile.tapPromise(pluginName, async () => {
227
- const langs = [
228
- ...new Set([
229
- 'plaintext',
230
- 'text',
231
- ...Object.values(this.siteVariables.codeLanguages || {}),
232
- ]),
233
- ];
234
- await initHighlighter(langs);
235
- });
236
-
237
- compiler.hooks.thisCompilation.tap(pluginName, compilation => {
238
- compilation.hooks.processAssets.tap(
239
- {
240
- name: pluginName,
241
- stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
242
- },
243
- () => {
244
- const contentDir = getContentDir();
245
- const buildContentFiles = this.getBuildContentFiles(compiler);
246
- log.info`Processing ${buildContentFiles.length} content file(s)`;
247
- const buildFileSet = new Set(buildContentFiles);
248
- const dirtySourceFiles = this.getDirtySourceFiles(
249
- compiler,
250
- buildContentFiles,
251
- );
252
- const dirtyCopiedSourceFiles = this.getDirtyCopiedSourceFiles(
253
- compiler,
254
- buildContentFiles,
255
- );
256
- const changedHtmlAssetPaths = new Set();
257
- const removedHtmlAssetPaths = new Set();
258
- const validInternalTargets = getValidInternalTargets(
259
- contentDir,
260
- buildContentFiles,
261
- Object.keys(this.siteVariables.codeLanguages || {}),
262
- );
263
- const watchState = getWatchState(compiler);
264
-
265
- this.pruneRemovedSources(buildFileSet, removedHtmlAssetPaths);
266
-
267
- for (const filePath of dirtySourceFiles) {
268
- let assets;
269
- try {
270
- assets = this.renderSourceAssets(
271
- filePath,
272
- contentDir,
273
- validInternalTargets,
274
- compilation,
275
- );
276
- } catch (err) {
277
- compilation.errors.push(err);
278
- continue;
279
- }
280
- this.updateSourceCache(
281
- filePath,
282
- assets,
283
- changedHtmlAssetPaths,
284
- removedHtmlAssetPaths,
285
- );
286
- }
287
-
288
- this.emitCachedAssets(compilation, buildContentFiles);
289
- this.emitUncachedAssets(
290
- compilation,
291
- dirtyCopiedSourceFiles,
292
- contentDir,
293
- );
294
- this.lastBuildFiles = buildFileSet;
295
-
296
- setBuildDelta(compiler, {
297
- changedSourceFiles: dirtySourceFiles,
298
- changedHtmlAssetPaths,
299
- removedHtmlAssetPaths,
300
- templatesChanged: Boolean(watchState?.templatesChanged),
301
- });
302
- },
303
- );
304
- });
305
- }
306
- }
307
-
308
- module.exports = GenerateContentAssetsPlugin;
@@ -1,198 +0,0 @@
1
- const path = require('path');
2
- const fontkit = require('fontkit');
3
- const sharp = require('sharp');
4
- const { default: pngToIco } = require('png-to-ico');
5
- const { makeLogger } = require('./log');
6
- const { getPackageDir } = require('./utils/paths');
7
-
8
- const log = makeLogger(__filename);
9
-
10
- const FONT_PATH = path.join(
11
- getPackageDir(),
12
- 'fonts',
13
- 'inter',
14
- 'InterVariable.ttf',
15
- );
16
-
17
- function createFaviconSvg(size, color, symbol, font, cssWeight) {
18
- if (size < 10) {
19
- throw new Error(`size is too small: ${size}`);
20
- }
21
-
22
- if (!symbol) {
23
- throw new Error(`invalid symbol: ${symbol}`);
24
- }
25
-
26
- if (symbol.length > 5) {
27
- throw new Error(`symbol is too long (must be <= 5 chars): ${symbol}`);
28
- }
29
-
30
- // Apply variation axes for variable fonts
31
- const instance =
32
- typeof font.getVariation === 'function' &&
33
- font.variationAxes &&
34
- Object.keys(font.variationAxes).length > 0
35
- ? font.getVariation({ wght: cssWeight })
36
- : font;
37
-
38
- const run = instance.layout(symbol);
39
-
40
- // Compute combined bounding box across all glyphs (font units, y-up)
41
- let x = 0;
42
- let totalMinX = Infinity,
43
- totalMaxX = -Infinity;
44
- let totalMinY = Infinity,
45
- totalMaxY = -Infinity;
46
- const advances = [];
47
-
48
- for (let i = 0; i < run.glyphs.length; i++) {
49
- const bbox = run.glyphs[i].bbox;
50
- totalMinX = Math.min(totalMinX, x + bbox.minX);
51
- totalMaxX = Math.max(totalMaxX, x + bbox.maxX);
52
- totalMinY = Math.min(totalMinY, bbox.minY);
53
- totalMaxY = Math.max(totalMaxY, bbox.maxY);
54
- advances.push(x);
55
- x += run.positions[i].xAdvance;
56
- }
57
-
58
- const textW = totalMaxX - totalMinX;
59
- const textH = totalMaxY - totalMinY;
60
-
61
- if (textW <= 0 || textH <= 0) {
62
- throw new Error(`Could not get glyph bounds for symbol: ${symbol}`);
63
- }
64
-
65
- const pad = size * 0.1;
66
- const contentW = size - pad * 2;
67
- const contentH = size - pad * 2;
68
-
69
- // Scale font units to SVG pixels to fill padded content area
70
- const s = Math.min(contentW / textW, contentH / textH);
71
-
72
- // Font coords are y-up; SVG is y-down. The outer transform flips y via scale(s, -s).
73
- // We want the bbox center to land at (size/2, size/2).
74
- const bboxCenterX = (totalMinX + totalMaxX) / 2;
75
- const bboxCenterY = (totalMinY + totalMaxY) / 2;
76
- const tx = size / 2 - bboxCenterX * s;
77
- const ty = size / 2 + bboxCenterY * s;
78
-
79
- // Each glyph is translated by its cumulative x-advance (in font units).
80
- // The outer group handles y-flip and centering.
81
- const paths = run.glyphs
82
- .map((glyph, i) => {
83
- const d = glyph.path.toSVG();
84
- return advances[i] === 0
85
- ? `<path d="${d}" fill="#fff" />`
86
- : `<path d="${d}" transform="translate(${advances[i]} 0)" fill="#fff" />`;
87
- })
88
- .join('\n ');
89
-
90
- return `<svg viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
91
- <rect width="${size}" height="${size}" rx="${size / 10}" fill="${color}" />
92
- <g transform="translate(${tx.toFixed(2)} ${ty.toFixed(2)}) scale(${s.toFixed(4)} ${(-s).toFixed(4)})">
93
- ${paths}
94
- </g>
95
- </svg>`;
96
- }
97
-
98
- class GenerateFaviconPlugin {
99
- _cachedAssets = null;
100
-
101
- constructor(siteVariables, options = {}) {
102
- this.options = {
103
- sizes: options.sizes || [16, 32, 48, 64, 128, 192, 256, 512, 1024],
104
- svgSize: options.svgSize || 512,
105
- filenameBase: options.filenameBase || 'favicon',
106
- color: siteVariables.faviconColor,
107
- symbol: siteVariables.faviconSymbol,
108
- fontWeight: siteVariables.faviconFontWeight || 700,
109
- };
110
- }
111
-
112
- apply(compiler) {
113
- compiler.hooks.thisCompilation.tap('GenerateFaviconPlugin', compilation => {
114
- const wp = compilation.compiler.webpack || {};
115
- const { RawSource } =
116
- (wp.sources && wp.sources) || require('webpack-sources');
117
-
118
- compilation.hooks.processAssets.tapPromise(
119
- {
120
- name: 'GenerateFaviconPlugin',
121
- stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
122
- },
123
- async assets => {
124
- if (this._cachedAssets) {
125
- for (const [name, source] of this._cachedAssets) {
126
- compilation.emitAsset(name, source);
127
- }
128
- return;
129
- }
130
-
131
- const { color, symbol, sizes, filenameBase, svgSize, fontWeight } =
132
- this.options;
133
-
134
- if (!color || !symbol) {
135
- throw new Error('Missing required "color" and/or "symbol" options');
136
- }
137
-
138
- this._cachedAssets = new Map();
139
-
140
- try {
141
- log.info`Generating favicons for "${symbol}"`;
142
- log.debug`Using font file: ${FONT_PATH}`;
143
- const font = fontkit.openSync(FONT_PATH);
144
-
145
- const svgMarkup = createFaviconSvg(
146
- svgSize,
147
- color,
148
- symbol,
149
- font,
150
- fontWeight,
151
- );
152
- const svgSource = new RawSource(svgMarkup);
153
- compilation.emitAsset(`${filenameBase}.svg`, svgSource);
154
- this._cachedAssets.set(`${filenameBase}.svg`, svgSource);
155
-
156
- const pngBuffers = await Promise.all(
157
- sizes.map(async size => {
158
- const svgForSize = createFaviconSvg(
159
- size,
160
- color,
161
- symbol,
162
- font,
163
- fontWeight,
164
- );
165
- const buf = await sharp(Buffer.from(svgForSize))
166
- .png()
167
- .toBuffer();
168
-
169
- const pngName = `${filenameBase}-${size}.png`;
170
- const pngSource = new RawSource(buf);
171
- compilation.emitAsset(pngName, pngSource);
172
- this._cachedAssets.set(pngName, pngSource);
173
- return { size, buf };
174
- }),
175
- );
176
-
177
- const icoBuffer = await pngToIco(
178
- pngBuffers
179
- .filter(b => b.size <= 256)
180
- .sort((a, b) => a.size - b.size)
181
- .map(x => x.buf),
182
- );
183
- const icoSource = new RawSource(icoBuffer);
184
- compilation.emitAsset(`${filenameBase}.ico`, icoSource);
185
- this._cachedAssets.set(`${filenameBase}.ico`, icoSource);
186
- } catch (err) {
187
- this._cachedAssets = null;
188
- compilation.errors.push(
189
- new Error(`Error: ${err && err.message ? err.message : err}`),
190
- );
191
- }
192
- },
193
- );
194
- });
195
- }
196
- }
197
-
198
- module.exports = GenerateFaviconPlugin;
@@ -1,69 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const wawoff2 = require('wawoff2');
4
- const { getPackageDir } = require('./utils/paths');
5
- const { makeLogger } = require('./log');
6
-
7
- const log = makeLogger(__filename);
8
- const FONTS_DIR = path.join(getPackageDir(), 'fonts');
9
-
10
- class GenerateFontsPlugin {
11
- _cachedAssets = null;
12
-
13
- apply(compiler) {
14
- compiler.hooks.thisCompilation.tap('GenerateFontsPlugin', compilation => {
15
- const { RawSource } =
16
- compilation.compiler.webpack?.sources || require('webpack-sources');
17
-
18
- compilation.hooks.processAssets.tapPromise(
19
- {
20
- name: 'GenerateFontsPlugin',
21
- stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
22
- },
23
- async () => {
24
- if (this._cachedAssets) {
25
- for (const [name, source] of this._cachedAssets) {
26
- compilation.emitAsset(name, source);
27
- }
28
- return;
29
- }
30
-
31
- this._cachedAssets = new Map();
32
-
33
- log.info`Bundling fonts from ${FONTS_DIR}`;
34
-
35
- for (const family of fs.readdirSync(FONTS_DIR)) {
36
- const familyDir = path.join(FONTS_DIR, family);
37
- if (!fs.statSync(familyDir).isDirectory()) {
38
- continue;
39
- }
40
-
41
- for (const file of fs.readdirSync(familyDir)) {
42
- const filePath = path.join(familyDir, file);
43
- let source;
44
-
45
- if (file.endsWith('.ttf')) {
46
- const ttfBuf = fs.readFileSync(filePath);
47
- const woff2Buf = Buffer.from(await wawoff2.compress(ttfBuf));
48
- const outName = file.replace(/\.ttf$/, '.woff2');
49
- const assetName = `${family}/${outName}`;
50
-
51
- source = new RawSource(woff2Buf);
52
- compilation.emitAsset(assetName, source);
53
- this._cachedAssets.set(assetName, source);
54
- log.debug`Converted ${family}/${file} to ${outName}`;
55
- } else {
56
- const assetName = `${family}/${file}`;
57
- source = new RawSource(fs.readFileSync(filePath));
58
- compilation.emitAsset(assetName, source);
59
- this._cachedAssets.set(assetName, source);
60
- }
61
- }
62
- }
63
- },
64
- );
65
- });
66
- }
67
- }
68
-
69
- module.exports = GenerateFontsPlugin;
@@ -1,74 +0,0 @@
1
- const timezones = require('../src/timezone/timezones.json');
2
-
3
- module.exports = function createGlobals(site, page, subPath) {
4
- const siteVars = page; // second arg receives siteVariables per render.js call convention
5
- const defaultTz = timezones.find(t => t.value === siteVars.defaultTimeZone);
6
- const timezoneChooser = defaultTz
7
- ? `<select class="time-zone" hidden></select><noscript>Times shown in ${defaultTz.abbreviation}.</noscript>`
8
- : '<select class="time-zone" hidden></select>';
9
-
10
- return {
11
- isHomePage: subPath === 'index',
12
- isoDate,
13
- readableDate,
14
- classNames,
15
- cx: classNames,
16
- timezoneChooser,
17
- };
18
- };
19
-
20
- function isoDate(str) {
21
- if (str == null || str == '') {
22
- return null;
23
- }
24
- const date = new Date(str);
25
- return date.toISOString().slice(0, 10);
26
- }
27
-
28
- function readableDate(date) {
29
- if (date == null || date == '') {
30
- return '';
31
- }
32
-
33
- if (!(date instanceof Date)) {
34
- date = new Date(date);
35
- }
36
-
37
- const str = date.toISOString();
38
- const year = str.slice(0, 4);
39
- let month = str.slice(5, 7);
40
- if (month[0] === '0') {
41
- month = month[1];
42
- }
43
- let day = str.slice(8, 10);
44
- if (day[0] === '0') {
45
- day = day[1];
46
- }
47
-
48
- const months = {
49
- 1: 'January',
50
- 2: 'February',
51
- 3: 'March',
52
- 4: 'April',
53
- 5: 'May',
54
- 6: 'June',
55
- 7: 'July',
56
- 8: 'August',
57
- 9: 'September',
58
- 10: 'October',
59
- 11: 'November',
60
- 12: 'December',
61
- };
62
-
63
- return `${months[month]} ${day}, ${year}`;
64
- }
65
-
66
- function classNames(obj) {
67
- const names = [];
68
- for (const key in obj) {
69
- if (!!obj[key]) {
70
- names.push(key);
71
- }
72
- }
73
- return names.join(' ');
74
- }
@@ -1,19 +0,0 @@
1
- const JsonSchemaCompiler = require('ajv');
2
-
3
- const compiler = new JsonSchemaCompiler();
4
-
5
- function compile(schema) {
6
- return compiler.compile(schema);
7
- }
8
-
9
- function doValidation(validator, input, fileName) {
10
- const valid = validator(input);
11
- if (!valid) {
12
- validator.errors.forEach(error => {
13
- console.error(error);
14
- });
15
- throw new Error(`JSON file failed validation: ${fileName}`);
16
- }
17
- }
18
-
19
- module.exports = { compile, doValidation };