@usels/vite-plugin-legend-memo 0.1.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.
package/README.md ADDED
@@ -0,0 +1,561 @@
1
+ # @usels/vite-plugin-legend-memo
2
+
3
+ A Vite plugin that applies [`@usels/babel-plugin-legend-memo`](../babel) during the transform phase. Automatically wraps Legend-State observable `.get()` calls in JSX with reactive `<Memo>` boundaries for fine-grained reactivity — without any boilerplate.
4
+
5
+ ```tsx
6
+ // You write this naturally
7
+ <div>{count$.get()}</div>
8
+
9
+ // Plugin transforms to this automatically
10
+ import { Memo } from "@legendapp/state/react";
11
+ <div><Memo>{() => count$.get()}</Memo></div>
12
+ ```
13
+
14
+ **One plugin replaces two** — no longer need `@legendapp/state/babel` alongside another auto-wrap plugin.
15
+
16
+ ---
17
+
18
+ ## Table of Contents
19
+
20
+ - [Installation](#installation)
21
+ - [Setup](#setup)
22
+ - [Plugin Order (Critical)](#plugin-order-critical)
23
+ - [Configuration](#configuration)
24
+ - [Features](#features)
25
+ - [Writing Components](#writing-components)
26
+ - [API Reference](#api-reference)
27
+ - [Troubleshooting](#troubleshooting)
28
+
29
+ ---
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ npm install -D @usels/vite-plugin-legend-memo @usels/babel-plugin-legend-memo @babel/core
35
+ # or
36
+ pnpm add -D @usels/vite-plugin-legend-memo @usels/babel-plugin-legend-memo @babel/core
37
+ # or
38
+ yarn add -D @usels/vite-plugin-legend-memo @usels/babel-plugin-legend-memo @babel/core
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Setup
44
+
45
+ ### Basic
46
+
47
+ ```typescript
48
+ // vite.config.ts
49
+ import { defineConfig } from 'vite';
50
+ import react from '@vitejs/plugin-react';
51
+ import { autoWrap } from '@usels/vite-plugin-legend-memo';
52
+
53
+ export default defineConfig({
54
+ plugins: [
55
+ autoWrap(), // ← Must be BEFORE react()
56
+ react(),
57
+ ],
58
+ });
59
+ ```
60
+
61
+ ### With options
62
+
63
+ ```typescript
64
+ // vite.config.ts
65
+ import { defineConfig } from 'vite';
66
+ import react from '@vitejs/plugin-react';
67
+ import { autoWrap } from '@usels/vite-plugin-legend-memo';
68
+
69
+ export default defineConfig({
70
+ plugins: [
71
+ autoWrap({
72
+ // Detect all .get() regardless of $ suffix (default: false)
73
+ allGet: false,
74
+
75
+ // Auto-wrap Memo/Show/Computed children (default: true)
76
+ wrapReactiveChildren: true,
77
+
78
+ // Custom wrapper component (default: "Memo")
79
+ componentName: 'Memo',
80
+
81
+ // Import source (default: "@legendapp/state/react")
82
+ importSource: '@legendapp/state/react',
83
+ }),
84
+ react(),
85
+ ],
86
+ });
87
+ ```
88
+
89
+ ### With a different observable library
90
+
91
+ ```typescript
92
+ import { defineConfig } from 'vite';
93
+ import react from '@vitejs/plugin-react';
94
+ import { autoWrap } from '@usels/vite-plugin-legend-memo';
95
+
96
+ export default defineConfig({
97
+ plugins: [
98
+ autoWrap({
99
+ componentName: 'Auto',
100
+ importSource: '@usels/core',
101
+ }),
102
+ react(),
103
+ ],
104
+ });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Plugin Order (Critical)
110
+
111
+ **`autoWrap()` MUST be placed BEFORE `react()` in the plugins array.**
112
+
113
+ ```typescript
114
+ // ✅ Correct — autoWrap processes JSX before React plugin
115
+ plugins: [autoWrap(), react()]
116
+
117
+ // ❌ Wrong — JSX is already transpiled when autoWrap runs
118
+ plugins: [react(), autoWrap()]
119
+ ```
120
+
121
+ ### Why order matters
122
+
123
+ The plugin uses `enforce: 'pre'` to run before `@vitejs/plugin-react`:
124
+
125
+ 1. **`autoWrap()` runs first** — processes `.jsx`/`.tsx` files while JSX syntax is intact
126
+ 2. **React plugin runs** — converts JSX to `React.createElement()` calls
127
+ 3. **esbuild bundles** — produces the final output
128
+
129
+ If `react()` runs before `autoWrap()`, the JSX is already converted to function calls and the plugin cannot find JSX expressions to wrap.
130
+
131
+ ---
132
+
133
+ ## Configuration
134
+
135
+ All options from `@usels/babel-plugin-legend-memo` are supported:
136
+
137
+ ```typescript
138
+ interface PluginOptions {
139
+ /**
140
+ * Wrapper component name
141
+ * @default "Memo"
142
+ */
143
+ componentName?: string;
144
+
145
+ /**
146
+ * Import source for the wrapper component
147
+ * @default "@legendapp/state/react"
148
+ */
149
+ importSource?: string;
150
+
151
+ /**
152
+ * Detect all .get() calls regardless of $ suffix
153
+ * @default false
154
+ */
155
+ allGet?: boolean;
156
+
157
+ /**
158
+ * Additional method names to detect beyond "get"
159
+ * @default ["get"]
160
+ */
161
+ methodNames?: string[];
162
+
163
+ /**
164
+ * Additional reactive component names to skip
165
+ * Merged with defaults: Auto, For, Show, Memo, Computed, Switch
166
+ */
167
+ reactiveComponents?: string[];
168
+
169
+ /**
170
+ * Observer HOC function names — skip content inside these
171
+ * @default ["observer"]
172
+ */
173
+ observerNames?: string[];
174
+
175
+ /**
176
+ * Auto-wrap non-function children of Memo/Show/Computed in () =>
177
+ * Replaces the need for @legendapp/state/babel plugin
178
+ * @default true
179
+ */
180
+ wrapReactiveChildren?: boolean;
181
+
182
+ /**
183
+ * Additional component names whose children should be auto-wrapped
184
+ * Merged with defaults: Memo, Show, Computed
185
+ */
186
+ wrapReactiveChildrenComponents?: string[];
187
+ }
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Features
193
+
194
+ ### 1. Auto-wrap `.get()` calls in JSX expressions
195
+
196
+ ```tsx
197
+ // Input
198
+ <div>{count$.get()}</div>
199
+
200
+ // Output
201
+ import { Memo } from "@legendapp/state/react";
202
+ <div><Memo>{() => count$.get()}</Memo></div>
203
+ ```
204
+
205
+ Complex expressions, ternaries, and conditionals are all handled:
206
+
207
+ ```tsx
208
+ // Multiple observables → one Memo
209
+ <p>{a$.get() + " " + b$.get()}</p>
210
+ // → <p><Memo>{() => a$.get() + " " + b$.get()}</Memo></p>
211
+
212
+ // Ternary
213
+ <div>{isActive$.get() ? "ON" : "OFF"}</div>
214
+ // → <div><Memo>{() => isActive$.get() ? "ON" : "OFF"}</Memo></div>
215
+
216
+ // Conditional rendering
217
+ <div>{show$.get() && <Modal />}</div>
218
+ // → <div><Memo>{() => show$.get() && <Modal />}</Memo></div>
219
+ ```
220
+
221
+ ### 2. Auto-wrap `.get()` calls in JSX attributes
222
+
223
+ When an element's props contain `.get()`, the entire element is wrapped:
224
+
225
+ ```tsx
226
+ // Input
227
+ <Component value={obs$.get()} />
228
+
229
+ // Output
230
+ <Memo>{() => <Component value={obs$.get()} />}</Memo>
231
+ ```
232
+
233
+ Attributes and children together → one `<Memo>`:
234
+
235
+ ```tsx
236
+ // Input
237
+ <div className={theme$.get()}>
238
+ {count$.get()}
239
+ </div>
240
+
241
+ // Output
242
+ <Memo>{() =>
243
+ <div className={theme$.get()}>
244
+ {count$.get()}
245
+ </div>
246
+ }</Memo>
247
+ ```
248
+
249
+ ### 3. Auto-wrap children of Memo/Show/Computed
250
+
251
+ Non-function children are automatically wrapped in `() =>`. This replaces `@legendapp/state/babel`:
252
+
253
+ ```tsx
254
+ // Input
255
+ <Memo>{count$.get()}</Memo>
256
+ <Show if={cond$}>{count$.get()}</Show>
257
+ <Computed>{price$.get() * qty$.get()}</Computed>
258
+
259
+ // Output
260
+ <Memo>{() => count$.get()}</Memo>
261
+ <Show if={cond$}>{() => count$.get()}</Show>
262
+ <Computed>{() => price$.get() * qty$.get()}</Computed>
263
+ ```
264
+
265
+ Direct JSX children and multiple children:
266
+
267
+ ```tsx
268
+ // Direct JSX child
269
+ <Memo><div>{count$.get()}</div></Memo>
270
+ // → <Memo>{() => <div>{count$.get()}</div>}</Memo>
271
+
272
+ // Multiple children → Fragment
273
+ <Memo><Header /><Body /></Memo>
274
+ // → <Memo>{() => <><Header /><Body /></>}</Memo>
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Writing Components
280
+
281
+ ### Basic principle: write `.get()` naturally, plugin handles wrapping
282
+
283
+ ```tsx
284
+ import { observable } from '@legendapp/state';
285
+ import { Show, Memo, For } from '@legendapp/state/react';
286
+
287
+ const count$ = observable(0);
288
+ const isVisible$ = observable(true);
289
+ const user$ = observable({ name: 'Alice', age: 30 });
290
+ const items$ = observable([{ id: 1, name: 'Item 1' }]);
291
+
292
+ export function App() {
293
+ return (
294
+ <div>
295
+ {/* Simple expressions — plugin wraps each */}
296
+ <h1>Count: {count$.get()}</h1>
297
+ <p>User: {user$.name.get()}</p>
298
+
299
+ {/* Memo children — plugin auto-wraps in () => */}
300
+ <Memo>
301
+ <div className="card">{count$.get()}</div>
302
+ </Memo>
303
+
304
+ {/* Show children — plugin auto-wraps in () => */}
305
+ <Show if={isVisible$}>
306
+ {user$.name.get()}
307
+ </Show>
308
+
309
+ {/* For — handles list reactivity, plugin skips inside */}
310
+ <For each={items$}>
311
+ {(item$) => <li>{item$.name.get()}</li>}
312
+ </For>
313
+ </div>
314
+ );
315
+ }
316
+ ```
317
+
318
+ ### Use `$` suffix for observables (required by default)
319
+
320
+ ```tsx
321
+ // ✅ Detected automatically — use $ suffix
322
+ const count$ = observable(0);
323
+ const profile$ = observable({ name: '', email: '' });
324
+
325
+ // ❌ Without $ — won't be wrapped (use allGet: true to override)
326
+ const count = observable(0);
327
+ ```
328
+
329
+ ### Use `observer()` for component-level reactivity
330
+
331
+ When the whole component is reactive, wrap with `observer()`. Plugin skips content inside — no double-wrapping:
332
+
333
+ ```tsx
334
+ import { observer } from '@legendapp/state/react';
335
+
336
+ // ✅ Entire component is reactive — no individual Memo wrappers needed
337
+ const Profile = observer(() => {
338
+ return (
339
+ <div>
340
+ <h2>{user$.name.get()}</h2>
341
+ <p>{user$.bio.get()}</p>
342
+ <span>{user$.age.get()} years old</span>
343
+ </div>
344
+ );
345
+ });
346
+ ```
347
+
348
+ **When to use `observer()`:** When the entire component needs reactivity and you want simpler code without individual `<Memo>` boundaries.
349
+
350
+ **When to use auto-wrap (default):** When you want fine-grained reactivity — only the specific expressions that use observables update, not the whole component.
351
+
352
+ ### Reactive attributes
353
+
354
+ ```tsx
355
+ const theme$ = observable({ color: '#007bff', size: 'lg' });
356
+ const isDark$ = observable(false);
357
+
358
+ function ThemedButton({ label }: { label: string }) {
359
+ return (
360
+ <button
361
+ className={`btn-${theme$.size.get()}`}
362
+ style={{ backgroundColor: theme$.color.get() }}
363
+ aria-pressed={isDark$.get()}
364
+ >
365
+ {label}
366
+ </button>
367
+ // ↑ Plugin wraps entire <button> since attributes have .get()
368
+ );
369
+ }
370
+ ```
371
+
372
+ ### Conditional rendering patterns
373
+
374
+ ```tsx
375
+ const auth$ = observable({ isLoggedIn: false, username: '' });
376
+
377
+ function Header() {
378
+ return (
379
+ <header>
380
+ {/* Show removes from DOM when false */}
381
+ <Show if={auth$.isLoggedIn}>
382
+ {/* Plugin auto-wraps children */}
383
+ Welcome, {auth$.username.get()}
384
+ </Show>
385
+
386
+ {/* Ternary with .get() */}
387
+ <nav>
388
+ {auth$.isLoggedIn.get()
389
+ ? <a href="/profile">Profile</a>
390
+ : <a href="/login">Login</a>
391
+ }
392
+ </nav>
393
+ </header>
394
+ );
395
+ }
396
+ ```
397
+
398
+ ### Reactive lists with `For`
399
+
400
+ ```tsx
401
+ const todos$ = observable([
402
+ { id: 1, text: 'Learn Legend-State', done: false },
403
+ ]);
404
+
405
+ function TodoList() {
406
+ return (
407
+ <ul>
408
+ <For each={todos$}>
409
+ {(todo$) => (
410
+ // item$ is already reactive — plugin skips inside For
411
+ <li
412
+ style={{ textDecoration: todo$.done.get() ? 'line-through' : 'none' }}
413
+ >
414
+ {todo$.text.get()}
415
+ </li>
416
+ )}
417
+ </For>
418
+ </ul>
419
+ );
420
+ }
421
+ ```
422
+
423
+ ### Computed derived values
424
+
425
+ ```tsx
426
+ import { computed } from '@legendapp/state';
427
+
428
+ const price$ = observable(100);
429
+ const qty$ = observable(2);
430
+ const discount$ = observable(0.1);
431
+
432
+ // Derived value
433
+ const total$ = computed(() =>
434
+ price$.get() * qty$.get() * (1 - discount$.get())
435
+ );
436
+
437
+ function OrderSummary() {
438
+ return (
439
+ <div>
440
+ <p>Price: {price$.get()}</p>
441
+ <p>Qty: {qty$.get()}</p>
442
+ <p>Total: {total$.get()}</p>
443
+ {/* Plugin wraps each expression individually */}
444
+ </div>
445
+ );
446
+ }
447
+ ```
448
+
449
+ ---
450
+
451
+ ## API Reference
452
+
453
+ ### `autoWrap(options?: PluginOptions): Plugin`
454
+
455
+ Returns a Vite plugin that transforms `.jsx` and `.tsx` files using `@usels/babel-plugin-legend-memo`.
456
+
457
+ **Parameters:**
458
+ - `options` (optional) — Configuration object. See [Configuration](#configuration).
459
+
460
+ **Returns:** Vite `Plugin` object
461
+
462
+ **Example:**
463
+
464
+ ```typescript
465
+ import { autoWrap } from '@usels/vite-plugin-legend-memo';
466
+
467
+ const plugin = autoWrap({
468
+ componentName: 'Memo',
469
+ importSource: '@legendapp/state/react',
470
+ wrapReactiveChildren: true,
471
+ });
472
+ ```
473
+
474
+ ### Type exports
475
+
476
+ ```typescript
477
+ import type { PluginOptions } from '@usels/vite-plugin-legend-memo';
478
+
479
+ const options: PluginOptions = {
480
+ allGet: false,
481
+ wrapReactiveChildren: true,
482
+ };
483
+ ```
484
+
485
+ ---
486
+
487
+ ## Troubleshooting
488
+
489
+ ### `.get()` calls aren't being wrapped
490
+
491
+ 1. Check plugin order — `autoWrap()` must come before `react()`
492
+ 2. Check that your observable uses `$` suffix (or enable `allGet: true`)
493
+ 3. Check if code is inside `observer()` — this is intentional (observer makes whole component reactive)
494
+
495
+ ```typescript
496
+ // Check 1: plugin order
497
+ plugins: [autoWrap(), react()] // ✅ correct order
498
+
499
+ // Check 2: $ suffix
500
+ const count$ = observable(0); // ✅ will be wrapped
501
+ const count = observable(0); // ❌ won't be wrapped (add allGet: true)
502
+
503
+ // Check 3: observer() is expected
504
+ const Comp = observer(() => {
505
+ return <div>{count$.get()}</div>; // intentionally not wrapped
506
+ });
507
+ ```
508
+
509
+ ### `Memo` is not defined error
510
+
511
+ The plugin auto-adds `import { Memo } from "@legendapp/state/react"` when wrapping. If you see this error:
512
+
513
+ 1. Ensure `@legendapp/state` is installed: `npm install @legendapp/state`
514
+ 2. If using a different import source, configure it: `autoWrap({ importSource: '...' })`
515
+
516
+ ### Source maps not working
517
+
518
+ The plugin preserves source maps automatically. If DevTools shows incorrect locations:
519
+
520
+ ```typescript
521
+ // vite.config.ts
522
+ export default defineConfig({
523
+ build: {
524
+ sourcemap: true, // Enable for production builds
525
+ },
526
+ });
527
+ ```
528
+
529
+ ### Performance: too many re-renders
530
+
531
+ If you see many components re-rendering, consider using `observer()` for entire components instead of fine-grained `<Memo>` boundaries:
532
+
533
+ ```tsx
534
+ // Fine-grained (default) — each expression gets its own Memo
535
+ function Component() {
536
+ return <div>{a$.get()} {b$.get()} {c$.get()}</div>;
537
+ }
538
+
539
+ // Component-level (use observer if whole component should update together)
540
+ const Component = observer(() => {
541
+ return <div>{a$.get()} {b$.get()} {c$.get()}</div>;
542
+ });
543
+ ```
544
+
545
+ ---
546
+
547
+ ## Peer Dependencies
548
+
549
+ | Package | Required Version |
550
+ |---------|-----------------|
551
+ | `vite` | `>=4.0.0` |
552
+ | `@babel/core` | `>=7.0.0` |
553
+ | `@usels/babel-plugin-legend-memo` | `workspace:*` |
554
+
555
+ ---
556
+
557
+ ## See Also
558
+
559
+ - [@usels/babel-plugin-legend-memo](../babel) — The underlying Babel plugin and its full documentation
560
+ - [Legend-State Documentation](https://legendapp.com/dev/state/v3/)
561
+ - [Vite Plugin API](https://vitejs.dev/guide/api-plugin.html)
@@ -0,0 +1,31 @@
1
+ import { Plugin } from 'vite';
2
+ import { PluginOptions } from '@usels/babel-plugin-legend-memo';
3
+ export { PluginOptions } from '@usels/babel-plugin-legend-memo';
4
+
5
+ /**
6
+ * Vite plugin that applies @usels/babel-plugin-legend-memo during transform.
7
+ *
8
+ * Wraps Legend-State observable .get() calls in JSX with <Auto> component
9
+ * for fine-grained reactive rendering.
10
+ *
11
+ * IMPORTANT: Must be placed BEFORE @vitejs/plugin-react in the plugins array,
12
+ * because `enforce: "pre"` ensures this runs before esbuild JSX transform.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // vite.config.ts
17
+ * import { defineConfig } from 'vite';
18
+ * import react from '@vitejs/plugin-react';
19
+ * import { autoWrap } from '@usels/vite-plugin-legend-memo';
20
+ *
21
+ * export default defineConfig({
22
+ * plugins: [
23
+ * autoWrap({ importSource: '@usels/core' }),
24
+ * react(),
25
+ * ],
26
+ * });
27
+ * ```
28
+ */
29
+ declare function autoWrap(opts?: PluginOptions): Plugin;
30
+
31
+ export { autoWrap, autoWrap as default };
@@ -0,0 +1,31 @@
1
+ import { Plugin } from 'vite';
2
+ import { PluginOptions } from '@usels/babel-plugin-legend-memo';
3
+ export { PluginOptions } from '@usels/babel-plugin-legend-memo';
4
+
5
+ /**
6
+ * Vite plugin that applies @usels/babel-plugin-legend-memo during transform.
7
+ *
8
+ * Wraps Legend-State observable .get() calls in JSX with <Auto> component
9
+ * for fine-grained reactive rendering.
10
+ *
11
+ * IMPORTANT: Must be placed BEFORE @vitejs/plugin-react in the plugins array,
12
+ * because `enforce: "pre"` ensures this runs before esbuild JSX transform.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // vite.config.ts
17
+ * import { defineConfig } from 'vite';
18
+ * import react from '@vitejs/plugin-react';
19
+ * import { autoWrap } from '@usels/vite-plugin-legend-memo';
20
+ *
21
+ * export default defineConfig({
22
+ * plugins: [
23
+ * autoWrap({ importSource: '@usels/core' }),
24
+ * react(),
25
+ * ],
26
+ * });
27
+ * ```
28
+ */
29
+ declare function autoWrap(opts?: PluginOptions): Plugin;
30
+
31
+ export { autoWrap, autoWrap as default };
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ autoWrap: () => autoWrap,
34
+ default: () => index_default
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+ function autoWrap(opts = {}) {
38
+ return {
39
+ name: "@usels/vite-plugin-legend-memo",
40
+ // Must run before esbuild JSX transform (before @vitejs/plugin-react)
41
+ enforce: "pre",
42
+ async transform(code, id) {
43
+ if (!/\.[jt]sx$/.test(id)) return null;
44
+ const babel = await import("@babel/core");
45
+ const result = await babel.transformAsync(code, {
46
+ filename: id,
47
+ plugins: [["@usels/babel-plugin-legend-memo", opts]],
48
+ parserOpts: {
49
+ plugins: ["jsx", "typescript"]
50
+ },
51
+ sourceMaps: true,
52
+ configFile: false,
53
+ babelrc: false
54
+ });
55
+ if (!result || !result.code) return null;
56
+ return {
57
+ code: result.code,
58
+ map: result.map ?? void 0
59
+ };
60
+ }
61
+ };
62
+ }
63
+ var index_default = autoWrap;
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ autoWrap
67
+ });
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport type { PluginOptions } from '@usels/babel-plugin-legend-memo';\n\n/**\n * Vite plugin that applies @usels/babel-plugin-legend-memo during transform.\n *\n * Wraps Legend-State observable .get() calls in JSX with <Auto> component\n * for fine-grained reactive rendering.\n *\n * IMPORTANT: Must be placed BEFORE @vitejs/plugin-react in the plugins array,\n * because `enforce: \"pre\"` ensures this runs before esbuild JSX transform.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import react from '@vitejs/plugin-react';\n * import { autoWrap } from '@usels/vite-plugin-legend-memo';\n *\n * export default defineConfig({\n * plugins: [\n * autoWrap({ importSource: '@usels/core' }),\n * react(),\n * ],\n * });\n * ```\n */\nexport function autoWrap(opts: PluginOptions = {}): Plugin {\n return {\n name: '@usels/vite-plugin-legend-memo',\n // Must run before esbuild JSX transform (before @vitejs/plugin-react)\n enforce: 'pre',\n\n async transform(code, id) {\n // Only process .jsx and .tsx files\n if (!/\\.[jt]sx$/.test(id)) return null;\n\n // Lazy import @babel/core to avoid bundling it\n const babel = await import('@babel/core');\n\n const result = await babel.transformAsync(code, {\n filename: id,\n plugins: [['@usels/babel-plugin-legend-memo', opts]],\n parserOpts: {\n plugins: ['jsx', 'typescript'],\n },\n sourceMaps: true,\n configFile: false,\n babelrc: false,\n });\n\n if (!result || !result.code) return null;\n\n return {\n code: result.code,\n map: result.map ?? undefined,\n };\n },\n };\n}\n\nexport default autoWrap;\nexport type { PluginOptions };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BO,SAAS,SAAS,OAAsB,CAAC,GAAW;AACzD,SAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAEN,SAAS;AAAA,IAET,MAAM,UAAU,MAAM,IAAI;AAExB,UAAI,CAAC,YAAY,KAAK,EAAE,EAAG,QAAO;AAGlC,YAAM,QAAQ,MAAM,OAAO,aAAa;AAExC,YAAM,SAAS,MAAM,MAAM,eAAe,MAAM;AAAA,QAC9C,UAAU;AAAA,QACV,SAAS,CAAC,CAAC,mCAAmC,IAAI,CAAC;AAAA,QACnD,YAAY;AAAA,UACV,SAAS,CAAC,OAAO,YAAY;AAAA,QAC/B;AAAA,QACA,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,SAAS;AAAA,MACX,CAAC;AAED,UAAI,CAAC,UAAU,CAAC,OAAO,KAAM,QAAO;AAEpC,aAAO;AAAA,QACL,MAAM,OAAO;AAAA,QACb,KAAK,OAAO,OAAO;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,33 @@
1
+ // src/index.ts
2
+ function autoWrap(opts = {}) {
3
+ return {
4
+ name: "@usels/vite-plugin-legend-memo",
5
+ // Must run before esbuild JSX transform (before @vitejs/plugin-react)
6
+ enforce: "pre",
7
+ async transform(code, id) {
8
+ if (!/\.[jt]sx$/.test(id)) return null;
9
+ const babel = await import("@babel/core");
10
+ const result = await babel.transformAsync(code, {
11
+ filename: id,
12
+ plugins: [["@usels/babel-plugin-legend-memo", opts]],
13
+ parserOpts: {
14
+ plugins: ["jsx", "typescript"]
15
+ },
16
+ sourceMaps: true,
17
+ configFile: false,
18
+ babelrc: false
19
+ });
20
+ if (!result || !result.code) return null;
21
+ return {
22
+ code: result.code,
23
+ map: result.map ?? void 0
24
+ };
25
+ }
26
+ };
27
+ }
28
+ var index_default = autoWrap;
29
+ export {
30
+ autoWrap,
31
+ index_default as default
32
+ };
33
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport type { PluginOptions } from '@usels/babel-plugin-legend-memo';\n\n/**\n * Vite plugin that applies @usels/babel-plugin-legend-memo during transform.\n *\n * Wraps Legend-State observable .get() calls in JSX with <Auto> component\n * for fine-grained reactive rendering.\n *\n * IMPORTANT: Must be placed BEFORE @vitejs/plugin-react in the plugins array,\n * because `enforce: \"pre\"` ensures this runs before esbuild JSX transform.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import react from '@vitejs/plugin-react';\n * import { autoWrap } from '@usels/vite-plugin-legend-memo';\n *\n * export default defineConfig({\n * plugins: [\n * autoWrap({ importSource: '@usels/core' }),\n * react(),\n * ],\n * });\n * ```\n */\nexport function autoWrap(opts: PluginOptions = {}): Plugin {\n return {\n name: '@usels/vite-plugin-legend-memo',\n // Must run before esbuild JSX transform (before @vitejs/plugin-react)\n enforce: 'pre',\n\n async transform(code, id) {\n // Only process .jsx and .tsx files\n if (!/\\.[jt]sx$/.test(id)) return null;\n\n // Lazy import @babel/core to avoid bundling it\n const babel = await import('@babel/core');\n\n const result = await babel.transformAsync(code, {\n filename: id,\n plugins: [['@usels/babel-plugin-legend-memo', opts]],\n parserOpts: {\n plugins: ['jsx', 'typescript'],\n },\n sourceMaps: true,\n configFile: false,\n babelrc: false,\n });\n\n if (!result || !result.code) return null;\n\n return {\n code: result.code,\n map: result.map ?? undefined,\n };\n },\n };\n}\n\nexport default autoWrap;\nexport type { PluginOptions };\n"],"mappings":";AA2BO,SAAS,SAAS,OAAsB,CAAC,GAAW;AACzD,SAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAEN,SAAS;AAAA,IAET,MAAM,UAAU,MAAM,IAAI;AAExB,UAAI,CAAC,YAAY,KAAK,EAAE,EAAG,QAAO;AAGlC,YAAM,QAAQ,MAAM,OAAO,aAAa;AAExC,YAAM,SAAS,MAAM,MAAM,eAAe,MAAM;AAAA,QAC9C,UAAU;AAAA,QACV,SAAS,CAAC,CAAC,mCAAmC,IAAI,CAAC;AAAA,QACnD,YAAY;AAAA,UACV,SAAS,CAAC,OAAO,YAAY;AAAA,QAC/B;AAAA,QACA,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,SAAS;AAAA,MACX,CAAC;AAED,UAAI,CAAC,UAAU,CAAC,OAAO,KAAM,QAAO;AAEpC,aAAO;AAAA,QACL,MAAM,OAAO;AAAA,QACb,KAAK,OAAO,OAAO;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":[]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@usels/vite-plugin-legend-memo",
3
+ "version": "0.1.0",
4
+ "description": "Vite plugin to auto-wrap Legend-State observable .get() calls in JSX with <Auto> component",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "peerDependencies": {
16
+ "@babel/core": ">=7.0.0",
17
+ "vite": ">=4.0.0",
18
+ "@usels/babel-plugin-legend-memo": "0.1.0"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "devDependencies": {
24
+ "@babel/core": "^7.24.0",
25
+ "@types/babel__core": "^7.20.0",
26
+ "tsup": "^8.0.0",
27
+ "typescript": "^5.0.0",
28
+ "vite": "^5.0.0",
29
+ "@usels/babel-plugin-legend-memo": "0.1.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "dev": "tsup --watch",
34
+ "typecheck": "tsc --noEmit"
35
+ }
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { PluginOptions } from '@usels/babel-plugin-legend-memo';
3
+
4
+ /**
5
+ * Vite plugin that applies @usels/babel-plugin-legend-memo during transform.
6
+ *
7
+ * Wraps Legend-State observable .get() calls in JSX with <Auto> component
8
+ * for fine-grained reactive rendering.
9
+ *
10
+ * IMPORTANT: Must be placed BEFORE @vitejs/plugin-react in the plugins array,
11
+ * because `enforce: "pre"` ensures this runs before esbuild JSX transform.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // vite.config.ts
16
+ * import { defineConfig } from 'vite';
17
+ * import react from '@vitejs/plugin-react';
18
+ * import { autoWrap } from '@usels/vite-plugin-legend-memo';
19
+ *
20
+ * export default defineConfig({
21
+ * plugins: [
22
+ * autoWrap({ importSource: '@usels/core' }),
23
+ * react(),
24
+ * ],
25
+ * });
26
+ * ```
27
+ */
28
+ export function autoWrap(opts: PluginOptions = {}): Plugin {
29
+ return {
30
+ name: '@usels/vite-plugin-legend-memo',
31
+ // Must run before esbuild JSX transform (before @vitejs/plugin-react)
32
+ enforce: 'pre',
33
+
34
+ async transform(code, id) {
35
+ // Only process .jsx and .tsx files
36
+ if (!/\.[jt]sx$/.test(id)) return null;
37
+
38
+ // Lazy import @babel/core to avoid bundling it
39
+ const babel = await import('@babel/core');
40
+
41
+ const result = await babel.transformAsync(code, {
42
+ filename: id,
43
+ plugins: [['@usels/babel-plugin-legend-memo', opts]],
44
+ parserOpts: {
45
+ plugins: ['jsx', 'typescript'],
46
+ },
47
+ sourceMaps: true,
48
+ configFile: false,
49
+ babelrc: false,
50
+ });
51
+
52
+ if (!result || !result.code) return null;
53
+
54
+ return {
55
+ code: result.code,
56
+ map: result.map ?? undefined,
57
+ };
58
+ },
59
+ };
60
+ }
61
+
62
+ export default autoWrap;
63
+ export type { PluginOptions };
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "include": ["src"],
4
+ "compilerOptions": {
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "moduleResolution": "node",
8
+ "module": "CommonJS"
9
+ }
10
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['cjs', 'esm'],
6
+ dts: true,
7
+ external: ['@babel/core', '@usels/babel-plugin-legend-memo', 'vite'],
8
+ clean: true,
9
+ sourcemap: true,
10
+ });