mirror of
https://github.com/immich-app/ui.git
synced 2026-09-30 13:32:54 +08:00
chore: migrate svelte-markdown-preprocess (#655)
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
{
|
||||
"packages/ui": "0.70.0",
|
||||
"packages/svelte-markdown-preprocess": "0.3.0"
|
||||
"packages/ui": "0.70.0"
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
src/
|
||||
tsconfig.json
|
||||
@@ -1,147 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## [0.3.0](https://github.com/immich-app/ui/compare/svelte-markdown-preprocess-v0.2.1...svelte-markdown-preprocess-v0.3.0) (2026-03-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* markdown image caption ([#654](https://github.com/immich-app/ui/issues/654)) ([fb7e9ae](https://github.com/immich-app/ui/commit/fb7e9aea1f29dbdc20ed49e8d288ab705b6c3254))
|
||||
|
||||
## 0.2.1 (2026-01-30)
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: dependencies by @jrasm91 in https://github.com/immich-app/ui/pull/563
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/svelte-markdown-preprocess-v0.2.0...svelte-markdown-preprocess-v0.2.1
|
||||
|
||||
## 0.2.0 (2026-01-30)
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🚀 Features
|
||||
* feat: context menu by @jrasm91 in https://github.com/immich-app/ui/pull/368
|
||||
* feat: required indicator by @bwees in https://github.com/immich-app/ui/pull/404
|
||||
* feat: form modal by @danieldietzler in https://github.com/immich-app/ui/pull/402
|
||||
* feat: badge by @jrasm91 in https://github.com/immich-app/ui/pull/412
|
||||
* feat: date picker by @bwees in https://github.com/immich-app/ui/pull/413
|
||||
* feat: progress bar by @insertish in https://github.com/immich-app/ui/pull/425
|
||||
* feat: context menu button by @danieldietzler in https://github.com/immich-app/ui/pull/435
|
||||
* feat: breadcrumbs by @jrasm91 in https://github.com/immich-app/ui/pull/438
|
||||
* feat: tooltip by @alextran1502 in https://github.com/immich-app/ui/pull/467
|
||||
* feat: image carousel by @jrasm91 in https://github.com/immich-app/ui/pull/499
|
||||
* feat: basic modal by @jrasm91 in https://github.com/immich-app/ui/pull/505
|
||||
* feat: tables by @jrasm91 in https://github.com/immich-app/ui/pull/511
|
||||
* feat: list button by @danieldietzler in https://github.com/immich-app/ui/pull/527
|
||||
* feat: control bar by @jrasm91 in https://github.com/immich-app/ui/pull/550
|
||||
* feat: markdown improvements by @jrasm91 in https://github.com/immich-app/ui/pull/561
|
||||
### 🌟 Enhancements
|
||||
* feat: layer-aware command palette by @danieldietzler in https://github.com/immich-app/ui/pull/376
|
||||
* feat: command palette shortcuts by @danieldietzler in https://github.com/immich-app/ui/pull/379
|
||||
* feat: command palette shortcut visualization by @danieldietzler in https://github.com/immich-app/ui/pull/382
|
||||
* feat: command palette $if by @danieldietzler in https://github.com/immich-app/ui/pull/394
|
||||
* feat: context menu $if by @danieldietzler in https://github.com/immich-app/ui/pull/396
|
||||
* feat: show all shortcuts for command by @danieldietzler in https://github.com/immich-app/ui/pull/420
|
||||
* feat: open menu translation by @jrasm91 in https://github.com/immich-app/ui/pull/451
|
||||
* feat: form onreset support by @jrasm91 in https://github.com/immich-app/ui/pull/504
|
||||
* feat: remove prompt snippet by @jrasm91 in https://github.com/immich-app/ui/pull/506
|
||||
* feat: add cancel text property by @jrasm91 in https://github.com/immich-app/ui/pull/508
|
||||
* feat: text improvements by @jrasm91 in https://github.com/immich-app/ui/pull/529
|
||||
* chore: stricter types for select components by @danieldietzler in https://github.com/immich-app/ui/pull/530
|
||||
* chore: improve nested modal handling by @danieldietzler in https://github.com/immich-app/ui/pull/532
|
||||
* feat: navbar improvements by @jrasm91 in https://github.com/immich-app/ui/pull/543
|
||||
* fix: improve progress bar design by @meesfrensel in https://github.com/immich-app/ui/pull/544
|
||||
### 🐛 Bug fixes
|
||||
* fix: inconsistent card width by @danieldietzler in https://github.com/immich-app/ui/pull/375
|
||||
* fix: dropdown options z-index on modal by @bwees in https://github.com/immich-app/ui/pull/377
|
||||
* fix: modal manager export by @jrasm91 in https://github.com/immich-app/ui/pull/380
|
||||
* fix: command shortcuts by @jrasm91 in https://github.com/immich-app/ui/pull/384
|
||||
* fix: command palette reactivity by @danieldietzler in https://github.com/immich-app/ui/pull/387
|
||||
* fix: menu manager interface by @jrasm91 in https://github.com/immich-app/ui/pull/392
|
||||
* fix: button wrap text by @meesfrensel in https://github.com/immich-app/ui/pull/400
|
||||
* fix: remove field top padding by @bwees in https://github.com/immich-app/ui/pull/414
|
||||
* fix: revert field background color changes by @bwees in https://github.com/immich-app/ui/pull/415
|
||||
* fix: remove modal open count by @danieldietzler in https://github.com/immich-app/ui/pull/417
|
||||
* fix: modal scroll by @jrasm91 in https://github.com/immich-app/ui/pull/427
|
||||
* fix: card scroll in modals by @jrasm91 in https://github.com/immich-app/ui/pull/436
|
||||
* fix: exports by @jrasm91 in https://github.com/immich-app/ui/pull/440
|
||||
* fix: toast panel attributes by @jrasm91 in https://github.com/immich-app/ui/pull/444
|
||||
* fix: wrap long breadcrumbs by @jrasm91 in https://github.com/immich-app/ui/pull/452
|
||||
* fix: broken action item data type by @jrasm91 in https://github.com/immich-app/ui/pull/453
|
||||
* fix: hide trailing dividers by @jrasm91 in https://github.com/immich-app/ui/pull/454
|
||||
* chore: expose more modal size options by @danieldietzler in https://github.com/immich-app/ui/pull/455
|
||||
* fix: alert content width by @jrasm91 in https://github.com/immich-app/ui/pull/464
|
||||
* fix: remove unnecessary cursor-pointer by @meesfrensel in https://github.com/immich-app/ui/pull/472
|
||||
* fix: compare origin in external link check by @YarosMallorca in https://github.com/immich-app/ui/pull/462
|
||||
* fix: focus outlines for buttons by @ben-basten in https://github.com/immich-app/ui/pull/477
|
||||
* fix: autogrow textarea bugs during animation by @midzelis in https://github.com/immich-app/ui/pull/480
|
||||
* fix: tooltip in toast by @alextran1502 in https://github.com/immich-app/ui/pull/485
|
||||
* fix: ignore disabled shortcuts by @jrasm91 in https://github.com/immich-app/ui/pull/491
|
||||
* fix: command palette by @jrasm91 in https://github.com/immich-app/ui/pull/493
|
||||
* fix: command palette shortcuts by @jrasm91 in https://github.com/immich-app/ui/pull/494
|
||||
* fix: optional icons by @jrasm91 in https://github.com/immich-app/ui/pull/496
|
||||
* fix: better is-enabled checks by @jrasm91 in https://github.com/immich-app/ui/pull/498
|
||||
* fix: on close by @jrasm91 in https://github.com/immich-app/ui/pull/509
|
||||
* fix: field context by @jrasm91 in https://github.com/immich-app/ui/pull/514
|
||||
* fix: title font size on different screen size by @alextran1502 in https://github.com/immich-app/ui/pull/519
|
||||
* fix: select by @jrasm91 in https://github.com/immich-app/ui/pull/521
|
||||
* fix: table html attributes by @jrasm91 in https://github.com/immich-app/ui/pull/522
|
||||
* fix: navigate external links by @jrasm91 in https://github.com/immich-app/ui/pull/533
|
||||
* fix: select bind value by @jrasm91 in https://github.com/immich-app/ui/pull/535
|
||||
* fix: allow select empty strings by @jrasm91 in https://github.com/immich-app/ui/pull/537
|
||||
* fix: borderless sidebar by @jrasm91 in https://github.com/immich-app/ui/pull/539
|
||||
* fix: control bar color by @jrasm91 in https://github.com/immich-app/ui/pull/551
|
||||
* chore: control bar tweaks by @jrasm91 in https://github.com/immich-app/ui/pull/553
|
||||
* fix: app shell bar & types by @jrasm91 in https://github.com/immich-app/ui/pull/555
|
||||
* fix: avatar by @jrasm91 in https://github.com/immich-app/ui/pull/557
|
||||
|
||||
## New Contributors
|
||||
* @meesfrensel made their first contribution in https://github.com/immich-app/ui/pull/389
|
||||
* @insertish made their first contribution in https://github.com/immich-app/ui/pull/425
|
||||
* @YarosMallorca made their first contribution in https://github.com/immich-app/ui/pull/462
|
||||
* @aviv926 made their first contribution in https://github.com/immich-app/ui/pull/486
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/svelte-markdown-preprocess-v0.1.0...svelte-markdown-preprocess-v0.2.0
|
||||
|
||||
## 0.1.0 (2025-10-30)
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🚀 Features
|
||||
* feat: default layout by @jrasm91 in https://github.com/immich-app/ui/pull/365
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/svelte-markdown-preprocess-v0.0.2...svelte-markdown-preprocess-v0.1.0
|
||||
|
||||
## 0.0.2 (2025-10-29)
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: bad import by @jrasm91 in https://github.com/immich-app/ui/pull/355
|
||||
* fix: markdown spacing by @jrasm91 in https://github.com/immich-app/ui/pull/359
|
||||
* fix: curly braces in code by @jrasm91 in https://github.com/immich-app/ui/pull/363
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/svelte-markdown-preprocess-v0.0.1...svelte-markdown-preprocess-v0.0.2
|
||||
|
||||
## 0.0.1 (2025-10-28)
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
||||
|
||||
## What's Changed
|
||||
### 🐛 Bug fixes
|
||||
* fix: markdown example by @jrasm91 in https://github.com/immich-app/ui/pull/350
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/immich-app/ui/compare/svelte-markdown-preprocess-v0.0.0...svelte-markdown-preprocess-v0.0.1
|
||||
|
||||
## 0.0.0 (2025-10-27)
|
||||
|
||||
Initial commit
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"name": "@immich/svelte-markdown-preprocess",
|
||||
"version": "0.3.0",
|
||||
"license": "GNU Affero General Public License version 3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/immich-app/ui.git",
|
||||
"directory": "packages/svelte-markdown-preprocess"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:dev",
|
||||
"build": "tsc --project ./tsconfig.json",
|
||||
"test": "vitest",
|
||||
"watch": "tsc --project ./tsconfig.json --watch"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.test.*",
|
||||
"!dist/**/*.spec.*"
|
||||
],
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"front-matter": "^4.0.2",
|
||||
"marked": "^17.0.0",
|
||||
"node-emoji": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.12.0",
|
||||
"vite": "^8.0.0",
|
||||
"vitest": "^4.0.5"
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.14.1"
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import fm from 'front-matter';
|
||||
import { marked, MarkedExtension } from 'marked';
|
||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
||||
import { dirname } from 'node:path';
|
||||
import type { PreprocessorGroup } from 'svelte/compiler';
|
||||
import { markedSvelte } from './marked-svelte.js';
|
||||
import { SvelteFile } from './svelte-file.js';
|
||||
|
||||
type Layouts = Record<string, string>;
|
||||
type Options = {
|
||||
/** defaults to `@immich/ui` */
|
||||
markdownPackageName?: string;
|
||||
debugPath?: string;
|
||||
layouts?: Layouts;
|
||||
/** additional extensions to use with marked */
|
||||
markedExtensions?: MarkedExtension[];
|
||||
};
|
||||
|
||||
const resolveLayout = (layout?: string, layouts?: Layouts) => {
|
||||
if (!layouts) {
|
||||
return;
|
||||
}
|
||||
|
||||
return layouts[layout || 'default'];
|
||||
};
|
||||
|
||||
const SCRIPT_BODY_REGEX = /<script.*>(?<body>(.|\n)*?)<\/script>/;
|
||||
|
||||
const parse = (content: string) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const { attributes, body: bodyParsed } = fm(content);
|
||||
|
||||
let scriptBody = '';
|
||||
let body = bodyParsed;
|
||||
const match = SCRIPT_BODY_REGEX.exec(body);
|
||||
const bodyGroup = match?.groups?.body;
|
||||
if (bodyGroup) {
|
||||
scriptBody = bodyGroup.trim();
|
||||
body = body.substring(match.index + match[0].length).trim();
|
||||
}
|
||||
|
||||
return { attributes, body, scriptBody };
|
||||
};
|
||||
|
||||
export const svelteMarkdownPreprocess = (options: Options) => {
|
||||
const { layouts, debugPath, markdownPackageName = '@immich/ui', markedExtensions = [] } = options || {};
|
||||
|
||||
const md = marked.use(...markedExtensions, markedSvelte());
|
||||
|
||||
return {
|
||||
name: '@immich/svelte-markdown-preprocess',
|
||||
async markup({ content, filename }) {
|
||||
if (!filename?.endsWith('.md')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { attributes, body, scriptBody } = parse(content);
|
||||
const markup = await md.parse(body);
|
||||
|
||||
const file = new SvelteFile();
|
||||
file.addScript(`import { Markdown } from '${markdownPackageName}';`);
|
||||
|
||||
const layout = resolveLayout(attributes.layout, layouts);
|
||||
if (layout) {
|
||||
file.addScript(`import Layout from '${layout}';`);
|
||||
file.addTag(`<Layout attributes={${JSON.stringify(attributes)}}>`, '</Layout>');
|
||||
}
|
||||
|
||||
file.addScript(scriptBody);
|
||||
file.addTemplate(markup);
|
||||
|
||||
const code = file.export();
|
||||
|
||||
if (debugPath) {
|
||||
const dir = dirname(debugPath);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(debugPath, code);
|
||||
}
|
||||
|
||||
return { code };
|
||||
},
|
||||
} satisfies PreprocessorGroup;
|
||||
};
|
||||
@@ -1,127 +0,0 @@
|
||||
import { MarkedExtension } from 'marked';
|
||||
import { emojify } from 'node-emoji';
|
||||
import { createAttributes, escapeSvelteCode, getIdFromText } from './utility.js';
|
||||
|
||||
export const markedSvelte = (): MarkedExtension => ({
|
||||
tokenizer: {
|
||||
code(src) {
|
||||
if (src.startsWith(' ')) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
renderer: {
|
||||
heading({ tokens, depth }) {
|
||||
const text = this.parser.parseInline(tokens);
|
||||
const id = getIdFromText(text);
|
||||
switch (depth) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6: {
|
||||
return `<Markdown.Heading id="${id}" level={${depth}}>${text}</Markdown.Heading>\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return `<Markdown.Paragraph>${text}</Markdown.Paragraph>\n`;
|
||||
},
|
||||
|
||||
list({ items, ordered, start }) {
|
||||
const startAttribute = start && start !== 1 ? ` start={${start}}` : '';
|
||||
let output = '';
|
||||
output += `<Markdown.List ordered={${ordered}}${startAttribute}>\n`;
|
||||
for (const item of items) {
|
||||
const content = this.parser.parse(item.tokens);
|
||||
const attributes = [item.task && 'task', item.checked && 'checked'].filter(Boolean);
|
||||
output += `<Markdown.ListItem ${attributes.join(' ')}>${content}</Markdown.ListItem>\n`;
|
||||
}
|
||||
output += `</Markdown.List>\n`;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
blockquote({ tokens }) {
|
||||
const children = this.parser.parse(tokens);
|
||||
return `<Markdown.BlockQuote>${children}</Markdown.BlockQuote>\n`;
|
||||
},
|
||||
|
||||
paragraph({ tokens }) {
|
||||
const children = this.parser.parseInline(tokens);
|
||||
|
||||
// pass through svelte syntax unchanged
|
||||
if (
|
||||
children.startsWith('{#') ||
|
||||
children.startsWith('{/') ||
|
||||
children.startsWith('{:') ||
|
||||
children.startsWith('{@')
|
||||
) {
|
||||
return children;
|
||||
}
|
||||
return `<Markdown.Paragraph>${children}</Markdown.Paragraph>\n`;
|
||||
},
|
||||
|
||||
text(token) {
|
||||
if (token.type === 'text') {
|
||||
return token.tokens ? this.parser.parseInline(token.tokens) : emojify(token.text);
|
||||
}
|
||||
|
||||
return token.text;
|
||||
},
|
||||
|
||||
image({ href, text: alt, title }) {
|
||||
return `<Markdown.Image${createAttributes({ src: href, alt, title })}/>\n`;
|
||||
},
|
||||
|
||||
space() {
|
||||
return `<Markdown.Space />\n`;
|
||||
},
|
||||
|
||||
table({ header, rows }) {
|
||||
// table
|
||||
let output = `<Markdown.Table>\n`;
|
||||
|
||||
// header
|
||||
output += `<Markdown.TableHeader>\n`;
|
||||
for (const cell of header) {
|
||||
const content = this.parser.parseInline(cell.tokens);
|
||||
output += `<Markdown.TableHeading>${content}</Markdown.TableHeading>\n`;
|
||||
}
|
||||
output += `</Markdown.TableHeader>\n`;
|
||||
|
||||
// body
|
||||
output += `<Markdown.TableBody>\n`;
|
||||
for (const row of rows) {
|
||||
output += `<Markdown.TableRow>\n`;
|
||||
for (const cell of row) {
|
||||
const content = this.parser.parseInline(cell.tokens);
|
||||
output += `<Markdown.TableCell>${content}</Markdown.TableCell>\n`;
|
||||
}
|
||||
output += `</Markdown.TableRow>\n`;
|
||||
}
|
||||
output += `</Markdown.TableBody>\n`;
|
||||
|
||||
output += `</Markdown.Table>\n`;
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
code({ text, lang }) {
|
||||
const escaped = escapeSvelteCode(text);
|
||||
return `<Markdown.Code lang="${lang}" code={\`${escaped}\`} multiline />\n`;
|
||||
},
|
||||
|
||||
hr() {
|
||||
return `<Markdown.LineBreak />\n`;
|
||||
},
|
||||
|
||||
link({ href, title, text }) {
|
||||
return `<Markdown.Link${createAttributes({ href, title })}>${text}</Markdown.Link>`;
|
||||
},
|
||||
|
||||
codespan({ text }) {
|
||||
return `<Markdown.Code code={\`${text}\`} />`;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
export class SvelteFile {
|
||||
private scripts: string[] = [];
|
||||
private lines: string[] = [];
|
||||
private closingTags: string[] = [];
|
||||
|
||||
addScript(line: string) {
|
||||
if (line) {
|
||||
this.scripts.push(line);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
addTemplate(line: string) {
|
||||
this.lines.push(line);
|
||||
return this;
|
||||
}
|
||||
|
||||
addTag(tag: string, closingTag: string) {
|
||||
this.lines.push(tag);
|
||||
this.closingTags.push(closingTag);
|
||||
}
|
||||
|
||||
export() {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push(`<script lang="ts">`);
|
||||
for (const line of this.scripts) {
|
||||
lines.push(' ' + line);
|
||||
}
|
||||
lines.push('</script>');
|
||||
|
||||
for (const line of this.lines) {
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
for (const line of this.closingTags.toReversed()) {
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { escapeSvelteCode, getIdFromText } from './utility.js';
|
||||
|
||||
describe('utility', () => {
|
||||
describe(getIdFromText.name, () => {
|
||||
it('should generate a valid ID from text', () => {
|
||||
const result = getIdFromText('Hello World!');
|
||||
expect(result).toBe('hello-world');
|
||||
});
|
||||
});
|
||||
|
||||
describe(escapeSvelteCode.name, () => {
|
||||
it('should escape curly braces', () => {
|
||||
const code = "const test = { foo: 'bar' };";
|
||||
const expected = `const test = \\{ foo: 'bar' \\};`;
|
||||
|
||||
expect(escapeSvelteCode(code)).toBe(expected);
|
||||
});
|
||||
|
||||
it('should escape backtics', () => {
|
||||
const code = 'console.log(`Hello World!`);';
|
||||
const expected = 'console.log(\\`Hello World!\\`);';
|
||||
|
||||
expect(escapeSvelteCode(code)).toBe(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
export const getIdFromText = (text: string) => {
|
||||
let id = text
|
||||
.toLowerCase()
|
||||
.replace(/<[^>]*>/g, '')
|
||||
.replace(/[^a-z1-9 ]/g, '')
|
||||
.replace(/\s+/g, '-');
|
||||
|
||||
if (id.endsWith('-')) {
|
||||
id = id.slice(0, -1);
|
||||
}
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
export const escapeSvelteCode = (text: string) => {
|
||||
let escaped = text;
|
||||
|
||||
for (const char of ['{', '}', '`']) {
|
||||
escaped = escaped.replaceAll(char, `\\${char}`);
|
||||
}
|
||||
|
||||
return escaped;
|
||||
};
|
||||
|
||||
export const createAttributes = (attributes: Record<string, string | null | undefined>) => {
|
||||
const results = Object.entries(attributes)
|
||||
.filter(([, value]) => !!value)
|
||||
.map(([key, value]) => `${key}="${value}"`);
|
||||
if (results.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return ' ' + results.join(' ');
|
||||
};
|
||||
@@ -1,169 +0,0 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`markup > should render 'code-block.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Code lang="ts" code={\`const foo = 'bar';\`} multiline />
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'code-block-curly-braces.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Code lang="ts" code={\`const name = 'world';
|
||||
const message = \\\`Hello $\\{name\\}!\\\`;\`} multiline />
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'h1.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Heading id="heading-1" level={1}>Heading 1</Markdown.Heading>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'h2.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Heading id="heading-2" level={2}>Heading 2</Markdown.Heading>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'h3.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Heading id="heading-3" level={2}>Heading 3</Markdown.Heading>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'h4.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Heading id="heading-4" level={2}>Heading 4</Markdown.Heading>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'h5.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Heading id="heading-5" level={2}>Heading 5</Markdown.Heading>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'h6.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Heading id="heading-6" level={2}>Heading 6</Markdown.Heading>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'image.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is an image <Markdown.Image src="https://immich.app/"/>
|
||||
|
||||
This is an image with alt <Markdown.Image src="https://immich.app/" alt="alt text"/>
|
||||
|
||||
This is an image with alt and a title <Markdown.Image src="https://immich.app/," alt="alt text" title="title text"/>
|
||||
</Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'link.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is a <Markdown.Link href="https://immich.app/">link</Markdown.Link></Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'link-shothand.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is a shorthand link: <Markdown.Link href="https://immich.app/">https://immich.app/</Markdown.Link></Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'snippets.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
{#snippet postScript()}<Markdown.Space />
|
||||
<Markdown.Paragraph>This should work</Markdown.Paragraph>
|
||||
<Markdown.Space />
|
||||
{/snippet}",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'text-bold.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is <strong>bold</strong> text.</Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'text-code.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is <Markdown.Code code={\`code\`} /> text.</Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'text-italic.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is <em>italic</em> text.</Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`markup > should render 'text-strike-through.md' correctly 1`] = `
|
||||
{
|
||||
"code": "<script lang="ts">
|
||||
import { Markdown } from '@immich/ui';
|
||||
</script>
|
||||
<Markdown.Paragraph>This is <del>strike-through</del> text.</Markdown.Paragraph>
|
||||
",
|
||||
}
|
||||
`;
|
||||
@@ -1,4 +0,0 @@
|
||||
```ts
|
||||
const name = 'world';
|
||||
const message = `Hello ${name}!`;
|
||||
```
|
||||
@@ -1,3 +0,0 @@
|
||||
```ts
|
||||
const foo = 'bar';
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
# Heading 1
|
||||
@@ -1 +0,0 @@
|
||||
## Heading 2
|
||||
@@ -1 +0,0 @@
|
||||
## Heading 3
|
||||
@@ -1 +0,0 @@
|
||||
## Heading 4
|
||||
@@ -1 +0,0 @@
|
||||
## Heading 5
|
||||
@@ -1 +0,0 @@
|
||||
## Heading 6
|
||||
@@ -1,3 +0,0 @@
|
||||
This is an image 
|
||||
This is an image with alt 
|
||||
This is an image with alt and a title 
|
||||
@@ -1 +0,0 @@
|
||||
This is a shorthand link: <https://immich.app/>
|
||||
@@ -1 +0,0 @@
|
||||
This is a [link](https://immich.app/)
|
||||
@@ -1,5 +0,0 @@
|
||||
{#snippet postScript()}
|
||||
|
||||
This should work
|
||||
|
||||
{/snippet}
|
||||
@@ -1 +0,0 @@
|
||||
This is **bold** text.
|
||||
@@ -1 +0,0 @@
|
||||
This is `code` text.
|
||||
@@ -1 +0,0 @@
|
||||
This is _italic_ text.
|
||||
@@ -1 +0,0 @@
|
||||
This is ~~strike-through~~ text.
|
||||
@@ -1,21 +0,0 @@
|
||||
import { basename } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { svelteMarkdownPreprocess } from '../src/index.js';
|
||||
|
||||
const fixtureMap: Record<string, { default: string }> = import.meta.glob('./fixtures/*.md', {
|
||||
query: '?raw',
|
||||
eager: true,
|
||||
});
|
||||
const fixtures = Object.entries(fixtureMap).map(([filepath, { default: content }]) => ({
|
||||
filename: basename(filepath),
|
||||
filepath,
|
||||
content,
|
||||
}));
|
||||
|
||||
describe('markup', () => {
|
||||
it.each(fixtures)('should render $filename correctly', async ({ filename, content }) => {
|
||||
const sut = svelteMarkdownPreprocess({});
|
||||
const output = await sut.markup({ content, filename });
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": false,
|
||||
"strict": true,
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"outDir": "./dist",
|
||||
"types": ["vite/client", "node"],
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["dist", "test", "**/*.spec.*", "vitest.config.js"]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['{src,test}/**/*.{test,spec}.{js,ts}'],
|
||||
},
|
||||
});
|
||||
@@ -60,7 +60,7 @@
|
||||
"vitest": "^4.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@immich/svelte-markdown-preprocess": "workspace:^",
|
||||
"@immich/svelte-markdown-preprocess": "^0.3.0",
|
||||
"@internationalized/date": "^3.10.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"bits-ui": "^2.15.7",
|
||||
|
||||
Generated
+17
-28
@@ -54,36 +54,11 @@ importers:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.1(@types/node@24.12.0)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.6.1))
|
||||
|
||||
packages/svelte-markdown-preprocess:
|
||||
dependencies:
|
||||
front-matter:
|
||||
specifier: ^4.0.2
|
||||
version: 4.0.2
|
||||
marked:
|
||||
specifier: ^17.0.0
|
||||
version: 17.0.5
|
||||
node-emoji:
|
||||
specifier: ^2.2.0
|
||||
version: 2.2.0
|
||||
svelte:
|
||||
specifier: ^5.0.0
|
||||
version: 5.55.0
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^24.12.0
|
||||
version: 24.12.0
|
||||
vite:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.6.1)
|
||||
vitest:
|
||||
specifier: ^4.0.5
|
||||
version: 4.1.1(@types/node@24.12.0)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.6.1))
|
||||
|
||||
packages/ui:
|
||||
dependencies:
|
||||
'@immich/svelte-markdown-preprocess':
|
||||
specifier: workspace:^
|
||||
version: link:../svelte-markdown-preprocess
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(svelte@5.55.0)
|
||||
'@internationalized/date':
|
||||
specifier: ^3.10.0
|
||||
version: 3.12.0
|
||||
@@ -393,6 +368,11 @@ packages:
|
||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@immich/svelte-markdown-preprocess@0.3.0':
|
||||
resolution: {integrity: sha512-6xspWnOgaTi+TasteJgI6DjOGjBQQI30mOYiY/FnyEjczNbrV6r5SFWjNbR+JY+Umn7MsPcZf5yzomK+q5AThg==}
|
||||
peerDependencies:
|
||||
svelte: ^5.0.0
|
||||
|
||||
'@internationalized/date@3.12.0':
|
||||
resolution: {integrity: sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==}
|
||||
|
||||
@@ -1965,6 +1945,13 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.3': {}
|
||||
|
||||
'@immich/svelte-markdown-preprocess@0.3.0(svelte@5.55.0)':
|
||||
dependencies:
|
||||
front-matter: 4.0.2
|
||||
marked: 17.0.5
|
||||
node-emoji: 2.2.0
|
||||
svelte: 5.55.0
|
||||
|
||||
'@internationalized/date@3.12.0':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.19
|
||||
@@ -2235,6 +2222,7 @@ snapshots:
|
||||
'@types/node@24.12.0':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
optional: true
|
||||
|
||||
'@types/trusted-types@2.0.7': {}
|
||||
|
||||
@@ -3138,7 +3126,8 @@ snapshots:
|
||||
|
||||
typescript@6.0.2: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
undici-types@7.16.0:
|
||||
optional: true
|
||||
|
||||
unicode-emoji-modifier-base@1.0.0: {}
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
"packages": {
|
||||
"packages/ui": {
|
||||
"component": "ui"
|
||||
},
|
||||
"packages/svelte-markdown-preprocess": {
|
||||
"component": "svelte-markdown-preprocess"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user