Skip to content

Commit

Permalink
Update create-spectacle to use import map and ESM-based one page te…
Browse files Browse the repository at this point in the history
…mplate. (#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.
  • Loading branch information
carloskelly13 committed Nov 20, 2023
1 parent 74c18f4 commit e7c3adb
Show file tree
Hide file tree
Showing 7 changed files with 928 additions and 375 deletions.
5 changes: 5 additions & 0 deletions .changeset/calm-pianos-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'create-spectacle': minor
---

Update the create-spectacle CLI to use new ESM/import map-based one page.
2 changes: 2 additions & 0 deletions packages/create-spectacle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"cli-spinners": "^2.6.1",
"log-update": "4.0.0",
"prompts": "^2.4.2",
"ts-node": "^10.9.1",
"yargs": "^17.5.1"
},
"devDependencies": {
Expand All @@ -29,6 +30,7 @@
},
"resolutions": {},
"scripts": {
"dev": "ts-node src/cli.ts",
"build": "wireit",
"types:check": "wireit",
"lint": "wireit",
Expand Down
13 changes: 3 additions & 10 deletions packages/create-spectacle/src/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'node:path';
import { exec } from 'node:child_process';
import fs from 'node:fs/promises';
import { generateImportMap } from './generators/one-page';

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

// Should have deps
const deps = [
'https://unpkg.com/[email protected]/umd/react.production.min.js',
'https://unpkg.com/[email protected]/umd/react-dom.production.min.js',
'https://unpkg.com/[email protected]/umd/react-is.production.min.js',
'https://unpkg.com/[email protected]/prop-types.min.js',
'https://unpkg.com/spectacle@^9/dist/spectacle.min.js'
];
deps.forEach((dep) => {
expect(contents).toContain(`<script src="${dep}"></script>`);
Array.from(generateImportMap().entries()).forEach(([pkg, url]) => {
expect(contents).toContain(`"${pkg}": "${url}"`);
});
});
});
Expand Down
63 changes: 63 additions & 0 deletions packages/create-spectacle/src/generators/one-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import path from 'path';
import { onePageTemplate } from '../templates/one-page';

const SPECTACLE_PATH = path.resolve(__dirname, '../../../spectacle');
const spectaclePackage = require(`${SPECTACLE_PATH}/package.json`);
const REACT_VERSION = spectaclePackage.devDependencies.react.replace('^', '');
const ESM_SH_VERSION = 'v121';

export const generateImportMap = () => {
const importMap = new Map<string, string>();
const {
dependencies,
peerDependencies
} = require(`${SPECTACLE_PATH}/package.json`);

importMap.set('htm', importUrl('htm', '^3'));
importMap.set('spectacle', 'https://esm.sh/spectacle@10?bundle');

const sortedDeps = <[string, string][]>Object.entries({
...dependencies,
...peerDependencies
}).sort(([a], [b]) => a.localeCompare(b));

for (const [pkg, version] of sortedDeps) {
if (importMap.has(pkg)) continue;
importMap.set(pkg, importUrl(pkg, version));
handlePackageExceptions(pkg, version, importMap);
}

return importMap;
};

export const createOnePage = (name: string, lang: string) => {
const importMap = generateImportMap();
return onePageTemplate({ importMap, name, lang });
};

const importUrl = (pkg: string, version: string, extra = '') => {
if (pkg === 'react') version = REACT_VERSION;
return `https://esm.sh/${ESM_SH_VERSION}/${pkg}@${version}${extra}?deps=react@${REACT_VERSION}`;
};

const handlePackageExceptions = (
pkg: string,
version: string,
importMap: Map<string, string>
) => {
if (pkg === 'react')
importMap.set(
`${pkg}/jsx-runtime`,
importUrl(pkg, version, '/jsx-runtime')
);
else if (pkg === 'react-syntax-highlighter') {
importMap.set(
`${pkg}/dist/cjs/styles/prism/vs-dark.js`,
importUrl(pkg, version, '/dist/esm/styles/prism/vs-dark.js')
);
importMap.set(
`${pkg}/dist/cjs/styles/prism/index.js`,
importUrl(pkg, version, '/dist/esm/styles/prism/index.js')
);
}
};
4 changes: 2 additions & 2 deletions packages/create-spectacle/src/templates/file-writers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from 'path';
import { mkdir, writeFile, rm } from 'fs/promises';
import { htmlTemplate } from './html';
import { onePageTemplate } from './one-page';
import { webpackTemplate } from './webpack';
import { babelTemplate } from './babel';
import { packageTemplate, vitePackageTemplate } from './package';
Expand All @@ -10,6 +9,7 @@ import { tsconfigTemplate } from './tsconfig';
import { gitignoreTemplate } from './gitignore';
import { readmeTemplate } from './readme';
import { viteConfigTemplate } from './viteConfig';
import { createOnePage } from '../generators/one-page';

export type FileOptions = {
snakeCaseName: string;
Expand Down Expand Up @@ -121,6 +121,6 @@ export const writeOnePageHTMLFile = async ({
}: FileOptions) => {
await writeFile(
path.resolve(process.cwd(), `${snakeCaseName}.html`),
onePageTemplate({ name, lang })
createOnePage(name, lang)
);
};
33 changes: 23 additions & 10 deletions packages/create-spectacle/src/templates/one-page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
type OnePageTemplateOptions = {
name: string;
lang: string;
importMap: Map<string, string>;
};

export const onePageTemplate = ({ name, lang }: OnePageTemplateOptions) => `
export const onePageTemplate = ({
name,
lang,
importMap
}: OnePageTemplateOptions) => `
<!DOCTYPE html>
<html lang="${lang}">
<head>
Expand All @@ -15,26 +20,34 @@ export const onePageTemplate = ({ name, lang }: OnePageTemplateOptions) => `
<body>
<div id="root"></div>
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/prop-types.min.js"></script>
<script src="https://unpkg.com/spectacle@^9/dist/spectacle.min.js"></script>
<script type="importmap">
{
"imports": {
${Array.from(importMap.entries())
.map(([pkg, url]) => `"${pkg}": "${url}"`)
.join(',\n ')}
}
}
</script>
<script type="module">
const {
import htm from 'htm';
import React from 'react';
import ReactDOM from 'react-dom';
import {
FlexBox,
Heading,
SpectacleLogo,
Slide,
Deck,
} = Spectacle;
DefaultTemplate
} from 'spectacle';
import htm from 'https://unpkg.com/htm@^3?module';
const html = htm.bind(React.createElement);
const template = () => html\`<\${DefaultTemplate} />\`;
const Presentation = () => html\`
<\${Deck}>
<\${Deck} template=\${template}>
<\${Slide}>
<\${FlexBox} height="100%">
<\${Heading}>${name}</\${Heading}>
Expand Down
Loading

1 comment on commit e7c3adb

@vercel
Copy link

@vercel vercel bot commented on e7c3adb Nov 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.