@abreen/tada 1.1.0 → 1.3.1
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 +69 -17
- package/bin/tada.ts +317 -25
- package/bin/validators.test.ts +4 -3
- package/bin/validators.ts +4 -2
- package/build/apply-base-path-plugin.ts +24 -13
- package/build/build-manifest.test.ts +349 -0
- package/build/build-manifest.ts +165 -0
- package/build/bundle.ts +19 -20
- package/build/code.test.ts +7 -3
- package/build/content-watch.ts +6 -6
- package/build/copy.ts +2 -2
- package/build/deflist-id-plugin.ts +1 -1
- package/build/external-links-plugin.ts +2 -2
- package/build/features.ts +1 -1
- package/build/generate-content-assets.ts +12 -7
- package/build/generate-favicon.ts +13 -6
- package/build/generate-fonts.ts +2 -2
- package/build/generate-katex-assets.ts +35 -0
- package/build/generate-manifest.ts +2 -2
- package/build/globals.test.ts +10 -11
- package/build/globals.ts +14 -7
- package/build/json-schema.test.ts +1 -1
- package/build/log.test.ts +1 -1
- package/build/log.ts +2 -2
- package/build/markdown-plugins.test.ts +116 -10
- package/build/pagefind.test.ts +2 -2
- package/build/pagefind.ts +5 -5
- package/build/pdf-text.test.ts +52 -0
- package/build/pipeline.ts +42 -15
- package/build/reachability.test.ts +1 -1
- package/build/serve.ts +20 -8
- package/build/site-variables.ts +13 -5
- package/build/templates.ts +16 -7
- package/build/toc-plugin.test.ts +2 -2
- package/build/toc-plugin.ts +1 -1
- package/build/types.ts +15 -6
- package/build/util.ts +6 -6
- package/build/utils/code.ts +5 -5
- package/build/utils/content-files.ts +3 -3
- package/build/utils/derive-theme.test.ts +1 -1
- package/build/utils/derive-theme.ts +1 -1
- package/build/utils/file-types.test.ts +1 -1
- package/build/utils/front-matter.test.ts +1 -4
- package/build/utils/front-matter.ts +2 -2
- package/build/utils/jdi-runner/LiterateRunner.java +13 -0
- package/build/utils/literate-java.ts +24 -12
- package/build/utils/markdown.ts +41 -9
- package/build/utils/paths.test.ts +2 -2
- package/build/utils/paths.ts +5 -1
- package/build/utils/render.ts +57 -36
- package/build/utils/shiki-highlighter.ts +1 -1
- package/build/validate-internal-links-plugin.test.ts +1 -1
- package/build/validate-internal-links-plugin.ts +1 -1
- package/build/watch-reachability-state.test.ts +2 -2
- package/build/watch-reachability-state.ts +1 -1
- package/build/watch.ts +91 -110
- package/init/content/labs/00/VowelCounter.java.md +129 -0
- package/init/content/labs/00/index.md +29 -0
- package/init/content/labs/index.md +9 -0
- package/{content → init/content}/lectures/01/Pair.java.md +1 -0
- package/{content → init/content}/lectures/index.md +1 -1
- package/{content → init/content}/markdown.md +26 -7
- package/package.json +19 -7
- package/{templates → schema}/nav.schema.json +1 -2
- package/{build → schema}/site.schema.json +3 -5
- package/src/_alerts.scss +4 -0
- package/src/_base.scss +90 -6
- package/src/_layout.scss +39 -2
- package/src/anchor/index.ts +0 -1
- package/src/index.ts +3 -3
- package/src/question/style.scss +8 -3
- package/src/search/index.test.ts +203 -0
- package/src/search/index.ts +3 -79
- package/src/search/pdf-utils.ts +79 -0
- package/src/search/style.scss +2 -20
- package/src/style.scss +20 -101
- package/src/timezone/index.ts +9 -25
- package/src/util.ts +1 -1
- package/templates/_top.html +2 -1
- package/types/dev.d.ts +1 -3
- package/types/site-variables.d.ts +6 -10
- package/types/untyped-modules.d.ts +7 -0
- package/build/utils/jdi-runner/LiterateRunner.class +0 -0
- package/config/authors.json +0 -1
- package/config/nav.json +0 -28
- /package/{content → init/content}/index.md +0 -0
- /package/{content → init/content}/lectures/01/Rectangle.java +0 -0
- /package/{content → init/content}/lectures/01/demo.py +0 -0
- /package/{content → init/content}/lectures/01/index.md +0 -0
- /package/{content → init/content}/lectures/01/lecture1.pdf +0 -0
- /package/{content → init/content}/problem_sets/index.html +0 -0
- /package/{public → init/public}/avatars/alex.jpg +0 -0
- /package/{public → init/public}/test.txt +0 -0
- /package/{templates → schema}/authors.schema.json +0 -0
package/build/serve.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { getDistDir } from './util
|
|
4
|
-
import { makeLogger } from './log
|
|
5
|
-
import { B } from './colors
|
|
3
|
+
import { getDistDir } from './util';
|
|
4
|
+
import { makeLogger } from './log';
|
|
5
|
+
import { B } from './colors';
|
|
6
6
|
|
|
7
7
|
const log = makeLogger(__filename);
|
|
8
8
|
|
|
@@ -20,7 +20,9 @@ try {
|
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function resolvePathname(
|
|
23
|
+
function resolvePathname(
|
|
24
|
+
pathname: string,
|
|
25
|
+
): { filePath: string; mtime: Date } | null {
|
|
24
26
|
let decodedPath: string;
|
|
25
27
|
try {
|
|
26
28
|
decodedPath = decodeURIComponent(pathname);
|
|
@@ -49,17 +51,27 @@ function resolvePathname(pathname: string): string | null {
|
|
|
49
51
|
return null;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
return resolvedPath;
|
|
54
|
+
return { filePath: resolvedPath, mtime: stat.mtime };
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
function createResponse(req: Request): Response {
|
|
56
58
|
const url = new URL(req.url);
|
|
57
|
-
const
|
|
58
|
-
if (!
|
|
59
|
+
const result = resolvePathname(url.pathname);
|
|
60
|
+
if (!result) {
|
|
59
61
|
return new Response('Not Found', { status: 404 });
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
const headers = {
|
|
65
|
+
'Cache-Control': 'no-cache',
|
|
66
|
+
'Last-Modified': result.mtime.toUTCString(),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const ims = req.headers.get('If-Modified-Since');
|
|
70
|
+
if (ims && new Date(ims) >= new Date(headers['Last-Modified'])) {
|
|
71
|
+
return new Response(null, { status: 304, headers });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return new Response(Bun.file(result.filePath), { headers });
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
function getPortArg(): number {
|
package/build/site-variables.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { compile as compileJsonSchema, doValidation } from './json-schema
|
|
4
|
-
import { getProjectDir } from './utils/paths
|
|
5
|
-
import type { SiteVariables } from './types
|
|
6
|
-
import siteSchema from '
|
|
3
|
+
import { compile as compileJsonSchema, doValidation } from './json-schema';
|
|
4
|
+
import { getProjectDir } from './utils/paths';
|
|
5
|
+
import type { SiteVariables } from './types';
|
|
6
|
+
import siteSchema from '../schema/site.schema.json' with { type: 'json' };
|
|
7
|
+
import timezones from '../src/timezone/timezones.json' with { type: 'json' };
|
|
7
8
|
|
|
8
9
|
const configDir = getProjectDir();
|
|
9
10
|
|
|
10
11
|
const DEFAULT: Partial<SiteVariables> = {
|
|
11
12
|
basePath: '/',
|
|
12
|
-
features: { search: true, code: true },
|
|
13
|
+
features: { search: true, code: true, favicon: true },
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
const isValid = compileJsonSchema(siteSchema);
|
|
@@ -48,6 +49,13 @@ function getSiteVariables(env: string): SiteVariables {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
doValidation(isValid, variables, fileName);
|
|
52
|
+
|
|
53
|
+
if (!timezones.some(t => t.value === variables.defaultTimeZone)) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`${fileName}: defaultTimeZone "${variables.defaultTimeZone}" is not a valid time zone`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
return variables;
|
|
52
60
|
}
|
|
53
61
|
|
package/build/templates.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import _ from 'lodash';
|
|
4
|
-
import { compile as compileJsonSchema, doValidation } from './json-schema
|
|
5
|
-
import { makeLogger } from './log
|
|
6
|
-
import { getPackageDir, getConfigDir } from './utils/paths
|
|
4
|
+
import { compile as compileJsonSchema, doValidation } from './json-schema';
|
|
5
|
+
import { makeLogger } from './log';
|
|
6
|
+
import { getPackageDir, getConfigDir } from './utils/paths';
|
|
7
7
|
import type { ValidateFunction } from 'ajv';
|
|
8
|
-
import type { Logger, SiteVariables } from './types
|
|
8
|
+
import type { Logger, SiteVariables } from './types';
|
|
9
9
|
|
|
10
10
|
const log: Logger = makeLogger(__filename);
|
|
11
11
|
|
|
@@ -23,7 +23,12 @@ const renderStack: string[] = [];
|
|
|
23
23
|
let errorStack: string[] | null = null;
|
|
24
24
|
|
|
25
25
|
// JSON data files that live in the user's config directory
|
|
26
|
-
const
|
|
26
|
+
const REQUIRED_JSON_DATA_FILES: string[] = ['nav.json'];
|
|
27
|
+
const OPTIONAL_JSON_DATA_FILES: string[] = ['authors.json'];
|
|
28
|
+
const JSON_DATA_FILES: string[] = [
|
|
29
|
+
...REQUIRED_JSON_DATA_FILES,
|
|
30
|
+
...OPTIONAL_JSON_DATA_FILES,
|
|
31
|
+
];
|
|
27
32
|
|
|
28
33
|
function getHtmlTemplatesDir(): string {
|
|
29
34
|
return path.resolve(getPackageDir(), 'templates');
|
|
@@ -97,12 +102,16 @@ export function compileTemplates(
|
|
|
97
102
|
for (const fileName of JSON_DATA_FILES) {
|
|
98
103
|
const filePath = path.join(jsonDir, fileName);
|
|
99
104
|
if (!fs.existsSync(filePath)) {
|
|
105
|
+
if (REQUIRED_JSON_DATA_FILES.includes(fileName)) {
|
|
106
|
+
throw new Error(`Missing required config file: ${filePath}`);
|
|
107
|
+
}
|
|
100
108
|
continue;
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
// Schema validation
|
|
111
|
+
// Schema validation
|
|
112
|
+
const schemaDir = path.resolve(getPackageDir(), 'schema');
|
|
104
113
|
const schemaFile = `${path.parse(fileName).name}.schema.json`;
|
|
105
|
-
const schemaPath = path.join(
|
|
114
|
+
const schemaPath = path.join(schemaDir, schemaFile);
|
|
106
115
|
if (!fs.existsSync(schemaPath)) {
|
|
107
116
|
throw new Error(`Missing JSON Schema for ${fileName}: ${schemaPath}`);
|
|
108
117
|
}
|
package/build/toc-plugin.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { generateTocHtml, generateCodeTocHtml } from './toc-plugin
|
|
3
|
-
import type { JavaTocEntry } from './types
|
|
2
|
+
import { generateTocHtml, generateCodeTocHtml } from './toc-plugin';
|
|
3
|
+
import type { JavaTocEntry } from './types';
|
|
4
4
|
|
|
5
5
|
describe('generateTocHtml', () => {
|
|
6
6
|
test('returns empty string for empty array', () => {
|
package/build/toc-plugin.ts
CHANGED
package/build/types.ts
CHANGED
|
@@ -2,23 +2,28 @@
|
|
|
2
2
|
export interface SiteVariables {
|
|
3
3
|
base: string;
|
|
4
4
|
basePath: string;
|
|
5
|
-
title
|
|
6
|
-
titlePostfix
|
|
5
|
+
title: string;
|
|
6
|
+
titlePostfix: string;
|
|
7
7
|
symbol?: string;
|
|
8
8
|
faviconSymbol?: string;
|
|
9
|
-
|
|
10
|
-
themeColor?: string;
|
|
9
|
+
themeColor: string;
|
|
11
10
|
faviconColor?: string;
|
|
12
11
|
faviconFontWeight?: number;
|
|
13
12
|
internalDomains?: string[];
|
|
14
|
-
defaultTimeZone
|
|
15
|
-
features?:
|
|
13
|
+
defaultTimeZone: string;
|
|
14
|
+
features?: FeatureConfig;
|
|
16
15
|
codeLanguages?: Record<string, string>;
|
|
17
16
|
tintHue?: number;
|
|
18
17
|
tintAmount?: number;
|
|
19
18
|
vars?: Record<string, unknown>;
|
|
20
19
|
}
|
|
21
20
|
|
|
21
|
+
export type FeatureConfig = {
|
|
22
|
+
search: boolean;
|
|
23
|
+
code: boolean;
|
|
24
|
+
favicon: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
22
27
|
/** A rendered content asset ready to write to dist/ */
|
|
23
28
|
export interface Asset {
|
|
24
29
|
assetPath: string;
|
|
@@ -69,6 +74,7 @@ export interface Logger {
|
|
|
69
74
|
export interface RenderPlainTextOptions {
|
|
70
75
|
filePath: string;
|
|
71
76
|
contentDir: string;
|
|
77
|
+
distDir: string;
|
|
72
78
|
siteVariables: SiteVariables;
|
|
73
79
|
validInternalTargets: Set<string>;
|
|
74
80
|
assetFiles: string[];
|
|
@@ -78,6 +84,7 @@ export interface RenderPlainTextOptions {
|
|
|
78
84
|
export interface RenderCodePageOptions {
|
|
79
85
|
filePath: string;
|
|
80
86
|
contentDir: string;
|
|
87
|
+
distDir: string;
|
|
81
88
|
siteVariables: SiteVariables;
|
|
82
89
|
assetFiles: string[];
|
|
83
90
|
}
|
|
@@ -86,6 +93,7 @@ export interface RenderCodePageOptions {
|
|
|
86
93
|
export interface RenderLiterateJavaOptions {
|
|
87
94
|
filePath: string;
|
|
88
95
|
contentDir: string;
|
|
96
|
+
distDir: string;
|
|
89
97
|
siteVariables: SiteVariables;
|
|
90
98
|
assetFiles: string[];
|
|
91
99
|
skipExecution?: boolean;
|
|
@@ -169,4 +177,5 @@ export interface SiteConfigInput {
|
|
|
169
177
|
base: string;
|
|
170
178
|
basePath: string;
|
|
171
179
|
internalDomains: string[];
|
|
180
|
+
features: FeatureConfig;
|
|
172
181
|
}
|
package/build/util.ts
CHANGED
|
@@ -4,9 +4,9 @@ export {
|
|
|
4
4
|
getFilesByExtensions,
|
|
5
5
|
getValidInternalTargets,
|
|
6
6
|
shouldSkipContentFile,
|
|
7
|
-
} from './utils/content-files
|
|
8
|
-
export { extensionIsMarkdown } from './utils/file-types
|
|
9
|
-
export { createMarkdown } from './utils/markdown
|
|
7
|
+
} from './utils/content-files';
|
|
8
|
+
export { extensionIsMarkdown } from './utils/file-types';
|
|
9
|
+
export { createMarkdown } from './utils/markdown';
|
|
10
10
|
export {
|
|
11
11
|
createApplyBasePath,
|
|
12
12
|
getContentDir,
|
|
@@ -15,12 +15,12 @@ export {
|
|
|
15
15
|
getProjectDir,
|
|
16
16
|
getPublicDir,
|
|
17
17
|
normalizeOutputPath,
|
|
18
|
-
} from './utils/paths
|
|
18
|
+
} from './utils/paths';
|
|
19
19
|
export {
|
|
20
20
|
injectAssetTags,
|
|
21
21
|
renderCodePageAsset,
|
|
22
22
|
renderCopiedContentAsset,
|
|
23
23
|
renderLiterateJavaPageAsset,
|
|
24
24
|
renderPlainTextPageAsset,
|
|
25
|
-
} from './utils/render
|
|
26
|
-
export { parseFrontMatter } from './utils/front-matter
|
|
25
|
+
} from './utils/render';
|
|
26
|
+
export { parseFrontMatter } from './utils/front-matter';
|
package/build/utils/code.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import MarkdownIt from 'markdown-it';
|
|
2
2
|
import { parse as parseJava } from 'java-parser';
|
|
3
3
|
import { JSDOM } from 'jsdom';
|
|
4
|
-
import { makeLogger } from '../log
|
|
5
|
-
import { getHighlighter } from './shiki-highlighter
|
|
6
|
-
import externalLinksPlugin from '../external-links-plugin
|
|
7
|
-
import applyBasePathPlugin from '../apply-base-path-plugin
|
|
8
|
-
import type { JavaTocEntry, SiteVariables } from '../types
|
|
4
|
+
import { makeLogger } from '../log';
|
|
5
|
+
import { getHighlighter } from './shiki-highlighter';
|
|
6
|
+
import externalLinksPlugin from '../external-links-plugin';
|
|
7
|
+
import applyBasePathPlugin from '../apply-base-path-plugin';
|
|
8
|
+
import type { JavaTocEntry, SiteVariables } from '../types';
|
|
9
9
|
|
|
10
10
|
interface CstNode {
|
|
11
11
|
name?: string;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { parseFrontMatterAndContent } from './front-matter
|
|
3
|
+
import { parseFrontMatterAndContent } from './front-matter';
|
|
4
4
|
import {
|
|
5
5
|
getProcessedExtensions,
|
|
6
6
|
extensionIsMarkdown,
|
|
7
7
|
isLiterateJava,
|
|
8
|
-
} from './file-types
|
|
9
|
-
import { getPublicDir, normalizeOutputPath } from './paths
|
|
8
|
+
} from './file-types';
|
|
9
|
+
import { getPublicDir, normalizeOutputPath } from './paths';
|
|
10
10
|
|
|
11
11
|
function walkFiles(dir: string): string[] {
|
|
12
12
|
return fs.readdirSync(dir).flatMap(file => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
import { parse, oklch } from 'culori';
|
|
3
|
-
import { deriveTheme, getTextOnColor } from './derive-theme
|
|
3
|
+
import { deriveTheme, getTextOnColor } from './derive-theme';
|
|
4
4
|
|
|
5
5
|
function getL(hex: string): number {
|
|
6
6
|
return oklch(parse(hex))!.l;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parse, oklch, rgb, toGamut, formatHex } from 'culori';
|
|
2
2
|
import type { Oklch, Rgb } from 'culori';
|
|
3
|
-
import type { DerivedTheme } from '../types
|
|
3
|
+
import type { DerivedTheme } from '../types';
|
|
4
4
|
|
|
5
5
|
// OKLCH lightness range for the theme color used as backgrounds/outlines
|
|
6
6
|
const LIGHT_THEME_L_MIN = 0.35;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import {
|
|
3
|
-
parseFrontMatterAndContent,
|
|
4
|
-
parseFrontMatter,
|
|
5
|
-
} from './front-matter.js';
|
|
2
|
+
import { parseFrontMatterAndContent, parseFrontMatter } from './front-matter';
|
|
6
3
|
|
|
7
4
|
describe('parseFrontMatter', () => {
|
|
8
5
|
test('parses markdown front matter separated by blank line', () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fm from 'front-matter';
|
|
2
|
-
import { extensionIsMarkdown } from './file-types
|
|
3
|
-
import type { ParsedContent } from '../types
|
|
2
|
+
import { extensionIsMarkdown } from './file-types';
|
|
3
|
+
import type { ParsedContent } from '../types';
|
|
4
4
|
|
|
5
5
|
interface RawParsedFrontMatter {
|
|
6
6
|
frontMatter: string | null;
|
|
@@ -95,6 +95,18 @@ public class LiterateRunner {
|
|
|
95
95
|
stderrThread.setDaemon(true);
|
|
96
96
|
stderrThread.start();
|
|
97
97
|
|
|
98
|
+
// Forward stdin to target VM in a background thread
|
|
99
|
+
OutputStream targetStdin = process.getOutputStream();
|
|
100
|
+
Thread stdinThread = new Thread(() -> {
|
|
101
|
+
try {
|
|
102
|
+
System.in.transferTo(targetStdin);
|
|
103
|
+
targetStdin.close();
|
|
104
|
+
} catch (IOException ignored) {
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
stdinThread.setDaemon(true);
|
|
108
|
+
stdinThread.start();
|
|
109
|
+
|
|
98
110
|
int activeBlock = -1;
|
|
99
111
|
|
|
100
112
|
try {
|
|
@@ -199,6 +211,7 @@ public class LiterateRunner {
|
|
|
199
211
|
} catch (VMDisconnectedException ignored) {
|
|
200
212
|
}
|
|
201
213
|
stderrThread.join(1000);
|
|
214
|
+
stdinThread.join(1000);
|
|
202
215
|
}
|
|
203
216
|
|
|
204
217
|
// Output JSON to our own stdout
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import {
|
|
5
|
-
import { createMarkdown } from './markdown
|
|
6
|
-
import { makeLogger } from '../log
|
|
7
|
-
import { parseFrontMatterAndContent } from './front-matter
|
|
4
|
+
import { execFileSync } from 'child_process';
|
|
5
|
+
import { createMarkdown } from './markdown';
|
|
6
|
+
import { makeLogger } from '../log';
|
|
7
|
+
import { parseFrontMatterAndContent } from './front-matter';
|
|
8
8
|
import type {
|
|
9
9
|
SiteVariables,
|
|
10
10
|
LiterateJavaParseResult,
|
|
11
11
|
LiterateCodeBlock,
|
|
12
12
|
LiterateRunnerEntry,
|
|
13
|
-
} from '../types
|
|
13
|
+
} from '../types';
|
|
14
14
|
|
|
15
15
|
const log = makeLogger(__filename);
|
|
16
16
|
|
|
@@ -39,6 +39,12 @@ export function parseLiterateJava(
|
|
|
39
39
|
continue;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// Skip fences with a non-Java language tag (e.g., ```text)
|
|
43
|
+
const lang = token.info.trim();
|
|
44
|
+
if (token.type === 'fence' && lang !== '' && lang !== 'java') {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
const code = token.content;
|
|
43
49
|
const codeLines = code.endsWith('\n')
|
|
44
50
|
? code.slice(0, -1).split('\n')
|
|
@@ -88,7 +94,7 @@ export function compileJavaSource(
|
|
|
88
94
|
log.debug`Compiling ${className}.java (${javaSource.split('\n').length} lines) in ${tempDir}`;
|
|
89
95
|
|
|
90
96
|
try {
|
|
91
|
-
|
|
97
|
+
execFileSync('javac', [`${className}.java`], {
|
|
92
98
|
cwd: tempDir,
|
|
93
99
|
timeout: 30000,
|
|
94
100
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -120,7 +126,7 @@ function ensureRunnerCompiled(runnerDir: string): void {
|
|
|
120
126
|
log.debug`Compiling LiterateRunner.java`;
|
|
121
127
|
|
|
122
128
|
try {
|
|
123
|
-
|
|
129
|
+
execFileSync('javac', ['LiterateRunner.java'], {
|
|
124
130
|
cwd: runnerDir,
|
|
125
131
|
timeout: 30000,
|
|
126
132
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -138,6 +144,7 @@ export function executeLiterateJava(
|
|
|
138
144
|
className: string,
|
|
139
145
|
classPath: string,
|
|
140
146
|
codeBlocks: LiterateCodeBlock[],
|
|
147
|
+
stdin?: string,
|
|
141
148
|
): LiterateRunnerEntry[] {
|
|
142
149
|
const runnerDir = path.join(__dirname, 'jdi-runner');
|
|
143
150
|
ensureRunnerCompiled(runnerDir);
|
|
@@ -150,11 +157,16 @@ export function executeLiterateJava(
|
|
|
150
157
|
log.debug`Running LiterateRunner with ${blockRanges.length} block range(s)`;
|
|
151
158
|
|
|
152
159
|
try {
|
|
153
|
-
const result =
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
const result = execFileSync(
|
|
161
|
+
'java',
|
|
162
|
+
['-cp', runnerDir, 'LiterateRunner', className, classPath, rangesJson],
|
|
163
|
+
{ timeout: 30000, encoding: 'utf-8', input: stdin },
|
|
156
164
|
);
|
|
157
|
-
const
|
|
165
|
+
const raw: [number, string][] = JSON.parse(result);
|
|
166
|
+
const entries: LiterateRunnerEntry[] = raw.map(([blockIndex, output]) => ({
|
|
167
|
+
blockIndex,
|
|
168
|
+
output,
|
|
169
|
+
}));
|
|
158
170
|
log.debug`LiterateRunner returned ${entries.length} output entries`;
|
|
159
171
|
return entries;
|
|
160
172
|
} catch (err: unknown) {
|
|
@@ -174,7 +186,7 @@ export function executeLiterateJava(
|
|
|
174
186
|
|
|
175
187
|
export function checkJavac(): boolean {
|
|
176
188
|
try {
|
|
177
|
-
|
|
189
|
+
execFileSync('javac', ['-version'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
178
190
|
return true;
|
|
179
191
|
} catch {
|
|
180
192
|
return false;
|
package/build/utils/markdown.ts
CHANGED
|
@@ -7,15 +7,18 @@ import markdownItAnchor from 'markdown-it-anchor';
|
|
|
7
7
|
import markdownItFootnote from 'markdown-it-footnote';
|
|
8
8
|
import markdownItDeflist from 'markdown-it-deflist';
|
|
9
9
|
import markdownItContainer from 'markdown-it-container';
|
|
10
|
-
import textToId from '../text-to-id
|
|
11
|
-
import { getHighlighter } from './shiki-highlighter
|
|
12
|
-
import headingSubtitlePlugin from '../heading-subtitle-plugin
|
|
13
|
-
import deflistIdPlugin from '../deflist-id-plugin
|
|
14
|
-
import externalLinksPlugin from '../external-links-plugin
|
|
15
|
-
import validateInternalLinksPlugin from '../validate-internal-links-plugin
|
|
16
|
-
import applyBasePathPlugin from '../apply-base-path-plugin
|
|
17
|
-
import { tocPlugin } from '../toc-plugin
|
|
18
|
-
import
|
|
10
|
+
import textToId from '../text-to-id';
|
|
11
|
+
import { getHighlighter } from './shiki-highlighter';
|
|
12
|
+
import headingSubtitlePlugin from '../heading-subtitle-plugin';
|
|
13
|
+
import deflistIdPlugin from '../deflist-id-plugin';
|
|
14
|
+
import externalLinksPlugin from '../external-links-plugin';
|
|
15
|
+
import validateInternalLinksPlugin from '../validate-internal-links-plugin';
|
|
16
|
+
import applyBasePathPlugin from '../apply-base-path-plugin';
|
|
17
|
+
import { tocPlugin } from '../toc-plugin';
|
|
18
|
+
import markdownItKatex from '@vscode/markdown-it-katex';
|
|
19
|
+
import katex from 'katex';
|
|
20
|
+
import renderA11yString from 'katex/contrib/render-a11y-string';
|
|
21
|
+
import type { SiteVariables } from '../types';
|
|
19
22
|
|
|
20
23
|
interface CreateMarkdownOptions {
|
|
21
24
|
validatorOptions?: Record<string, unknown>;
|
|
@@ -44,6 +47,7 @@ export function createMarkdown(
|
|
|
44
47
|
.use(validateInternalLinksPlugin, validatorOptions)
|
|
45
48
|
.use(applyBasePathPlugin, siteVariables)
|
|
46
49
|
.use(tocPlugin)
|
|
50
|
+
.use(markdownItKatex)
|
|
47
51
|
.use(markdownItContainer, 'details', {
|
|
48
52
|
marker: '<',
|
|
49
53
|
validate: function (params: string) {
|
|
@@ -78,6 +82,34 @@ export function createMarkdown(
|
|
|
78
82
|
},
|
|
79
83
|
});
|
|
80
84
|
|
|
85
|
+
// The @vscode/markdown-it-katex plugin's renderers swallow errors thrown
|
|
86
|
+
// by katex.renderToString() and return error HTML instead of propagating
|
|
87
|
+
// the exception. Override the renderers so parse errors fail the build.
|
|
88
|
+
// The plugin's tokenizer ($ and $$ delimiter parsing) is kept.
|
|
89
|
+
// Each renderer also adds an aria-label with a screen-reader-friendly
|
|
90
|
+
// description generated by katex/contrib/render-a11y-string.
|
|
91
|
+
markdown.renderer.rules.math_inline = (tokens: Token[], idx: number) => {
|
|
92
|
+
const latex = tokens[idx].content;
|
|
93
|
+
const html = katex.renderToString(latex, { throwOnError: true });
|
|
94
|
+
const a11y = markdown.utils.escapeHtml(renderA11yString(latex));
|
|
95
|
+
return html.replace(
|
|
96
|
+
'<span class="katex">',
|
|
97
|
+
`<span class="katex" aria-label="${a11y}">`,
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
const katexBlockRenderer = (tokens: Token[], idx: number) => {
|
|
101
|
+
const latex = tokens[idx].content;
|
|
102
|
+
const html = katex.renderToString(latex, {
|
|
103
|
+
throwOnError: true,
|
|
104
|
+
displayMode: true,
|
|
105
|
+
});
|
|
106
|
+
const a11y = markdown.utils.escapeHtml(renderA11yString(latex));
|
|
107
|
+
return `<p class="katex-block" aria-label="${a11y}">` + html + `</p>\n`;
|
|
108
|
+
};
|
|
109
|
+
markdown.renderer.rules.math_block = katexBlockRenderer;
|
|
110
|
+
markdown.renderer.rules.math_inline_block = katexBlockRenderer;
|
|
111
|
+
markdown.renderer.rules.math_inline_bare_block = katexBlockRenderer;
|
|
112
|
+
|
|
81
113
|
const usedIds = new Map<string, number>();
|
|
82
114
|
markdown.use(markdownItContainer, 'alert', {
|
|
83
115
|
marker: '!',
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
getConfigDir,
|
|
10
10
|
createApplyBasePath,
|
|
11
11
|
normalizeOutputPath,
|
|
12
|
-
} from './paths
|
|
13
|
-
import type { SiteVariables } from '../types
|
|
12
|
+
} from './paths';
|
|
13
|
+
import type { SiteVariables } from '../types';
|
|
14
14
|
|
|
15
15
|
describe('getPackageDir', () => {
|
|
16
16
|
test('returns an absolute path', () => {
|
package/build/utils/paths.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import type { SiteVariables } from '../types
|
|
2
|
+
import type { SiteVariables } from '../types';
|
|
3
3
|
|
|
4
4
|
export function getPackageDir(): string {
|
|
5
5
|
return path.resolve(__dirname, '..', '..');
|
|
@@ -17,6 +17,10 @@ export function getDistDir(): string {
|
|
|
17
17
|
return path.resolve(getProjectDir(), 'dist');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export function getProdDistDir(): string {
|
|
21
|
+
return path.resolve(getProjectDir(), 'dist-prod');
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
export function getPublicDir(): string {
|
|
21
25
|
return path.resolve(getProjectDir(), 'public');
|
|
22
26
|
}
|