@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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. 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.1.6",
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": "^0.1.7",
32
- "@hyperspan/framework": "^0.4.2"
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: './public/' + ISLAND_PUBLIC_PATH,
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
- clientImportMap.set('preact', PREACT_PUBLIC_FILE_PATH);
22
- clientImportMap.set('preact/compat', PREACT_PUBLIC_FILE_PATH);
23
- clientImportMap.set('preact/hooks', PREACT_PUBLIC_FILE_PATH);
24
- clientImportMap.set('preact/jsx-runtime', PREACT_PUBLIC_FILE_PATH);
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', PREACT_PUBLIC_FILE_PATH);
28
- clientImportMap.set('react-dom', PREACT_PUBLIC_FILE_PATH);
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
- // Ensure Preact can be loaded on the client
54
- if (!clientImportMap.has('preact')) {
55
- await copyPreactToPublicFolder();
56
- }
57
-
58
- if (contents) {
59
- // We need to build the file to ensure we can ship it to the client with dependencies
60
- // Ironic, right? Calling Bun.build() inside of a plugin that runs on Bun.build()?
61
- const result = await Bun.build({
62
- entrypoints: [args.path],
63
- //outdir: './public/' + ISLAND_PUBLIC_PATH,
64
- //naming: '[name]-[hash].js',
65
- external: Array.from(clientImportMap.keys()),
66
- minify: true,
67
- format: 'esm',
68
- target: 'browser',
69
- env: 'APP_PUBLIC_*',
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\{(\w+) as default\}/;
77
- const RE_EXPORT_DEFAULT_FN = /export default function\s+(\w+)/;
78
- const RE_EXPORT_DEFAULT_CONST = /export default const\s+(\w+)/;
79
- const RE_EXPORT_DEFAULT_ANY = /export default\s+(\w+)/;
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><script type="module" data-source-id="${jsId}">${contents}\${jsContent}</script></template></div>\`;
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><script type="module" data-source-id="${jsId}">${contents}\${jsContent}</script>\`;
125
+ return \`<div id="${jsId}">\${ssrContent}</div>\n\${scriptTag}\`;
120
126
  }
121
127
  ${componentName}.__HS_ISLAND = {
122
128
  id: "${jsId}",