updated stuff
This commit is contained in:
@@ -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
|
||||
@@ -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)');
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user