@codepress/codepress-engine 0.9.4 → 0.10.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.
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ /**
3
+ * Vite plugin for CodePress visual editing
4
+ *
5
+ * Enables the browser extension's preview/HMR pipeline for Vite+React projects.
6
+ * In dev mode, this plugin:
7
+ * 1. Injects bundler detection (window.__CP_BUNDLER__ = 'vite')
8
+ * 2. Registers source module exports in a global map (window.__CP_VITE_MODULES__)
9
+ * 3. Provides a dev server endpoint for the extension to query module info
10
+ *
11
+ * The module registry uses getter-based live bindings so module exports
12
+ * are always up-to-date, even after HMR updates.
13
+ *
14
+ * @example
15
+ * // vite.config.ts
16
+ * import react from '@vitejs/plugin-react';
17
+ * import codepressVitePlugin from '@codepress/codepress-engine/vite-plugin';
18
+ *
19
+ * export default defineConfig({
20
+ * plugins: [
21
+ * react({ babel: { plugins: ['@codepress/codepress-engine'] } }),
22
+ * codepressVitePlugin(),
23
+ * ],
24
+ * });
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.parseExports = parseExports;
28
+ exports.buildRegistrationCode = buildRegistrationCode;
29
+ exports.default = codepressVitePlugin;
30
+ exports.CodePressVitePlugin = codepressVitePlugin;
31
+ /**
32
+ * Parse export declarations from source code.
33
+ * Returns the local binding name and exported name for each export.
34
+ *
35
+ * Handles:
36
+ * - export function Foo()
37
+ * - export async function Foo()
38
+ * - export class Foo
39
+ * - export const/let/var Foo
40
+ * - export default function Foo()
41
+ * - export default class Foo
42
+ * - export { Foo, Bar } (local scope only, not re-exports)
43
+ * - export { Foo as Bar }
44
+ * - export default Identifier (simple identifier, not a call or member expression)
45
+ *
46
+ * Does NOT handle:
47
+ * - Anonymous default exports (export default () => ...)
48
+ * - Wrapped default exports (export default memo(...), export default forwardRef(...))
49
+ * - Re-exports (export { Foo } from './other')
50
+ * - export * from './other'
51
+ * - Destructured exports (export const { a, b } = ...)
52
+ *
53
+ * Note: This plugin runs with enforce:'post', so comments are already stripped
54
+ * by Babel before our transform sees the code. String false positives are
55
+ * theoretically possible but extremely rare in practice.
56
+ */
57
+ function parseExports(code) {
58
+ const seen = new Set();
59
+ const positioned = [];
60
+ function add(pos, localName, exportedName) {
61
+ if (seen.has(exportedName))
62
+ return;
63
+ seen.add(exportedName);
64
+ positioned.push({ pos, localName, exportedName });
65
+ }
66
+ // export default function Foo() / export default async function Foo()
67
+ // export default class Foo
68
+ for (const m of code.matchAll(/export\s+default\s+(?:async\s+)?(?:function|class)\s+(\w+)/g)) {
69
+ add(m.index, m[1], "default");
70
+ }
71
+ // export default Identifier (not a function/class declaration, not a call or member access)
72
+ // Matches: export default Hero; export default Component;
73
+ // Skips: export default memo(...); export default React.memo(...); export default 42;
74
+ // The \b after \w* prevents backtracking from matching a partial identifier.
75
+ for (const m of code.matchAll(/export\s+default\s+(?!function\b|class\b|async\b|new\b)([a-zA-Z_$]\w*)\b(?!\s*[.(])/g)) {
76
+ add(m.index, m[1], "default");
77
+ }
78
+ // export function Foo() (excludes "export default function")
79
+ for (const m of code.matchAll(/export\s+(?!default\b)(?:async\s+)?function\s+(\w+)/g)) {
80
+ add(m.index, m[1], m[1]);
81
+ }
82
+ // export class Foo (excludes "export default class")
83
+ for (const m of code.matchAll(/export\s+(?!default\b)class\s+(\w+)/g)) {
84
+ add(m.index, m[1], m[1]);
85
+ }
86
+ // export const/let/var Foo
87
+ for (const m of code.matchAll(/export\s+(?:const|let|var)\s+(\w+)/g)) {
88
+ add(m.index, m[1], m[1]);
89
+ }
90
+ // export { Foo, Bar } — but NOT export { Foo } from './other'
91
+ for (const m of code.matchAll(/export\s*\{([^}]+)\}(?!\s*from)/g)) {
92
+ const inner = m[1];
93
+ for (const binding of inner.split(",")) {
94
+ const trimmed = binding.trim();
95
+ // export { local as exported }
96
+ const asMatch = trimmed.match(/^(\w+)\s+as\s+(\w+)$/);
97
+ if (asMatch) {
98
+ add(m.index, asMatch[1], asMatch[2]);
99
+ }
100
+ else if (/^\w+$/.test(trimmed)) {
101
+ add(m.index, trimmed, trimmed);
102
+ }
103
+ }
104
+ }
105
+ // Sort by source position for predictable output
106
+ positioned.sort((a, b) => a.pos - b.pos);
107
+ return positioned.map(({ localName, exportedName }) => ({
108
+ localName,
109
+ exportedName,
110
+ }));
111
+ }
112
+ /**
113
+ * Generate a registration IIFE that captures module exports in
114
+ * window.__CP_VITE_MODULES__ using getter-based live bindings.
115
+ *
116
+ * The getters reference local variable bindings from the module scope,
117
+ * which means they always return the current value (important for HMR).
118
+ */
119
+ function buildRegistrationCode(relativePath, bindings) {
120
+ if (bindings.length === 0)
121
+ return "";
122
+ const getters = bindings
123
+ .map((e) => ` get ${JSON.stringify(e.exportedName)}() { return ${e.localName}; }`)
124
+ .join(",\n");
125
+ return [
126
+ "",
127
+ ";(function() {",
128
+ ' if (typeof window === "undefined" || !window.__CP_VITE_MODULES__) return;',
129
+ ` window.__CP_VITE_MODULES__[${JSON.stringify(relativePath)}] = {`,
130
+ getters,
131
+ " };",
132
+ "})();",
133
+ ].join("\n");
134
+ }
135
+ /**
136
+ * Create the CodePress Vite plugin.
137
+ *
138
+ * This plugin runs with `enforce: 'post'` so it executes after
139
+ * @vitejs/plugin-react (which runs the Babel instrumentation plugin
140
+ * and adds React Fast Refresh). By the time our transform runs,
141
+ * codepress-data-fp attributes are already injected and the export
142
+ * structure is finalized.
143
+ */
144
+ function codepressVitePlugin() {
145
+ let projectRoot = "";
146
+ const moduleExports = new Map();
147
+ return {
148
+ name: "codepress",
149
+ enforce: "post",
150
+ configResolved(config) {
151
+ projectRoot = config.root;
152
+ },
153
+ transformIndexHtml() {
154
+ return [
155
+ {
156
+ tag: "script",
157
+ children: [
158
+ 'window.__CP_BUNDLER__ = "vite";',
159
+ "window.__CP_VITE_MODULES__ = {};",
160
+ ].join("\n"),
161
+ injectTo: "head-prepend",
162
+ },
163
+ ];
164
+ },
165
+ transform(code, id, options) {
166
+ // Skip SSR builds
167
+ if (options === null || options === void 0 ? void 0 : options.ssr)
168
+ return;
169
+ // Skip dependencies (use path separator to avoid false positives on
170
+ // filenames that happen to contain "node_modules" as a substring)
171
+ if (id.includes("/node_modules/"))
172
+ return;
173
+ // Strip Vite query strings (e.g., ?t=123456 for HMR cache busting)
174
+ // Must happen before extension check since ?t= is appended to the filename
175
+ const cleanId = id.split("?")[0];
176
+ // Only process JS/TS source files
177
+ if (!/\.(tsx?|jsx?|mjs)$/.test(cleanId))
178
+ return;
179
+ // Compute path relative to project root
180
+ const relativePath = cleanId.startsWith(projectRoot + "/")
181
+ ? cleanId.slice(projectRoot.length + 1)
182
+ : cleanId;
183
+ const bindings = parseExports(code);
184
+ const previousBindings = moduleExports.get(relativePath);
185
+ moduleExports.set(relativePath, bindings);
186
+ // If file lost all exports during HMR, clean up stale registry entry
187
+ if (bindings.length === 0) {
188
+ if (previousBindings && previousBindings.length > 0) {
189
+ const cleanup = `\n;(function(){if(typeof window!=="undefined"&&window.__CP_VITE_MODULES__)delete window.__CP_VITE_MODULES__[${JSON.stringify(relativePath)}];})();`;
190
+ return { code: code + cleanup, map: null };
191
+ }
192
+ return;
193
+ }
194
+ const registration = buildRegistrationCode(relativePath, bindings);
195
+ return {
196
+ code: code + registration,
197
+ map: null,
198
+ };
199
+ },
200
+ configureServer(server) {
201
+ server.middlewares.use("/__codepress/modules", (_req, res) => {
202
+ const map = {};
203
+ for (const [filePath, bindings] of moduleExports) {
204
+ map[filePath] = {
205
+ path: filePath,
206
+ exports: bindings.map((e) => e.exportedName),
207
+ };
208
+ }
209
+ res.setHeader("Content-Type", "application/json");
210
+ res.end(JSON.stringify(map));
211
+ });
212
+ },
213
+ };
214
+ }
215
+ module.exports = codepressVitePlugin;
216
+ module.exports.codepressVitePlugin = codepressVitePlugin;
217
+ module.exports.CodePressVitePlugin = codepressVitePlugin;
218
+ module.exports.default = codepressVitePlugin;
219
+ module.exports.parseExports = parseExports;
220
+ module.exports.buildRegistrationCode = buildRegistrationCode;
221
+ //# sourceMappingURL=vite-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,8 @@ export default class CodePressWebpackPlugin {
38
38
  */
39
39
  readonly name = "CodePressWebpackPlugin";
40
40
  private readonly options;
41
+ /** Cached monorepo root (found once per build via pnpm-workspace.yaml) */
42
+ private monorepoRoot;
41
43
  /**
42
44
  * @param options - Plugin options for conditional execution
43
45
  */
@@ -104,7 +106,35 @@ export default class CodePressWebpackPlugin {
104
106
  * "/project/node_modules/lucide-react/dist/esm/icons/ChevronDown.js" -> "lucide-react"
105
107
  * "/project/node_modules/@radix-ui/react-dialog/dist/index.js" -> "@radix-ui/react-dialog"
106
108
  */
109
+ /**
110
+ * Parse the last node_modules/ segment from a resource path.
111
+ * Returns the package name and the remainder after the package name,
112
+ * or null if the path doesn't contain node_modules.
113
+ *
114
+ * Uses the LAST node_modules/ segment so pnpm paths like
115
+ * "node_modules/.pnpm/pkg@ver/node_modules/pkg/..." resolve correctly.
116
+ */
117
+ private parseNodeModulesPath;
107
118
  private extractNpmPackageName;
119
+ /**
120
+ * Extract an npm-like package name from a module's incoming import
121
+ * specifiers using webpack's module graph (deterministic — uses the
122
+ * exact string the developer wrote in the import statement).
123
+ *
124
+ * Returns the root package name (e.g., "@scope/pkg" from "@scope/pkg/sub").
125
+ */
126
+ private extractPackageNameFromImports;
127
+ /**
128
+ * Extract the full npm import specifier(s) from a module's incoming
129
+ * connections. Unlike extractPackageNameFromImports which returns just
130
+ * the root package name, this returns the complete specifiers (e.g.,
131
+ * "@scope/pkg/sub/path") so they can be used as MODULE_MAP keys.
132
+ *
133
+ * This is critical for workspace packages where the file system path
134
+ * (e.g., "apps/extension/lib/chat/models.ts") doesn't contain the
135
+ * npm package name, making path-based matching impossible at runtime.
136
+ */
137
+ private extractFullImportSpecifiers;
108
138
  /**
109
139
  * Derive an export name from a file path.
110
140
  * Used when we don't have explicit export mappings.
@@ -114,6 +144,23 @@ export default class CodePressWebpackPlugin {
114
144
  * "/project/node_modules/lucide-react/dist/esm/icons/chevron-down.js" -> "ChevronDown"
115
145
  */
116
146
  private deriveExportNameFromPath;
147
+ /**
148
+ * Find the monorepo root by walking up from the webpack context.
149
+ * Checks for common workspace markers: pnpm-workspace.yaml, lerna.json,
150
+ * turbo.json, nx.json, or a package.json with a "workspaces" field.
151
+ * Cached for the lifetime of the plugin.
152
+ * Falls back to the context itself if no workspace root is found.
153
+ */
154
+ private findMonorepoRoot;
155
+ /**
156
+ * Make a resource path relative to the monorepo root.
157
+ *
158
+ * @example
159
+ * // monorepo root = /vercel/path0 (has pnpm-workspace.yaml)
160
+ * makeRelativeToMonorepoRoot("/vercel/path0/apps/extension/dist/lib/chat.js", "/vercel/path0/apps/web")
161
+ * // => "apps/extension/dist/lib/chat.js"
162
+ */
163
+ private makeRelativeToMonorepoRoot;
117
164
  /**
118
165
  * Normalize a module path to a human-readable format
119
166
  *