Skip to content

Commit 89ed672

Browse files
authored
build(docs): replace Astro with custom Vite plugin for demo (#55)
1 parent b5768f4 commit 89ed672

25 files changed

+228
-1135
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ package
33
dist
44
node_modules
55
.svelte-kit
6+
.svelte-kit/build
67
public/build
78
webpack/public
8-
.astro

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ yarn add svelte-time
4848

4949
The displayed time defaults to `new Date().toISOString()` and is formatted as `"MMM DD, YYYY"`.
5050

51+
<!-- render:Basic -->
52+
5153
```svelte
5254
<script>
5355
import Time from "svelte-time";
@@ -58,6 +60,8 @@ The displayed time defaults to `new Date().toISOString()` and is formatted as `"
5860

5961
The `timestamp` prop can be any of the following `dayjs` values: `string | number | Date | Dayjs`.
6062

63+
<!-- render:CustomTimestamp -->
64+
6165
```svelte
6266
<Time timestamp="2020-02-01" />
6367
@@ -68,6 +72,8 @@ The `timestamp` prop can be any of the following `dayjs` values: `string | numbe
6872

6973
Use the `format` prop to format the timestamp. Refer to the [dayjs format documentation](https://day.js.org/docs/en/display/format) for acceptable formats.
7074

75+
<!-- render:CustomFormat -->
76+
7177
```svelte
7278
<Time timestamp="2020-02-01" format="dddd @ h:mm A · MMMM D, YYYY" />
7379
@@ -80,6 +86,8 @@ Use the `format` prop to format the timestamp. Refer to the [dayjs format docume
8086

8187
Set the `relative` prop value to `true` for the relative time displayed in a human-readable format.
8288

89+
<!-- render:RelativeTime -->
90+
8391
```svelte
8492
<Time relative />
8593
@@ -92,18 +100,24 @@ When using relative time, the `title` attribute will display a formatted timesta
92100

93101
Use the `format` prop to customize the [format](https://day.js.org/docs/en/display/format).
94102

103+
<!-- render:RelativeTimeCustomFormat -->
104+
95105
```svelte
96106
<Time relative format="dddd @ h:mm A · MMMM D, YYYY" />
97107
```
98108

99109
When using `relative`, the `time` element will set the formatted timestamp as the `title` attribute. Specify a custom `title` to override this.
100110

111+
<!-- render:RelativeTimeCustomTitle -->
112+
101113
```svelte
102114
<Time relative title="Custom title" />
103115
```
104116

105117
Set the value to `undefined` to omit the `title` altogether.
106118

119+
<!-- render:RelativeTimeNoTitle -->
120+
107121
```svelte
108122
<Time relative title={undefined} />
109123
```
@@ -132,6 +146,8 @@ An alternative to the `Time` component is to use the `svelteTime` action to form
132146

133147
The API is the same as the `Time` component.
134148

149+
<!-- render:SvelteTimeAction -->
150+
135151
```svelte
136152
<script>
137153
import { svelteTime } from "svelte-time";
@@ -216,6 +232,8 @@ The `dayjs` library is exported from this package for your convenience.
216232

217233
**Note**: the exported `dayjs` function already extends the [relativeTime plugin](https://day.js.org/docs/en/plugin/relative-time).
218234

235+
<!-- render:DayjsExport -->
236+
219237
```svelte
220238
<script>
221239
import { dayjs } from "svelte-time";
@@ -237,6 +255,8 @@ The default `dayjs` locale is English. No other locale is loaded by default for
237255

238256
To use a [custome locale](https://day.js.org/docs/en/i18n/changing-locale), import the relevant language from `dayjs`. See a list of [supported locales](https://github.com/iamkun/dayjs/tree/dev/src/locale).
239257

258+
<!-- render:CustomLocale -->
259+
240260
```svelte
241261
<script>
242262
import "dayjs/locale/de"; // German locale
@@ -264,6 +284,8 @@ Use the [`dayjs.locale`](https://day.js.org/docs/en/i18n/changing-locale) method
264284

265285
To use a [custom timezone](https://day.js.org/docs/en/timezone/timezone), import the `utc` and `timezone` plugins from `dayjs`.
266286

287+
<!-- render:CustomTimezone -->
288+
267289
```svelte
268290
<script>
269291
import utc from "dayjs/plugin/utc";

astro.config.ts

-27
This file was deleted.

bun.lock

+18-609
Large diffs are not rendered by default.

package.json

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
"type": "module",
88
"svelte": "./src/index.js",
99
"scripts": {
10-
"dev": "bun --bun astro dev",
11-
"build": "bun --bun astro build",
12-
"preview": "bun --bun astro preview",
10+
"dev": "vite",
11+
"build": "vite build",
12+
"preview": "vite preview",
1313
"test": "vitest",
1414
"package": "bun --run dlz",
1515
"format": "bun --bun prettier --write . --cache",
@@ -19,16 +19,17 @@
1919
"dayjs": "^1.11.13"
2020
},
2121
"devDependencies": {
22-
"@astrojs/mdx": "^4.0.8",
23-
"@astrojs/svelte": "^7.0.4",
2422
"@sveltejs/vite-plugin-svelte": "^5.0.3",
2523
"@testing-library/svelte": "^5.2.7",
26-
"astro": "^5.3.0",
2724
"dlz": "^0.1.3",
2825
"github-markdown-css": "^5.8.1",
2926
"jsdom": "^26.0.0",
27+
"marked": "^15.0.7",
28+
"marked-base-url": "^1.1.6",
29+
"marked-highlight": "^2.2.1",
3030
"prettier": "^3.5.1",
3131
"prettier-plugin-svelte": "^3.3.3",
32+
"shiki": "^3.0.0",
3233
"svelte": "^5.20.2",
3334
"typescript": "^5.7.3",
3435
"vite": "^6.1.1",

plugin-readme.ts

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { Marked } from "marked";
2+
import { baseUrl } from "marked-base-url";
3+
import { markedHighlight } from "marked-highlight";
4+
import fsp from "node:fs/promises";
5+
import path from "node:path";
6+
import { codeToHtml } from "shiki";
7+
import type { Plugin } from "vite";
8+
9+
type PluginReadmeOptions = {
10+
title: string;
11+
description: string;
12+
watchDir: string;
13+
baseUrl: string;
14+
};
15+
16+
export const pluginReadme = (options: PluginReadmeOptions): Plugin => {
17+
const watchDir = path.join(__dirname, options.watchDir);
18+
let base = "/";
19+
20+
const marked = new Marked(
21+
markedHighlight({
22+
async: true,
23+
async highlight(code, lang) {
24+
const highlightedCode = await codeToHtml(code, {
25+
lang,
26+
theme: "github-dark",
27+
});
28+
29+
return `{@html ${JSON.stringify(highlightedCode)}}\n`;
30+
},
31+
}),
32+
baseUrl(options.baseUrl),
33+
{
34+
renderer: {
35+
html(html) {
36+
if (html.text.includes("render:")) {
37+
// Parse the name from the comment (after "render:")
38+
const regex = /<!--\s*render:(\w+)\s*-->/;
39+
const match = html.text.match(regex);
40+
const componentName = match?.[1];
41+
42+
if (!componentName) {
43+
return html.text;
44+
}
45+
46+
return `<div class="code-demo"><${componentName} /></div>`;
47+
}
48+
return html.text;
49+
},
50+
code(code) {
51+
// Shiki returns a string with the code wrapped in a <pre> tag.
52+
// We need to return the code without the extra <pre> tag.
53+
return code.text;
54+
},
55+
},
56+
gfm: true,
57+
breaks: true,
58+
},
59+
);
60+
61+
return {
62+
name: "vite:process-readme",
63+
64+
// Run before the Svelte plugin.
65+
enforce: "pre",
66+
configureServer(server) {
67+
server.watcher.add(watchDir);
68+
},
69+
handleHotUpdate({ file, server }) {
70+
// If a README.md file changed, force reload the page
71+
// This ensures clean state and no duplicate scripts.
72+
if (file.endsWith("README.md")) {
73+
server.ws.send({ type: "full-reload" });
74+
return [];
75+
}
76+
77+
// If a Svelte component in the watched directory changed,
78+
// let the Svelte plugin handle the HMR.
79+
if (file.startsWith(watchDir) && file.endsWith(".svelte")) {
80+
return;
81+
}
82+
},
83+
configResolved(config) {
84+
// Get the base URL from Vite config.
85+
base = config.base;
86+
},
87+
transformIndexHtml(html) {
88+
return `<html lang="en">
89+
<head>
90+
<meta charset="utf-8" />
91+
<meta name="viewport" content="width=device-width, initial-scale=1" />
92+
<meta
93+
name="description"
94+
content="${options.description}"
95+
/>
96+
<link rel="icon" type="image/svg+xml" href="${base}favicon.svg" />
97+
<title>${options.title}</title>
98+
<style>
99+
html {
100+
color-scheme: dark;
101+
}
102+
103+
main {
104+
max-width: 960px;
105+
margin: auto;
106+
padding: 0 1rem;
107+
}
108+
109+
.markdown-body pre.shiki {
110+
border-radius: 0;
111+
}
112+
113+
.code-demo {
114+
padding: 1rem;
115+
border: 1px solid #24292e;
116+
}
117+
</style>
118+
</head>
119+
120+
<body class="markdown-body">
121+
<main id="readme"></main>
122+
${html}
123+
</body>
124+
</html>
125+
`;
126+
},
127+
async transform(code, id) {
128+
if (id.endsWith("README.md")) {
129+
let imports = "";
130+
131+
const watchedFiles = await fsp.readdir(
132+
path.join(__dirname, options.watchDir),
133+
);
134+
135+
for (const file of watchedFiles) {
136+
if (file.endsWith(".svelte")) {
137+
const moduleName = file.replace(".svelte", "");
138+
imports += `import ${moduleName} from "${options.watchDir}/${file}";\n`;
139+
}
140+
}
141+
142+
const importsBlock = `<script>${imports}</script>`;
143+
const html = await marked.parse(code);
144+
145+
return importsBlock + html;
146+
}
147+
},
148+
};
149+
};

public/favicon.svg

+1
Loading
File renamed without changes.
File renamed without changes.
File renamed without changes.

tests/index.html

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script type="module">
2+
import "github-markdown-css/github-markdown-dark.css";
3+
import { mount } from "svelte";
4+
import Readme from "../README.md";
5+
6+
mount(Readme, { target: document.getElementById("readme") });
7+
</script>

tsconfig.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
"noEmit": true,
44
"esModuleInterop": true,
55
"forceConsistentCasingInFileNames": true,
6+
"noUnusedLocals": true,
7+
"noUnusedParameters": true,
8+
"noImplicitAny": true,
69
"verbatimModuleSyntax": true,
710
"isolatedModules": true,
811
"target": "ESNext",
912
"module": "ESNext",
1013
"moduleResolution": "node",
14+
"resolveJsonModule": true,
1115
"strict": true,
1216
"types": ["svelte", "vitest/globals"],
1317
"paths": {
1418
"svelte-time": ["./src"],
1519
"svelte-time/*": ["./src/*"]
1620
}
1721
},
18-
"include": ["src", "tests", "www"]
22+
"include": ["src", "tests", "plugin-readme.ts", "vite.config.ts"]
1923
}

vite.config.ts

+18-16
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
1-
import { svelte, vitePreprocess } from "@sveltejs/vite-plugin-svelte";
1+
import { svelte } from "@sveltejs/vite-plugin-svelte";
22
import path from "node:path";
33
import { defineConfig } from "vite";
44
import pkg from "./package.json";
5+
import { pluginReadme } from "./plugin-readme";
56

6-
export default defineConfig(({ mode }) => ({
7+
export default defineConfig({
8+
base: "/" + pkg.name,
9+
root: "./tests",
10+
build: { outDir: "../dist", emptyOutDir: true },
711
plugins: [
12+
pluginReadme({
13+
title: pkg.name,
14+
description: pkg.description,
15+
watchDir: "./tests/examples",
16+
baseUrl: "https://github.com/metonym/svelte-time/tree/master/",
17+
}),
818
svelte({
9-
compilerOptions: {
10-
runes: true,
11-
},
12-
hot: false,
13-
preprocess: [vitePreprocess()],
19+
compilerOptions: { runes: true },
20+
extensions: [".svelte", ".md"],
1421
}),
1522
],
1623
resolve: {
17-
alias: {
18-
[pkg.name]: path.resolve("./src"),
19-
},
20-
conditions: mode === "test" ? ["browser"] : [],
21-
},
22-
test: {
23-
globals: true,
24-
environment: "jsdom",
24+
alias: { [pkg.name]: path.resolve("./src") },
25+
conditions: ["browser"],
2526
},
26-
}));
27+
test: { globals: true, environment: "jsdom" },
28+
});

0 commit comments

Comments
 (0)