@base44/vite-plugin 1.0.30 → 1.0.32

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 (34) hide show
  1. package/dist/capabilities/inline-edit/controller.d.ts.map +1 -1
  2. package/dist/capabilities/inline-edit/controller.js +2 -5
  3. package/dist/capabilities/inline-edit/controller.js.map +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +33 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/injections/unhandled-errors-handlers.d.ts +2 -1
  8. package/dist/injections/unhandled-errors-handlers.d.ts.map +1 -1
  9. package/dist/injections/unhandled-errors-handlers.js +34 -19
  10. package/dist/injections/unhandled-errors-handlers.js.map +1 -1
  11. package/dist/injections/utils.d.ts +6 -0
  12. package/dist/injections/utils.d.ts.map +1 -1
  13. package/dist/injections/utils.js +8 -0
  14. package/dist/injections/utils.js.map +1 -1
  15. package/dist/injections/visual-edit-agent.d.ts.map +1 -1
  16. package/dist/injections/visual-edit-agent.js +2 -5
  17. package/dist/injections/visual-edit-agent.js.map +1 -1
  18. package/dist/processors/collection-item-field-processor.d.ts.map +1 -1
  19. package/dist/processors/collection-item-field-processor.js +11 -0
  20. package/dist/processors/collection-item-field-processor.js.map +1 -1
  21. package/dist/statics/index.mjs +7 -7
  22. package/dist/statics/index.mjs.map +1 -1
  23. package/dist/visual-edit-plugin.d.ts.map +1 -1
  24. package/dist/visual-edit-plugin.js +7 -1
  25. package/dist/visual-edit-plugin.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/capabilities/inline-edit/controller.ts +2 -6
  28. package/src/index.ts +36 -2
  29. package/src/injections/unhandled-errors-handlers.ts +36 -19
  30. package/src/injections/utils.ts +9 -0
  31. package/src/injections/visual-edit-agent.ts +2 -6
  32. package/src/processors/collection-item-field-processor.ts +12 -0
  33. package/src/visual-edit-plugin.md +3 -2
  34. package/src/visual-edit-plugin.ts +9 -2
@@ -1 +1 @@
1
- {"version":3,"file":"visual-edit-plugin.d.ts","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,wBAAgB,gBAAgB,IAwEzB,MAAM,CACZ"}
1
+ {"version":3,"file":"visual-edit-plugin.d.ts","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,MAAM,EAAc,MAAM,MAAM,CAAC;AAI1D,wBAAgB,gBAAgB,IA+EzB,MAAM,CACZ"}
@@ -7,7 +7,13 @@ import { JSXUtils } from "./jsx-utils.js";
7
7
  export function visualEditPlugin() {
8
8
  return {
9
9
  name: "visual-edit-transform",
10
- apply: (config) => config.mode === "development",
10
+ // Dev SERVER only: the injected data-* markers are consumed by the
11
+ // visual-edit agent in the live preview, which vite serves — never by
12
+ // build output. Gating on mode alone also caught `vite build --mode
13
+ // development` (the platform's validation build), where instrumenting
14
+ // user code is pure risk: an injection bug there fails the build on
15
+ // code that doesn't exist on disk.
16
+ apply: (config, env) => env.command === "serve" && config.mode === "development",
11
17
  enforce: "pre",
12
18
  order: "pre",
13
19
  transformIndexHtml(html) {
@@ -1 +1 @@
1
- {"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"visual-edit-plugin.js","sourceRoot":"","sources":["../src/visual-edit-plugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44/vite-plugin",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "The Vite plugin for base44 based applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -6,7 +6,7 @@ import {
6
6
  shouldEnterInlineEditingMode,
7
7
  isStaticArrayTextElement,
8
8
  } from "./dom-utils.js";
9
- import { PLUGIN_ELEMENT_ATTR } from "../../injections/utils.js";
9
+ import { PLUGIN_ELEMENT_ATTR, readElementClasses } from "../../injections/utils.js";
10
10
 
11
11
  const DEBOUNCE_MS = 500;
12
12
 
@@ -36,17 +36,13 @@ export function createInlineEditController(
36
36
  const originalContent = element.dataset.originalTextContent;
37
37
  const newContent = element.textContent;
38
38
 
39
- const svgElement = element as unknown as SVGElement;
40
39
  const rect = element.getBoundingClientRect();
41
40
 
42
41
  const message: Record<string, unknown> = {
43
42
  type: "inline-edit",
44
43
  elementInfo: {
45
44
  tagName: element.tagName,
46
- classes:
47
- (svgElement.className as unknown as SVGAnimatedString)?.baseVal ||
48
- element.className ||
49
- "",
45
+ classes: readElementClasses(element),
50
46
  visualSelectorId: host.getSelectedElementId(),
51
47
  content: newContent,
52
48
  dataSourceLocation: element.dataset.sourceLocation,
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import path from "node:path";
1
2
  import type { Plugin, UserConfig } from "vite";
2
3
  import { loadEnv } from "vite";
3
4
  import { errorOverlayPlugin } from "./error-overlay-plugin.js";
@@ -8,6 +9,19 @@ import { buildStatusPlugin } from "./build-status-plugin.js";
8
9
 
9
10
  const isRunningInSandbox = !!process.env.MODAL_SANDBOX_ID;
10
11
 
12
+ const ENV_UNSET_GUIDANCE = [
13
+ "[base44] No Base44 backend configured — VITE_BASE44_APP_BASE_URL is not set, so /api requests have nowhere to go.",
14
+ "[base44] Run the app through the Base44 CLI instead:",
15
+ "[base44] base44 dev app + local backend (throwaway local data)",
16
+ "[base44] base44 dev --remote app + your app's real backend (production data)",
17
+ ].join("\n");
18
+
19
+ const BUILD_WITHOUT_APP_ID_WARNING = [
20
+ "[base44] Warning: VITE_BASE44_APP_ID is not set — this build",
21
+ "[base44] will not know its app id and its API calls will fail.",
22
+ "[base44] Build with: base44 build (or base44 deploy --build)",
23
+ ].join("\n");
24
+
11
25
  export default function vitePlugin(
12
26
  opts: {
13
27
  legacySDKImports?: boolean;
@@ -28,9 +42,14 @@ export default function vitePlugin(
28
42
  return [
29
43
  {
30
44
  name: "base44",
31
- config: ({ mode, root = process.cwd() }) => {
45
+ config: (config, { command }) => {
46
+ const { mode, root = process.cwd() } = config;
32
47
  const env = loadEnv(mode ?? "development", root, "");
33
48
 
49
+ if (!isRunningInSandbox && command === "build" && !env.VITE_BASE44_APP_ID) {
50
+ console.log(BUILD_WITHOUT_APP_ID_WARNING);
51
+ }
52
+
34
53
  return {
35
54
  resolve: {
36
55
  alias: {
@@ -59,6 +78,19 @@ export default function vitePlugin(
59
78
  strictPort: true,
60
79
  // Allow all hosts - essential for Modal tunnel URLs
61
80
  allowedHosts: true,
81
+ fs: {
82
+ strict: true,
83
+ // Vite replaces its default deny list when one is provided, so preserve those defaults here.
84
+ deny: [
85
+ ".env",
86
+ ".env.*",
87
+ "*.{crt,pem}",
88
+ "**/.git/**",
89
+ // base44/ contains backend code and the Base44 manifest — never serve it from the dev server.
90
+ // Root-anchored on purpose: some apps have a stray src/base44/ that IS frontend code.
91
+ path.resolve(root, "base44").replace(/\\/g, "/") + "/**",
92
+ ],
93
+ },
62
94
  // Route the HMR WebSocket through the preview proxy instead of
63
95
  // the sandbox origin. When BASE44_HMR_HOST is unset (e.g. before
64
96
  // the proxy is deployed), fall back to Vite's default behavior
@@ -110,7 +142,9 @@ export default function vitePlugin(
110
142
  };
111
143
  }
112
144
  console.log(
113
- "[base44] Proxy not enabled (VITE_BASE44_APP_BASE_URL not set)"
145
+ command === "serve"
146
+ ? ENV_UNSET_GUIDANCE
147
+ : "[base44] Proxy not enabled (VITE_BASE44_APP_BASE_URL not set)"
114
148
  );
115
149
  return {};
116
150
  })()),
@@ -56,7 +56,7 @@ function onAppError({
56
56
  }: {
57
57
  title: string;
58
58
  details: string;
59
- componentName: string;
59
+ componentName: string | undefined;
60
60
  originalError: any;
61
61
  }) {
62
62
  if (originalError?.response?.status === 402) {
@@ -80,35 +80,52 @@ function onAppError({
80
80
  );
81
81
  }
82
82
 
83
- function handleUnhandledRejection(event: any) {
84
- const stack = event.reason.stack;
85
- // extract function name from "at X (eval" where x is the function name
86
- const functionName = stack.match(/at\s+(\w+)\s+\(eval/)?.[1];
87
- const msg = functionName
88
- ? `Error in ${functionName}: ${event.reason.toString()}`
89
- : event.reason.toString();
83
+ // extract function name from "at X (eval" where X is the function name
84
+ function extractFunctionName(stack: unknown): string | undefined {
85
+ if (typeof stack !== "string") return undefined;
86
+ return stack.match(/at\s+(\w+)\s+\(eval/)?.[1];
87
+ }
88
+
89
+ // A rejection reason or error can be any value — undefined, a string, a
90
+ // null-prototype object. Throwing here masks the real error entirely.
91
+ function describeErrorValue(value: unknown): string {
92
+ try {
93
+ return String(value);
94
+ } catch {
95
+ return Object.prototype.toString.call(value);
96
+ }
97
+ }
98
+
99
+ export function handleUnhandledRejection(event: any) {
100
+ const reason = event?.reason;
101
+ const functionName = extractFunctionName(reason?.stack);
102
+ const text = describeErrorValue(reason);
103
+ const msg = functionName ? `Error in ${functionName}: ${text}` : text;
90
104
  onAppError({
91
105
  title: msg,
92
- details: event.reason.toString(),
106
+ details: text,
93
107
  componentName: functionName,
94
- originalError: event.reason,
108
+ originalError: reason,
95
109
  });
96
110
  }
97
111
 
98
- function handleWindowError(event: any) {
99
- const stack = event.error?.stack;
100
- let functionName = stack.match(/at\s+(\w+)\s+\(eval/)?.[1];
112
+ export function handleWindowError(event: any) {
113
+ const error = event?.error;
114
+ let functionName = extractFunctionName(error?.stack);
101
115
  if (functionName === "eval") {
102
- functionName = null;
116
+ functionName = undefined;
103
117
  }
104
118
 
105
- const msg = functionName
106
- ? `in ${functionName}: ${event.error.toString()}`
107
- : event.error.toString();
119
+ // window.onerror fires with a null error for cross-origin script failures
120
+ const text =
121
+ error == null && typeof event?.message === "string"
122
+ ? event.message
123
+ : describeErrorValue(error);
124
+ const msg = functionName ? `in ${functionName}: ${text}` : text;
108
125
  onAppError({
109
126
  title: msg,
110
- details: event.error.toString(),
127
+ details: text,
111
128
  componentName: functionName,
112
- originalError: event.error,
129
+ originalError: error,
113
130
  });
114
131
  }
@@ -98,6 +98,15 @@ export function updateElementAttribute(elements: Element[], attribute: string, v
98
98
  });
99
99
  }
100
100
 
101
+ /**
102
+ * Read an element's classes as a plain string, safe to postMessage.
103
+ * `element.className` is an SVGAnimatedString on SVG elements, which is not
104
+ * structured-cloneable — posting it throws DataCloneError and the preview hangs.
105
+ */
106
+ export function readElementClasses(element: Element): string {
107
+ return element.getAttribute("class") ?? "";
108
+ }
109
+
101
110
  /** Collect attribute values from an element for a given allowlist. */
102
111
  export function collectAllowedAttributes(element: Element, allowedAttributes: string[]): Record<string, string> {
103
112
  const attributes: Record<string, string> = {};
@@ -1,4 +1,4 @@
1
- import { findElementsById, updateElementClasses, updateElementAttribute, collectAllowedAttributes, ALLOWED_ATTRIBUTES, getElementSelectorId, stopAnimations, resumeAnimations, findInstrumentedElement, resolveHoverTarget, positionLabel, injectFontFaceCss } from "./utils.js";
1
+ import { findElementsById, updateElementClasses, updateElementAttribute, collectAllowedAttributes, readElementClasses, ALLOWED_ATTRIBUTES, getElementSelectorId, stopAnimations, resumeAnimations, findInstrumentedElement, resolveHoverTarget, positionLabel, injectFontFaceCss } from "./utils.js";
2
2
  import { createLayerController } from "./layer-dropdown/controller.js";
3
3
  import { LAYER_DROPDOWN_ATTR } from "./layer-dropdown/consts.js";
4
4
  import { createInlineEditController } from "../capabilities/inline-edit/index.js";
@@ -137,7 +137,6 @@ export function setupVisualEditAgent() {
137
137
  const notifyElementSelected = (element: Element) => {
138
138
  const htmlElement = element as HTMLElement;
139
139
  const rect = element.getBoundingClientRect();
140
- const svgElement = element as SVGElement;
141
140
  const isTextElement = TEXT_TAGS.includes(element.tagName?.toLowerCase());
142
141
 
143
142
  const arrEl = htmlElement.closest("[data-arr-variable-name]") as HTMLElement | null;
@@ -153,10 +152,7 @@ export function setupVisualEditAgent() {
153
152
  window.parent.postMessage({
154
153
  type: "element-selected",
155
154
  tagName: element.tagName,
156
- classes:
157
- (svgElement.className as unknown as SVGAnimatedString)?.baseVal ||
158
- element.className ||
159
- "",
155
+ classes: readElementClasses(element),
160
156
  visualSelectorId: getElementSelectorId(element),
161
157
  content: isTextElement ? htmlElement.innerText : undefined,
162
158
  dataSourceLocation: htmlElement.dataset.sourceLocation,
@@ -382,6 +382,18 @@ export class DataItemFieldProcessor {
382
382
  }
383
383
 
384
384
  const fieldToInject = idField ?? "id";
385
+
386
+ // Never inject a param whose name is already bound where the JSX
387
+ // element sits. A function-scope binding (e.g. `const { id } =
388
+ // useParams()`) makes the output fail to compile — "The symbol 'id'
389
+ // has already been declared" — an outer-scope binding would be
390
+ // silently shadowed by an undefined prop, and a block-scope binding
391
+ // between the element and the function would make the emitted
392
+ // attribute read the wrong value. Anchoring on the element's scope
393
+ // chain covers all three; skipping just degrades visual-edit tracing
394
+ // for this element.
395
+ if (path.scope.hasBinding(fieldToInject)) return;
396
+
385
397
  const newProp = this.types.objectProperty(
386
398
  this.types.identifier(fieldToInject),
387
399
  this.types.identifier(fieldToInject),
@@ -29,7 +29,7 @@ It adds two HTML `data-*` attributes to every JSX element:
29
29
  | Condition | Where | Why |
30
30
  |-----------|-------|-----|
31
31
  | `MODAL_SANDBOX_ID` env var is set | `index.ts:8` | Only runs inside Modal sandbox containers |
32
- | Vite mode is `"development"` | `apply: (config) => config.mode === "development"` | No overhead in production builds |
32
+ | Dev **server** in `"development"` mode | `apply: (config, env) => env.command === "serve" && config.mode === "development"` | The injected `data-*` markers are only consumed by the visual-edit agent in the live preview, which the dev server serves. Builds never use them — including `vite build --mode development` (the platform's validation build), where instrumenting user code once produced compile errors on code that wasn't on disk |
33
33
  | File is `.js`, `.jsx`, `.ts`, or `.tsx` | `id.match(/\.(jsx?\|tsx?)$/)` | Only JSX-capable files |
34
34
  | File is NOT in `node_modules` | `id.includes("node_modules")` | Skip third-party code |
35
35
  | File is NOT `visual-edit-agent` | `id.includes("visual-edit-agent")` | Don't instrument the agent itself |
@@ -37,7 +37,8 @@ It adds two HTML `data-*` attributes to every JSX element:
37
37
  ### Execution Timeline
38
38
 
39
39
  ```
40
- 1. npm run dev
40
+ 1. npm run dev (dev SERVER only — `vite build` never registers this plugin,
41
+ regardless of mode)
41
42
  2. Vite starts, loads plugin config
42
43
  3. index.ts checks: isRunningInSandbox = !!process.env.MODAL_SANDBOX_ID
43
44
  4. If true → registers visualEditPlugin() in the plugin pipeline
@@ -2,14 +2,21 @@ import { parse } from "@babel/parser";
2
2
  import { default as traverse } from "@babel/traverse";
3
3
  import { default as generate } from "@babel/generator";
4
4
  import * as t from "@babel/types";
5
- import type { Plugin } from "vite";
5
+ import type { ConfigEnv, Plugin, UserConfig } from "vite";
6
6
  import { JSXProcessor } from "./jsx-processor.js";
7
7
  import { JSXUtils } from "./jsx-utils.js";
8
8
 
9
9
  export function visualEditPlugin() {
10
10
  return {
11
11
  name: "visual-edit-transform",
12
- apply: (config) => config.mode === "development",
12
+ // Dev SERVER only: the injected data-* markers are consumed by the
13
+ // visual-edit agent in the live preview, which vite serves — never by
14
+ // build output. Gating on mode alone also caught `vite build --mode
15
+ // development` (the platform's validation build), where instrumenting
16
+ // user code is pure risk: an injection bug there fails the build on
17
+ // code that doesn't exist on disk.
18
+ apply: (config: UserConfig, env: ConfigEnv) =>
19
+ env.command === "serve" && config.mode === "development",
13
20
  enforce: "pre",
14
21
  order: "pre",
15
22
  transformIndexHtml(html: any) {