@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.
- package/README.md +29 -33
- package/bin/tada.ts +356 -0
- package/bin/validators.test.ts +204 -0
- package/bin/validators.ts +83 -0
- package/{webpack/apply-base-path-plugin.js → build/apply-base-path-plugin.ts} +16 -7
- package/build/bundle.ts +117 -0
- package/{webpack/code.test.js → build/code.test.ts} +6 -7
- package/build/colors.ts +25 -0
- package/build/content-watch.ts +107 -0
- package/build/copy.ts +118 -0
- package/{webpack/deflist-id-plugin.js → build/deflist-id-plugin.ts} +7 -6
- package/{webpack/external-links-plugin.js → build/external-links-plugin.ts} +14 -5
- package/build/features.ts +11 -0
- package/build/generate-content-assets.ts +315 -0
- package/build/generate-favicon.ts +165 -0
- package/build/generate-fonts.ts +31 -0
- package/{webpack/generate-manifest-plugin.js → build/generate-manifest.ts} +29 -36
- package/build/globals.test.ts +101 -0
- package/{webpack/globals.js → build/globals.ts} +28 -13
- package/{webpack/heading-subtitle-plugin.js → build/heading-subtitle-plugin.ts} +4 -2
- package/build/json-schema.test.ts +57 -0
- package/build/json-schema.ts +33 -0
- package/build/log.test.ts +111 -0
- package/build/log.ts +167 -0
- package/{webpack/markdown-plugins.test.js → build/markdown-plugins.test.ts} +94 -9
- package/{webpack/pagefind-plugin.test.js → build/pagefind.test.ts} +74 -13
- package/build/pagefind.ts +339 -0
- package/{webpack/pdf-text.js → build/pdf-text.ts} +47 -27
- package/build/pipeline.ts +93 -0
- package/{webpack/reachability.test.js → build/reachability.test.ts} +3 -3
- package/{webpack/reachability.js → build/reachability.ts} +77 -34
- package/build/serve.ts +112 -0
- package/{webpack/site-variables.js → build/site-variables.ts} +22 -15
- package/{webpack → build}/site.schema.json +3 -10
- package/{webpack/templates.js → build/templates.ts} +35 -33
- package/{webpack/text-to-id.js → build/text-to-id.ts} +2 -2
- package/build/toc-plugin.test.ts +105 -0
- package/{webpack/toc-plugin.js → build/toc-plugin.ts} +32 -13
- package/build/types.ts +172 -0
- package/build/util.ts +26 -0
- package/{webpack/utils/code.js → build/utils/code.ts} +119 -60
- package/{webpack/utils/content-files.js → build/utils/content-files.ts} +40 -35
- package/build/utils/derive-theme.test.ts +111 -0
- package/build/utils/derive-theme.ts +85 -0
- package/build/utils/file-types.test.ts +61 -0
- package/build/utils/file-types.ts +13 -0
- package/build/utils/front-matter.test.ts +80 -0
- package/{webpack/utils/front-matter.js → build/utils/front-matter.ts} +22 -9
- package/{webpack → build}/utils/jdi-runner/LiterateRunner.java +1 -1
- package/{webpack/utils/literate-java.js → build/utils/literate-java.ts} +63 -34
- package/{webpack/utils/markdown.js → build/utils/markdown.ts} +94 -49
- package/build/utils/paths.test.ts +91 -0
- package/{webpack/utils/paths.js → build/utils/paths.ts} +14 -22
- package/{webpack/utils/render.js → build/utils/render.ts} +188 -123
- package/build/utils/shiki-highlighter.ts +29 -0
- package/build/validate-internal-links-plugin.test.ts +106 -0
- package/{webpack/validate-internal-links-plugin.js → build/validate-internal-links-plugin.ts} +47 -20
- package/{webpack/watch-reachability-state.test.js → build/watch-reachability-state.test.ts} +8 -8
- package/{webpack/watch-reachability-state.js → build/watch-reachability-state.ts} +63 -24
- package/{webpack/watch-reload-client.js → build/watch-reload-client.ts} +3 -1
- package/build/watch.ts +573 -0
- package/content/index.md +9 -3
- package/content/markdown.md +2 -1
- package/content/problem_sets/index.html +14 -0
- package/fonts/google-sans-code/woff2/GoogleSansCodeVariable-Italic.woff2 +0 -0
- package/fonts/google-sans-code/woff2/GoogleSansCodeVariable.woff2 +0 -0
- package/fonts/inter/woff2/InterVariable-Italic.woff2 +0 -0
- package/fonts/inter/woff2/InterVariable.woff2 +0 -0
- package/package.json +28 -19
- package/src/_alerts.scss +92 -0
- package/src/_base.scss +106 -0
- package/src/{layout.scss → _layout.scss} +0 -2
- package/src/anchor/style.scss +1 -9
- package/src/code/index.ts +3 -3
- package/src/code.scss +1 -1
- package/src/critical.scss +5 -0
- package/src/header/_base.scss +129 -0
- package/src/header/style.scss +3 -131
- package/src/index.ts +1 -2
- package/src/question/style.scss +1 -1
- package/src/search/index.ts +36 -15
- package/src/search/style.scss +9 -15
- package/src/style.scss +6 -269
- package/src/toc/style.scss +5 -39
- package/src/util.ts +8 -5
- package/templates/_theme.scss +38 -14
- package/tsconfig.json +10 -6
- package/types/file-system-access.d.ts +5 -0
- package/types/markdown-it-plugins.d.ts +11 -0
- package/types/untyped-modules.d.ts +40 -0
- package/bin/tada.js +0 -361
- package/content/problem_sets/index.md +0 -6
- package/webpack/build-state.js +0 -97
- package/webpack/colors.js +0 -15
- package/webpack/config.base.js +0 -151
- package/webpack/config.dev.js +0 -23
- package/webpack/config.prod.js +0 -32
- package/webpack/content-watch-plugin.js +0 -153
- package/webpack/features.js +0 -5
- package/webpack/generate-content-assets-plugin.js +0 -308
- package/webpack/generate-favicon-plugin.js +0 -198
- package/webpack/generate-fonts-plugin.js +0 -69
- package/webpack/json-schema.js +0 -19
- package/webpack/log.js +0 -143
- package/webpack/pagefind-plugin.js +0 -379
- package/webpack/print-flair-plugin.js +0 -22
- package/webpack/serve.js +0 -104
- package/webpack/util.js +0 -49
- package/webpack/utils/define-plugin.js +0 -20
- package/webpack/utils/file-types.js +0 -26
- package/webpack/utils/parse-hsl.js +0 -8
- package/webpack/utils/shiki-highlighter.js +0 -26
- package/webpack/watch.js +0 -166
- /package/{webpack → build}/flair.json +0 -0
- /package/{webpack → build}/utils/jdi-runner/LiterateRunner.class +0 -0
- /package/fonts/google-sans-code/{GoogleSansCodeVariable-Italic.ttf → ttf/GoogleSansCodeVariable-Italic.ttf} +0 -0
- /package/fonts/google-sans-code/{GoogleSansCodeVariable.ttf → ttf/GoogleSansCodeVariable.ttf} +0 -0
- /package/fonts/inter/{InterVariable-Italic.ttf → ttf/InterVariable-Italic.ttf} +0 -0
- /package/fonts/inter/{InterVariable.ttf → ttf/InterVariable.ttf} +0 -0
- /package/types/{dev.ts → dev.d.ts} +0 -0
package/webpack/config.base.js
DELETED
|
@@ -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 };
|
package/webpack/config.dev.js
DELETED
|
@@ -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
|
-
};
|
package/webpack/config.prod.js
DELETED
|
@@ -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;
|
package/webpack/features.js
DELETED
|
@@ -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;
|