asciidoclint 0.5.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/LICENSE +21 -0
- package/README.md +258 -0
- package/assets/README.md +12 -0
- package/assets/icon.svg +198 -0
- package/assets/logo.svg +203 -0
- package/dist/api/fixes.d.ts +6 -0
- package/dist/api/fixes.js +61 -0
- package/dist/api/lint.d.ts +2 -0
- package/dist/api/lint.js +191 -0
- package/dist/api/rules.d.ts +33 -0
- package/dist/api/rules.js +115 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +86 -0
- package/dist/cli/init-rule.d.ts +7 -0
- package/dist/cli/init-rule.js +74 -0
- package/dist/cli/install-skill.d.ts +10 -0
- package/dist/cli/install-skill.js +37 -0
- package/dist/formatters/json.d.ts +2 -0
- package/dist/formatters/json.js +30 -0
- package/dist/formatters/pretty.d.ts +2 -0
- package/dist/formatters/pretty.js +41 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/parsers/asciidoctor.d.ts +4 -0
- package/dist/parsers/asciidoctor.js +444 -0
- package/dist/parsers/tolerant.d.ts +4 -0
- package/dist/parsers/tolerant.js +528 -0
- package/dist/rules/AD001.d.ts +2 -0
- package/dist/rules/AD001.js +28 -0
- package/dist/rules/AD002.d.ts +2 -0
- package/dist/rules/AD002.js +30 -0
- package/dist/rules/AD003.d.ts +2 -0
- package/dist/rules/AD003.js +28 -0
- package/dist/rules/AD004.d.ts +2 -0
- package/dist/rules/AD004.js +58 -0
- package/dist/rules/AD005.d.ts +2 -0
- package/dist/rules/AD005.js +31 -0
- package/dist/rules/AD006.d.ts +2 -0
- package/dist/rules/AD006.js +53 -0
- package/dist/rules/AD007.d.ts +2 -0
- package/dist/rules/AD007.js +39 -0
- package/dist/rules/AD008.d.ts +2 -0
- package/dist/rules/AD008.js +88 -0
- package/dist/rules/AD010.d.ts +2 -0
- package/dist/rules/AD010.js +39 -0
- package/dist/rules/AD011.d.ts +2 -0
- package/dist/rules/AD011.js +31 -0
- package/dist/rules/AD012.d.ts +2 -0
- package/dist/rules/AD012.js +28 -0
- package/dist/rules/AD013.d.ts +2 -0
- package/dist/rules/AD013.js +43 -0
- package/dist/rules/AD016.d.ts +2 -0
- package/dist/rules/AD016.js +83 -0
- package/dist/rules/AD017.d.ts +2 -0
- package/dist/rules/AD017.js +53 -0
- package/dist/rules/AD019.d.ts +2 -0
- package/dist/rules/AD019.js +58 -0
- package/dist/rules/AD020.d.ts +2 -0
- package/dist/rules/AD020.js +40 -0
- package/dist/rules/AD022.d.ts +2 -0
- package/dist/rules/AD022.js +55 -0
- package/dist/rules/AD023.d.ts +2 -0
- package/dist/rules/AD023.js +59 -0
- package/dist/rules/AD024.d.ts +2 -0
- package/dist/rules/AD024.js +30 -0
- package/dist/rules/AD025.d.ts +2 -0
- package/dist/rules/AD025.js +32 -0
- package/dist/rules/AD026.d.ts +2 -0
- package/dist/rules/AD026.js +26 -0
- package/dist/rules/AD027.d.ts +2 -0
- package/dist/rules/AD027.js +31 -0
- package/dist/rules/AD028.d.ts +2 -0
- package/dist/rules/AD028.js +113 -0
- package/dist/rules/AD029.d.ts +2 -0
- package/dist/rules/AD029.js +46 -0
- package/dist/rules/AD030.d.ts +2 -0
- package/dist/rules/AD030.js +33 -0
- package/dist/rules/AD031.d.ts +2 -0
- package/dist/rules/AD031.js +66 -0
- package/dist/rules/AD032.d.ts +2 -0
- package/dist/rules/AD032.js +81 -0
- package/dist/rules/AD034.d.ts +2 -0
- package/dist/rules/AD034.js +50 -0
- package/dist/rules/AD035.d.ts +2 -0
- package/dist/rules/AD035.js +77 -0
- package/dist/rules/AD036.d.ts +2 -0
- package/dist/rules/AD036.js +34 -0
- package/dist/rules/AD037.d.ts +2 -0
- package/dist/rules/AD037.js +34 -0
- package/dist/rules/AD039.d.ts +2 -0
- package/dist/rules/AD039.js +58 -0
- package/dist/rules/AD040.d.ts +2 -0
- package/dist/rules/AD040.js +56 -0
- package/dist/rules/AD041.d.ts +2 -0
- package/dist/rules/AD041.js +66 -0
- package/dist/rules/AD042.d.ts +2 -0
- package/dist/rules/AD042.js +62 -0
- package/dist/rules/AD043.d.ts +2 -0
- package/dist/rules/AD043.js +30 -0
- package/dist/rules/AD044.d.ts +2 -0
- package/dist/rules/AD044.js +54 -0
- package/dist/rules/AD045.d.ts +2 -0
- package/dist/rules/AD045.js +66 -0
- package/dist/rules/builtin.d.ts +3 -0
- package/dist/rules/builtin.js +81 -0
- package/dist/rules/helpers.d.ts +2 -0
- package/dist/rules/helpers.js +11 -0
- package/dist/rules/registry.d.ts +3 -0
- package/dist/rules/registry.js +34 -0
- package/dist/rules/utils.d.ts +42 -0
- package/dist/rules/utils.js +274 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.js +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/package.json +70 -0
- package/skills/asciidoclint/SKILL.md +84 -0
- package/skills/asciidoclint/references/ai-fix-policy.md +11 -0
- package/skills/asciidoclint/references/result-schema.md +22 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const require = createRequire(typeof __filename === "string" ? __filename : import.meta.url);
|
|
6
|
+
let asciidoctor;
|
|
7
|
+
export function collectAsciidoctorDiagnostics(file) {
|
|
8
|
+
if (shouldIsolateAsciidoctor()) {
|
|
9
|
+
return collectAsciidoctorDiagnosticsInChild(file);
|
|
10
|
+
}
|
|
11
|
+
const processor = getAsciidoctor();
|
|
12
|
+
const logger = processor.MemoryLogger.create();
|
|
13
|
+
processor.LoggerManager.setLogger(logger);
|
|
14
|
+
try {
|
|
15
|
+
processor.convertFile(file, {
|
|
16
|
+
safe: "unsafe",
|
|
17
|
+
to_file: false,
|
|
18
|
+
mkdirs: false,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Diagnostics are collected below; conversion exceptions should not mask lint findings.
|
|
23
|
+
}
|
|
24
|
+
return logger.getMessages().map((message) => {
|
|
25
|
+
return toFinding(message, file);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function collectAsciidoctorBlocks(file) {
|
|
29
|
+
if (shouldIsolateAsciidoctor()) {
|
|
30
|
+
return collectAsciidoctorBlocksInChild(file);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const processor = getAsciidoctor();
|
|
34
|
+
const logger = processor.MemoryLogger.create();
|
|
35
|
+
processor.LoggerManager.setLogger(logger);
|
|
36
|
+
const document = processor.loadFile(file, {
|
|
37
|
+
safe: "unsafe",
|
|
38
|
+
sourcemap: true,
|
|
39
|
+
});
|
|
40
|
+
return blocksFromDocument(document);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function collectAsciidoctorReferenceTargets(file) {
|
|
47
|
+
if (shouldIsolateAsciidoctor()) {
|
|
48
|
+
return collectAsciidoctorReferenceTargetsInChild(file);
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const processor = getAsciidoctor();
|
|
52
|
+
const logger = processor.MemoryLogger.create();
|
|
53
|
+
processor.LoggerManager.setLogger(logger);
|
|
54
|
+
const document = processor.loadFile(file, {
|
|
55
|
+
safe: "unsafe",
|
|
56
|
+
sourcemap: true,
|
|
57
|
+
});
|
|
58
|
+
return referenceTargetsFromDocument(document, file);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function shouldIsolateAsciidoctor() {
|
|
65
|
+
return process.env.ASCIIDOCLINT_ISOLATE_ASCIIDOCTOR === "1" || !!process.versions.electron;
|
|
66
|
+
}
|
|
67
|
+
function collectAsciidoctorDiagnosticsInChild(file) {
|
|
68
|
+
const asciidoctorEntry = require.resolve("asciidoctor");
|
|
69
|
+
const child = spawnSync(process.execPath, ["-e", isolatedAsciidoctorScript(), file, asciidoctorEntry], {
|
|
70
|
+
encoding: "utf8",
|
|
71
|
+
env: {
|
|
72
|
+
...process.env,
|
|
73
|
+
ELECTRON_RUN_AS_NODE: "1",
|
|
74
|
+
ASCIIDOCLINT_ISOLATE_ASCIIDOCTOR: "0",
|
|
75
|
+
},
|
|
76
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
77
|
+
});
|
|
78
|
+
if (child.status !== 0) {
|
|
79
|
+
return [parserFailureFinding(file, child.stderr || child.stdout || `child process exited with status ${child.status}`)];
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
return JSON.parse(child.stdout);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return [parserFailureFinding(file, `failed to parse child diagnostics output: ${error instanceof Error ? error.message : String(error)}`)];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function collectAsciidoctorBlocksInChild(file) {
|
|
89
|
+
const asciidoctorEntry = require.resolve("asciidoctor");
|
|
90
|
+
const child = spawnSync(process.execPath, ["-e", isolatedAsciidoctorBlocksScript(), file, asciidoctorEntry], {
|
|
91
|
+
encoding: "utf8",
|
|
92
|
+
env: {
|
|
93
|
+
...process.env,
|
|
94
|
+
ELECTRON_RUN_AS_NODE: "1",
|
|
95
|
+
ASCIIDOCLINT_ISOLATE_ASCIIDOCTOR: "0",
|
|
96
|
+
},
|
|
97
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
98
|
+
});
|
|
99
|
+
if (child.status !== 0) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(child.stdout);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function collectAsciidoctorReferenceTargetsInChild(file) {
|
|
110
|
+
const asciidoctorEntry = require.resolve("asciidoctor");
|
|
111
|
+
const child = spawnSync(process.execPath, ["-e", isolatedAsciidoctorReferenceTargetsScript(), file, asciidoctorEntry], {
|
|
112
|
+
encoding: "utf8",
|
|
113
|
+
env: {
|
|
114
|
+
...process.env,
|
|
115
|
+
ELECTRON_RUN_AS_NODE: "1",
|
|
116
|
+
ASCIIDOCLINT_ISOLATE_ASCIIDOCTOR: "0",
|
|
117
|
+
},
|
|
118
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
119
|
+
});
|
|
120
|
+
if (child.status !== 0) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(child.stdout);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function isolatedAsciidoctorScript() {
|
|
131
|
+
return `
|
|
132
|
+
const path = require("node:path");
|
|
133
|
+
const file = process.argv[1];
|
|
134
|
+
const asciidoctorEntry = process.argv[2];
|
|
135
|
+
delete globalThis.Opal;
|
|
136
|
+
const asciidoctor = require(asciidoctorEntry)();
|
|
137
|
+
const logger = asciidoctor.MemoryLogger.create();
|
|
138
|
+
asciidoctor.LoggerManager.setLogger(logger);
|
|
139
|
+
const diagramStyles = new Set(["a2s","actdiag","blockdiag","bytefield","dbml","ditaa","dot","dpic","drawio","erd","gnuplot","goat","graphviz","lilypond","matplotlib","mermaid","mmpviz","mscgen","nomnoml","nwdiag","packetdiag","penrose","pikchr","pintora","plantuml","rackdiag","seqdiag","shaape","smcat","state-machine-cat","structurizr","svgbob","symbolator","syntrax","umlet","vega","vega-lite","vegalite","wavedrom"]);
|
|
140
|
+
try {
|
|
141
|
+
asciidoctor.convertFile(file, { safe: "unsafe", to_file: false, mkdirs: false });
|
|
142
|
+
} catch {}
|
|
143
|
+
const findings = logger.getMessages().map((message) => {
|
|
144
|
+
const location = message.getSourceLocation?.();
|
|
145
|
+
const sourceFile = location?.file ? path.resolve(String(location.file)) : path.resolve(file);
|
|
146
|
+
const line = Number(location?.lineno ?? 1);
|
|
147
|
+
const severity = String(message.getSeverity?.() ?? "WARN").toUpperCase() === "ERROR" ? "error" : "warning";
|
|
148
|
+
return {
|
|
149
|
+
ruleId: "AD000",
|
|
150
|
+
alias: "asciidoctor-diagnostic",
|
|
151
|
+
severity,
|
|
152
|
+
message: String(message.getText?.() ?? message),
|
|
153
|
+
range: { start: { file: sourceFile, line, column: 1 } },
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
process.stdout.write(JSON.stringify(findings));
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
function isolatedAsciidoctorBlocksScript() {
|
|
160
|
+
return `
|
|
161
|
+
const fs = require("node:fs");
|
|
162
|
+
const path = require("node:path");
|
|
163
|
+
const file = process.argv[1];
|
|
164
|
+
const asciidoctorEntry = process.argv[2];
|
|
165
|
+
delete globalThis.Opal;
|
|
166
|
+
const asciidoctor = require(asciidoctorEntry)();
|
|
167
|
+
const logger = asciidoctor.MemoryLogger.create();
|
|
168
|
+
asciidoctor.LoggerManager.setLogger(logger);
|
|
169
|
+
function pos(file, line, column) {
|
|
170
|
+
return { file, line, column };
|
|
171
|
+
}
|
|
172
|
+
function findClosingDelimiter(sourceFile, startLine, delimiter) {
|
|
173
|
+
try {
|
|
174
|
+
const lines = fs.readFileSync(sourceFile, "utf8").split(/\\r?\\n/);
|
|
175
|
+
for (let index = startLine; index < lines.length; index += 1) {
|
|
176
|
+
if ((lines[index] || "").trim() === delimiter) {
|
|
177
|
+
return { line: index + 1, column: (lines[index] || "").length + 1 };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
function blocksFromDocument(document) {
|
|
184
|
+
const tables = document.findBy({ context: "table" }) || [];
|
|
185
|
+
const images = document.findBy({ context: "image" }) || [];
|
|
186
|
+
const diagrams = []
|
|
187
|
+
.concat(document.findBy({ context: "listing" }) || [])
|
|
188
|
+
.concat(document.findBy({ context: "literal" }) || [])
|
|
189
|
+
.filter((block) => diagramStyles.has(String((block.getStyle && block.getStyle()) || "")));
|
|
190
|
+
return []
|
|
191
|
+
.concat(tables.map((block) => {
|
|
192
|
+
const location = block.getSourceLocation && block.getSourceLocation();
|
|
193
|
+
const sourceFile = location && location.file ? path.resolve(String(location.file)) : path.resolve(file);
|
|
194
|
+
const startLine = Number((location && (location.getLineNumber && location.getLineNumber())) || (location && location.lineno) || 1);
|
|
195
|
+
const end = findClosingDelimiter(sourceFile, startLine, "|===");
|
|
196
|
+
const attributes = block.getAttributes ? block.getAttributes() : {};
|
|
197
|
+
return {
|
|
198
|
+
kind: "block",
|
|
199
|
+
type: "table",
|
|
200
|
+
style: block.getStyle && block.getStyle() ? String(block.getStyle()) : undefined,
|
|
201
|
+
title: block.getTitle && block.getTitle() ? String(block.getTitle()) : undefined,
|
|
202
|
+
attributes: Object.fromEntries(Object.entries(attributes).filter(([, value]) => ["string", "number", "boolean"].includes(typeof value)).map(([key, value]) => [key, String(value)])),
|
|
203
|
+
table: tableInfo(block),
|
|
204
|
+
range: { start: pos(sourceFile, startLine, 1), end: end ? pos(sourceFile, end.line, end.column) : undefined },
|
|
205
|
+
contentRange: end ? { start: pos(sourceFile, startLine + 1, 1), end: pos(sourceFile, end.line - 1, 1) } : undefined,
|
|
206
|
+
};
|
|
207
|
+
}))
|
|
208
|
+
.concat(images.map((block) => blockNode(block, "image")))
|
|
209
|
+
.concat(diagrams.map((block) => blockNode(block, "diagram")));
|
|
210
|
+
}
|
|
211
|
+
function blockNode(block, type) {
|
|
212
|
+
const location = block.getSourceLocation && block.getSourceLocation();
|
|
213
|
+
const sourceFile = location && location.file ? path.resolve(String(location.file)) : path.resolve(file);
|
|
214
|
+
const startLine = Number((location && (location.getLineNumber && location.getLineNumber())) || (location && location.lineno) || 1);
|
|
215
|
+
const attributes = block.getAttributes ? block.getAttributes() : {};
|
|
216
|
+
return {
|
|
217
|
+
kind: "block",
|
|
218
|
+
type,
|
|
219
|
+
style: block.getStyle && block.getStyle() ? String(block.getStyle()) : undefined,
|
|
220
|
+
title: block.getTitle && block.getTitle() ? String(block.getTitle()) : undefined,
|
|
221
|
+
attributes: Object.fromEntries(Object.entries(attributes).filter(([, value]) => ["string", "number", "boolean"].includes(typeof value)).map(([key, value]) => [key, typeof value === "boolean" ? value : String(value)])),
|
|
222
|
+
range: { start: pos(sourceFile, startLine, 1) },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function primitiveNumber(value) {
|
|
226
|
+
return typeof value === "number" ? value : undefined;
|
|
227
|
+
}
|
|
228
|
+
function tableInfo(block) {
|
|
229
|
+
const rows = block.getRows && block.getRows();
|
|
230
|
+
const cells = []
|
|
231
|
+
.concat(rows && rows.head ? rows.head.flat() : [])
|
|
232
|
+
.concat(rows && rows.body ? rows.body.flat() : [])
|
|
233
|
+
.concat(rows && rows.foot ? rows.foot.flat() : []);
|
|
234
|
+
return {
|
|
235
|
+
columnCount: primitiveNumber(block.getColumnCount && block.getColumnCount()),
|
|
236
|
+
renderedCellCount: cells.length,
|
|
237
|
+
renderedCellSourceLines: cells
|
|
238
|
+
.map((cell) => {
|
|
239
|
+
const location = cell && cell.getSourceLocation && cell.getSourceLocation();
|
|
240
|
+
return Number((location && (location.getLineNumber && location.getLineNumber())) || (location && location.lineno) || 0);
|
|
241
|
+
})
|
|
242
|
+
.filter((line) => line > 0),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
const document = asciidoctor.loadFile(file, { safe: "unsafe", sourcemap: true });
|
|
247
|
+
process.stdout.write(JSON.stringify(blocksFromDocument(document)));
|
|
248
|
+
} catch {
|
|
249
|
+
process.stdout.write("[]");
|
|
250
|
+
}
|
|
251
|
+
`;
|
|
252
|
+
}
|
|
253
|
+
function isolatedAsciidoctorReferenceTargetsScript() {
|
|
254
|
+
return `
|
|
255
|
+
const path = require("node:path");
|
|
256
|
+
const file = process.argv[1];
|
|
257
|
+
const asciidoctorEntry = process.argv[2];
|
|
258
|
+
delete globalThis.Opal;
|
|
259
|
+
const asciidoctor = require(asciidoctorEntry)();
|
|
260
|
+
const logger = asciidoctor.MemoryLogger.create();
|
|
261
|
+
asciidoctor.LoggerManager.setLogger(logger);
|
|
262
|
+
function referenceTargetsFromDocument(document) {
|
|
263
|
+
const refs = document.getCatalog && document.getCatalog().refs;
|
|
264
|
+
const map = refs && refs.$$smap ? refs.$$smap : {};
|
|
265
|
+
return Object.entries(map).flatMap(([id, node]) => {
|
|
266
|
+
const location = node && node.getSourceLocation ? node.getSourceLocation() : node && node.source_location;
|
|
267
|
+
const sourceFile = location && location.file ? path.resolve(String(location.file)) : path.resolve(file);
|
|
268
|
+
const title = node && node.title ? String(node.title) : undefined;
|
|
269
|
+
const context = node && node.context ? String(node.context) : undefined;
|
|
270
|
+
const aliases = title && context === "section" && title !== id ? [title] : [];
|
|
271
|
+
return [{ id, file: sourceFile, aliases, source: "asciidoctor" }];
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const document = asciidoctor.loadFile(file, { safe: "unsafe", sourcemap: true });
|
|
276
|
+
process.stdout.write(JSON.stringify(referenceTargetsFromDocument(document)));
|
|
277
|
+
} catch {
|
|
278
|
+
process.stdout.write("[]");
|
|
279
|
+
}
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
282
|
+
function blocksFromDocument(document) {
|
|
283
|
+
const tables = document.findBy?.({ context: "table" }) ?? [];
|
|
284
|
+
const images = document.findBy?.({ context: "image" }) ?? [];
|
|
285
|
+
const diagrams = [
|
|
286
|
+
...(document.findBy?.({ context: "listing" }) ?? []),
|
|
287
|
+
...(document.findBy?.({ context: "literal" }) ?? []),
|
|
288
|
+
].filter((block) => diagramStyles.has(String(block.getStyle?.() ?? "")));
|
|
289
|
+
return [
|
|
290
|
+
...tables.map((block) => {
|
|
291
|
+
const location = block.getSourceLocation?.();
|
|
292
|
+
const sourceFile = location?.file ? path.resolve(String(location.file)) : undefined;
|
|
293
|
+
if (!sourceFile) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const startLine = Number(location.getLineNumber?.() ?? location.lineno ?? 1);
|
|
297
|
+
const end = findClosingDelimiter(sourceFile, startLine, "|===");
|
|
298
|
+
const attributes = primitiveAttributes(block.getAttributes?.() ?? {});
|
|
299
|
+
return {
|
|
300
|
+
kind: "block",
|
|
301
|
+
type: "table",
|
|
302
|
+
style: block.getStyle?.() ? String(block.getStyle()) : undefined,
|
|
303
|
+
title: block.getTitle?.() ? String(block.getTitle()) : undefined,
|
|
304
|
+
attributes,
|
|
305
|
+
table: tableInfo(block),
|
|
306
|
+
range: {
|
|
307
|
+
start: { file: sourceFile, line: startLine, column: 1 },
|
|
308
|
+
end: end ? { file: sourceFile, line: end.line, column: end.column } : undefined,
|
|
309
|
+
},
|
|
310
|
+
contentRange: end ? {
|
|
311
|
+
start: { file: sourceFile, line: startLine + 1, column: 1 },
|
|
312
|
+
end: { file: sourceFile, line: end.line - 1, column: 1 },
|
|
313
|
+
} : undefined,
|
|
314
|
+
};
|
|
315
|
+
}),
|
|
316
|
+
...images.map((block) => genericBlockNode(block, "image")),
|
|
317
|
+
...diagrams.map((block) => genericBlockNode(block, "diagram")),
|
|
318
|
+
].filter((block) => !!block);
|
|
319
|
+
}
|
|
320
|
+
function referenceTargetsFromDocument(document, rootFile) {
|
|
321
|
+
const refs = document.getCatalog?.().refs;
|
|
322
|
+
const map = refs?.$$smap ?? {};
|
|
323
|
+
return Object.entries(map).map(([id, node]) => {
|
|
324
|
+
const location = node.getSourceLocation?.() ?? node.source_location;
|
|
325
|
+
const sourceFile = location?.file ? path.resolve(String(location.file)) : path.resolve(rootFile);
|
|
326
|
+
const title = node.title ? String(node.title) : undefined;
|
|
327
|
+
const context = node.context ? String(node.context) : undefined;
|
|
328
|
+
return {
|
|
329
|
+
id,
|
|
330
|
+
file: sourceFile,
|
|
331
|
+
aliases: title && context === "section" && title !== id ? [title] : [],
|
|
332
|
+
source: "asciidoctor",
|
|
333
|
+
};
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const diagramStyles = new Set([
|
|
337
|
+
"a2s", "actdiag", "blockdiag", "bytefield", "dbml", "ditaa", "dot", "dpic",
|
|
338
|
+
"drawio", "erd", "gnuplot", "goat", "graphviz", "lilypond", "matplotlib",
|
|
339
|
+
"mermaid", "mmpviz", "mscgen", "nomnoml", "nwdiag", "packetdiag", "penrose",
|
|
340
|
+
"pikchr", "pintora", "plantuml", "rackdiag", "seqdiag", "shaape", "smcat",
|
|
341
|
+
"state-machine-cat", "structurizr", "svgbob", "symbolator", "syntrax",
|
|
342
|
+
"umlet", "vega", "vega-lite", "vegalite", "wavedrom",
|
|
343
|
+
]);
|
|
344
|
+
function genericBlockNode(block, type) {
|
|
345
|
+
const location = block.getSourceLocation?.();
|
|
346
|
+
const sourceFile = location?.file ? path.resolve(String(location.file)) : undefined;
|
|
347
|
+
if (!sourceFile) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
const startLine = Number(location.getLineNumber?.() ?? location.lineno ?? 1);
|
|
351
|
+
return {
|
|
352
|
+
kind: "block",
|
|
353
|
+
type,
|
|
354
|
+
style: block.getStyle?.() ? String(block.getStyle()) : undefined,
|
|
355
|
+
title: block.getTitle?.() ? String(block.getTitle()) : undefined,
|
|
356
|
+
attributes: primitiveAttributes(block.getAttributes?.() ?? {}),
|
|
357
|
+
range: {
|
|
358
|
+
start: { file: sourceFile, line: startLine, column: 1 },
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function tableInfo(block) {
|
|
363
|
+
const rows = block.getRows?.();
|
|
364
|
+
const cells = [
|
|
365
|
+
...(rows?.head ?? []).flat(),
|
|
366
|
+
...(rows?.body ?? []).flat(),
|
|
367
|
+
...(rows?.foot ?? []).flat(),
|
|
368
|
+
];
|
|
369
|
+
return {
|
|
370
|
+
columnCount: primitiveNumber(block.getColumnCount?.()),
|
|
371
|
+
renderedCellCount: cells.length,
|
|
372
|
+
renderedCellSourceLines: cells
|
|
373
|
+
.map((cell) => {
|
|
374
|
+
const location = cell?.getSourceLocation?.();
|
|
375
|
+
return Number(location?.getLineNumber?.() ?? location?.lineno ?? 0);
|
|
376
|
+
})
|
|
377
|
+
.filter((line) => line > 0),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function primitiveNumber(value) {
|
|
381
|
+
return typeof value === "number" ? value : undefined;
|
|
382
|
+
}
|
|
383
|
+
function primitiveAttributes(attributes) {
|
|
384
|
+
return Object.fromEntries(Object.entries(attributes)
|
|
385
|
+
.filter(([, value]) => ["string", "number", "boolean"].includes(typeof value))
|
|
386
|
+
.map(([key, value]) => [key, typeof value === "boolean" ? value : String(value)]));
|
|
387
|
+
}
|
|
388
|
+
function findClosingDelimiter(sourceFile, startLine, delimiter) {
|
|
389
|
+
try {
|
|
390
|
+
const lines = fs.readFileSync(sourceFile, "utf8").split(/\r?\n/);
|
|
391
|
+
for (let index = startLine; index < lines.length; index += 1) {
|
|
392
|
+
if ((lines[index] ?? "").trim() === delimiter) {
|
|
393
|
+
return { line: index + 1, column: (lines[index] ?? "").length + 1 };
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
function parserFailureFinding(file, detail) {
|
|
403
|
+
return {
|
|
404
|
+
ruleId: "AD000",
|
|
405
|
+
alias: "asciidoctor-diagnostic",
|
|
406
|
+
severity: "warning",
|
|
407
|
+
message: `Asciidoctor diagnostics failed: ${detail.split("\\n")[0]}`,
|
|
408
|
+
range: {
|
|
409
|
+
start: { file: path.resolve(file), line: 1, column: 1 },
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function toFinding(message, file) {
|
|
414
|
+
const location = message.getSourceLocation?.();
|
|
415
|
+
const sourceFile = location?.file ? path.resolve(String(location.file)) : path.resolve(file);
|
|
416
|
+
const line = Number(location?.lineno ?? 1);
|
|
417
|
+
const severity = String(message.getSeverity?.() ?? "WARN").toUpperCase() === "ERROR" ? "error" : "warning";
|
|
418
|
+
return {
|
|
419
|
+
ruleId: "AD000",
|
|
420
|
+
alias: "asciidoctor-diagnostic",
|
|
421
|
+
severity,
|
|
422
|
+
message: String(message.getText?.() ?? message),
|
|
423
|
+
range: {
|
|
424
|
+
start: { file: sourceFile, line, column: 1 },
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function getAsciidoctor() {
|
|
429
|
+
asciidoctor ??= loadAsciidoctor();
|
|
430
|
+
return asciidoctor;
|
|
431
|
+
}
|
|
432
|
+
function loadAsciidoctor() {
|
|
433
|
+
const globalObject = globalThis;
|
|
434
|
+
const hadOpal = Object.prototype.hasOwnProperty.call(globalObject, "Opal");
|
|
435
|
+
if (hadOpal) {
|
|
436
|
+
Reflect.deleteProperty(globalObject, "Opal");
|
|
437
|
+
}
|
|
438
|
+
const factory = require("asciidoctor");
|
|
439
|
+
const processor = factory();
|
|
440
|
+
// Asciidoctor.js keeps using globalThis.Opal after initialization. If the
|
|
441
|
+
// editor host already had an Opal object from another extension/runtime,
|
|
442
|
+
// restoring or reusing it can recurse in Opal constant resolution.
|
|
443
|
+
return processor;
|
|
444
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NormalizedDocument } from "../types.js";
|
|
2
|
+
export declare function parseDocument(file: string): NormalizedDocument;
|
|
3
|
+
export declare function resolveDocumentXrefs(document: NormalizedDocument): void;
|
|
4
|
+
export declare function substituteAttributes(value: string, attributes: Record<string, string>): string;
|