@tuqulore-inc/eleventy-plugin-preact-island 0.0.1 → 4.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) tuqulore inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,45 +1,86 @@
1
1
  # @tuqulore-inc/eleventy-plugin-preact-island
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ Eleventy plugin for Preact partial hydration with is-land.
4
4
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ This plugin automatically injects the necessary scripts for Preact hydration into all HTML pages.
6
6
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
7
+ ## Installation
8
8
 
9
- ## Purpose
9
+ ```bash
10
+ npm install -D @11ty/eleventy @tuqulore-inc/eleventy-plugin-preact-island
11
+ ```
10
12
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@tuqulore-inc/eleventy-plugin-preact-island`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
13
+ ## Usage
15
14
 
16
- ## What is OIDC Trusted Publishing?
15
+ ```javascript
16
+ // eleventy.config.mjs
17
+ import preactIsland from "@tuqulore-inc/eleventy-plugin-preact-island";
17
18
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
+ export default function (eleventyConfig) {
20
+ eleventyConfig.addPlugin(preactIsland);
21
+ }
22
+ ```
19
23
 
20
- ## Setup Instructions
24
+ ## What it does
21
25
 
22
- To properly configure OIDC trusted publishing for this package:
26
+ 1. **Copies `is-land.js`** from `@11ty/is-land` to the output directory
27
+ 2. **Injects scripts** before `</head>`:
28
+ - Import map for is-land and Preact (from esm.sh CDN)
29
+ - Development-only WebSocket hook for rehydration after hot reload
30
+ - is-land setup script with `Island.addInitType("preact", mount)`
23
31
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
32
+ ## Options
28
33
 
29
- ## DO NOT USE THIS PACKAGE
34
+ ### `preactVersion`
30
35
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
+ - Type: `string`
37
+ - Default: `""` (latest)
36
38
 
37
- ## More Information
39
+ Specify the Preact version to use from esm.sh CDN.
38
40
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
41
+ ```javascript
42
+ eleventyConfig.addPlugin(preactIsland, {
43
+ preactVersion: "10.26.4",
44
+ });
45
+ ```
42
46
 
43
- ---
47
+ ## Partial Hydration
44
48
 
45
- **Maintained for OIDC setup purposes only**
49
+ To hydrate a component, use the `<is-land>` web component with `type="preact"`.
50
+
51
+ ```mdx
52
+ import Component from "./component.hydrate.jsx";
53
+
54
+ <is-land
55
+ land-on:visible
56
+ type="preact"
57
+ import="./component.hydrate.js"
58
+ props='{ "someProp": "value" }'
59
+
60
+ >
61
+
62
+ <Component someProp="value" />
63
+ </is-land>
64
+ ```
65
+
66
+ - The inner `<Component />` is rendered at build time (SSR)
67
+ - The component is hydrated in the browser when the `<is-land>` becomes visible
68
+
69
+ > **Note:** The `on:*` attribute prefix is changed to `land-on:*` to avoid conflicts with JSX syntax.
70
+
71
+ ### `<is-land>` Attributes
72
+
73
+ | Attribute | Description |
74
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------------- |
75
+ | `type` | Hydration runtime. Set to `"preact"`. |
76
+ | `import` | Path to the hydration script (e.g., `./component.hydrate.js`). |
77
+ | `props` | JSON-stringified props to pass to the component. |
78
+ | `land-on:*` | [Initialization conditions](https://github.com/11ty/is-land?tab=readme-ov-file#usage) (e.g., `land-on:visible`, `land-on:idle`). |
79
+
80
+ ## Requirements
81
+
82
+ - Eleventy 3.0 or higher
83
+
84
+ ## License
85
+
86
+ MIT
package/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { UserConfig } from "@11ty/eleventy";
2
+
3
+ export interface PluginOptions {
4
+ /**
5
+ * Preact version for esm.sh CDN (e.g., "10.26.4").
6
+ * If not specified, the latest version will be used.
7
+ */
8
+ preactVersion?: string;
9
+ }
10
+
11
+ /**
12
+ * Eleventy plugin for Preact partial hydration with is-land
13
+ */
14
+ declare function preactIslandPlugin(
15
+ eleventyConfig: UserConfig,
16
+ pluginOptions?: PluginOptions,
17
+ ): void;
18
+
19
+ export default preactIslandPlugin;
package/index.mjs ADDED
@@ -0,0 +1,126 @@
1
+ import url from "node:url";
2
+
3
+ /**
4
+ * Eleventy plugin for Preact partial hydration with is-land
5
+ * @param {import("@11ty/eleventy").UserConfig} eleventyConfig
6
+ * @param {Object} pluginOptions
7
+ * @param {string} [pluginOptions.preactVersion] - Preact version for esm.sh CDN
8
+ */
9
+ export default function (eleventyConfig, pluginOptions = {}) {
10
+ try {
11
+ eleventyConfig.versionCheck(">=3.0");
12
+ } catch (e) {
13
+ console.log(`[eleventy-plugin-preact-island] WARN: ${e.message}`);
14
+ }
15
+
16
+ const { preactVersion = "" } = pluginOptions;
17
+
18
+ // Copy is-land.js to output directory
19
+ eleventyConfig.addPassthroughCopy({
20
+ [url.fileURLToPath(import.meta.resolve("@11ty/is-land/is-land.js"))]: "/",
21
+ });
22
+ const preactSuffix = preactVersion ? `@${preactVersion}` : "";
23
+
24
+ const generateImportMap = () => `<script type="importmap">
25
+ {
26
+ "imports": {
27
+ "is-land": "/is-land.js",
28
+ "preact": "https://esm.sh/preact${preactSuffix}",
29
+ "preact/hooks": "https://esm.sh/preact${preactSuffix}/hooks?external=preact",
30
+ "preact/jsx-runtime": "https://esm.sh/preact${preactSuffix}/jsx-runtime?external=preact"
31
+ }
32
+ }
33
+ </script>`;
34
+
35
+ const generateDevScript = () =>
36
+ process.env.NODE_ENV === "development"
37
+ ? `<script>
38
+ (function() {
39
+ const OriginalWebSocket = window.WebSocket;
40
+ window.WebSocket = function(url, protocols) {
41
+ const ws = new OriginalWebSocket(url, protocols);
42
+ ws.addEventListener("message", (event) => {
43
+ try {
44
+ const data = JSON.parse(event.data);
45
+ if (data.type === "eleventy.reload") {
46
+ requestAnimationFrame(() => {
47
+ requestAnimationFrame(() => {
48
+ window.__eleventyRehydrate?.();
49
+ });
50
+ });
51
+ }
52
+ } catch (e) {
53
+ console.error("Failed to parse WebSocket message:", e);
54
+ }
55
+ });
56
+ return ws;
57
+ };
58
+ window.WebSocket.prototype = OriginalWebSocket.prototype;
59
+ window.WebSocket.CONNECTING = OriginalWebSocket.CONNECTING;
60
+ window.WebSocket.OPEN = OriginalWebSocket.OPEN;
61
+ window.WebSocket.CLOSING = OriginalWebSocket.CLOSING;
62
+ window.WebSocket.CLOSED = OriginalWebSocket.CLOSED;
63
+ })();
64
+ </script>`
65
+ : "";
66
+
67
+ const generateIslandSetup = () => {
68
+ const rehydrateScript =
69
+ process.env.NODE_ENV === "development"
70
+ ? `
71
+ window.__eleventyRehydrate = () => {
72
+ document.querySelectorAll('is-land[type="preact"]').forEach(el => {
73
+ if (el.hasAttribute("import")) {
74
+ mount(el);
75
+ }
76
+ });
77
+ };`
78
+ : "";
79
+
80
+ return `<script type="module">
81
+ import { Island } from "is-land";
82
+ import { h, hydrate } from "preact";
83
+ Island.attributePrefix = "land-on:";
84
+
85
+ const mount = async (target) => {
86
+ try {
87
+ const component = await import(target.getAttribute("import"));
88
+ const propsAttr = target.getAttribute("props");
89
+ const props = propsAttr ? JSON.parse(propsAttr) : {};
90
+ hydrate(h(component.default, props), target);
91
+ } catch (e) {
92
+ console.error("Failed to mount component:", e);
93
+ }
94
+ };
95
+
96
+ Island.addInitType("preact", mount);
97
+ ${rehydrateScript}
98
+ </script>`;
99
+ };
100
+
101
+ eleventyConfig.addTransform(
102
+ "preact-island-inject",
103
+ function (content, outputPath) {
104
+ if (!outputPath?.endsWith?.(".html")) {
105
+ return content;
106
+ }
107
+
108
+ if (!content.includes("</head>")) {
109
+ console.warn(
110
+ `[eleventy-plugin-preact-island] WARN: No </head> tag found in ${outputPath}. Scripts not injected.`,
111
+ );
112
+ return content;
113
+ }
114
+
115
+ const scripts = [
116
+ generateImportMap(),
117
+ generateDevScript(),
118
+ generateIslandSetup(),
119
+ ]
120
+ .filter(Boolean)
121
+ .join("\n");
122
+
123
+ return content.replace("</head>", `${scripts}\n</head>`);
124
+ },
125
+ );
126
+ }
package/package.json CHANGED
@@ -1,10 +1,48 @@
1
1
  {
2
2
  "name": "@tuqulore-inc/eleventy-plugin-preact-island",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @tuqulore-inc/eleventy-plugin-preact-island",
3
+ "version": "4.0.0",
4
+ "description": "Eleventy plugin for Preact partial hydration with is-land",
5
5
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
10
- }
6
+ "eleventy",
7
+ "eleventy-plugin",
8
+ "preact",
9
+ "is-land",
10
+ "partial-hydration"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/tuqulore/website-boilerplate.git",
15
+ "directory": "packages/eleventy-plugin-preact-island"
16
+ },
17
+ "license": "MIT",
18
+ "author": "tuqulore inc.",
19
+ "type": "module",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./index.d.ts",
23
+ "default": "./index.mjs"
24
+ }
25
+ },
26
+ "main": "./index.mjs",
27
+ "files": [
28
+ "README.md",
29
+ "index.d.ts",
30
+ "index.mjs"
31
+ ],
32
+ "dependencies": {
33
+ "@11ty/is-land": "^5.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "eslint": "^9.39.2",
37
+ "@tuqulore-inc/eslint-config": "4.0.0"
38
+ },
39
+ "peerDependencies": {
40
+ "@11ty/eleventy": ">=3.0.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=20"
44
+ },
45
+ "scripts": {
46
+ "lint": "eslint --fix ."
47
+ }
48
+ }