Skip to content

Commit e7c3adb

Browse files
Update create-spectacle to use import map and ESM-based one page template. (#1304)
* Update to use new EESM/importmap one page * Cleaned up imports, use dynamic React version from Spectacle. * Remove test generated file. * Update tests to use new import maps. * Update cli.test.ts * Update file-writers.ts * Remove async keyword where it's not needed anymore. * Clean up test for cli.
1 parent 74c18f4 commit e7c3adb

File tree

7 files changed

+928
-375
lines changed

7 files changed

+928
-375
lines changed

.changeset/calm-pianos-confess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'create-spectacle': minor
3+
---
4+
5+
Update the create-spectacle CLI to use new ESM/import map-based one page.

packages/create-spectacle/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"cli-spinners": "^2.6.1",
2121
"log-update": "4.0.0",
2222
"prompts": "^2.4.2",
23+
"ts-node": "^10.9.1",
2324
"yargs": "^17.5.1"
2425
},
2526
"devDependencies": {
@@ -29,6 +30,7 @@
2930
},
3031
"resolutions": {},
3132
"scripts": {
33+
"dev": "ts-node src/cli.ts",
3234
"build": "wireit",
3335
"types:check": "wireit",
3436
"lint": "wireit",

packages/create-spectacle/src/cli.test.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from 'node:path';
22
import { exec } from 'node:child_process';
33
import fs from 'node:fs/promises';
4+
import { generateImportMap } from './generators/one-page';
45

56
const CLI_PATH = path.resolve(__dirname, '../bin/cli.js');
67
const TMP_PATH = path.resolve(__dirname, '../tmp');
@@ -214,16 +215,8 @@ describe('create-spectacle', () => {
214215
.readFile(HTML_PATH, 'utf8')
215216
.then((buffer) => buffer.toString());
216217

217-
// Should have deps
218-
const deps = [
219-
'https://unpkg.com/[email protected]/umd/react.production.min.js',
220-
'https://unpkg.com/[email protected]/umd/react-dom.production.min.js',
221-
'https://unpkg.com/[email protected]/umd/react-is.production.min.js',
222-
'https://unpkg.com/[email protected]/prop-types.min.js',
223-
'https://unpkg.com/spectacle@^9/dist/spectacle.min.js'
224-
];
225-
deps.forEach((dep) => {
226-
expect(contents).toContain(`<script src="${dep}"></script>`);
218+
Array.from(generateImportMap().entries()).forEach(([pkg, url]) => {
219+
expect(contents).toContain(`"${pkg}": "${url}"`);
227220
});
228221
});
229222
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import path from 'path';
2+
import { onePageTemplate } from '../templates/one-page';
3+
4+
const SPECTACLE_PATH = path.resolve(__dirname, '../../../spectacle');
5+
const spectaclePackage = require(`${SPECTACLE_PATH}/package.json`);
6+
const REACT_VERSION = spectaclePackage.devDependencies.react.replace('^', '');
7+
const ESM_SH_VERSION = 'v121';
8+
9+
export const generateImportMap = () => {
10+
const importMap = new Map<string, string>();
11+
const {
12+
dependencies,
13+
peerDependencies
14+
} = require(`${SPECTACLE_PATH}/package.json`);
15+
16+
importMap.set('htm', importUrl('htm', '^3'));
17+
importMap.set('spectacle', 'https://esm.sh/spectacle@10?bundle');
18+
19+
const sortedDeps = <[string, string][]>Object.entries({
20+
...dependencies,
21+
...peerDependencies
22+
}).sort(([a], [b]) => a.localeCompare(b));
23+
24+
for (const [pkg, version] of sortedDeps) {
25+
if (importMap.has(pkg)) continue;
26+
importMap.set(pkg, importUrl(pkg, version));
27+
handlePackageExceptions(pkg, version, importMap);
28+
}
29+
30+
return importMap;
31+
};
32+
33+
export const createOnePage = (name: string, lang: string) => {
34+
const importMap = generateImportMap();
35+
return onePageTemplate({ importMap, name, lang });
36+
};
37+
38+
const importUrl = (pkg: string, version: string, extra = '') => {
39+
if (pkg === 'react') version = REACT_VERSION;
40+
return `https://esm.sh/${ESM_SH_VERSION}/${pkg}@${version}${extra}?deps=react@${REACT_VERSION}`;
41+
};
42+
43+
const handlePackageExceptions = (
44+
pkg: string,
45+
version: string,
46+
importMap: Map<string, string>
47+
) => {
48+
if (pkg === 'react')
49+
importMap.set(
50+
`${pkg}/jsx-runtime`,
51+
importUrl(pkg, version, '/jsx-runtime')
52+
);
53+
else if (pkg === 'react-syntax-highlighter') {
54+
importMap.set(
55+
`${pkg}/dist/cjs/styles/prism/vs-dark.js`,
56+
importUrl(pkg, version, '/dist/esm/styles/prism/vs-dark.js')
57+
);
58+
importMap.set(
59+
`${pkg}/dist/cjs/styles/prism/index.js`,
60+
importUrl(pkg, version, '/dist/esm/styles/prism/index.js')
61+
);
62+
}
63+
};

packages/create-spectacle/src/templates/file-writers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import path from 'path';
22
import { mkdir, writeFile, rm } from 'fs/promises';
33
import { htmlTemplate } from './html';
4-
import { onePageTemplate } from './one-page';
54
import { webpackTemplate } from './webpack';
65
import { babelTemplate } from './babel';
76
import { packageTemplate, vitePackageTemplate } from './package';
@@ -10,6 +9,7 @@ import { tsconfigTemplate } from './tsconfig';
109
import { gitignoreTemplate } from './gitignore';
1110
import { readmeTemplate } from './readme';
1211
import { viteConfigTemplate } from './viteConfig';
12+
import { createOnePage } from '../generators/one-page';
1313

1414
export type FileOptions = {
1515
snakeCaseName: string;
@@ -121,6 +121,6 @@ export const writeOnePageHTMLFile = async ({
121121
}: FileOptions) => {
122122
await writeFile(
123123
path.resolve(process.cwd(), `${snakeCaseName}.html`),
124-
onePageTemplate({ name, lang })
124+
createOnePage(name, lang)
125125
);
126126
};

packages/create-spectacle/src/templates/one-page.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
type OnePageTemplateOptions = {
22
name: string;
33
lang: string;
4+
importMap: Map<string, string>;
45
};
56

6-
export const onePageTemplate = ({ name, lang }: OnePageTemplateOptions) => `
7+
export const onePageTemplate = ({
8+
name,
9+
lang,
10+
importMap
11+
}: OnePageTemplateOptions) => `
712
<!DOCTYPE html>
813
<html lang="${lang}">
914
<head>
@@ -15,26 +20,34 @@ export const onePageTemplate = ({ name, lang }: OnePageTemplateOptions) => `
1520
<body>
1621
<div id="root"></div>
1722
18-
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
19-
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
20-
<script src="https://unpkg.com/[email protected]/umd/react-is.production.min.js"></script>
21-
<script src="https://unpkg.com/[email protected]/prop-types.min.js"></script>
22-
<script src="https://unpkg.com/spectacle@^9/dist/spectacle.min.js"></script>
23+
<script type="importmap">
24+
{
25+
"imports": {
26+
${Array.from(importMap.entries())
27+
.map(([pkg, url]) => `"${pkg}": "${url}"`)
28+
.join(',\n ')}
29+
}
30+
}
31+
</script>
2332
2433
<script type="module">
25-
const {
34+
import htm from 'htm';
35+
import React from 'react';
36+
import ReactDOM from 'react-dom';
37+
import {
2638
FlexBox,
2739
Heading,
2840
SpectacleLogo,
2941
Slide,
3042
Deck,
31-
} = Spectacle;
43+
DefaultTemplate
44+
} from 'spectacle';
3245
33-
import htm from 'https://unpkg.com/htm@^3?module';
3446
const html = htm.bind(React.createElement);
47+
const template = () => html\`<\${DefaultTemplate} />\`;
3548
3649
const Presentation = () => html\`
37-
<\${Deck}>
50+
<\${Deck} template=\${template}>
3851
<\${Slide}>
3952
<\${FlexBox} height="100%">
4053
<\${Heading}>${name}</\${Heading}>

0 commit comments

Comments
 (0)