@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
@@ -1,151 +0,0 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
- const _ = require('lodash');
5
- const CopyPlugin = require('copy-webpack-plugin');
6
- const MiniCssExtractPlugin = require('mini-css-extract-plugin');
7
- const GenerateContentAssetsPlugin = require('./generate-content-assets-plugin');
8
- const PagefindPlugin = require('./pagefind-plugin');
9
- const GenerateFaviconPlugin = require('./generate-favicon-plugin');
10
- const GenerateManifestPlugin = require('./generate-manifest-plugin');
11
- const GenerateFontsPlugin = require('./generate-fonts-plugin');
12
- const { getDistDir, createDefinePlugin } = require('./util');
13
- const { isFeatureEnabled } = require('./features');
14
- const {
15
- getContentDir,
16
- getPackageDir,
17
- getProjectDir,
18
- getPublicDir,
19
- } = require('./utils/paths');
20
- const { parseHsl } = require('./utils/parse-hsl');
21
-
22
- const distDir = getDistDir();
23
-
24
- function renderThemeScss(siteVariables) {
25
- const templatePath = path.join(getPackageDir(), 'templates/_theme.scss');
26
- const template = fs.readFileSync(templatePath, 'utf-8');
27
- const { hue, saturation, lightness } = parseHsl(siteVariables.themeColor);
28
- const tintHue = siteVariables.tintHue ?? 20;
29
- const tintAmount = siteVariables.tintAmount ?? 100;
30
- const rendered = _.template(template)({
31
- themeHue: hue,
32
- themeSaturation: saturation,
33
- themeLightness: lightness,
34
- tintHue,
35
- tintAmount,
36
- });
37
-
38
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tada-'));
39
- const configDir = path.join(tmpDir, 'config');
40
- fs.mkdirSync(configDir);
41
- fs.writeFileSync(path.join(configDir, '_theme.scss'), rendered);
42
-
43
- return tmpDir;
44
- }
45
-
46
- function createModuleRules(siteVariables) {
47
- const packageDir = getPackageDir();
48
- const themeDir = renderThemeScss(siteVariables);
49
-
50
- return [
51
- { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } },
52
- {
53
- test: /\.tsx?$/,
54
- include: path.resolve(packageDir, 'src'),
55
- loader: 'ts-loader',
56
- options: { configFile: path.resolve(packageDir, 'tsconfig.json') },
57
- },
58
- {
59
- test: /\.(sa|sc|c)ss$/,
60
- use: [
61
- MiniCssExtractPlugin.loader,
62
- {
63
- loader: 'css-loader',
64
- options: {
65
- url: {
66
- // Don't bundle fonts (they are handled by GenerateFontsPlugin)
67
- filter: url => !url.endsWith('.woff2'),
68
- },
69
- },
70
- },
71
- {
72
- loader: 'sass-loader',
73
- options: { sassOptions: { loadPaths: [themeDir, getProjectDir()] } },
74
- },
75
- ],
76
- },
77
- ];
78
- }
79
-
80
- async function createPlugins(
81
- siteVariables,
82
- { defineIsDev = false, plugins = [] } = {},
83
- ) {
84
- return [
85
- new GenerateContentAssetsPlugin(siteVariables),
86
- createDefinePlugin(siteVariables, defineIsDev),
87
- new MiniCssExtractPlugin({
88
- filename: '[name].css',
89
- chunkFilename: '[id].css',
90
- }),
91
- new CopyPlugin({
92
- patterns: [
93
- { from: getPublicDir(), to: '.', noErrorOnMissing: true },
94
- {
95
- from: '**/*.{png,jpg,jpeg,gif,svg,txt,zip}',
96
- context: getContentDir(),
97
- to: '[path][name][ext]',
98
- noErrorOnMissing: true,
99
- },
100
- ],
101
- }),
102
- isFeatureEnabled(siteVariables, 'search')
103
- ? new PagefindPlugin(siteVariables)
104
- : null,
105
- isFeatureEnabled(siteVariables, 'favicon')
106
- ? new GenerateFaviconPlugin(siteVariables)
107
- : null,
108
- isFeatureEnabled(siteVariables, 'favicon')
109
- ? new GenerateManifestPlugin(siteVariables)
110
- : null,
111
- new GenerateFontsPlugin(),
112
- ...plugins,
113
- require('./print-flair-plugin'),
114
- ].filter(Boolean);
115
- }
116
-
117
- async function createBaseConfig({
118
- mode,
119
- siteVariables,
120
- entry,
121
- devtool,
122
- defineIsDev = false,
123
- optimization,
124
- plugins,
125
- }) {
126
- const packageDir = getPackageDir();
127
- return {
128
- mode,
129
- entry,
130
- output: {
131
- path: distDir,
132
- publicPath: siteVariables.basePath,
133
- filename: '[name].bundle.js',
134
- },
135
- resolve: { extensions: ['.ts', '.js', '.json'] },
136
- resolveLoader: {
137
- modules: [
138
- path.resolve(packageDir, 'node_modules'),
139
- path.resolve(packageDir, '..', '..'),
140
- 'node_modules',
141
- ],
142
- },
143
- devtool,
144
- module: { rules: createModuleRules(siteVariables) },
145
- ...(optimization && { optimization }),
146
- plugins: await createPlugins(siteVariables, { defineIsDev, plugins }),
147
- stats: 'errors-only',
148
- };
149
- }
150
-
151
- module.exports = { createBaseConfig };
@@ -1,23 +0,0 @@
1
- const path = require('path');
2
- const ContentWatchPlugin = require('./content-watch-plugin');
3
- const { createBaseConfig } = require('./config.base');
4
- const { getDevSiteVariables } = require('./site-variables');
5
- const { getPackageDir } = require('./utils/paths');
6
-
7
- module.exports = async (env = {}) => {
8
- const siteVariables = getDevSiteVariables();
9
- const packageDir = getPackageDir();
10
- const entry = { index: path.resolve(packageDir, 'src/index.ts') };
11
- if (env.watchMode) {
12
- entry.reload = path.resolve(packageDir, 'webpack/watch-reload-client.js');
13
- }
14
-
15
- return createBaseConfig({
16
- mode: 'development',
17
- devtool: 'inline-source-map',
18
- siteVariables,
19
- entry,
20
- defineIsDev: true,
21
- plugins: [new ContentWatchPlugin(siteVariables)],
22
- });
23
- };
@@ -1,32 +0,0 @@
1
- const path = require('path');
2
- const TerserPlugin = require('terser-webpack-plugin');
3
- const { createBaseConfig } = require('./config.base');
4
- const { compileTemplates } = require('./templates');
5
- const { getProdSiteVariables } = require('./site-variables');
6
- const { getPackageDir } = require('./utils/paths');
7
-
8
- const siteVariables = getProdSiteVariables();
9
-
10
- module.exports = async () => {
11
- compileTemplates(siteVariables);
12
-
13
- return createBaseConfig({
14
- mode: 'production',
15
- siteVariables,
16
- entry: { index: path.resolve(getPackageDir(), 'src/index.ts') },
17
- devtool: false,
18
- optimization: {
19
- minimizer: [
20
- /*
21
- * Bun's event loop doesn't track TerserPlugin's worker threads,
22
- * causing a premature exit; set parallel to false to prevent worker
23
- * threads from being used.
24
- */
25
- new TerserPlugin({
26
- parallel: false,
27
- terserOptions: { output: { comments: false } },
28
- }),
29
- ],
30
- },
31
- });
32
- };
@@ -1,153 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const {
4
- getContentDir,
5
- getContentFiles,
6
- getBuildContentFiles,
7
- } = require('./util');
8
- const { setWatchState } = require('./build-state');
9
- const {
10
- compileTemplates,
11
- getHtmlTemplatesDir,
12
- getJsonDataDir,
13
- JSON_DATA_FILES,
14
- } = require('./templates');
15
- const { getProjectDir } = require('./utils/paths');
16
-
17
- let _needsRestart = false;
18
-
19
- class ContentWatchPlugin {
20
- constructor(siteVariables) {
21
- this.siteVariables = siteVariables;
22
- this.siteConfigPath = path.resolve(
23
- getProjectDir(),
24
- 'config',
25
- 'site.dev.json',
26
- );
27
- }
28
-
29
- apply(compiler) {
30
- let lastSig = null;
31
-
32
- compiler.hooks.make.tap('ContentWatchPlugin', compilation => {
33
- const htmlTemplatesDir = getHtmlTemplatesDir();
34
- const jsonDataDir = getJsonDataDir();
35
-
36
- // Refresh templates cache from disk
37
- try {
38
- compileTemplates(this.siteVariables);
39
- } catch (err) {
40
- compilation.errors.push(err);
41
- for (const fileName of fs.readdirSync(htmlTemplatesDir)) {
42
- compilation.fileDependencies.add(
43
- path.join(htmlTemplatesDir, fileName),
44
- );
45
- }
46
- for (const dataFile of JSON_DATA_FILES) {
47
- const dataPath = path.join(jsonDataDir, dataFile);
48
- if (fs.existsSync(dataPath)) {
49
- compilation.fileDependencies.add(dataPath);
50
- }
51
- }
52
- return;
53
- }
54
-
55
- const contentDir = getContentDir();
56
- const modifiedFiles = new Set(
57
- [...(compiler.modifiedFiles || [])].map(filePath =>
58
- path.resolve(filePath),
59
- ),
60
- );
61
- const normalizedContentDir = path.resolve(contentDir) + path.sep;
62
- const normalizedHtmlDir = path.resolve(htmlTemplatesDir) + path.sep;
63
- const contentFiles = getContentFiles(
64
- contentDir,
65
- Object.keys(this.siteVariables.codeLanguages),
66
- );
67
- const buildContentFiles = getBuildContentFiles(
68
- contentDir,
69
- Object.keys(this.siteVariables.codeLanguages),
70
- );
71
-
72
- // Detect structural changes (files added or deleted) that require a
73
- // fresh compiler with updated content asset caches
74
- const sig = buildContentFiles.slice().sort().join('\0');
75
- let structureChanged = false;
76
- if (lastSig !== null && sig !== lastSig) {
77
- _needsRestart = true;
78
- structureChanged = true;
79
- }
80
- lastSig = sig;
81
-
82
- const changedContentFiles = new Set(
83
- [...modifiedFiles].filter(filePath =>
84
- filePath.startsWith(normalizedContentDir),
85
- ),
86
- );
87
-
88
- // Check if any HTML template or JSON data file changed
89
- const jsonDataPaths = JSON_DATA_FILES.map(f =>
90
- path.resolve(jsonDataDir, f),
91
- );
92
- const templatesChanged = [...modifiedFiles].some(
93
- filePath =>
94
- filePath === path.resolve(htmlTemplatesDir) ||
95
- filePath.startsWith(normalizedHtmlDir) ||
96
- jsonDataPaths.includes(filePath),
97
- );
98
-
99
- setWatchState(compiler, {
100
- contentFiles,
101
- buildContentFiles,
102
- changedContentFiles,
103
- templatesChanged,
104
- structureChanged,
105
- });
106
-
107
- // Tell webpack to watch all content files
108
- for (const filePath of contentFiles) {
109
- compilation.fileDependencies.add(filePath);
110
- }
111
-
112
- // Watch content directories so webpack detects newly added files
113
- function addDirs(dir) {
114
- compilation.contextDependencies.add(dir);
115
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
116
- if (entry.isDirectory()) {
117
- addDirs(path.join(dir, entry.name));
118
- }
119
- }
120
- }
121
- addDirs(contentDir);
122
-
123
- // Watch HTML template files from the package
124
- for (const fileName of fs.readdirSync(htmlTemplatesDir)) {
125
- compilation.fileDependencies.add(path.join(htmlTemplatesDir, fileName));
126
- }
127
-
128
- // Watch JSON data files from the project config
129
- for (const dataFile of JSON_DATA_FILES) {
130
- const dataPath = path.join(jsonDataDir, dataFile);
131
- if (fs.existsSync(dataPath)) {
132
- compilation.fileDependencies.add(dataPath);
133
- }
134
- }
135
-
136
- // Watch the site config file and restart when it changes
137
- compilation.fileDependencies.add(this.siteConfigPath);
138
- const siteConfigChanged = modifiedFiles.has(
139
- path.resolve(this.siteConfigPath),
140
- );
141
- if (siteConfigChanged) {
142
- _needsRestart = true;
143
- }
144
- });
145
- }
146
- }
147
-
148
- ContentWatchPlugin.needsRestart = () => _needsRestart;
149
- ContentWatchPlugin.clearRestart = () => {
150
- _needsRestart = false;
151
- };
152
-
153
- module.exports = ContentWatchPlugin;
@@ -1,5 +0,0 @@
1
- function isFeatureEnabled(siteVariables, featureName) {
2
- return siteVariables.features?.[featureName] !== false;
3
- }
4
-
5
- module.exports = { isFeatureEnabled };
@@ -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;