Skip to content

Commit 76b8ac6

Browse files
authored
Merge pull request #65 from mainmatter/pichfl/themes
Allow auto-reveal to launch without relying on installing a theme
2 parents 16d88d0 + 26ceca2 commit 76b8ac6

File tree

7 files changed

+130
-56
lines changed

7 files changed

+130
-56
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ automatically be used for your presentation. For example, to use the
5151
npm add auto-reveal-theme-mainmatter
5252
```
5353

54-
If no theme is installed, the default Reveal.js `simple` theme will be used
54+
If no theme is installed, the default Reveal.js `black` theme will be used
5555
by default.
5656

5757
### Document Title
@@ -79,8 +79,8 @@ There is none (yet).
7979

8080
### Contributing
8181

82-
This project uses Vite under the hood. Linting and formatting is handled by
83-
Biome.
82+
This project uses [Vite](https://vite.dev) under the hood. Linting and formatting is handled by
83+
[Biome](https://biomejs.dev).
8484

8585
### Building themes
8686

lib/common-utils.cjs

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
const fs = require('node:fs');
22
const path = require('node:path');
33

4-
let packageJson;
4+
function readJsonSync(filePath, silent = true) {
5+
try {
6+
if (fs.accessSync(filePath, fs.constants.R_OK)) {
7+
if (!silent) {
8+
console.error(`${filePath} can't be read.`);
9+
}
510

6-
function getPackageJson(cwd = process.cwd()) {
7-
if (packageJson) {
8-
return packageJson;
9-
}
11+
return {};
12+
}
1013

11-
try {
12-
packageJson = JSON.parse(
13-
fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'),
14-
);
14+
const content = fs.readFileSync(filePath, 'utf-8');
1515

16-
return packageJson;
16+
return JSON.parse(content);
1717
} catch {
18-
console.error('No package.json found');
19-
}
18+
if (!silent) {
19+
console.error(`${filePath} can't be parsed as JSON.`);
20+
}
2021

21-
return { name: '', dependencies: {} };
22+
return {};
23+
}
2224
}
2325

2426
function getThemePackage() {
25-
const packageJson = getPackageJson();
27+
const packageJson = readJsonSync(path.join(process.cwd(), 'package.json'));
2628
return [
2729
...Object.keys(packageJson.dependencies ?? {}),
2830
...Object.keys(packageJson.devDependencies ?? {}),
@@ -33,17 +35,41 @@ function getTheme() {
3335
const theme = getThemePackage();
3436

3537
if (!theme) {
36-
return 'reveal.js/dist/theme/simple.css';
38+
return;
3739
}
3840

3941
// This can't be done with `import.meta.resolve` because it doesn't support a parent as of Node.js 20,
4042
// which is the reason why this whole file is a CommonJS module.
41-
return require.resolve(path.join(theme, 'package.json'), {
43+
const themePkg = require.resolve(theme, {
44+
paths: [process.cwd()],
45+
});
46+
const dir = path.dirname(themePkg);
47+
const config = path.resolve(dir, 'config.json');
48+
49+
return {
50+
dir,
51+
main: themePkg,
52+
config: readJsonSync(config),
53+
};
54+
}
55+
56+
function getRevealJsTheme(theme = 'black') {
57+
const revealJs = require.resolve('reveal.js', {
4258
paths: [process.cwd()],
4359
});
60+
61+
const main = path.join(path.dirname(revealJs), 'theme', `${theme}.css`);
62+
63+
return {
64+
dir: path.dirname(main),
65+
main,
66+
config: {},
67+
};
4468
}
4569

4670
module.exports = {
71+
readJsonSync,
4772
getTheme,
4873
getThemePackage,
74+
getRevealJsTheme,
4975
};

lib/config.js

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,83 @@
1-
import { dirname, join } from 'node:path';
2-
import { fileURLToPath } from 'node:url';
1+
import path from 'node:path';
2+
import url from 'node:url';
33
import { ViteEjsPlugin as viteEjsPlugin } from 'vite-plugin-ejs';
4-
import { getTheme, getTitle } from '../lib/utils.js';
4+
import {
5+
getRevealJsTheme,
6+
getTheme,
7+
getTitle,
8+
readJsonSync,
9+
} from '../lib/utils.js';
510

6-
const __dirname = fileURLToPath(new URL('.', import.meta.url));
11+
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
712
const cwd = process.cwd();
8-
const outDir = join(process.cwd(), 'dist');
13+
const outDir = path.join(process.cwd(), 'dist');
14+
const pkg = readJsonSync(path.join(cwd, 'package.json'));
15+
const configFromPackage = pkg['auto-reveal'] || {
16+
theme: undefined,
17+
config: {},
18+
};
19+
20+
export default function themeConfigPlugin() {
21+
const theme = getTheme();
22+
const revealJsTheme = getRevealJsTheme();
23+
24+
return {
25+
name: 'auto-reveal',
26+
resolveId(id) {
27+
if (id === 'virtual:auto-reveal/config') {
28+
return '\0virtual:auto-reveal/config';
29+
}
30+
},
31+
load(id) {
32+
if (id === '\0virtual:auto-reveal/config') {
33+
let config = configFromPackage.config;
34+
35+
config = {
36+
...config,
37+
...(theme?.config ?? {}),
38+
};
39+
40+
return `const config = ${JSON.stringify(config)};export default config;`;
41+
}
42+
},
43+
config() {
44+
return {
45+
server: {
46+
fs: {
47+
allow: [theme?.dir, revealJsTheme.dir, '.'].filter(Boolean),
48+
},
49+
},
50+
resolve: {
51+
alias: {
52+
'@theme': theme?.main ? theme.main : revealJsTheme.main,
53+
},
54+
},
55+
};
56+
},
57+
};
58+
}
959

10-
const themeFolder = dirname(getTheme());
1160
const config = {
1261
configFile: false,
13-
root: join(__dirname, '..', 'src'),
14-
publicDir: join(cwd, 'public'),
62+
root: path.join(__dirname, '..', 'src'),
63+
publicDir: path.join(cwd, 'public'),
1564
base: './',
1665
server: {
1766
port: 1337,
18-
fs: {
19-
allow: [themeFolder, '.'],
20-
},
2167
},
2268
plugins: [
2369
viteEjsPlugin({
2470
title: getTitle(),
2571
}),
72+
themeConfigPlugin(),
2673
],
2774
resolve: {
2875
alias: {
29-
slides: join(cwd, 'slides'),
30-
'@theme': themeFolder,
76+
slides: path.join(cwd, 'slides'),
3177
},
3278
},
3379
build: {
34-
outDir: join(cwd, 'dist'),
80+
outDir: path.join(cwd, 'dist'),
3581
},
3682
};
3783

lib/utils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export function getTitle(defaultTitle = 'auto-reveal') {
2020
return process.env.npm_package_name ?? defaultTitle;
2121
}
2222

23-
const { getTheme, getThemePackage } = utils;
23+
const { getTheme, getThemePackage, getRevealJsTheme, readJsonSync } = utils;
2424

25-
export { getTheme, getThemePackage };
25+
export { getTheme, getThemePackage, getRevealJsTheme, readJsonSync };

src/main.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Notes from 'reveal.js/plugin/notes/notes.esm.js';
66
import 'reveal.js/dist/reveal.css';
77

88
import '@theme';
9+
import themeConfig from 'virtual:auto-reveal/config';
910

1011
const markdownFiles = import.meta.glob('slides/*.md', {
1112
query: '?raw',
@@ -22,7 +23,7 @@ const sortedMarkdownFiles = Object.entries(markdownFiles).sort(([a], [b]) =>
2223

2324
const sections = sortedMarkdownFiles.map(
2425
([, content]) => `
25-
<section
26+
<section
2627
data-markdown
2728
data-separator="^\n___\n$"
2829
data-separator-vertical="^\n---\n$"
@@ -37,25 +38,21 @@ document.querySelector('.slides').innerHTML = sections.join('');
3738

3839
const deck = new Reveal();
3940

40-
const defaultConfig = {
41+
const preConfig = {
4142
hash: true,
4243
width: 1280,
4344
height: 960,
4445
margin: 0.1,
4546
highlight: {},
46-
plugins: [Markdown, Highlight, Notes],
4747
};
4848

49-
const themeConfig = {};
50-
51-
// FIXME: This fails hard if no config is present
52-
// try {
53-
// const findingConfig = import.meta.glob('@theme/config.json', { eager: true });
54-
// const [filename] = Object.keys(findingConfig);
55-
56-
// if (filename) {
57-
// themeConfig = findingConfig[filename];
58-
// }
59-
// } catch {}
49+
const afterConfig = {
50+
// Ensure plugins are always loaded and not touched by the theme configuraiton
51+
plugins: [Markdown, Highlight, Notes],
52+
};
6053

61-
deck.initialize({ ...defaultConfig, ...themeConfig });
54+
deck.initialize({
55+
...preConfig,
56+
...themeConfig,
57+
...afterConfig,
58+
});

tests/helpers.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { readFileSync } from 'node:fs';
2-
import { join } from 'node:path';
1+
import fs from 'node:fs';
2+
import path from 'node:path';
33
import { Project } from 'fixturify-project';
44
import { globby } from 'globby';
55

@@ -9,7 +9,12 @@ export async function makeProject({ files = {}, theme } = {}) {
99
});
1010

1111
// setup the current auto-reveal as a dev dependency to link its bin
12-
project.linkDevDependency('auto-reveal', { resolveName: '.', baseDir: '.' });
12+
project.linkDevDependency('auto-reveal', { baseDir: '.', resolveName: '.' });
13+
14+
project.linkDevDependency('reveal.js', {
15+
baseDir: '.',
16+
resolveName: 'reveal.js',
17+
});
1318

1419
project.addDependency(theme);
1520

@@ -21,5 +26,5 @@ export async function makeProject({ files = {}, theme } = {}) {
2126
export async function getFileContents(glob, cwd) {
2227
// there should only be one file that matches this glob
2328
const [indexCss] = await globby([glob], { cwd });
24-
return readFileSync(join(cwd, indexCss), 'utf8');
29+
return fs.readFileSync(path.join(cwd, indexCss), 'utf8');
2530
}

tests/themes/default.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { describe, expect, it } from 'vitest';
33

44
import { makeProject } from '../helpers';
55

6-
// not using a theme doesn't work right now
7-
describe.skip('default theme tests', () => {
6+
describe('default theme tests', () => {
87
it('can work without a theme or any slides', async () => {
98
const { cwd } = await makeProject();
109
const result = await execa({
1110
cwd,
1211
})`./node_modules/.bin/auto-reveal build`;
12+
1313
expect(result.exitCode).to.equal(0);
1414
});
1515
});

0 commit comments

Comments
 (0)