fortiplugin-bundle-adapter 0.0.3 → 0.0.4

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/README.md +166 -20
  2. package/package.json +19 -4
package/README.md CHANGED
@@ -19,9 +19,9 @@ Your plugin entry ends up like this (conceptually):
19
19
 
20
20
  ```ts
21
21
  export default function factory(deps) {
22
- // deps.imports["react"], deps.imports["@host/ui"], ...
23
- // plugin module code (rewritten)
24
- return DefaultExport;
22
+ // deps.imports["react"], deps.imports["@host/ui"], ...
23
+ // plugin module code (rewritten)
24
+ return DefaultExport;
25
25
  }
26
26
  ```
27
27
 
@@ -128,6 +128,152 @@ and the plugin still gets the “default” value.
128
128
 
129
129
  ---
130
130
 
131
+ ## Runtime helpers (createFactory + resolver)
132
+
133
+ Calling `factory({ imports })` directly works, but FortiPlugin hosts usually want a reusable runtime utility that:
134
+
135
+ * imports the plugin entry by URL/path
136
+ * resolves the correct export (`default` or named)
137
+ * detects whether the export is a **prep factory** or a **component**
138
+ * injects host dependencies (`react`, `react/jsx-runtime`, optional `@host/*`, optional `@inertiajs/*`)
139
+ * merges props correctly (**host props win**)
140
+
141
+ This package provides two runtime helpers for that.
142
+
143
+ ### `createFactory(file, env, opts?, hostProps?)`
144
+
145
+ `createFactory()` dynamically imports the `file` and returns a callable renderer:
146
+
147
+ * `render(props)` → returns a React element
148
+ * `props` passed to `render()` are **defaults**
149
+ * `hostProps` are **priority** (override collisions)
150
+
151
+ ```ts
152
+ import React from "react";
153
+ import * as JsxRuntime from "react/jsx-runtime";
154
+
155
+ import { createFactory } from "fortiplugin-bundle-adapter/runtime/create-factory";
156
+
157
+ const render = await createFactory(
158
+ "/build/plugins/foo.entry.mjs",
159
+ {
160
+ react: React,
161
+ jsxRuntime: JsxRuntime,
162
+
163
+ // optional: already-available host modules
164
+ imports: {
165
+ // "@host/ui": HostUI,
166
+ // "@inertiajs/core": InertiaCore,
167
+ },
168
+
169
+ // optional: dev-mode host bundles by CORS-safe URL
170
+ hostUrls: {
171
+ // "@host/ui": "https://host.example.com/forti/dev/exports/ui.mjs",
172
+ },
173
+ },
174
+ {
175
+ exportName: "default",
176
+ mode: "auto",
177
+ },
178
+ {
179
+ accountId: 123, // PRIORITY host props
180
+ }
181
+ );
182
+
183
+ // render() props are defaults; host props override collisions
184
+ const element = render({ title: "Dashboard" });
185
+ ```
186
+
187
+ ### `createPluginResolver({ env, options, hostProps })`
188
+
189
+ In real hosts, you don’t want to pass `env` everywhere. Instead, create a resolver once and reuse it.
190
+
191
+ A resolver bundles your defaults (React/JSX runtime, import map, dev URLs, base host props) and exposes:
192
+
193
+ * `resolver.resolve(file, overrides?)` → returns a prepared renderer (same shape as `createFactory`)
194
+ * `resolver.with(overrides)` → creates a new resolver layered on top (great for “with inertia”, “with ui”, etc.)
195
+ * `resolver.Embed` → a React component bound to the resolver (you only pass `file` + props)
196
+
197
+ ```ts
198
+ import React from "react";
199
+ import * as JsxRuntime from "react/jsx-runtime";
200
+
201
+ import { createPluginResolver } from "fortiplugin-bundle-adapter/runtime/create-resolver";
202
+
203
+ export const resolvePlugin = createPluginResolver({
204
+ env: {
205
+ react: React,
206
+ jsxRuntime: JsxRuntime,
207
+ },
208
+
209
+ // base PRIORITY props applied to every plugin render
210
+ hostProps: {
211
+ // accountId, permissions, pluginMeta, etc.
212
+ },
213
+ });
214
+ ```
215
+
216
+ #### Add optional deps once (Inertia / Host UI)
217
+
218
+ ```ts
219
+ import * as InertiaCore from "@inertiajs/core";
220
+ import * as HostUI from "./host-ui";
221
+
222
+ export const resolvePluginWithInertia = resolvePlugin.with({
223
+ env: {
224
+ imports: {
225
+ "@inertiajs/core": InertiaCore,
226
+ },
227
+ },
228
+ });
229
+
230
+ export const resolvePluginWithUI = resolvePluginWithInertia.with({
231
+ env: {
232
+ imports: {
233
+ "@host/ui": HostUI,
234
+ },
235
+ },
236
+ });
237
+ ```
238
+
239
+ #### Dev-mode: load host bundles by URL
240
+
241
+ ```ts
242
+ export const resolvePluginDev = resolvePlugin.with({
243
+ env: {
244
+ hostUrls: {
245
+ "@host/ui": "https://host.example.com/forti/dev/exports/ui.mjs",
246
+ },
247
+ },
248
+ });
249
+ ```
250
+
251
+ #### Use the resolver everywhere
252
+
253
+ ```ts
254
+ const render = await resolvePluginWithUI.resolve("/build/plugins/foo.entry.mjs", {
255
+ hostProps: { accountId: 123 }, // per-call PRIORITY props
256
+ });
257
+
258
+ // defaults + host override
259
+ const element = render({ title: "Dashboard" });
260
+ ```
261
+
262
+ #### Use the bound React component
263
+
264
+ ```tsx
265
+ const Embed = resolvePluginWithUI.Embed;
266
+
267
+ <Embed
268
+ file="/build/plugins/foo.entry.mjs"
269
+ props={{ title: "Dashboard" }}
270
+ hostProps={{ accountId: 123 }}
271
+ fallback={<div>Loading…</div>}
272
+ />;
273
+ ```
274
+
275
+ ---
276
+
131
277
  ## Configuration
132
278
 
133
279
  ### `injectedIds?: string[]`
@@ -327,24 +473,24 @@ const outFile = resolve(process.cwd(), process.argv[3] ?? "tests/fixture-output.
327
473
  const input = readFileSync(inputFile, "utf-8");
328
474
 
329
475
  const result = transformSync(input, {
330
- filename: inputFile,
331
- sourceType: "module",
332
- plugins: [
333
- [
334
- fortiPrepTransform,
335
- {
336
- injectedIds: ["react", "react/jsx-runtime"],
337
- injectedPrefixes: ["@inertiajs/", "@host/"],
338
- runtimeKey: "imports",
339
- depsParam: "deps",
340
- },
476
+ filename: inputFile,
477
+ sourceType: "module",
478
+ plugins: [
479
+ [
480
+ fortiPrepTransform,
481
+ {
482
+ injectedIds: ["react", "react/jsx-runtime"],
483
+ injectedPrefixes: ["@inertiajs/", "@host/"],
484
+ runtimeKey: "imports",
485
+ depsParam: "deps",
486
+ },
487
+ ],
341
488
  ],
342
- ],
343
- generatorOpts: {
344
- compact: false,
345
- comments: true,
346
- retainLines: false,
347
- },
489
+ generatorOpts: {
490
+ compact: false,
491
+ comments: true,
492
+ retainLines: false,
493
+ },
348
494
  });
349
495
 
350
496
  if (!result?.code) throw new Error("No output produced");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fortiplugin-bundle-adapter",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "files": [
6
6
  "dist"
@@ -22,22 +22,37 @@
22
22
  "typecheck": "tsc -p tsconfig.json",
23
23
  "test:transform": "node tests/run-transform.mjs",
24
24
  "prepublishOnly": "npm run build",
25
- "postPublish": "npm version patch && git push origin --follow-tags"
25
+ "postpublish": "npm version patch && git push origin --follow-tags"
26
26
  },
27
27
  "dependencies": {
28
28
  "@babel/core": "^7.28.5",
29
29
  "@babel/types": "^7.28.5"
30
30
  },
31
31
  "peerDependencies": {
32
- "vite": "^6.4.1"
32
+ "react": "^18.0.0 || ^19.0.0",
33
+ "rollup": "^4.0.0",
34
+ "vite": "^5.0.0 || ^6.0.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "vite": {
38
+ "optional": true
39
+ },
40
+ "rollup": {
41
+ "optional": true
42
+ },
43
+ "react": {
44
+ "optional": true
45
+ }
33
46
  },
34
47
  "devDependencies": {
35
48
  "@types/babel__core": "^7.20.5",
36
49
  "@types/node": "^25.0.3",
50
+ "@types/react": "^19.2.7",
37
51
  "react": "^19.2.3",
38
52
  "rollup": "^4.55.1",
39
53
  "tsup": "^8.5.1",
40
- "typescript": "^5.9.3"
54
+ "typescript": "^5.9.3",
55
+ "vite": "^6"
41
56
  },
42
57
  "repository": {
43
58
  "type": "git",