@vmz/plugin-shiki 0.0.1 → 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 +36 -1
- package/components/Shiki.vmz +20 -0
- package/package.json +38 -5
- package/runtime.ts +62 -0
- package/vmz.plugin.ts +50 -0
package/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
1
|
# @vmz/plugin-shiki
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Code examples that are part of the reading experience
|
|
4
|
+
|
|
5
|
+
`@vmz/plugin-shiki` brings Shiki's high-quality syntax highlighting to VMZ applications. It is designed for guides, API
|
|
6
|
+
reference, technical blogs, source browsers, tutorials, and any product surface where code is meant to be read
|
|
7
|
+
carefully.
|
|
8
|
+
|
|
9
|
+
## Why Shiki fits VMZ documents
|
|
10
|
+
|
|
11
|
+
Code snippets should remain useful before a page becomes interactive. Shiki supports a content-first experience: source
|
|
12
|
+
can be highlighted as part of the rendered document instead of relying on a browser-only code viewer to make a page
|
|
13
|
+
readable.
|
|
14
|
+
|
|
15
|
+
For VMZ examples, the plugin can share the same language presentation used by VMZ editor tooling. That consistency helps
|
|
16
|
+
readers move from documentation to a real `.vmz` file without relearning the visual grammar.
|
|
17
|
+
|
|
18
|
+
It is especially useful for:
|
|
19
|
+
|
|
20
|
+
- product documentation that must be readable before interaction starts;
|
|
21
|
+
- API and configuration references where source is the main content;
|
|
22
|
+
- tutorials that move between prose and real VMZ components;
|
|
23
|
+
- source browsers that need trustworthy language presentation.
|
|
24
|
+
|
|
25
|
+
## VMZ boundary
|
|
26
|
+
|
|
27
|
+
Shiki owns source presentation. VMZ owns document structure, SSR, optional interaction, testing, and delivery. The
|
|
28
|
+
plugin should make code easier to understand without becoming a parallel application runtime.
|
|
29
|
+
|
|
30
|
+
## A content-first feature set 🎨
|
|
31
|
+
|
|
32
|
+
- **Readable SSR:** highlighted source can arrive with the page instead of flashing into place later.
|
|
33
|
+
- **VMZ consistency:** examples can share the grammar readers see in their editor.
|
|
34
|
+
- **Theme-aware presentation:** technical content can belong to the surrounding design.
|
|
35
|
+
- **Graceful fallback:** source remains readable when rich highlighting is unavailable.
|
|
36
|
+
|
|
37
|
+
Use Shiki where code itself is part of the product. If users are meant to edit the code, pair the reading experience
|
|
38
|
+
with CodeMirror or Monaco rather than stretching a highlighter into an editor.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="shiki-host" html={html != null ? html : highlightSync(code, lang, theme)}></div>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script client>
|
|
6
|
+
import { highlight, highlightSync } from '@vmz/plugin-shiki/runtime';
|
|
7
|
+
|
|
8
|
+
export default class Shiki {
|
|
9
|
+
public code: string = '';
|
|
10
|
+
public lang: string = 'text';
|
|
11
|
+
public theme: string = 'vitesse-dark';
|
|
12
|
+
|
|
13
|
+
/** null → sync fallback / prewarm cache; set after async highlight. */
|
|
14
|
+
html: string | null = null;
|
|
15
|
+
|
|
16
|
+
async onMount() {
|
|
17
|
+
this.html = await highlight(this.code, this.lang, this.theme);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
</script>
|
package/package.json
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/plugin-shiki",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "VMZ Shiki adapter - <Shiki> + code engine registration",
|
|
5
6
|
"license": "MIT",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
7
|
+
"main": "./vmz.plugin.ts",
|
|
8
|
+
"types": "./vmz.plugin.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./vmz.plugin.ts",
|
|
12
|
+
"default": "./vmz.plugin.ts"
|
|
13
|
+
},
|
|
14
|
+
"./runtime": {
|
|
15
|
+
"types": "./runtime.ts",
|
|
16
|
+
"default": "./runtime.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@vmz/plugin": "0.0.3"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"shiki": ">=3",
|
|
24
|
+
"vmz-textmate": "0.0.3"
|
|
25
|
+
},
|
|
26
|
+
"peerDependenciesMeta": {
|
|
27
|
+
"shiki": {
|
|
28
|
+
"optional": false
|
|
29
|
+
},
|
|
30
|
+
"vmz-textmate": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"vmz",
|
|
36
|
+
"plugin",
|
|
37
|
+
"shiki",
|
|
38
|
+
"code"
|
|
9
39
|
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
10
43
|
"repository": {
|
|
11
44
|
"type": "git",
|
|
12
45
|
"url": "git+https://github.com/doki-land/vmz-framework.git"
|
package/runtime.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shiki highlight helper — async with optional sync cache after prewarm.
|
|
3
|
+
* For `lang === 'vmz'`, prefers `vmz-textmate/shiki` when available.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Highlighter } from 'shiki';
|
|
7
|
+
|
|
8
|
+
let cached: Highlighter | null = null;
|
|
9
|
+
let pending: Promise<Highlighter> | null = null;
|
|
10
|
+
|
|
11
|
+
export async function prewarmShiki(opts: { themes?: string[] } = {}): Promise<Highlighter> {
|
|
12
|
+
if (cached) return cached;
|
|
13
|
+
if (pending) return pending;
|
|
14
|
+
pending = (async () => {
|
|
15
|
+
const themes = opts.themes?.length ? opts.themes : ['vitesse-dark'];
|
|
16
|
+
try {
|
|
17
|
+
const { createVmzHighlighter } = await import('vmz-textmate/shiki');
|
|
18
|
+
cached = await createVmzHighlighter({ themes });
|
|
19
|
+
} catch {
|
|
20
|
+
const { createHighlighter } = await import('shiki');
|
|
21
|
+
cached = await createHighlighter({
|
|
22
|
+
themes,
|
|
23
|
+
langs: ['javascript', 'typescript', 'tsx', 'jsx', 'json', 'html', 'css', 'markdown', 'bash'],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return cached!;
|
|
27
|
+
})();
|
|
28
|
+
return pending;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function highlight(code: string, lang = 'text', theme = 'vitesse-dark'): Promise<string> {
|
|
32
|
+
const highlighter = await prewarmShiki({ themes: [theme] });
|
|
33
|
+
try {
|
|
34
|
+
return highlighter.codeToHtml(code ?? '', {
|
|
35
|
+
lang: lang || 'text',
|
|
36
|
+
theme,
|
|
37
|
+
});
|
|
38
|
+
} catch {
|
|
39
|
+
return fallbackPre(code);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Sync highlight when prewarmed; otherwise escaped `<pre><code>`. */
|
|
44
|
+
export function highlightSync(code: string, lang = 'text', theme = 'vitesse-dark'): string {
|
|
45
|
+
if (!cached) return fallbackPre(code);
|
|
46
|
+
try {
|
|
47
|
+
return cached.codeToHtml(code ?? '', {
|
|
48
|
+
lang: lang || 'text',
|
|
49
|
+
theme,
|
|
50
|
+
});
|
|
51
|
+
} catch {
|
|
52
|
+
return fallbackPre(code);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fallbackPre(code: string): string {
|
|
57
|
+
const escaped = String(code ?? '')
|
|
58
|
+
.replace(/&/g, '&')
|
|
59
|
+
.replace(/</g, '<')
|
|
60
|
+
.replace(/>/g, '>');
|
|
61
|
+
return `<pre class="shiki shiki-fallback"><code>${escaped}</code></pre>`;
|
|
62
|
+
}
|
package/vmz.plugin.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { definePlugin, loadPluginSource } from '@vmz/plugin';
|
|
2
|
+
|
|
3
|
+
const source = loadPluginSource(import.meta.url, 'components/Shiki.vmz');
|
|
4
|
+
|
|
5
|
+
export default definePlugin({
|
|
6
|
+
name: '@vmz/plugin-shiki',
|
|
7
|
+
version: '0.1.0',
|
|
8
|
+
protocol: '0.1.0',
|
|
9
|
+
stages: ['workspace_resolve', 'analyzer'],
|
|
10
|
+
deterministic: true,
|
|
11
|
+
async contribute(ctx) {
|
|
12
|
+
if (ctx.stage === 'workspace_resolve') {
|
|
13
|
+
return {
|
|
14
|
+
stage: 'workspace_resolve',
|
|
15
|
+
cacheKey: `@vmz/plugin-shiki:Shiki.vmz:${source.contentHash.slice(0, 12)}`,
|
|
16
|
+
items: [
|
|
17
|
+
{
|
|
18
|
+
id: 'component-shiki',
|
|
19
|
+
kind: 'source',
|
|
20
|
+
path: 'src/components/Shiki.vmz',
|
|
21
|
+
content: source.content,
|
|
22
|
+
contentHash: source.contentHash,
|
|
23
|
+
materialize: true,
|
|
24
|
+
engine: 'shiki',
|
|
25
|
+
engineKind: 'code',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (ctx.stage === 'analyzer') {
|
|
31
|
+
return {
|
|
32
|
+
stage: 'analyzer',
|
|
33
|
+
cacheKey: '@vmz/plugin-shiki:analyzer',
|
|
34
|
+
items: [
|
|
35
|
+
{
|
|
36
|
+
id: 'engine-shiki',
|
|
37
|
+
kind: 'analyzer',
|
|
38
|
+
path: 'src/components/Shiki.vmz',
|
|
39
|
+
severity: 'advice',
|
|
40
|
+
message: 'code engine shiki online',
|
|
41
|
+
code: 'vmz.engine.shiki',
|
|
42
|
+
engine: 'shiki',
|
|
43
|
+
engineKind: 'code',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { stage: ctx.stage, items: [] };
|
|
49
|
+
},
|
|
50
|
+
});
|