@abreen/tada 1.8.1 → 1.10.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 +24 -34
- package/bin/tada.ts +1 -1
- package/bin/validators.ts +1 -1
- package/build/apply-base-path-plugin.ts +79 -17
- package/build/bundle.ts +9 -1
- package/build/content-watch.ts +3 -3
- package/build/copy.ts +30 -0
- package/build/generate-content-assets.ts +45 -59
- package/build/pagefind.ts +0 -7
- package/build/serve.ts +4 -0
- package/build/templates.ts +0 -2
- package/build/types.d.ts +19 -1
- package/build/util.ts +3 -0
- package/build/utils/content-files.ts +98 -0
- package/build/utils/include.ts +4 -0
- package/build/utils/python-runner/trace_runner.py +272 -0
- package/build/utils/render.ts +47 -15
- package/build/utils/source-template.ts +27 -0
- package/build/utils/trace-core.ts +203 -0
- package/build/utils/trace-java.ts +132 -0
- package/build/utils/trace-layout.ts +31 -3
- package/build/utils/trace-python.ts +26 -0
- package/build/utils/trace.ts +174 -294
- package/build/validate-internal-links-plugin.ts +16 -4
- package/build/watch/assets.ts +111 -0
- package/build/watch/build-full.ts +124 -0
- package/build/watch/build-incremental.ts +139 -0
- package/build/watch/compiler-types.d.ts +19 -0
- package/build/watch/compiler.ts +112 -0
- package/build/watch/index.ts +22 -0
- package/build/watch/mutations.ts +75 -0
- package/build/watch/planner.ts +230 -0
- package/build/watch/records.ts +190 -0
- package/build/watch/runtime.ts +143 -0
- package/build/watch/snapshot.ts +602 -0
- package/build/watch/validation.ts +36 -0
- package/build/watch-reload-client.ts +10 -5
- package/init/content/labs/01/index.md +6 -0
- package/init/content/labs/01/trace_demo.py +16 -0
- package/init/content/lectures/01/Rectangle.java +2 -0
- package/init/content/lectures/01/index.md +6 -0
- package/init/content/lectures/01/rectangle.py +5 -0
- package/package.json +42 -23
- package/python/command.ts +95 -0
- package/python/module.ts +18 -0
- package/src/_alerts.scss +11 -8
- package/src/_base.scss +25 -29
- package/src/_layout.scss +17 -14
- package/src/_mixins.scss +10 -9
- package/src/anchor/style.scss +4 -2
- package/src/code/index.ts +9 -2
- package/src/code/style.scss +14 -13
- package/src/code.scss +14 -13
- package/src/header/_base.scss +21 -24
- package/src/header/style.scss +16 -12
- package/src/index.ts +3 -0
- package/src/literate/style.scss +4 -4
- package/src/navigate/index.ts +43 -279
- package/src/navigate/lifecycle.ts +1 -1
- package/src/navigate/runtime.ts +341 -0
- package/src/page-update/README.md +4 -0
- package/src/page-update/index.ts +280 -0
- package/src/page-update/style.scss +53 -0
- package/src/print/style.scss +4 -4
- package/src/question/style.scss +10 -6
- package/src/search/index.ts +9 -12
- package/src/search/style.scss +44 -42
- package/src/style.scss +63 -58
- package/src/timezone/style.scss +5 -5
- package/src/toc/style.scss +17 -16
- package/src/trace/style.scss +27 -27
- package/src/validators.ts +45 -0
- package/templates/_footer.html +1 -1
- package/templates/_theme.scss +8 -19
- package/watch/engine.ts +296 -0
- package/watch/events.ts +10 -0
- package/watch/fs-commit.ts +102 -0
- package/watch/types.d.ts +103 -0
- package/build/watch.ts +0 -493
package/build/types.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export interface ContentRenderResult {
|
|
|
50
50
|
errors: Error[];
|
|
51
51
|
changedHtmlAssetPaths: Set<string>;
|
|
52
52
|
removedHtmlAssetPaths: Set<string>;
|
|
53
|
+
removedOutputRelPaths: Set<string>;
|
|
53
54
|
htmlAssetsByPath: Map<string, string>;
|
|
54
55
|
buildContentFiles: string[];
|
|
55
56
|
}
|
|
@@ -75,6 +76,8 @@ export interface RenderPlainTextOptions {
|
|
|
75
76
|
validInternalTargets: Set<string>;
|
|
76
77
|
assetFiles: string[];
|
|
77
78
|
literateJavaOutputPaths?: Set<string>;
|
|
79
|
+
dependencyCollector?: RenderDependencyCollector;
|
|
80
|
+
cachedTraceSourceDir?: string;
|
|
78
81
|
traceCache?: Map<
|
|
79
82
|
string,
|
|
80
83
|
{
|
|
@@ -84,7 +87,12 @@ export interface RenderPlainTextOptions {
|
|
|
84
87
|
mtime: number;
|
|
85
88
|
}
|
|
86
89
|
>;
|
|
87
|
-
|
|
90
|
+
traceToolAvailability?: TraceToolAvailability;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface TraceToolAvailability {
|
|
94
|
+
java?: boolean;
|
|
95
|
+
python?: boolean;
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
/** Options for code page asset rendering */
|
|
@@ -94,6 +102,7 @@ export interface RenderCodePageOptions {
|
|
|
94
102
|
distDir: string;
|
|
95
103
|
siteVariables: SiteVariables;
|
|
96
104
|
assetFiles: string[];
|
|
105
|
+
dependencyCollector?: RenderDependencyCollector;
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
/** Options for literate Java asset rendering */
|
|
@@ -106,6 +115,7 @@ export interface RenderLiterateJavaOptions {
|
|
|
106
115
|
skipExecution?: boolean;
|
|
107
116
|
validInternalTargets: Set<string>;
|
|
108
117
|
literateJavaOutputPaths?: Set<string>;
|
|
118
|
+
dependencyCollector?: RenderDependencyCollector;
|
|
109
119
|
}
|
|
110
120
|
|
|
111
121
|
/** Options for copied content asset rendering */
|
|
@@ -115,6 +125,14 @@ export interface RenderCopiedContentOptions {
|
|
|
115
125
|
siteVariables: SiteVariables;
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
export interface RenderDependencyCollector {
|
|
129
|
+
partials?: Set<string>;
|
|
130
|
+
traceFiles?: Set<string>;
|
|
131
|
+
internalTargets?: Set<string>;
|
|
132
|
+
generatedOutputPaths?: Set<string>;
|
|
133
|
+
setAuthorKey?: (authorKey: string) => void;
|
|
134
|
+
}
|
|
135
|
+
|
|
118
136
|
/** Front matter parse result (from parseFrontMatterAndContent) */
|
|
119
137
|
export interface ParsedContent {
|
|
120
138
|
pageVariables: Record<string, unknown>;
|
package/build/util.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export {
|
|
2
2
|
getBuildContentFiles,
|
|
3
3
|
getContentFiles,
|
|
4
|
+
getContentOutputRelPaths,
|
|
5
|
+
getContentSourceOutputRelPaths,
|
|
4
6
|
getFilesByExtensions,
|
|
5
7
|
getValidInternalTargets,
|
|
6
8
|
shouldSkipContentFile,
|
|
@@ -15,6 +17,7 @@ export {
|
|
|
15
17
|
getProjectDir,
|
|
16
18
|
getPublicDir,
|
|
17
19
|
normalizeOutputPath,
|
|
20
|
+
toPosix,
|
|
18
21
|
} from './utils/paths';
|
|
19
22
|
export {
|
|
20
23
|
injectAssetTags,
|
|
@@ -51,6 +51,104 @@ export function getBuildContentFiles(
|
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
export function getContentSourceOutputRelPaths(
|
|
55
|
+
contentDir: string,
|
|
56
|
+
filePath: string,
|
|
57
|
+
codeExtensions: string[],
|
|
58
|
+
codeEnabled: boolean,
|
|
59
|
+
): Set<string> {
|
|
60
|
+
const processedExtSet = new Set(getProcessedExtensions(codeExtensions));
|
|
61
|
+
const relPath = toPosix(path.relative(contentDir, filePath));
|
|
62
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
63
|
+
const parsed = path.parse(relPath);
|
|
64
|
+
const outputs = new Set<string>();
|
|
65
|
+
|
|
66
|
+
if (!processedExtSet.has(ext)) {
|
|
67
|
+
outputs.add(relPath);
|
|
68
|
+
return outputs;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const buildContentFiles = new Set(
|
|
72
|
+
getBuildContentFiles(contentDir, codeExtensions),
|
|
73
|
+
);
|
|
74
|
+
if (!buildContentFiles.has(filePath)) {
|
|
75
|
+
return outputs;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isLiterateJava(filePath)) {
|
|
79
|
+
outputs.add(toPosix(path.join(parsed.dir, `${parsed.name}.html`)));
|
|
80
|
+
outputs.add(toPosix(path.join(parsed.dir, parsed.name)));
|
|
81
|
+
return outputs;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
extensionIsMarkdown(parsed.ext.toLowerCase()) ||
|
|
86
|
+
parsed.ext.toLowerCase() === '.html'
|
|
87
|
+
) {
|
|
88
|
+
outputs.add(toPosix(path.join(parsed.dir, `${parsed.name}.html`)));
|
|
89
|
+
return outputs;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const codeExtensionSet = new Set(
|
|
93
|
+
codeExtensions.map(value => value.toLowerCase()),
|
|
94
|
+
);
|
|
95
|
+
if (codeExtensionSet.has(ext)) {
|
|
96
|
+
outputs.add(relPath);
|
|
97
|
+
if (codeEnabled) {
|
|
98
|
+
outputs.add(`${relPath}.html`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return outputs;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getContentOutputRelPaths(
|
|
106
|
+
contentDir: string,
|
|
107
|
+
codeExtensions: string[],
|
|
108
|
+
codeEnabled: boolean,
|
|
109
|
+
): Set<string> {
|
|
110
|
+
const buildContentFiles = getBuildContentFiles(contentDir, codeExtensions);
|
|
111
|
+
const codeExtensionSet = new Set(
|
|
112
|
+
codeExtensions.map(ext => ext.toLowerCase()),
|
|
113
|
+
);
|
|
114
|
+
const outputs = new Set<string>();
|
|
115
|
+
|
|
116
|
+
for (const filePath of buildContentFiles) {
|
|
117
|
+
const relPath = toPosix(path.relative(contentDir, filePath));
|
|
118
|
+
const parsed = path.parse(relPath);
|
|
119
|
+
const ext = parsed.ext.toLowerCase();
|
|
120
|
+
|
|
121
|
+
if (isLiterateJava(filePath)) {
|
|
122
|
+
outputs.add(toPosix(path.join(parsed.dir, `${parsed.name}.html`)));
|
|
123
|
+
outputs.add(toPosix(path.join(parsed.dir, parsed.name)));
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (extensionIsMarkdown(ext) || ext === '.html') {
|
|
128
|
+
outputs.add(toPosix(path.join(parsed.dir, `${parsed.name}.html`)));
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (codeExtensionSet.has(ext.slice(1))) {
|
|
133
|
+
outputs.add(relPath);
|
|
134
|
+
if (codeEnabled) {
|
|
135
|
+
outputs.add(`${relPath}.html`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const processedExtSet = new Set(getProcessedExtensions(codeExtensions));
|
|
141
|
+
for (const filePath of walkFiles(contentDir)) {
|
|
142
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
143
|
+
if (processedExtSet.has(ext)) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
outputs.add(toPosix(path.relative(contentDir, filePath)));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return outputs;
|
|
150
|
+
}
|
|
151
|
+
|
|
54
152
|
function addGeneratedRouteAliases(
|
|
55
153
|
pathSet: Set<string>,
|
|
56
154
|
outputPath: string,
|
package/build/utils/include.ts
CHANGED
|
@@ -3,12 +3,14 @@ import path from 'path';
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import { isPartial } from './file-types';
|
|
5
5
|
import { stripHtmlComments } from './render';
|
|
6
|
+
import type { RenderDependencyCollector } from '../types';
|
|
6
7
|
|
|
7
8
|
const MAX_INCLUDE_DEPTH = 10;
|
|
8
9
|
|
|
9
10
|
export function createIncludeFunction(
|
|
10
11
|
callerFilePath: string,
|
|
11
12
|
templateParams: Record<string, unknown>,
|
|
13
|
+
collector?: RenderDependencyCollector,
|
|
12
14
|
depth: number = 0,
|
|
13
15
|
): (relativePath: string) => string {
|
|
14
16
|
return (relativePath: string): string => {
|
|
@@ -25,6 +27,7 @@ export function createIncludeFunction(
|
|
|
25
27
|
`${callerFilePath}: include target must start with "_": ${relativePath}`,
|
|
26
28
|
);
|
|
27
29
|
}
|
|
30
|
+
collector?.partials?.add(resolved);
|
|
28
31
|
if (depth >= MAX_INCLUDE_DEPTH) {
|
|
29
32
|
throw new Error(
|
|
30
33
|
`${callerFilePath}: maximum include depth (${MAX_INCLUDE_DEPTH}) exceeded`,
|
|
@@ -37,6 +40,7 @@ export function createIncludeFunction(
|
|
|
37
40
|
const nestedInclude = createIncludeFunction(
|
|
38
41
|
resolved,
|
|
39
42
|
templateParams,
|
|
43
|
+
collector,
|
|
40
44
|
depth + 1,
|
|
41
45
|
);
|
|
42
46
|
const nestedParams = { ...templateParams, include: nestedInclude };
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import pdb
|
|
7
|
+
import sys
|
|
8
|
+
import types
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CaptureStream(io.TextIOBase):
|
|
12
|
+
def __init__(self, sink):
|
|
13
|
+
self._sink = sink
|
|
14
|
+
|
|
15
|
+
def write(self, s):
|
|
16
|
+
if not isinstance(s, str):
|
|
17
|
+
s = str(s)
|
|
18
|
+
self._sink.append(s)
|
|
19
|
+
return len(s)
|
|
20
|
+
|
|
21
|
+
def flush(self):
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
def isatty(self):
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TracePdb(pdb.Pdb):
|
|
29
|
+
SYNTHETIC_FRAME_NAMES = {"<listcomp>", "<dictcomp>", "<setcomp>"}
|
|
30
|
+
|
|
31
|
+
def __init__(self, target_path, trace_writer, output_chunks):
|
|
32
|
+
super().__init__(nosigint=True, readrc=False)
|
|
33
|
+
self.target_path = os.path.abspath(target_path)
|
|
34
|
+
self.trace_writer = trace_writer
|
|
35
|
+
self.output_chunks = output_chunks
|
|
36
|
+
self.pending_snapshot = None
|
|
37
|
+
self.object_ids = {}
|
|
38
|
+
self.next_object_id = 1
|
|
39
|
+
|
|
40
|
+
def user_line(self, frame):
|
|
41
|
+
if not self._is_traced_frame(frame):
|
|
42
|
+
self.set_step()
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
snapshot = self._capture_snapshot(frame)
|
|
46
|
+
self._emit_pending()
|
|
47
|
+
self.pending_snapshot = snapshot
|
|
48
|
+
self.set_step()
|
|
49
|
+
|
|
50
|
+
def user_call(self, frame, argument_list):
|
|
51
|
+
if self._is_traced_frame(frame):
|
|
52
|
+
self.set_step()
|
|
53
|
+
|
|
54
|
+
def user_return(self, frame, return_value):
|
|
55
|
+
if self._is_traced_frame(frame):
|
|
56
|
+
snapshot = self._capture_snapshot(frame)
|
|
57
|
+
self._emit_pending()
|
|
58
|
+
self.pending_snapshot = snapshot
|
|
59
|
+
self.set_step()
|
|
60
|
+
|
|
61
|
+
def user_exception(self, frame, exc_info):
|
|
62
|
+
if self._is_traced_frame(frame):
|
|
63
|
+
self.set_step()
|
|
64
|
+
|
|
65
|
+
def flush_pending(self):
|
|
66
|
+
self._emit_pending()
|
|
67
|
+
|
|
68
|
+
def _emit_pending(self):
|
|
69
|
+
if self.pending_snapshot is None:
|
|
70
|
+
return
|
|
71
|
+
payload = dict(self.pending_snapshot)
|
|
72
|
+
payload["stdout"] = self._drain_output()
|
|
73
|
+
self.trace_writer.write(json.dumps(payload) + "\n")
|
|
74
|
+
self.trace_writer.flush()
|
|
75
|
+
self.pending_snapshot = None
|
|
76
|
+
|
|
77
|
+
def _drain_output(self):
|
|
78
|
+
if not self.output_chunks:
|
|
79
|
+
return ""
|
|
80
|
+
output = "".join(self.output_chunks)
|
|
81
|
+
self.output_chunks.clear()
|
|
82
|
+
return output
|
|
83
|
+
|
|
84
|
+
def _capture_snapshot(self, frame):
|
|
85
|
+
reachable = {}
|
|
86
|
+
return {
|
|
87
|
+
"line": frame.f_lineno,
|
|
88
|
+
"file": os.path.basename(frame.f_code.co_filename),
|
|
89
|
+
"stack": self._serialize_stack(frame, reachable),
|
|
90
|
+
"heap": self._serialize_heap(reachable),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def _serialize_stack(self, frame, reachable):
|
|
94
|
+
frames = []
|
|
95
|
+
current = frame
|
|
96
|
+
while current is not None:
|
|
97
|
+
if self._is_traced_frame(current):
|
|
98
|
+
locals_json = {}
|
|
99
|
+
for name, value in current.f_locals.items():
|
|
100
|
+
if self._should_skip_local(name, value):
|
|
101
|
+
continue
|
|
102
|
+
locals_json[name] = self._serialize_value(value, reachable)
|
|
103
|
+
frames.append(
|
|
104
|
+
{
|
|
105
|
+
"method": current.f_code.co_name,
|
|
106
|
+
"class": "Python",
|
|
107
|
+
"line": current.f_lineno,
|
|
108
|
+
"locals": locals_json,
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
current = current.f_back
|
|
112
|
+
return frames
|
|
113
|
+
|
|
114
|
+
def _serialize_heap(self, reachable):
|
|
115
|
+
heap = {}
|
|
116
|
+
queue = list(reachable.items())
|
|
117
|
+
seen = set()
|
|
118
|
+
idx = 0
|
|
119
|
+
while idx < len(queue):
|
|
120
|
+
obj_id, obj = queue[idx]
|
|
121
|
+
idx += 1
|
|
122
|
+
if obj_id in seen:
|
|
123
|
+
continue
|
|
124
|
+
seen.add(obj_id)
|
|
125
|
+
heap[obj_id] = self._serialize_heap_object(obj, reachable)
|
|
126
|
+
for new_id, new_obj in reachable.items():
|
|
127
|
+
if new_id not in seen:
|
|
128
|
+
queue.append((new_id, new_obj))
|
|
129
|
+
return heap
|
|
130
|
+
|
|
131
|
+
def _serialize_heap_object(self, obj, reachable):
|
|
132
|
+
if isinstance(obj, str):
|
|
133
|
+
return {"type": "String", "value": obj}
|
|
134
|
+
|
|
135
|
+
if isinstance(obj, (list, tuple)):
|
|
136
|
+
return {
|
|
137
|
+
"type": type(obj).__name__,
|
|
138
|
+
"elements": [self._serialize_value(value, reachable) for value in obj],
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if isinstance(obj, dict) and all(isinstance(key, str) for key in obj.keys()):
|
|
142
|
+
return {
|
|
143
|
+
"type": "dict",
|
|
144
|
+
"fields": {
|
|
145
|
+
key: self._serialize_value(value, reachable)
|
|
146
|
+
for key, value in obj.items()
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
obj_dict = getattr(obj, "__dict__", None)
|
|
151
|
+
if isinstance(obj_dict, dict):
|
|
152
|
+
return {
|
|
153
|
+
"type": type(obj).__name__,
|
|
154
|
+
"fields": {
|
|
155
|
+
key: self._serialize_value(value, reachable)
|
|
156
|
+
for key, value in obj_dict.items()
|
|
157
|
+
if not key.startswith("__") and not self._should_skip_local(key, value)
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {"type": type(obj).__name__, "value": self._safe_repr(obj)}
|
|
162
|
+
|
|
163
|
+
def _serialize_value(self, value, reachable):
|
|
164
|
+
if value is None:
|
|
165
|
+
return {"type": "null"}
|
|
166
|
+
if isinstance(value, bool):
|
|
167
|
+
return {"type": "boolean", "value": value}
|
|
168
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
169
|
+
return {"type": "int", "value": value}
|
|
170
|
+
if isinstance(value, float):
|
|
171
|
+
return {"type": "double", "value": value}
|
|
172
|
+
if isinstance(value, str):
|
|
173
|
+
obj_id = self._get_object_id(value)
|
|
174
|
+
reachable.setdefault(obj_id, value)
|
|
175
|
+
return {"type": "ref", "id": obj_id}
|
|
176
|
+
|
|
177
|
+
if self._is_heap_value(value):
|
|
178
|
+
obj_id = self._get_object_id(value)
|
|
179
|
+
reachable.setdefault(obj_id, value)
|
|
180
|
+
return {"type": "ref", "id": obj_id}
|
|
181
|
+
|
|
182
|
+
return {"type": "unknown"}
|
|
183
|
+
|
|
184
|
+
def _is_heap_value(self, value):
|
|
185
|
+
if isinstance(value, str):
|
|
186
|
+
return True
|
|
187
|
+
if isinstance(value, (list, tuple)):
|
|
188
|
+
return True
|
|
189
|
+
if isinstance(value, dict):
|
|
190
|
+
return all(isinstance(key, str) for key in value.keys())
|
|
191
|
+
obj_dict = getattr(value, "__dict__", None)
|
|
192
|
+
return isinstance(obj_dict, dict)
|
|
193
|
+
|
|
194
|
+
def _get_object_id(self, obj):
|
|
195
|
+
key = id(obj)
|
|
196
|
+
if key not in self.object_ids:
|
|
197
|
+
self.object_ids[key] = f"obj_{self.next_object_id}"
|
|
198
|
+
self.next_object_id += 1
|
|
199
|
+
return self.object_ids[key]
|
|
200
|
+
|
|
201
|
+
def _safe_repr(self, value):
|
|
202
|
+
try:
|
|
203
|
+
return repr(value)
|
|
204
|
+
except Exception:
|
|
205
|
+
return f"<{type(value).__name__}>"
|
|
206
|
+
|
|
207
|
+
def _is_traced_frame(self, frame):
|
|
208
|
+
return (
|
|
209
|
+
os.path.abspath(frame.f_code.co_filename) == self.target_path
|
|
210
|
+
and frame.f_code.co_name not in self.SYNTHETIC_FRAME_NAMES
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def _should_skip_local(self, name, value):
|
|
214
|
+
if name.startswith("."):
|
|
215
|
+
return True
|
|
216
|
+
if name.startswith("__"):
|
|
217
|
+
return True
|
|
218
|
+
if isinstance(value, (types.ModuleType, types.FunctionType, type)):
|
|
219
|
+
return True
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def main():
|
|
224
|
+
if len(sys.argv) != 2:
|
|
225
|
+
print("Usage: trace_runner.py <python_file>", file=sys.stderr)
|
|
226
|
+
sys.exit(1)
|
|
227
|
+
|
|
228
|
+
target_path = os.path.abspath(sys.argv[1])
|
|
229
|
+
target_dir = os.path.dirname(target_path)
|
|
230
|
+
target_name = os.path.basename(target_path)
|
|
231
|
+
|
|
232
|
+
if target_dir and target_dir not in sys.path:
|
|
233
|
+
sys.path.insert(0, target_dir)
|
|
234
|
+
|
|
235
|
+
trace_writer = sys.stdout
|
|
236
|
+
output_chunks = []
|
|
237
|
+
capture = CaptureStream(output_chunks)
|
|
238
|
+
debugger = TracePdb(target_path, trace_writer, output_chunks)
|
|
239
|
+
|
|
240
|
+
code_globals = {
|
|
241
|
+
"__name__": "__main__",
|
|
242
|
+
"__file__": target_path,
|
|
243
|
+
"__package__": None,
|
|
244
|
+
"__cached__": None,
|
|
245
|
+
"__builtins__": __builtins__,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
original_stdout = sys.stdout
|
|
249
|
+
original_stderr = sys.stderr
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
with open(target_path, "r", encoding="utf-8") as f:
|
|
253
|
+
source = f.read()
|
|
254
|
+
compiled = compile(source, target_path, "exec")
|
|
255
|
+
|
|
256
|
+
sys.stdout = capture
|
|
257
|
+
sys.stderr = capture
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
debugger.set_step()
|
|
261
|
+
debugger.runcall(exec, compiled, code_globals, code_globals)
|
|
262
|
+
except SystemExit:
|
|
263
|
+
pass
|
|
264
|
+
finally:
|
|
265
|
+
debugger.flush_pending()
|
|
266
|
+
finally:
|
|
267
|
+
sys.stdout = original_stdout
|
|
268
|
+
sys.stderr = original_stderr
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
main()
|
package/build/utils/render.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
toPosix,
|
|
25
25
|
toUrlPath,
|
|
26
26
|
} from './paths';
|
|
27
|
+
import { applySourceTemplate } from './source-template';
|
|
27
28
|
import pkg from '../../package.json' with { type: 'json' };
|
|
28
29
|
import { parseFrontMatterAndContent } from './front-matter';
|
|
29
30
|
import { createMarkdown } from './markdown';
|
|
@@ -42,6 +43,8 @@ import type {
|
|
|
42
43
|
RenderCodePageOptions,
|
|
43
44
|
RenderLiterateJavaOptions,
|
|
44
45
|
RenderCopiedContentOptions,
|
|
46
|
+
RenderDependencyCollector,
|
|
47
|
+
TraceToolAvailability,
|
|
45
48
|
} from '../types';
|
|
46
49
|
|
|
47
50
|
const log = makeLogger(import.meta.url);
|
|
@@ -80,6 +83,7 @@ interface TemplateParametersInput {
|
|
|
80
83
|
function resolveAuthor(
|
|
81
84
|
pageVariables: Record<string, unknown>,
|
|
82
85
|
filePath: string,
|
|
86
|
+
dependencyCollector?: RenderDependencyCollector,
|
|
83
87
|
): void {
|
|
84
88
|
if (!pageVariables.author) {
|
|
85
89
|
return;
|
|
@@ -93,6 +97,7 @@ function resolveAuthor(
|
|
|
93
97
|
);
|
|
94
98
|
}
|
|
95
99
|
const authorKey = pageVariables.author as string;
|
|
100
|
+
dependencyCollector?.setAuthorKey?.(authorKey);
|
|
96
101
|
const authorEntry = authors[authorKey];
|
|
97
102
|
if (!authorEntry) {
|
|
98
103
|
throw new Error(
|
|
@@ -130,8 +135,6 @@ function createTemplateParameters({
|
|
|
130
135
|
vars: siteVariables.vars || {},
|
|
131
136
|
...createGlobals(pageVariables, siteVariables, subPath),
|
|
132
137
|
site: siteVariables,
|
|
133
|
-
base: siteVariables.base,
|
|
134
|
-
basePath: siteVariables.basePath,
|
|
135
138
|
page: pageVariables,
|
|
136
139
|
content,
|
|
137
140
|
applyBasePath,
|
|
@@ -206,8 +209,10 @@ export function renderPlainTextPageAsset({
|
|
|
206
209
|
validInternalTargets,
|
|
207
210
|
assetFiles,
|
|
208
211
|
literateJavaOutputPaths,
|
|
212
|
+
dependencyCollector,
|
|
213
|
+
cachedTraceSourceDir,
|
|
209
214
|
traceCache,
|
|
210
|
-
|
|
215
|
+
traceToolAvailability,
|
|
211
216
|
}: RenderPlainTextOptions): Asset[] {
|
|
212
217
|
const { dir, name, ext } = path.parse(filePath);
|
|
213
218
|
const subPath = toPosix(path.relative(contentDir, path.join(dir, name)));
|
|
@@ -224,11 +229,13 @@ export function renderPlainTextPageAsset({
|
|
|
224
229
|
watchMode,
|
|
225
230
|
{
|
|
226
231
|
validateInternalLinks: extensionIsMarkdown(ext.toLowerCase()),
|
|
232
|
+
dependencyCollector,
|
|
227
233
|
traceCache,
|
|
228
234
|
contentDir,
|
|
229
235
|
distDir,
|
|
230
236
|
literateJavaOutputPaths,
|
|
231
|
-
|
|
237
|
+
cachedTraceSourceDir,
|
|
238
|
+
traceToolAvailability,
|
|
232
239
|
},
|
|
233
240
|
);
|
|
234
241
|
|
|
@@ -282,7 +289,8 @@ export function renderCodePageAsset({
|
|
|
282
289
|
const subPath = toPosix(path.relative(contentDir, path.join(dir, name)));
|
|
283
290
|
const applyBasePath = createApplyBasePath(siteVariables);
|
|
284
291
|
const lang = siteVariables.codeLanguages![ext.slice(1).toLowerCase()];
|
|
285
|
-
const
|
|
292
|
+
const rawSource = fs.readFileSync(filePath, 'utf-8');
|
|
293
|
+
const sourceCode = applySourceTemplate(rawSource, siteVariables, filePath);
|
|
286
294
|
const pageDirPath = toPosix(path.relative(contentDir, dir));
|
|
287
295
|
|
|
288
296
|
log.info`Rendering code page ${B`${subPath + ext}`}`;
|
|
@@ -346,20 +354,26 @@ export function renderCopiedContentAsset({
|
|
|
346
354
|
|
|
347
355
|
log.info`${label} ${B`${relPath}`}`;
|
|
348
356
|
|
|
349
|
-
if (
|
|
350
|
-
|
|
357
|
+
if (!isFeatureEnabled(siteVariables, 'code')) {
|
|
358
|
+
return [{ assetPath: relPath, content: fs.readFileSync(filePath) }];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const rawSource = fs.readFileSync(filePath, 'utf-8');
|
|
362
|
+
const templated = applySourceTemplate(rawSource, siteVariables, filePath);
|
|
363
|
+
|
|
364
|
+
if (filePath.endsWith('.java')) {
|
|
351
365
|
const pageDirPath = toPosix(
|
|
352
366
|
path.relative(contentDir, path.dirname(filePath)),
|
|
353
367
|
);
|
|
354
368
|
const rewrittenLines = rewriteProseLinks(
|
|
355
|
-
|
|
369
|
+
templated.split('\n'),
|
|
356
370
|
siteVariables,
|
|
357
371
|
pageDirPath,
|
|
358
372
|
);
|
|
359
373
|
return [{ assetPath: relPath, content: rewrittenLines.join('\n') }];
|
|
360
374
|
}
|
|
361
375
|
|
|
362
|
-
return [{ assetPath: relPath, content:
|
|
376
|
+
return [{ assetPath: relPath, content: templated }];
|
|
363
377
|
}
|
|
364
378
|
|
|
365
379
|
/** Parses the file, renders using template, returns HTML & params used to generate page */
|
|
@@ -372,13 +386,16 @@ function renderPlainTextContent(
|
|
|
372
386
|
isWatchMode: boolean,
|
|
373
387
|
{
|
|
374
388
|
validateInternalLinks = true,
|
|
389
|
+
dependencyCollector,
|
|
375
390
|
traceCache,
|
|
376
391
|
contentDir,
|
|
377
392
|
distDir,
|
|
378
393
|
literateJavaOutputPaths,
|
|
379
|
-
|
|
394
|
+
cachedTraceSourceDir,
|
|
395
|
+
traceToolAvailability,
|
|
380
396
|
}: {
|
|
381
397
|
validateInternalLinks?: boolean;
|
|
398
|
+
dependencyCollector?: RenderDependencyCollector;
|
|
382
399
|
traceCache?: Map<
|
|
383
400
|
string,
|
|
384
401
|
{
|
|
@@ -391,7 +408,8 @@ function renderPlainTextContent(
|
|
|
391
408
|
contentDir?: string;
|
|
392
409
|
distDir?: string;
|
|
393
410
|
literateJavaOutputPaths?: Set<string>;
|
|
394
|
-
|
|
411
|
+
cachedTraceSourceDir?: string;
|
|
412
|
+
traceToolAvailability?: TraceToolAvailability;
|
|
395
413
|
} = {},
|
|
396
414
|
): {
|
|
397
415
|
content: string | null;
|
|
@@ -409,6 +427,9 @@ function renderPlainTextContent(
|
|
|
409
427
|
codeExtensions: isFeatureEnabled(siteVariables, 'code')
|
|
410
428
|
? Object.keys(siteVariables.codeLanguages!)
|
|
411
429
|
: [],
|
|
430
|
+
onValidLink: (targetPath: string) => {
|
|
431
|
+
dependencyCollector?.internalTargets?.add(targetPath);
|
|
432
|
+
},
|
|
412
433
|
},
|
|
413
434
|
literateJavaOutputPaths,
|
|
414
435
|
sourceUrlPath,
|
|
@@ -440,7 +461,7 @@ function renderPlainTextContent(
|
|
|
440
461
|
renderInlineField(md, pageVariablesProcessed, 'title');
|
|
441
462
|
renderInlineField(md, pageVariablesProcessed, 'description');
|
|
442
463
|
|
|
443
|
-
resolveAuthor(pageVariablesProcessed, filePath);
|
|
464
|
+
resolveAuthor(pageVariablesProcessed, filePath, dependencyCollector);
|
|
444
465
|
|
|
445
466
|
const parentError = validateParentLink(
|
|
446
467
|
pageVariablesProcessed.parent,
|
|
@@ -450,6 +471,11 @@ function renderPlainTextContent(
|
|
|
450
471
|
if (parentError) {
|
|
451
472
|
throw new Error(parentError);
|
|
452
473
|
}
|
|
474
|
+
if (typeof pageVariablesProcessed.parent === 'string') {
|
|
475
|
+
dependencyCollector?.internalTargets?.add(
|
|
476
|
+
normalizeOutputPath(pageVariablesProcessed.parent),
|
|
477
|
+
);
|
|
478
|
+
}
|
|
453
479
|
|
|
454
480
|
const strippedContent = stripHtmlComments(content);
|
|
455
481
|
|
|
@@ -469,12 +495,14 @@ function renderPlainTextContent(
|
|
|
469
495
|
distDir,
|
|
470
496
|
applyBasePath,
|
|
471
497
|
cache: traceCache,
|
|
472
|
-
|
|
498
|
+
toolAvailability: traceToolAvailability,
|
|
499
|
+
dependencyCollector,
|
|
500
|
+
cachedTraceSourceDir,
|
|
473
501
|
});
|
|
474
502
|
params.renderTrace = helpers.renderTrace;
|
|
475
503
|
}
|
|
476
504
|
|
|
477
|
-
params.include = createIncludeFunction(filePath, params);
|
|
505
|
+
params.include = createIncludeFunction(filePath, params, dependencyCollector);
|
|
478
506
|
|
|
479
507
|
let html: string;
|
|
480
508
|
try {
|
|
@@ -516,6 +544,7 @@ export function renderLiterateJavaPageAsset({
|
|
|
516
544
|
skipExecution,
|
|
517
545
|
validInternalTargets,
|
|
518
546
|
literateJavaOutputPaths,
|
|
547
|
+
dependencyCollector,
|
|
519
548
|
}: RenderLiterateJavaOptions): Asset[] {
|
|
520
549
|
const { dir, name } = path.parse(filePath);
|
|
521
550
|
const className = deriveClassName(filePath);
|
|
@@ -575,6 +604,9 @@ export function renderLiterateJavaPageAsset({
|
|
|
575
604
|
codeExtensions: isFeatureEnabled(siteVariables, 'code')
|
|
576
605
|
? Object.keys(siteVariables.codeLanguages!)
|
|
577
606
|
: [],
|
|
607
|
+
onValidLink: (targetPath: string) => {
|
|
608
|
+
dependencyCollector?.internalTargets?.add(targetPath);
|
|
609
|
+
},
|
|
578
610
|
},
|
|
579
611
|
literateJavaOutputPaths,
|
|
580
612
|
sourceUrlPath,
|
|
@@ -650,7 +682,7 @@ export function renderLiterateJavaPageAsset({
|
|
|
650
682
|
);
|
|
651
683
|
}
|
|
652
684
|
|
|
653
|
-
resolveAuthor(pageVariables, filePath);
|
|
685
|
+
resolveAuthor(pageVariables, filePath, dependencyCollector);
|
|
654
686
|
|
|
655
687
|
const templateParameters = createTemplateParameters({
|
|
656
688
|
pageVariables,
|