Remove redundant Markdown preview provider
This commit is contained in:
parent
ff805c28ad
commit
a7aa176e67
|
|
@ -14,7 +14,6 @@ verstak-official-plugins/
|
||||||
files/
|
files/
|
||||||
notes/
|
notes/
|
||||||
markdown-editor/
|
markdown-editor/
|
||||||
markdown-preview/
|
|
||||||
file-preview/
|
file-preview/
|
||||||
activity/
|
activity/
|
||||||
journal/
|
journal/
|
||||||
|
|
@ -45,7 +44,7 @@ verstak-official-plugins/
|
||||||
- `official.files` — файловый менеджер
|
- `official.files` — файловый менеджер
|
||||||
- `official.notes` — заметки
|
- `official.notes` — заметки
|
||||||
- `official.markdown-editor` — редактор
|
- `official.markdown-editor` — редактор
|
||||||
- `official.markdown-preview` — предпросмотр
|
- `official.file-preview` — предпросмотр изображений/metadata
|
||||||
- `official.activity` — активность
|
- `official.activity` — активность
|
||||||
- `official.browser-inbox` — браузерный inbox
|
- `official.browser-inbox` — браузерный inbox
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
'.fp-btn{font-size:.75rem;padding:.28rem .58rem;border:1px solid #333;border-radius:4px;background:#1a1a2e;color:#ccc;cursor:pointer}',
|
'.fp-btn{font-size:.75rem;padding:.28rem .58rem;border:1px solid #333;border-radius:4px;background:#1a1a2e;color:#ccc;cursor:pointer}',
|
||||||
'.fp-btn:hover{background:#2a2a4e;border-color:#4ecca3;color:#4ecca3}',
|
'.fp-btn:hover{background:#2a2a4e;border-color:#4ecca3;color:#4ecca3}',
|
||||||
'.fp-body{flex:1;min-height:0;overflow:auto;padding:1rem 1.2rem}',
|
'.fp-body{flex:1;min-height:0;overflow:auto;padding:1rem 1.2rem}',
|
||||||
'.fp-pre{margin:0;white-space:pre-wrap;font:13px/1.6 "SF Mono","Fira Code","Cascadia Code",Consolas,monospace;color:#d8d8e8}',
|
|
||||||
'.fp-meta{display:grid;grid-template-columns:max-content 1fr;gap:.45rem .8rem;max-width:760px;font-size:.86rem}',
|
'.fp-meta{display:grid;grid-template-columns:max-content 1fr;gap:.45rem .8rem;max-width:760px;font-size:.86rem}',
|
||||||
'.fp-meta dt{color:#8b8ba8}.fp-meta dd{margin:0;color:#e0e0e0;word-break:break-word}',
|
'.fp-meta dt{color:#8b8ba8}.fp-meta dd{margin:0;color:#e0e0e0;word-break:break-word}',
|
||||||
'.fp-kind{margin:0 0 1rem;color:#f0f0ff;font-size:1.1rem}',
|
'.fp-kind{margin:0 0 1rem;color:#f0f0ff;font-size:1.1rem}',
|
||||||
|
|
@ -111,15 +110,7 @@
|
||||||
containerEl.appendChild(body);
|
containerEl.appendChild(body);
|
||||||
|
|
||||||
api.files.metadata(path).then(function (meta) {
|
api.files.metadata(path).then(function (meta) {
|
||||||
if (IMAGE_EXTS.indexOf(ext) !== -1 || meta.isText === false) {
|
|
||||||
renderMeta(body, path, meta || {}, ext);
|
renderMeta(body, path, meta || {}, ext);
|
||||||
return;
|
|
||||||
}
|
|
||||||
return api.files.readText(path).then(function (text) {
|
|
||||||
body.className = 'fp-body';
|
|
||||||
body.innerHTML = '';
|
|
||||||
body.appendChild(el('pre', { className: 'fp-pre' }, [text]));
|
|
||||||
});
|
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
body.className = 'fp-error';
|
body.className = 'fp-error';
|
||||||
body.textContent = 'Preview error: ' + (err && err.message ? err.message : String(err));
|
body.textContent = 'Preview error: ' + (err && err.message ? err.message : String(err));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"name": "File Preview",
|
"name": "File Preview",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"apiVersion": "0.1.0",
|
"apiVersion": "0.1.0",
|
||||||
"description": "Read-only preview provider for text-like and image vault files.",
|
"description": "Read-only image metadata preview provider for vault files.",
|
||||||
"source": "official",
|
"source": "official",
|
||||||
"icon": "eye",
|
"icon": "eye",
|
||||||
"provides": [
|
"provides": [
|
||||||
|
|
@ -23,23 +23,9 @@
|
||||||
},
|
},
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"openProviders": [
|
"openProviders": [
|
||||||
{
|
|
||||||
"id": "verstak.file-preview.text",
|
|
||||||
"title": "File Preview",
|
|
||||||
"priority": 80,
|
|
||||||
"component": "FilePreview",
|
|
||||||
"supports": [
|
|
||||||
{
|
|
||||||
"kind": "vault-file",
|
|
||||||
"extensions": [".txt", ".log", ".conf", ".ini", ".toml", ".yaml", ".yml", ".json", ".csv", ".xml", ".html", ".css", ".js", ".ts"],
|
|
||||||
"contexts": ["generic-text"],
|
|
||||||
"modes": ["view"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "verstak.file-preview.image",
|
"id": "verstak.file-preview.image",
|
||||||
"title": "Image File Preview",
|
"title": "Image Preview",
|
||||||
"priority": 80,
|
"priority": 80,
|
||||||
"component": "FilePreview",
|
"component": "FilePreview",
|
||||||
"supports": [
|
"supports": [
|
||||||
|
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
/* ===========================================================
|
|
||||||
Markdown Preview Plugin — Verstak v2 Frontend Bundle
|
|
||||||
Contract: window.VerstakPluginRegister(id, { components })
|
|
||||||
=========================================================== */
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function injectStyles() {
|
|
||||||
if (document.getElementById('md-preview-style-injected')) return;
|
|
||||||
var style = document.createElement('style');
|
|
||||||
style.id = 'md-preview-style-injected';
|
|
||||||
style.textContent = STYLES;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
var STYLES = [
|
|
||||||
'.mp-root{height:100%;min-height:0;display:flex;flex-direction:column;background:#0d0d1a;color:#d8d8e8;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif}',
|
|
||||||
'.mp-toolbar{display:flex;align-items:center;gap:.5rem;padding:.45rem .75rem;border-bottom:1px solid #16213e;background:#12122a;flex-shrink:0}',
|
|
||||||
'.mp-mode{font-size:.72rem;color:#4ecca3;background:#1a2a3a;border-radius:3px;padding:.14rem .45rem}',
|
|
||||||
'.mp-path{font-size:.75rem;color:#a0a0bb;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
|
||||||
'.mp-body{flex:1;min-height:0;overflow:auto;padding:1rem 1.2rem;line-height:1.7;font-size:.93rem}',
|
|
||||||
'.mp-body h1,.mp-body h2,.mp-body h3,.mp-body h4{color:#f0f0ff;margin:1rem 0 .5rem}',
|
|
||||||
'.mp-body h1{font-size:1.55rem;border-bottom:1px solid #16213e;padding-bottom:.35rem}.mp-body h2{font-size:1.3rem;border-bottom:1px solid #16213e;padding-bottom:.25rem}.mp-body h3{font-size:1.12rem}',
|
|
||||||
'.mp-body p{margin:.55rem 0}.mp-body code{background:#1a1a2e;padding:.15rem .35rem;border-radius:3px;font-size:.87em;color:#4ecca3}',
|
|
||||||
'.mp-body pre{background:#1a1a2e;padding:.85rem;border-radius:4px;overflow:auto;margin:.8rem 0}.mp-body pre code{background:none;padding:0;color:#d8d8e8}',
|
|
||||||
'.mp-body ul,.mp-body ol{padding-left:1.5rem;margin:.55rem 0}.mp-body li{margin:.25rem 0}',
|
|
||||||
'.mp-body blockquote{border-left:3px solid #4ecca3;margin:.6rem 0;padding:.25rem .85rem;color:#aaa;background:#101028}',
|
|
||||||
'.mp-body a{color:#4ecca3;text-decoration:none}.mp-body a:hover{text-decoration:underline}',
|
|
||||||
'.mp-body table{border-collapse:collapse;margin:.8rem 0;max-width:100%;display:block;overflow:auto}.mp-body th,.mp-body td{border:1px solid #333;padding:.35rem .6rem;text-align:left}.mp-body th{background:#1a1a2e}',
|
|
||||||
'.mp-loading,.mp-error{flex:1;display:flex;align-items:center;justify-content:center;color:#777;padding:2rem}.mp-error{color:#e74c3c;flex-direction:column;gap:.5rem}'
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
function el(tag, attrs, children) {
|
|
||||||
var elem = document.createElement(tag);
|
|
||||||
if (attrs) {
|
|
||||||
Object.keys(attrs).forEach(function (k) {
|
|
||||||
if (k === 'className') elem.className = attrs[k];
|
|
||||||
else if (k === 'style' && typeof attrs[k] === 'object') Object.assign(elem.style, attrs[k]);
|
|
||||||
else if (k.slice(0, 2) === 'on') elem.addEventListener(k.slice(2).toLowerCase(), attrs[k]);
|
|
||||||
else if (k === 'innerHTML') elem.innerHTML = attrs[k];
|
|
||||||
else if (k === 'textContent') elem.textContent = attrs[k];
|
|
||||||
else elem.setAttribute(k, attrs[k]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (children) {
|
|
||||||
(Array.isArray(children) ? children : [children]).forEach(function (c) {
|
|
||||||
if (c == null) return;
|
|
||||||
elem.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeHtml(value) {
|
|
||||||
return String(value == null ? '' : value)
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''');
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderInline(text) {
|
|
||||||
var html = escapeHtml(text);
|
|
||||||
html = html.replace(/`([^`\n]+)`/g, '<code>$1</code>');
|
|
||||||
html = html.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+|mailto:[^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
|
||||||
html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
|
|
||||||
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
||||||
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMarkdown(markdown) {
|
|
||||||
var lines = String(markdown || '').replace(/\r\n/g, '\n').split('\n');
|
|
||||||
var html = [];
|
|
||||||
var inCode = false;
|
|
||||||
var code = [];
|
|
||||||
var inList = false;
|
|
||||||
|
|
||||||
function closeList() {
|
|
||||||
if (inList) {
|
|
||||||
html.push('</ul>');
|
|
||||||
inList = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function closeCode() {
|
|
||||||
if (inCode) {
|
|
||||||
html.push('<pre><code>' + escapeHtml(code.join('\n')) + '</code></pre>');
|
|
||||||
code = [];
|
|
||||||
inCode = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.forEach(function (line) {
|
|
||||||
if (/^```/.test(line)) {
|
|
||||||
if (inCode) closeCode();
|
|
||||||
else {
|
|
||||||
closeList();
|
|
||||||
inCode = true;
|
|
||||||
code = [];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (inCode) {
|
|
||||||
code.push(line);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!line.trim()) {
|
|
||||||
closeList();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var heading = /^(#{1,4})\s+(.+)$/.exec(line);
|
|
||||||
if (heading) {
|
|
||||||
closeList();
|
|
||||||
html.push('<h' + heading[1].length + '>' + renderInline(heading[2]) + '</h' + heading[1].length + '>');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var quote = /^>\s?(.*)$/.exec(line);
|
|
||||||
if (quote) {
|
|
||||||
closeList();
|
|
||||||
html.push('<blockquote>' + renderInline(quote[1]) + '</blockquote>');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var item = /^\s*[-*]\s+(.+)$/.exec(line);
|
|
||||||
if (item) {
|
|
||||||
if (!inList) {
|
|
||||||
html.push('<ul>');
|
|
||||||
inList = true;
|
|
||||||
}
|
|
||||||
html.push('<li>' + renderInline(item[1]) + '</li>');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
closeList();
|
|
||||||
html.push('<p>' + renderInline(line) + '</p>');
|
|
||||||
});
|
|
||||||
closeCode();
|
|
||||||
closeList();
|
|
||||||
return html.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
var MarkdownPreview = {
|
|
||||||
mount: function (containerEl, props, api) {
|
|
||||||
injectStyles();
|
|
||||||
var request = props && props.request || {};
|
|
||||||
var path = request.path || '';
|
|
||||||
containerEl.innerHTML = '';
|
|
||||||
containerEl.className = 'mp-root';
|
|
||||||
containerEl.setAttribute('data-plugin-id', 'verstak.markdown-preview');
|
|
||||||
containerEl.setAttribute('data-preview-path', path);
|
|
||||||
containerEl.appendChild(el('div', { className: 'mp-toolbar' }, [
|
|
||||||
el('span', { className: 'mp-mode' }, ['Preview']),
|
|
||||||
el('span', { className: 'mp-path' }, [path])
|
|
||||||
]));
|
|
||||||
var body = el('div', { className: 'mp-loading' }, ['Loading...']);
|
|
||||||
containerEl.appendChild(body);
|
|
||||||
api.files.readText(path).then(function (text) {
|
|
||||||
body.className = 'mp-body';
|
|
||||||
body.innerHTML = renderMarkdown(text);
|
|
||||||
}).catch(function (err) {
|
|
||||||
body.className = 'mp-error';
|
|
||||||
body.textContent = 'Preview error: ' + (err && err.message ? err.message : String(err));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
unmount: function (containerEl) {
|
|
||||||
containerEl.innerHTML = '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.VerstakPluginRegister('verstak.markdown-preview', {
|
|
||||||
components: { MarkdownPreview: MarkdownPreview }
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
{
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"id": "verstak.markdown-preview",
|
|
||||||
"name": "Markdown Preview",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"apiVersion": "0.1.0",
|
|
||||||
"description": "Read-only Markdown preview provider for vault Markdown files.",
|
|
||||||
"source": "official",
|
|
||||||
"icon": "eye",
|
|
||||||
"provides": [
|
|
||||||
"verstak/markdown-preview/v1"
|
|
||||||
],
|
|
||||||
"requires": [
|
|
||||||
"verstak/core/files/v1",
|
|
||||||
"verstak/core/workbench/v1"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"files.read"
|
|
||||||
],
|
|
||||||
"frontend": {
|
|
||||||
"entry": "frontend/src/index.js"
|
|
||||||
},
|
|
||||||
"contributes": {
|
|
||||||
"openProviders": [
|
|
||||||
{
|
|
||||||
"id": "verstak.markdown-preview.generic",
|
|
||||||
"title": "Markdown Preview",
|
|
||||||
"priority": 80,
|
|
||||||
"component": "MarkdownPreview",
|
|
||||||
"supports": [
|
|
||||||
{
|
|
||||||
"kind": "vault-file",
|
|
||||||
"extensions": [".md", ".markdown"],
|
|
||||||
"contexts": ["generic-markdown"],
|
|
||||||
"modes": ["view"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "verstak.markdown-preview.notes",
|
|
||||||
"title": "Notes Markdown Preview",
|
|
||||||
"priority": 80,
|
|
||||||
"component": "MarkdownPreview",
|
|
||||||
"supports": [
|
|
||||||
{
|
|
||||||
"kind": "vault-file",
|
|
||||||
"extensions": [".md", ".markdown"],
|
|
||||||
"contexts": ["notes-markdown"],
|
|
||||||
"modes": ["view"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -213,8 +213,6 @@ if command -v node &>/dev/null; then
|
||||||
report "platform-test frontend components mount" $?
|
report "platform-test frontend components mount" $?
|
||||||
node "$ROOT/scripts/smoke-notes-plugin.js"
|
node "$ROOT/scripts/smoke-notes-plugin.js"
|
||||||
report "notes frontend behavior" $?
|
report "notes frontend behavior" $?
|
||||||
node "$ROOT/scripts/smoke-markdown-preview-plugin.js"
|
|
||||||
report "markdown-preview frontend behavior" $?
|
|
||||||
node "$ROOT/scripts/smoke-file-preview-plugin.js"
|
node "$ROOT/scripts/smoke-file-preview-plugin.js"
|
||||||
report "file-preview frontend behavior" $?
|
report "file-preview frontend behavior" $?
|
||||||
node "$ROOT/scripts/smoke-files-plugin.js"
|
node "$ROOT/scripts/smoke-files-plugin.js"
|
||||||
|
|
|
||||||
|
|
@ -121,24 +121,6 @@ async function flush() {
|
||||||
const document = makeDocument();
|
const document = makeDocument();
|
||||||
const component = loadComponent(document);
|
const component = loadComponent(document);
|
||||||
|
|
||||||
const textContainer = new FakeNode('div');
|
|
||||||
const readPaths = [];
|
|
||||||
component.mount(textContainer, {
|
|
||||||
request: { path: 'Docs/app.log', extension: '.log', mode: 'view' },
|
|
||||||
}, {
|
|
||||||
files: {
|
|
||||||
metadata: async () => ({ type: 'file', size: 11, isText: true, modifiedAt: '2026-06-27T00:00:00Z' }),
|
|
||||||
readText: async (relativePath) => {
|
|
||||||
readPaths.push(relativePath);
|
|
||||||
return 'hello log';
|
|
||||||
},
|
|
||||||
openExternal: async () => undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await flush();
|
|
||||||
if (readPaths[0] !== 'Docs/app.log') throw new Error('text preview did not read text file');
|
|
||||||
if (!textContainer.textContent.includes('hello log')) throw new Error('text preview did not render file text');
|
|
||||||
|
|
||||||
const imageContainer = new FakeNode('div');
|
const imageContainer = new FakeNode('div');
|
||||||
const opened = [];
|
const opened = [];
|
||||||
component.mount(imageContainer, {
|
component.mount(imageContainer, {
|
||||||
|
|
@ -146,7 +128,7 @@ async function flush() {
|
||||||
}, {
|
}, {
|
||||||
files: {
|
files: {
|
||||||
metadata: async () => ({ type: 'file', size: 2048, isText: false, modifiedAt: '2026-06-27T00:00:00Z' }),
|
metadata: async () => ({ type: 'file', size: 2048, isText: false, modifiedAt: '2026-06-27T00:00:00Z' }),
|
||||||
readText: async () => { throw new Error('image preview should not read bytes through readText'); },
|
readText: async () => { throw new Error('file preview should not read text content'); },
|
||||||
openExternal: async (relativePath) => { opened.push(relativePath); },
|
openExternal: async (relativePath) => { opened.push(relativePath); },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const vm = require('vm');
|
|
||||||
|
|
||||||
const root = path.resolve(__dirname, '..');
|
|
||||||
const sourcePath = path.join(root, 'plugins', 'markdown-preview', 'frontend', 'src', 'index.js');
|
|
||||||
const source = fs.readFileSync(sourcePath, 'utf8');
|
|
||||||
|
|
||||||
class FakeNode {
|
|
||||||
constructor(tagName) {
|
|
||||||
this.tagName = String(tagName || '').toUpperCase();
|
|
||||||
this.children = [];
|
|
||||||
this.attributes = {};
|
|
||||||
this.listeners = {};
|
|
||||||
this.style = {};
|
|
||||||
this.className = '';
|
|
||||||
this.id = '';
|
|
||||||
this.parentNode = null;
|
|
||||||
this._innerHTML = '';
|
|
||||||
this._textContent = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
appendChild(node) {
|
|
||||||
if (!(node instanceof FakeNode)) throw new TypeError('appendChild expects FakeNode');
|
|
||||||
this.children.push(node);
|
|
||||||
node.parentNode = this;
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAttribute(name, value) {
|
|
||||||
this.attributes[name] = String(value);
|
|
||||||
if (name === 'id') this.id = String(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAttribute(name) {
|
|
||||||
return this.attributes[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener(type, handler) {
|
|
||||||
this.listeners[type] = this.listeners[type] || [];
|
|
||||||
this.listeners[type].push(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
set innerHTML(value) {
|
|
||||||
this._innerHTML = String(value || '');
|
|
||||||
this.children = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get innerHTML() {
|
|
||||||
return this._innerHTML + this.children.map((child) => child.innerHTML).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
set textContent(value) {
|
|
||||||
this._textContent = String(value || '');
|
|
||||||
this.children = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
get textContent() {
|
|
||||||
if (this.tagName === '#TEXT') return this._textContent;
|
|
||||||
return this._textContent + this.children.map((child) => child.textContent).join('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeDocument() {
|
|
||||||
return {
|
|
||||||
body: new FakeNode('body'),
|
|
||||||
head: new FakeNode('head'),
|
|
||||||
createElement(tagName) {
|
|
||||||
return new FakeNode(tagName);
|
|
||||||
},
|
|
||||||
createTextNode(text) {
|
|
||||||
const node = new FakeNode('#text');
|
|
||||||
node.textContent = text;
|
|
||||||
return node;
|
|
||||||
},
|
|
||||||
getElementById() {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadComponent(document) {
|
|
||||||
const registry = {};
|
|
||||||
const sandbox = {
|
|
||||||
console,
|
|
||||||
document,
|
|
||||||
window: {
|
|
||||||
VerstakPluginRegister(pluginId, bundle) {
|
|
||||||
registry[pluginId] = bundle.components || {};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sandbox.window.window = sandbox.window;
|
|
||||||
sandbox.window.document = document;
|
|
||||||
vm.runInNewContext(source, sandbox, { filename: sourcePath });
|
|
||||||
const component = registry['verstak.markdown-preview'] && registry['verstak.markdown-preview'].MarkdownPreview;
|
|
||||||
if (!component) throw new Error('MarkdownPreview was not registered');
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function flush() {
|
|
||||||
for (let i = 0; i < 8; i++) await Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const document = makeDocument();
|
|
||||||
const component = loadComponent(document);
|
|
||||||
const container = new FakeNode('div');
|
|
||||||
const readPaths = [];
|
|
||||||
component.mount(container, {
|
|
||||||
request: { path: 'Docs/readme.md', mode: 'view' },
|
|
||||||
}, {
|
|
||||||
files: {
|
|
||||||
readText: async (relativePath) => {
|
|
||||||
readPaths.push(relativePath);
|
|
||||||
return '# Title\n\nParagraph with `code`.\n\n- one\n- two\n';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await flush();
|
|
||||||
|
|
||||||
if (container.getAttribute('data-plugin-id') !== 'verstak.markdown-preview') {
|
|
||||||
throw new Error('plugin id marker missing');
|
|
||||||
}
|
|
||||||
if (readPaths[0] !== 'Docs/readme.md') {
|
|
||||||
throw new Error(`expected readText Docs/readme.md, got ${readPaths[0] || '<none>'}`);
|
|
||||||
}
|
|
||||||
const html = container.innerHTML;
|
|
||||||
if (!html.includes('<h1>Title</h1>')) throw new Error('heading was not rendered');
|
|
||||||
if (!html.includes('<code>code</code>')) throw new Error('inline code was not rendered');
|
|
||||||
if (!html.includes('<li>one</li>') || !html.includes('<li>two</li>')) throw new Error('list items were not rendered');
|
|
||||||
|
|
||||||
console.log('markdown-preview smoke passed');
|
|
||||||
})().catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue