@slidev-react/node 0.2.2 → 0.2.4
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.
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { resolveSlidesSourceFile } from "./slidesSourceFile.mjs";
|
|
2
2
|
import { generatedSlidesAlias, generatedSlidesEntry, pluginCompileTimeSlides } from "./generateCompiledSlides.mjs";
|
|
3
|
+
import { pluginTheme } from "./themePlugin.mjs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import react from "@vitejs/plugin-react";
|
|
5
6
|
//#region src/slides/build/createSlidesViteConfig.ts
|
|
6
7
|
function createSlidesViteConfig(options) {
|
|
7
8
|
const { appRoot, slidesFile } = options;
|
|
9
|
+
const slidesSourceFile = resolveSlidesSourceFile(appRoot, slidesFile);
|
|
8
10
|
return {
|
|
9
11
|
root: appRoot,
|
|
10
|
-
plugins: [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
plugins: [
|
|
13
|
+
pluginCompileTimeSlides({
|
|
14
|
+
appRoot,
|
|
15
|
+
slidesSourceFile
|
|
16
|
+
}),
|
|
17
|
+
pluginTheme({ slidesSourceFile }),
|
|
18
|
+
react()
|
|
19
|
+
],
|
|
14
20
|
resolve: { alias: {
|
|
15
21
|
"@": path.resolve(appRoot, "./packages/client/src"),
|
|
16
22
|
[generatedSlidesAlias]: path.resolve(appRoot, generatedSlidesEntry),
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { __require } from "../../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { parse } from "yaml";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
//#region src/slides/build/themePlugin.ts
|
|
5
|
+
const VIRTUAL_THEME = "virtual:slidev-react/active-theme";
|
|
6
|
+
const RESOLVED_VIRTUAL = "\0" + VIRTUAL_THEME;
|
|
7
|
+
function extractThemeIdFromSlidesFile(slidesSourceFile) {
|
|
8
|
+
let source;
|
|
9
|
+
try {
|
|
10
|
+
source = readFileSync(slidesSourceFile, "utf8");
|
|
11
|
+
} catch {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
15
|
+
if (!match) return void 0;
|
|
16
|
+
try {
|
|
17
|
+
const data = parse(match[1]);
|
|
18
|
+
return typeof data?.theme === "string" ? data.theme : void 0;
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resolveThemePackage(themeId) {
|
|
24
|
+
const candidates = [`@slidev-react/theme-${themeId}`, `slidev-react-theme-${themeId}`];
|
|
25
|
+
for (const pkg of candidates) try {
|
|
26
|
+
__require.resolve(pkg);
|
|
27
|
+
return pkg;
|
|
28
|
+
} catch {}
|
|
29
|
+
}
|
|
30
|
+
function generateThemeModuleCode(options) {
|
|
31
|
+
const { themeId } = options;
|
|
32
|
+
if (!themeId) return "export default undefined;\n";
|
|
33
|
+
const themePackage = resolveThemePackage(themeId);
|
|
34
|
+
if (themePackage) return [
|
|
35
|
+
`import theme from '${themePackage}';`,
|
|
36
|
+
`import '${themePackage}/style.css';`,
|
|
37
|
+
`export default theme;`,
|
|
38
|
+
""
|
|
39
|
+
].join("\n");
|
|
40
|
+
console.warn(`[slidev-react] Theme "${themeId}" not found. Falling back to default theme.`);
|
|
41
|
+
return "export default undefined;\n";
|
|
42
|
+
}
|
|
43
|
+
function pluginTheme(options) {
|
|
44
|
+
const themeId = extractThemeIdFromSlidesFile(options.slidesSourceFile);
|
|
45
|
+
return {
|
|
46
|
+
name: "slidev-react:themes",
|
|
47
|
+
enforce: "pre",
|
|
48
|
+
resolveId(id) {
|
|
49
|
+
if (id === VIRTUAL_THEME) return RESOLVED_VIRTUAL;
|
|
50
|
+
},
|
|
51
|
+
load(id) {
|
|
52
|
+
if (id !== RESOLVED_VIRTUAL) return;
|
|
53
|
+
return generateThemeModuleCode({ themeId });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { pluginTheme };
|
|
@@ -2,8 +2,8 @@ import path from "node:path";
|
|
|
2
2
|
import { readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { layoutNames } from "@slidev-react/core/slides/layout";
|
|
4
4
|
//#region src/slides/validation/validateSlidesAuthoring.ts
|
|
5
|
-
const CLIENT_THEME_THEMES_DIR = "packages/client/src/theme/themes";
|
|
6
5
|
const CLIENT_ADDONS_DIR = "packages/client/src/addons";
|
|
6
|
+
const THEME_PACKAGE_PREFIX = "theme-";
|
|
7
7
|
function collectKnownLayouts() {
|
|
8
8
|
return new Set(layoutNames);
|
|
9
9
|
}
|
|
@@ -17,6 +17,13 @@ async function readLocalIds(rootDir) {
|
|
|
17
17
|
return [];
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
async function readThemePackageIds(packagesDir) {
|
|
21
|
+
try {
|
|
22
|
+
return (await readdir(packagesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory() && entry.name.startsWith(THEME_PACKAGE_PREFIX)).map((entry) => entry.name.slice(6));
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
20
27
|
async function findDefinitionFile(rootDir) {
|
|
21
28
|
for (const candidate of [
|
|
22
29
|
"index.ts",
|
|
@@ -64,20 +71,35 @@ async function readCustomLayoutIds(rootDir) {
|
|
|
64
71
|
}));
|
|
65
72
|
return layouts;
|
|
66
73
|
}
|
|
74
|
+
async function readThemePackageLayoutIds(packagesDir) {
|
|
75
|
+
const layouts = /* @__PURE__ */ new Set();
|
|
76
|
+
try {
|
|
77
|
+
const themeEntries = (await readdir(packagesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory() && entry.name.startsWith(THEME_PACKAGE_PREFIX));
|
|
78
|
+
await Promise.all(themeEntries.map(async (entry) => {
|
|
79
|
+
const definitionFile = await findDefinitionFile(path.join(packagesDir, entry.name));
|
|
80
|
+
if (!definitionFile) return;
|
|
81
|
+
try {
|
|
82
|
+
const source = await readFile(definitionFile, "utf8");
|
|
83
|
+
for (const layoutName of extractObjectLiteralKeys(source, "layouts")) layouts.add(layoutName);
|
|
84
|
+
} catch {}
|
|
85
|
+
}));
|
|
86
|
+
} catch {}
|
|
87
|
+
return layouts;
|
|
88
|
+
}
|
|
67
89
|
async function validateSlidesAuthoring({ appRoot, slides }) {
|
|
68
90
|
const warnings = [];
|
|
69
|
-
const themeRootDir = path.join(appRoot, CLIENT_THEME_THEMES_DIR);
|
|
70
91
|
const addonsRootDir = path.join(appRoot, CLIENT_ADDONS_DIR);
|
|
71
|
-
const
|
|
72
|
-
|
|
92
|
+
const packagesDir = path.join(appRoot, "packages");
|
|
93
|
+
const [themePackageIdList, addonIdList, themePackageLayouts, addonLayouts] = await Promise.all([
|
|
94
|
+
readThemePackageIds(packagesDir),
|
|
73
95
|
readLocalIds(addonsRootDir),
|
|
74
|
-
|
|
96
|
+
readThemePackageLayoutIds(packagesDir),
|
|
75
97
|
readCustomLayoutIds(addonsRootDir)
|
|
76
98
|
]);
|
|
77
|
-
const themeIds = new Set(
|
|
99
|
+
const themeIds = new Set(themePackageIdList);
|
|
78
100
|
const addonIds = new Set(addonIdList);
|
|
79
101
|
const knownLayouts = collectKnownLayouts();
|
|
80
|
-
for (const layoutName of
|
|
102
|
+
for (const layoutName of themePackageLayouts) knownLayouts.add(layoutName);
|
|
81
103
|
for (const layoutName of addonLayouts) knownLayouts.add(layoutName);
|
|
82
104
|
if (slides.meta.theme && !themeIds.has(slides.meta.theme)) warnings.push(`Unknown theme "${slides.meta.theme}". The runtime will fall back to the default theme.`);
|
|
83
105
|
for (const addonId of slides.meta.addons ?? []) if (!addonIds.has(addonId)) warnings.push(`Unknown addon "${addonId}". It will be ignored until a matching local addon exists.`);
|
|
@@ -87,6 +109,9 @@ async function validateSlidesAuthoring({ appRoot, slides }) {
|
|
|
87
109
|
if (!slideLayout || knownLayouts.has(slideLayout)) continue;
|
|
88
110
|
warnings.push(`Unknown layout "${slideLayout}" in ${formatSlideLabel(slide)}. The runtime will fall back to the default layout.`);
|
|
89
111
|
}
|
|
112
|
+
const declaredAddons = new Set(slides.meta.addons ?? []);
|
|
113
|
+
const allSource = slides.slides.map((slide) => slide.source).join("\n");
|
|
114
|
+
if (!declaredAddons.has("mermaid") && /^```mermaid\s*$/m.test(allSource)) warnings.push("Slides contain mermaid code fences but the \"mermaid\" addon is not declared. Add addons: [\"mermaid\"] to the deck frontmatter.");
|
|
90
115
|
return warnings;
|
|
91
116
|
}
|
|
92
117
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev-react/node",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Node-side command APIs for slidev-react authoring and build workflows",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"vite": "^8.0.0",
|
|
34
34
|
"yaml": "2.8.2",
|
|
35
35
|
"zod": "4.3.6",
|
|
36
|
-
"@slidev-react/core": "0.2.
|
|
36
|
+
"@slidev-react/core": "0.2.4"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|