@hyperspan/plugin-preact 0.1.6 → 0.2.0
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.
- package/package.json +3 -3
- package/src/index.ts +44 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperspan/plugin-preact",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Hyperspan Plugin for Preact",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"typescript": "^5.0.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@hyperspan/html": "
|
|
32
|
-
"@hyperspan/framework": "
|
|
31
|
+
"@hyperspan/html": "0.1.7",
|
|
32
|
+
"@hyperspan/framework": "0.5.3"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"preact": "^10.26.5",
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { clientImportMap, assetHash, ISLAND_PUBLIC_PATH } from '@hyperspan/framework/assets';
|
|
2
|
+
import { IS_PROD } from '@hyperspan/framework/server';
|
|
2
3
|
import { resolve } from 'node:path';
|
|
3
4
|
|
|
4
5
|
// External ESM = https://esm.sh/preact@10.26.4/compat
|
|
5
|
-
const PREACT_PUBLIC_FILE_PATH = ISLAND_PUBLIC_PATH + '/preact-client.js';
|
|
6
6
|
const PREACT_ISLAND_CACHE = new Map<string, string>();
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -10,22 +10,26 @@ const PREACT_ISLAND_CACHE = new Map<string, string>();
|
|
|
10
10
|
*/
|
|
11
11
|
async function copyPreactToPublicFolder() {
|
|
12
12
|
const sourceFile = resolve(__dirname, './preact-client.ts');
|
|
13
|
-
await Bun.build({
|
|
13
|
+
const result = await Bun.build({
|
|
14
14
|
entrypoints: [sourceFile],
|
|
15
|
-
outdir:
|
|
15
|
+
outdir: `./public/${ISLAND_PUBLIC_PATH}`,
|
|
16
|
+
naming: IS_PROD ? '[dir]/[name]-[hash].[ext]' : undefined,
|
|
16
17
|
minify: true,
|
|
17
18
|
format: 'esm',
|
|
18
19
|
target: 'browser',
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
clientImportMap.set('preact
|
|
22
|
+
const builtFileName = String(result.outputs[0].path.split('/').reverse()[0]).replace('.js', '');
|
|
23
|
+
const builtFilePath = `${ISLAND_PUBLIC_PATH}/${builtFileName}.js`;
|
|
24
|
+
|
|
25
|
+
clientImportMap.set('preact', builtFilePath);
|
|
26
|
+
clientImportMap.set('preact/compat', builtFilePath);
|
|
27
|
+
clientImportMap.set('preact/hooks', builtFilePath);
|
|
28
|
+
clientImportMap.set('preact/jsx-runtime', builtFilePath);
|
|
25
29
|
|
|
26
30
|
if (!clientImportMap.has('react')) {
|
|
27
|
-
clientImportMap.set('react',
|
|
28
|
-
clientImportMap.set('react-dom',
|
|
31
|
+
clientImportMap.set('react', builtFilePath);
|
|
32
|
+
clientImportMap.set('react-dom', builtFilePath);
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
|
|
@@ -33,6 +37,11 @@ async function copyPreactToPublicFolder() {
|
|
|
33
37
|
* Hyperspan Preact Plugin
|
|
34
38
|
*/
|
|
35
39
|
export async function preactPlugin() {
|
|
40
|
+
// Ensure Preact can be loaded on the client
|
|
41
|
+
if (!clientImportMap.has('preact')) {
|
|
42
|
+
await copyPreactToPublicFolder();
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
// Define a Bun plugin to handle .tsx files
|
|
37
46
|
await Bun.plugin({
|
|
38
47
|
name: 'Hyperspan Preact Loader',
|
|
@@ -40,7 +49,6 @@ export async function preactPlugin() {
|
|
|
40
49
|
// when a .tsx file is imported...
|
|
41
50
|
build.onLoad({ filter: /\.tsx$/ }, async (args) => {
|
|
42
51
|
const jsId = assetHash(args.path);
|
|
43
|
-
let contents = await Bun.file(args.path).text();
|
|
44
52
|
|
|
45
53
|
// Cache: Avoid re-processing the same file
|
|
46
54
|
if (PREACT_ISLAND_CACHE.has(jsId)) {
|
|
@@ -50,33 +58,30 @@ export async function preactPlugin() {
|
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
contents = await result.outputs[0].text();
|
|
73
|
-
}
|
|
61
|
+
// We need to build the file to ensure we can ship it to the client with dependencies
|
|
62
|
+
// Ironic, right? Calling Bun.build() inside of a plugin that runs on Bun.build()?
|
|
63
|
+
const result = await Bun.build({
|
|
64
|
+
entrypoints: [args.path],
|
|
65
|
+
outdir: `./public/${ISLAND_PUBLIC_PATH}`,
|
|
66
|
+
naming: IS_PROD ? '[dir]/[name]-[hash].[ext]' : undefined,
|
|
67
|
+
external: Array.from(clientImportMap.keys()),
|
|
68
|
+
minify: true,
|
|
69
|
+
format: 'esm',
|
|
70
|
+
target: 'browser',
|
|
71
|
+
env: 'APP_PUBLIC_*',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Add output file to import map
|
|
75
|
+
const esmName = String(result.outputs[0].path.split('/').reverse()[0]).replace('.js', '');
|
|
76
|
+
clientImportMap.set(esmName, `${ISLAND_PUBLIC_PATH}/${esmName}.js`);
|
|
77
|
+
|
|
78
|
+
let contents = await result.outputs[0].text();
|
|
74
79
|
|
|
75
80
|
// Look for the default export
|
|
76
|
-
const RE_EXPORT_DEFAULT = /export\{(
|
|
77
|
-
const RE_EXPORT_DEFAULT_FN = /export default function\s+(
|
|
78
|
-
const RE_EXPORT_DEFAULT_CONST = /export default const\s+(
|
|
79
|
-
const RE_EXPORT_DEFAULT_ANY = /export default\s+(
|
|
81
|
+
const RE_EXPORT_DEFAULT = /export\{([^\s]+) as default\}/;
|
|
82
|
+
const RE_EXPORT_DEFAULT_FN = /export default function\s+([^\s]+)/;
|
|
83
|
+
const RE_EXPORT_DEFAULT_CONST = /export default const\s+([^\s]+)/;
|
|
84
|
+
const RE_EXPORT_DEFAULT_ANY = /export default\s+([^\s]+)/;
|
|
80
85
|
|
|
81
86
|
const exportedDefault = contents.match(RE_EXPORT_DEFAULT);
|
|
82
87
|
const exportedDefaultFn = contents.match(RE_EXPORT_DEFAULT_FN);
|
|
@@ -96,7 +101,7 @@ export async function preactPlugin() {
|
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
// Add to contents so this is in the client JS as well
|
|
99
|
-
contents = `import { h as __hs_h, render as __hs_render, hydrate as __hs_hydrate } from 'preact';${contents}`;
|
|
104
|
+
contents = `import { h, h as __hs_h, render as __hs_render, hydrate as __hs_hydrate } from 'preact';${contents}`;
|
|
100
105
|
|
|
101
106
|
// Some _interesting_ work at play here...
|
|
102
107
|
// We have to modify the original file contents to add an __HS_PLUGIN export that the renderIsland() function can use to render the component.
|
|
@@ -112,11 +117,12 @@ ${contents}
|
|
|
112
117
|
|
|
113
118
|
// hyperspan:preact-plugin
|
|
114
119
|
function __hs_renderIsland(jsContent = '', ssrContent = '', options = {}) {
|
|
120
|
+
const scriptTag = \`<script type="module" id="${jsId}_script" data-source-id="${jsId}">import ${componentName} from "${esmName}";\${jsContent}</script>\`;
|
|
115
121
|
if (options.loading === 'lazy') {
|
|
116
|
-
return \`<div id="${jsId}">\${ssrContent}</div><div data-loading="lazy" style="height:1px;width:1px;overflow:hidden;"><template
|
|
122
|
+
return \`<div id="${jsId}">\${ssrContent}</div><div data-loading="lazy" style="height:1px;width:1px;overflow:hidden;"><template>\n\${scriptTag}</template></div>\`;
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
return \`<div id="${jsId}">\${ssrContent}</div
|
|
125
|
+
return \`<div id="${jsId}">\${ssrContent}</div>\n\${scriptTag}\`;
|
|
120
126
|
}
|
|
121
127
|
${componentName}.__HS_ISLAND = {
|
|
122
128
|
id: "${jsId}",
|