@d10f/asciidoc-astro-loader 0.0.2 → 0.0.3
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 +132 -92
- package/dist/chunk-2UGTFP4R.js +22 -0
- package/dist/chunk-5P6LDJGO.js +65 -0
- package/dist/{chunk-BRMWIQA2.js → chunk-KZRXEKZK.js} +5 -9
- package/dist/{index-BNxO58s3.d.cts → index-sFlXF8Qm.d.cts} +142 -65
- package/dist/{index-BNxO58s3.d.ts → index-sFlXF8Qm.d.ts} +142 -65
- package/dist/index.cjs +225 -254
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +47 -37
- package/dist/lib/asciidoc/converters/index.cjs +3 -99
- package/dist/lib/asciidoc/converters/index.d.cts +2 -2
- package/dist/lib/asciidoc/converters/index.d.ts +2 -2
- package/dist/lib/asciidoc/converters/index.js +2 -2
- package/dist/lib/asciidoc/templates/engines/index.d.cts +1 -1
- package/dist/lib/asciidoc/templates/engines/index.d.ts +1 -1
- package/dist/lib/shiki/transformers/index.cjs +88 -79
- package/dist/lib/shiki/transformers/index.d.cts +23 -32
- package/dist/lib/shiki/transformers/index.d.ts +23 -32
- package/dist/lib/shiki/transformers/index.js +45 -4
- package/package.json +1 -1
- package/dist/chunk-DDIUST2Z.js +0 -113
package/README.md
CHANGED
|
@@ -8,39 +8,39 @@ This package will allow you to load Asciidoc files (with either an `.asciidoc` o
|
|
|
8
8
|
- [x] Support for custom templating engines.
|
|
9
9
|
- [x] Support for custom converters for maximum control over the output HTML.
|
|
10
10
|
- [x] Full TypeScript support.
|
|
11
|
+
- [x] Restructure configuration options regarding Shiki transformer integration.
|
|
11
12
|
|
|
12
13
|
## Roadmap
|
|
13
14
|
|
|
14
15
|
- [ ] Async support on custom template and converter class render methods.
|
|
15
16
|
- [ ] Include support for more template engines out of the box.
|
|
16
|
-
- [ ] Restructure configuration options regarding Shiki transformer integration.
|
|
17
17
|
|
|
18
18
|
## Getting Started
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Install the package from `npm`:
|
|
21
21
|
|
|
22
22
|
```console
|
|
23
23
|
npm install @d10f/asciidoc-astro-loader
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
And import the loader function inside your `content.config.ts` file to define a new collection:
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
```ts
|
|
29
|
+
import { defineCollection, z } from "astro:content";
|
|
30
|
+
import { asciidocLoader } from "asciidoc-astro-loader";
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
const blog = defineCollection({
|
|
33
|
+
loader: asciidocLoader({
|
|
34
|
+
base: ".src/content/blog",
|
|
35
|
+
}),
|
|
36
|
+
schema: z.object({
|
|
37
|
+
title: z.string(),
|
|
38
|
+
preamble: z.string().optional(),
|
|
39
|
+
createdAt: z.coerce.date(),
|
|
40
|
+
updatedAt: z.coerce.date().optional(),
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
## Configuration
|
|
@@ -64,15 +64,15 @@ You can specify an object for different light and dark themes, as well:
|
|
|
64
64
|
|
|
65
65
|
```ts
|
|
66
66
|
const blog = defineCollection({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
loader: asciidocLoader({
|
|
68
|
+
base: ".src/content/blog",
|
|
69
|
+
syntaxHighlighting: {
|
|
70
|
+
theme: {
|
|
71
|
+
light: "everforest-light",
|
|
72
|
+
dark: "everforest-dark",
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
76
|
});
|
|
77
77
|
```
|
|
78
78
|
|
|
@@ -80,16 +80,16 @@ And even provide additional themes. Note that you will have to take care of impl
|
|
|
80
80
|
|
|
81
81
|
```ts
|
|
82
82
|
const blog = defineCollection({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
loader: asciidocLoader({
|
|
84
|
+
base: ".src/content/blog",
|
|
85
|
+
syntaxHighlighting: {
|
|
86
|
+
theme: {
|
|
87
|
+
light: "gruvbox-light-hard",
|
|
88
|
+
dark: "gruvbox-dark-hard",
|
|
89
|
+
dim: "gruvbox-dark-medium"
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
}),
|
|
93
93
|
});
|
|
94
94
|
```
|
|
95
95
|
|
|
@@ -99,26 +99,66 @@ One of the coolest features from Shiki are [transformers](https://shiki.style/gu
|
|
|
99
99
|
|
|
100
100
|
```ts
|
|
101
101
|
import {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
transformerNotationDiff,
|
|
103
|
+
transformerNotationFocus,
|
|
104
|
+
transformerNotationHighlight,
|
|
105
105
|
} from '@shikijs/transformers';
|
|
106
106
|
|
|
107
107
|
const blog = defineCollection({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
loader: asciidocLoader({
|
|
109
|
+
base: ".src/content/blog",
|
|
110
|
+
syntaxHighlighting: {
|
|
111
|
+
transformers: [
|
|
112
|
+
transformerNotationDiff(),
|
|
113
|
+
transformerNotationHighlight(),
|
|
114
|
+
transformerNotationFocus(),
|
|
115
|
+
],
|
|
116
|
+
}
|
|
117
|
+
}),
|
|
118
118
|
});
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
If you want to write your own transformers, you can just follow Shiki's documentation and provide them here. However, you might also be interested in performing some conditional logic based on the type of Asciidoc node you're working with. To gain access to the node, define the transformer as a factory function using the `ShikiTransformerFactory` type:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import type { ShikiTransformerFactory } from '../../../types/index.js';
|
|
125
|
+
|
|
126
|
+
type TranformerOptions = {
|
|
127
|
+
cssClasses: string;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const transformerAsciidocSomething: ShikiTransformerFactory<
|
|
131
|
+
TransformerOptions
|
|
132
|
+
> = ({ cssClasses }) => {
|
|
133
|
+
return (node) => {
|
|
134
|
+
return {
|
|
135
|
+
preprocess() {
|
|
136
|
+
if (node.getAttribute('custom-attribute')) {
|
|
137
|
+
// Maybe you only want to do something based on
|
|
138
|
+
// some custom attribute?
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { transformerAsciidocSomething } from './usr/share/transformers';
|
|
148
|
+
|
|
149
|
+
const blog = defineCollection({
|
|
150
|
+
loader: asciidocLoader({
|
|
151
|
+
base: ".src/content/blog",
|
|
152
|
+
syntaxHighlighting: {
|
|
153
|
+
transformers: [
|
|
154
|
+
transformerAsciidocSomethign({
|
|
155
|
+
cssClasses: 'text-red-500'
|
|
156
|
+
}),
|
|
157
|
+
],
|
|
158
|
+
}
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
```
|
|
122
162
|
|
|
123
163
|
## Custom Templates
|
|
124
164
|
|
|
@@ -126,12 +166,12 @@ A nice feature of Asciidoctor.js is the use of default templates for rendering t
|
|
|
126
166
|
|
|
127
167
|
```ts
|
|
128
168
|
const blog = defineCollection({
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
loader: asciidocLoader({
|
|
170
|
+
base: ".src/content/blog",
|
|
171
|
+
document: {
|
|
172
|
+
template: "./usr/share/templates"
|
|
173
|
+
}
|
|
174
|
+
}),
|
|
135
175
|
});
|
|
136
176
|
```
|
|
137
177
|
|
|
@@ -162,7 +202,7 @@ export class PhpEngine extends AbstractEngine implements AsciidocTemplate, Files
|
|
|
162
202
|
/**
|
|
163
203
|
* This method is enforced by the FilesystemTemplate interface.
|
|
164
204
|
*/
|
|
165
|
-
|
|
205
|
+
renderFile(filepath: string, options?: Record<string, unknown>) {}
|
|
166
206
|
}
|
|
167
207
|
```
|
|
168
208
|
|
|
@@ -177,15 +217,15 @@ When your engine is ready, you can import it and pass it to the loader configura
|
|
|
177
217
|
import { PhpEngine } from './usr/share/engines';
|
|
178
218
|
|
|
179
219
|
const blog = defineCollection({
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
220
|
+
loader: asciidocLoader({
|
|
221
|
+
base: ".src/content/blog",
|
|
222
|
+
document: {
|
|
223
|
+
template: "./usr/share/templates",
|
|
224
|
+
templateEngines: [
|
|
225
|
+
New PhpEngine({ name: 'php', extensions: ['php'], root: './usr/share/templates' })
|
|
226
|
+
],
|
|
227
|
+
}
|
|
228
|
+
}),
|
|
189
229
|
});
|
|
190
230
|
```
|
|
191
231
|
|
|
@@ -197,40 +237,40 @@ import { AbstractEngine } from '@d10f/asciidoc-astro-loader/engines';
|
|
|
197
237
|
|
|
198
238
|
import type { AbstractBlock } from 'asciidoctor';
|
|
199
239
|
import type {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
240
|
+
AsciidocTemplate,
|
|
241
|
+
FilesystemTemplate,
|
|
242
|
+
NodeContext
|
|
203
243
|
} from '@d10f/asciidoc-astro-loader';
|
|
204
244
|
|
|
205
245
|
export class PhpEngine extends AbstractEngine implements AsciidocTemplate, FilesystemTemplate {
|
|
206
|
-
|
|
246
|
+
private server: Php;
|
|
207
247
|
|
|
208
248
|
constructor({ name = 'php', extensions = ['php'], root: string }) {
|
|
209
249
|
super(name, extensions);
|
|
210
250
|
this.server = new Php({ docroot: root });
|
|
211
251
|
}
|
|
212
252
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
253
|
+
renderNode(node: AbstractBlock, options?: Record<string, unknown>) {
|
|
254
|
+
const context = node.getNodeName() as NodeContext;
|
|
255
|
+
const content = node.getContent();
|
|
256
|
+
const templateFile = this.templateList.get(context);
|
|
257
|
+
|
|
258
|
+
if (templateFile) {
|
|
259
|
+
const filepath = templateFile.replace(/^.+\/(.+)$/, '$1');
|
|
260
|
+
return this.renderFile(filepath, { content });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
renderFile(filepath: string, options?: Record<string, unknown>): string {
|
|
265
|
+
const request = new Request({
|
|
266
|
+
method: 'POST',
|
|
267
|
+
url: 'http://localhost/' + filepath,
|
|
268
|
+
body: Buffer.from(JSON.stringify(options)),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const response = this.server.handleRequestSync(request);
|
|
272
|
+
return response.body.toString();
|
|
273
|
+
}
|
|
234
274
|
}
|
|
235
275
|
```
|
|
236
276
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/lib/utils.ts
|
|
2
|
+
function slugify(text) {
|
|
3
|
+
return text.trim().normalize().toLowerCase().replace(/\s+/g, "-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
|
|
4
|
+
}
|
|
5
|
+
function escapeRegexCharacters(str) {
|
|
6
|
+
const re = /[-\\^$*+?.()|\[\]{}]/g;
|
|
7
|
+
return str.replace(re, "\\$&");
|
|
8
|
+
}
|
|
9
|
+
function splitFilenameComponents(filename) {
|
|
10
|
+
const match = filename.match(/^(?<path>.*\/)*(?<name>[^\.]+)\.(?<ext>.*)$/);
|
|
11
|
+
return {
|
|
12
|
+
filepath: match?.groups?.path ?? null,
|
|
13
|
+
filename: match?.groups?.name ?? null,
|
|
14
|
+
extension: match?.groups?.ext ?? null
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
slugify,
|
|
20
|
+
escapeRegexCharacters,
|
|
21
|
+
splitFilenameComponents
|
|
22
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
escapeRegexCharacters
|
|
3
|
+
} from "./chunk-2UGTFP4R.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/shiki/transformers/transformAsciidocCallout.ts
|
|
6
|
+
var transformAsciidocCallout = (options) => {
|
|
7
|
+
return (node) => {
|
|
8
|
+
const lineComments = ["//", "#", ";;"];
|
|
9
|
+
const customLineComment = node.getAttribute("line-comment");
|
|
10
|
+
if (customLineComment) {
|
|
11
|
+
lineComments.push(escapeRegexCharacters(customLineComment));
|
|
12
|
+
}
|
|
13
|
+
const calloutReList = [
|
|
14
|
+
// Handles C-style and similar comments like Perl, Python...
|
|
15
|
+
new RegExp(`\\s+(?:${lineComments.join("|")})((?:\\s+<(\\d+)>)+)`),
|
|
16
|
+
// Handles XML comments
|
|
17
|
+
new RegExp(/((?:\s*<!--(\d+)-->)+)/)
|
|
18
|
+
];
|
|
19
|
+
const commentTokensRe = new RegExp(
|
|
20
|
+
`(?:${lineComments.join("|")}|<!--|-->|[<>])`,
|
|
21
|
+
"g"
|
|
22
|
+
);
|
|
23
|
+
const linesWithCallout = {};
|
|
24
|
+
return {
|
|
25
|
+
preprocess(code) {
|
|
26
|
+
return code.split("\n").map((line, idx) => {
|
|
27
|
+
for (const re of calloutReList) {
|
|
28
|
+
const match = line.match(re);
|
|
29
|
+
if (!match) continue;
|
|
30
|
+
const callouts = match[0].replaceAll(commentTokensRe, "").trim().split(" ");
|
|
31
|
+
linesWithCallout[idx + 1] = callouts;
|
|
32
|
+
return line.replace(re, "");
|
|
33
|
+
}
|
|
34
|
+
return line;
|
|
35
|
+
}).join("\n");
|
|
36
|
+
},
|
|
37
|
+
line(hast, line) {
|
|
38
|
+
const callouts = linesWithCallout[line];
|
|
39
|
+
if (!callouts) return;
|
|
40
|
+
callouts.forEach((calloutId) => {
|
|
41
|
+
hast.properties[`data-callout-${calloutId}`] = "";
|
|
42
|
+
hast.children.push({
|
|
43
|
+
type: "element",
|
|
44
|
+
tagName: "span",
|
|
45
|
+
properties: {
|
|
46
|
+
class: options?.cssClasses ?? "conum",
|
|
47
|
+
style: options?.cssClasses ? "" : "user-select:none; pointer-events:none; opacity:0.5; margin-inline:8px;",
|
|
48
|
+
"data-value": calloutId
|
|
49
|
+
},
|
|
50
|
+
children: [
|
|
51
|
+
{
|
|
52
|
+
type: "text",
|
|
53
|
+
value: calloutId
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
transformAsciidocCallout
|
|
65
|
+
};
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
splitFilenameComponents
|
|
3
|
-
|
|
4
|
-
transformConsoleCodeBlock
|
|
5
|
-
} from "./chunk-DDIUST2Z.js";
|
|
2
|
+
splitFilenameComponents
|
|
3
|
+
} from "./chunk-2UGTFP4R.js";
|
|
6
4
|
|
|
7
5
|
// src/lib/asciidoc/converters/sourceCodeConverter.ts
|
|
8
6
|
import { resolve } from "path";
|
|
@@ -17,11 +15,9 @@ var sourceCodeConverter = ({ transformers, template }) => {
|
|
|
17
15
|
const output = highlighter.codeToHtml(input, {
|
|
18
16
|
...options.syntaxHighlighting,
|
|
19
17
|
lang,
|
|
20
|
-
transformers: [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
transformConsoleCodeBlock()
|
|
24
|
-
]
|
|
18
|
+
transformers: (transformers ?? []).map((transformer) => {
|
|
19
|
+
return typeof transformer === "function" ? transformer(node) : transformer;
|
|
20
|
+
})
|
|
25
21
|
});
|
|
26
22
|
if (templateEngine && template) {
|
|
27
23
|
const { extension } = splitFilenameComponents(template);
|