updated stuff

This commit is contained in:
SinachPat
2026-05-13 15:12:51 +01:00
parent 8356e6278c
commit 7bf43fc481
19 changed files with 1632 additions and 308 deletions
+52
View File
@@ -0,0 +1,52 @@
# @originmain/next
Next.js plugin for [Originmain](https://originmain.com) — automatically injects the Originmain live SDK before React loads, enabling the canvas to inspect your component tree in real time.
## Installation
```bash
npm install @originmain/next
# or
pnpm add @originmain/next
```
## Usage
Wrap your Next.js config with `withOriginmain`:
```ts
// next.config.ts
import { withOriginmain } from '@originmain/next';
const nextConfig = {
reactStrictMode: true,
};
export default withOriginmain(nextConfig);
```
Or in CommonJS format:
```js
// next.config.js
const { withOriginmain } = require('@originmain/next');
module.exports = withOriginmain({
reactStrictMode: true,
});
```
## What it does
1. Prepends `import '@originmain/live'` to **every page entry point** at build time via webpack entry modification.
2. The live SDK installs `__REACT_DEVTOOLS_GLOBAL_HOOK__` before React's module body evaluates — this is required for React to capture component commits.
3. The hook activates **only** when the page runs inside an Originmain artboard iframe. In all other contexts it is a complete no-op with zero runtime cost.
## Requirements
- Next.js `>=14.0.0`
- Node.js `>=18`
## License
MIT
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env node
// ── @originmain/next build script ────────────────────────────────────────────
// Produces ESM + CJS bundles and TypeScript declarations under dist/:
//
// dist/index.js — ESM bundle for next.config.mjs / next.config.ts
// dist/index.cjs — CJS bundle for next.config.js (legacy require())
// dist/index.d.ts — TypeScript declarations for withOriginmain()
//
// Run: node build.mjs (or via "pnpm build")
import { build } from 'esbuild';
import { execFileSync } from 'node:child_process';
import { createRequire } from 'node:module';
const req = createRequire(import.meta.url);
// Resolve the tsc binary explicitly — avoids relying on PATH.
const tscBin = req.resolve('typescript/bin/tsc');
const SHARED = {
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
target: ['node18'],
// 'next' is a peer dep — leave it external so users' own copy is used.
external: ['next'],
logLevel: 'info',
};
// ── ESM bundle ────────────────────────────────────────────────────────────────
await build({
...SHARED,
format: 'esm',
outfile: 'dist/index.js',
});
// ── CJS bundle ────────────────────────────────────────────────────────────────
// Required for projects where next.config.js uses module.exports = ...
await build({
...SHARED,
format: 'cjs',
outfile: 'dist/index.cjs',
});
// ── TypeScript declarations ───────────────────────────────────────────────────
// withOriginmain() is a typed export — consumers need the .d.ts so their IDE
// and type-checker know the function's signature.
execFileSync(process.execPath, [tscBin, '--project', 'tsconfig.build.json'], {
stdio: 'inherit',
});
console.log(' ✓ dist/index.js (ESM)');
console.log(' ✓ dist/index.cjs (CJS)');
console.log(' ✓ dist/index.d.ts (types)');
+34
View File
@@ -0,0 +1,34 @@
{
"name": "@originmain/next",
"version": "0.0.1",
"private": false,
"description": "Originmain Next.js plugin — wraps next.config.js to auto-inject the live SDK before React loads.",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"files": ["dist", "README.md"],
"scripts": {
"build": "node build.mjs",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@originmain/live": "workspace:*"
},
"peerDependencies": {
"next": ">=14.0.0"
},
"peerDependenciesMeta": {
"next": { "optional": false }
},
"devDependencies": {
"esbuild": "^0.25.0",
"typescript": "^5.5.0"
},
"keywords": ["originmain", "next", "nextjs", "react", "plugin"],
"license": "MIT"
}
+102
View File
@@ -0,0 +1,102 @@
// ── @originmain/next ──────────────────────────────────────────────────────────
// Next.js plugin that injects @originmain/live before React loads, enabling
// the Originmain canvas to inspect React component trees in real-time.
//
// Usage (next.config.ts or next.config.js):
//
// import { withOriginmain } from '@originmain/next';
//
// const nextConfig = { /* your config */ };
// export default withOriginmain(nextConfig);
//
// What it does:
// 1. Prepends `import '@originmain/live'` to every page entry point at build
// time via webpack entry modification.
// 2. The live SDK installs __REACT_DEVTOOLS_GLOBAL_HOOK__ before React's module
// body evaluates, so React captures component commits from the very first render.
// 3. The hook is a complete no-op when the app is NOT running inside an
// Originmain artboard iframe — zero runtime cost in production.
//
// Environment:
// The SDK activates ONLY when the page is rendered inside an Originmain iframe
// (detected via URL fragment: #__om_artboard=<id>). No opt-in flag or env var
// needed — it self-activates in the right context and stays dormant otherwise.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type NextConfig = Record<string, any>;
/** The entry point that installs the Originmain fiber hook before React. */
const LIVE_SDK_ENTRY = '@originmain/live';
/**
* Wraps a Next.js config to inject the Originmain live SDK into every page
* entry point. The SDK is a no-op outside Originmain artboard iframes.
*
* @example
* ```ts
* // next.config.ts
* import { withOriginmain } from '@originmain/next';
* export default withOriginmain({ reactStrictMode: true });
* ```
*/
export function withOriginmain(nextConfig: NextConfig = {}): NextConfig {
return {
...nextConfig,
webpack(
config: WebpackConfig,
context: { buildId: string; dev: boolean; isServer: boolean; nextRuntime?: string },
) {
// Only patch the client-side bundle. The fiber hook is browser-only.
// isServer covers both Node.js runtime and Edge runtime (nextRuntime).
if (!context.isServer) {
config.entry = prependLiveSdk(config.entry);
}
// Delegate to any existing webpack customisation in the user's config.
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, context);
}
return config;
},
};
}
// ── Entry prepend helper ──────────────────────────────────────────────────────
// Next.js entry can be a plain object, a function returning an object, or a
// function returning a Promise. We wrap all three shapes uniformly.
type EntryValue = string | string[] | EntryObject;
type EntryObject = Record<string, string | string[]>;
type EntryFn = () => EntryValue | Promise<EntryValue>;
type Entry = EntryValue | EntryFn;
function prependLiveSdk(entry: Entry): EntryFn {
return async () => {
const resolved = typeof entry === 'function' ? await entry() : entry;
return injectIntoEntries(resolved as EntryObject);
};
}
function injectIntoEntries(entries: EntryObject): EntryObject {
const out: EntryObject = {};
for (const [key, value] of Object.entries(entries)) {
out[key] = prependToChunk(value);
}
return out;
}
function prependToChunk(chunk: string | string[]): string[] {
const arr = Array.isArray(chunk) ? chunk : [chunk];
// Avoid duplicating if already present (e.g. running withOriginmain twice).
if (arr.includes(LIVE_SDK_ENTRY)) return arr;
return [LIVE_SDK_ENTRY, ...arr];
}
// ── Minimal webpack types (avoids adding webpack as a dev dep) ────────────────
interface WebpackConfig {
entry: Entry;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
}
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"noEmit": false,
"emitDeclarationOnly": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"moduleResolution": "NodeNext",
"module": "NodeNext"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"noEmit": true,
"jsx": "preserve"
},
"include": ["src"],
"exclude": ["node_modules"]
}