Skip to content

Commit fb4bec9

Browse files
committed
add jsx runtime compilation
1 parent a674364 commit fb4bec9

3 files changed

Lines changed: 140 additions & 37 deletions

File tree

deploy-server.js

Lines changed: 120 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,32 @@ function removeSlashes(path) {
3636
return removeTrailingSlash(removeLeadingSlash(path));
3737
}
3838

39+
/**
40+
* A `maidenPathname` is a `URL.pathname` without any path prefix, relative or absolute.
41+
* e.g. 👇
42+
* ```
43+
* https://www.example.com/js/main.js -> js/main.js
44+
* /js/main.js -> js/main.js
45+
* ./js/main.js -> js/main.js
46+
* js/main.js -> js/main.js
47+
* ```
48+
* @param {string} maidenPathname
49+
* @returns {string}
50+
*/
3951
function getFileExtensionFromPath(maidenPathname) {
4052
const [fileExtension] = maidenPathname.split('.').reverse();
4153
return `.${fileExtension}`;
4254
}
4355

56+
/**
57+
* @param {Request} request
58+
* @returns {Promise<Response>}
59+
*/
4460
async function requestHandler(request) {
45-
const mode = request.headers.get('sec-fetch-mode');
46-
const dest = request.headers.get('sec-fetch-dest');
47-
const site = request.headers.get('sec-fetch-site');
48-
4961
const { pathname } = new URL(request.url);
50-
const assetPath = assetMap[pathname];
51-
const maidenPathname = removeSlashes(assetPath);
5262

5363
const { sessionStorage, localStorage } = globalThis;
54-
64+
5565
const storage = sessionStorage || localStorage;
5666

5767
if (storage) {
@@ -70,34 +80,117 @@ async function requestHandler(request) {
7080
}
7181
}
7282

73-
const textFileContent = await Deno.readTextFile(assetPath);
74-
// Absolute paths.
75-
// The content of the repository is available under at Deno.cwd().
76-
// const readmeAbsolute = await Deno.readFile(`${Deno.cwd()}/README.md`);
77-
// File URLs are also supported.
78-
// const readmeFileUrl = await Deno.readFile(
79-
// new URL(`file://${Deno.cwd()}/README.md`),
80-
// );
81-
82-
// Decode the Uint8Array as string.
83-
// const readme = new TextDecoder().decode(readmeRelative);
84-
// return new Response(textFileContent);
85-
return new Response(textFileContent, {
86-
headers: {
87-
"content-type": MEDIA_TYPES[getFileExtensionFromPath(maidenPathname)],
83+
const assetPath = assetMap[pathname];
84+
85+
if (assetPath) {
86+
try {
87+
88+
const mode = request.headers.get('sec-fetch-mode');
89+
const dest = request.headers.get('sec-fetch-dest');
90+
const contentTypeHTML = mode === 'navigate' || dest === 'document';
91+
92+
if (contentTypeHTML) {
93+
const content = await Deno.readTextFile(assetPath);
94+
const { main } = await import('./js/importmap-generator.js');
95+
const importMap = await main();
96+
97+
const [beforeImportmap, afterImportmap] = content.split("//__importmap");
98+
const html = `${beforeImportmap}${importMap}${afterImportmap}`;
99+
100+
return new Response(html, {
101+
headers: {
102+
"content-type": MEDIA_TYPES['.html'],
103+
}
104+
});
105+
}
106+
107+
const content = Deno.readFile(assetPath);
108+
const textDecodedContent = new TextDecoder().decode(fileContent);
109+
const maidenPathname = removeSlashes(assetPath);
110+
111+
return new Response(textDecodedContent, {
112+
headers: {
113+
"content-type": MEDIA_TYPES[getFileExtensionFromPath(maidenPathname)],
114+
}
115+
});
116+
} catch (error) {
117+
return new Response(error.message || error.toString(), { status: 500 });
118+
}
119+
}
120+
121+
122+
const site = request.headers.get('sec-fetch-site');
123+
const contentTypeCompiledJSX = dest === 'script' && mode === 'cors' && site === 'same-origin' && pathname.endsWith(".jsx.js");
124+
125+
if (contentTypeCompiledJSX) {
126+
try {
127+
const { files, diagnostics } = await Deno.emit(`.${pathname}`.slice(0, -3));
128+
129+
if (diagnostics.length) {
130+
// there is something that impacted the emit
131+
console.warn(Deno.formatDiagnostics(diagnostics));
132+
}
133+
// @ts-ignore
134+
const [, content] = Object.entries(files).find(([fileName]) => {
135+
const cwd = toFileUrl(Deno.cwd()).href;
136+
const commonPath = common([
137+
cwd,
138+
fileName,
139+
]);
140+
const shortFileName = fileName.replace(commonPath, `/`);
141+
142+
return shortFileName === pathname;
143+
});
144+
return new Response(content);
145+
} catch (error) {
146+
return new Response(error.message || error.toString(), { status: 500 })
147+
}
148+
}
149+
150+
if (pathname.endsWith(".jsx")) {
151+
try {
152+
if (storage) {
153+
const { files, diagnostics } = await Deno.emit(`.${pathname}`);
154+
155+
if (diagnostics.length) {
156+
// there is something that impacted the emit
157+
console.warn(Deno.formatDiagnostics(diagnostics));
158+
}
159+
160+
for (const [fileName, text] of Object.entries(files)) {
161+
162+
const cwd = toFileUrl(Deno.cwd()).href;
163+
const commonPath = common([
164+
cwd,
165+
fileName,
166+
]);
167+
const maidenPathname = fileName.replace(commonPath, ``);
168+
169+
storage.setItem(maidenPathname, text);
170+
}
171+
}
172+
173+
return new Response(pathname, {
174+
status: 303,
175+
headers: {
176+
"location": `${request.url}.js`,
177+
},
178+
});
179+
} catch (error) {
180+
return new Response(error.message || error.toString(), { status: 500 })
88181
}
182+
}
183+
184+
return new Response(null, {
185+
status: 404,
89186
});
90187
}
91188

92-
// console.log("Listening on http://localhost:8080");
93-
// await listenAndServe(":8080", handler);
94-
95189
if (import.meta.main) {
96190
const PORT = Deno.env.get("PORT") || 1729;
97191
const timestamp = Date.now();
98192
const humanReadableDateTime = new Date(timestamp).toLocaleString();
99193
console.log('Current Date: ', humanReadableDateTime)
100194
console.info(`Server Listening on http://localhost:${PORT}`);
101195
listenAndServe(`:${PORT}`, requestHandler);
102-
}
103-
196+
}

deploy.html

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
<!DOCTYPE html>
2-
<html>
2+
<html lang="en">
33
<head>
4-
<title>Excalidraw in browser</title>
54
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>@fusionstrings/river</title>
8+
<link rel="stylesheet" href="css/style.css" />
9+
<script
10+
async
11+
src="https://ga.jspm.io/npm:es-module-shims@1.3.0/dist/es-module-shims.js"
12+
></script>
13+
<script type="importmap" id="importmap">
14+
//__importmap
15+
</script>
616
</head>
7-
817
<body>
9-
<main>
10-
<h1>Excalidraw Importmap Example</h1>
11-
</main>
12-
<footer>
13-
Deployed on Deno Deploy
14-
</footer>
18+
<main id="root"><h1>@fusionstrings/river</h1></main>
19+
20+
<script type="module">
21+
import { main } from "./js/deploy.js";
22+
main({ mountElementID: "root" });
23+
</script>
1524
</body>
16-
</html>
25+
</html>

js/deploy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { main } from './dom.jsx';

0 commit comments

Comments
 (0)