loopwind 0.18.1 → 0.19.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,251 @@
1
+ /**
2
+ * Example: Template Compiler Workflow
3
+ *
4
+ * This example demonstrates the recommended production workflow for JSX templates:
5
+ *
6
+ * 1. ADMIN PANEL: Use compileTemplate() to transform JSX → JavaScript
7
+ * 2. DATABASE: Store the compiled JavaScript
8
+ * 3. PRODUCTION API: Use defineTemplateFromSource() to render
9
+ *
10
+ * Benefits:
11
+ * - JSX developer experience in admin panel
12
+ * - No @babel/standalone in production (36 MB smaller!)
13
+ * - Faster cold starts in serverless functions
14
+ * - Syntax errors caught at save time, not render time
15
+ */
16
+
17
+ import { compileTemplate, containsJSX } from 'loopwind/sdk/compiler';
18
+ import { defineTemplateFromSource, renderImage } from 'loopwind/sdk';
19
+ import fs from 'fs/promises';
20
+
21
+ // ============================================================================
22
+ // PART 1: Admin Panel - Compile and Save
23
+ // ============================================================================
24
+
25
+ /**
26
+ * Simulates an admin panel API where users edit templates in a code editor
27
+ */
28
+ async function adminPanelSaveTemplate(templateId: string, userCode: string) {
29
+ console.log('📝 Admin Panel: User saved template\n');
30
+ console.log('User-provided JSX:');
31
+ console.log(userCode.substring(0, 200) + '...\n');
32
+
33
+ try {
34
+ // Check if the code contains JSX
35
+ if (containsJSX(userCode)) {
36
+ console.log('✓ Detected JSX syntax, compiling...');
37
+
38
+ // Compile JSX to JavaScript
39
+ const compiled = compileTemplate(userCode);
40
+
41
+ console.log('✓ Compilation successful!');
42
+ console.log(' Original size:', userCode.length, 'bytes');
43
+ console.log(' Compiled size:', compiled.length, 'bytes\n');
44
+
45
+ // In a real app, you would save to a database:
46
+ // await db.templates.update(templateId, {
47
+ // sourceCode: compiled,
48
+ // updatedAt: new Date()
49
+ // });
50
+
51
+ // For this example, save to a file
52
+ await fs.writeFile(`template-${templateId}-compiled.js`, compiled);
53
+ console.log('✓ Saved compiled template to database\n');
54
+
55
+ return { success: true, compiled };
56
+ } else {
57
+ console.log('ℹ No JSX detected, saving as-is\n');
58
+ return { success: true, compiled: userCode };
59
+ }
60
+ } catch (error) {
61
+ console.error('✗ Compilation failed:', (error as Error).message);
62
+ return {
63
+ success: false,
64
+ error: (error as Error).message,
65
+ };
66
+ }
67
+ }
68
+
69
+ // ============================================================================
70
+ // PART 2: Production API - Load and Render
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Simulates a production API endpoint that renders templates
75
+ * This runs in Vercel, AWS Lambda, or any serverless function
76
+ */
77
+ async function productionApiRenderTemplate(
78
+ templateId: string,
79
+ props: Record<string, any>
80
+ ) {
81
+ console.log('🚀 Production API: Rendering template\n');
82
+
83
+ try {
84
+ // Load pre-compiled template from database
85
+ // In a real app:
86
+ // const template = await db.templates.findById(templateId);
87
+ // const compiledCode = template.sourceCode;
88
+
89
+ // For this example, load from file
90
+ const compiledCode = await fs.readFile(
91
+ `template-${templateId}-compiled.js`,
92
+ 'utf-8'
93
+ );
94
+
95
+ console.log('✓ Loaded pre-compiled template from database');
96
+ console.log(' Size:', compiledCode.length, 'bytes');
97
+ console.log(' No Babel needed! 🎉\n');
98
+
99
+ // Define template from pre-compiled JavaScript
100
+ // This is FAST - no JSX transformation at runtime
101
+ const templateDef = defineTemplateFromSource(compiledCode, {
102
+ config: {
103
+ colors: {
104
+ primary: '#3b82f6',
105
+ secondary: '#8b5cf6',
106
+ },
107
+ },
108
+ });
109
+
110
+ console.log('✓ Template loaded');
111
+ console.log(' Name:', templateDef.name);
112
+ console.log(' Type:', templateDef.type);
113
+ console.log(' Size:', templateDef.size.width, 'x', templateDef.size.height, '\n');
114
+
115
+ // Render the image
116
+ const png = await renderImage(templateDef, props);
117
+
118
+ console.log('✓ Image rendered');
119
+ console.log(' PNG size:', png.length, 'bytes\n');
120
+
121
+ return png;
122
+ } catch (error) {
123
+ console.error('✗ Rendering failed:', (error as Error).message);
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ // ============================================================================
129
+ // DEMO: Full Workflow
130
+ // ============================================================================
131
+
132
+ async function demo() {
133
+ console.log('='.repeat(70));
134
+ console.log('🎨 Template Compiler Workflow Demo');
135
+ console.log('='.repeat(70) + '\n');
136
+
137
+ // Example JSX template that a user might write in a code editor
138
+ const userJSX = `
139
+ export const meta = {
140
+ name: 'social-card',
141
+ type: 'image',
142
+ size: { width: 1200, height: 630 }
143
+ };
144
+
145
+ export default ({ tw, title, description, author }) => (
146
+ <div style={tw('flex flex-col justify-between w-full h-full bg-gradient-to-br from-primary to-secondary p-12')}>
147
+ <div style={tw('flex flex-col')}>
148
+ <h1 style={tw('text-6xl font-bold text-white mb-4')}>
149
+ {title}
150
+ </h1>
151
+ <p style={tw('text-2xl text-white/90')}>
152
+ {description}
153
+ </p>
154
+ </div>
155
+ <div style={tw('flex items-center gap-4')}>
156
+ <div style={tw('w-16 h-16 rounded-full bg-white/20')} />
157
+ <span style={tw('text-xl text-white/80')}>{author}</span>
158
+ </div>
159
+ </div>
160
+ );
161
+ `;
162
+
163
+ const templateId = 'card-123';
164
+
165
+ // STEP 1: Admin panel compiles and saves
166
+ console.log('STEP 1: Admin Panel Workflow');
167
+ console.log('-'.repeat(70) + '\n');
168
+ await adminPanelSaveTemplate(templateId, userJSX);
169
+
170
+ // STEP 2: Production API loads and renders
171
+ console.log('STEP 2: Production API Workflow');
172
+ console.log('-'.repeat(70) + '\n');
173
+ const png = await productionApiRenderTemplate(templateId, {
174
+ title: 'Building with loopwind',
175
+ description: 'Generate images and videos programmatically',
176
+ author: 'Tommy Vedvik',
177
+ });
178
+
179
+ // Save the final result
180
+ await fs.writeFile('social-card-demo.png', png);
181
+ console.log('✅ Saved final image to social-card-demo.png\n');
182
+
183
+ // Cleanup
184
+ await fs.unlink(`template-${templateId}-compiled.js`);
185
+
186
+ console.log('='.repeat(70));
187
+ console.log('📊 Workflow Summary');
188
+ console.log('='.repeat(70));
189
+ console.log('\n✅ Benefits of this approach:\n');
190
+ console.log('1. Users write natural JSX syntax in admin panel');
191
+ console.log('2. JSX compiled once at save time (not every render)');
192
+ console.log('3. Compiled templates stored in database');
193
+ console.log('4. Production bundle 36 MB smaller (no @babel/standalone)');
194
+ console.log('5. Faster cold starts in serverless functions');
195
+ console.log('6. Syntax errors caught early (at save, not production)\n');
196
+
197
+ console.log('🔧 For Vercel/Lambda deployments:\n');
198
+ console.log('- Admin panel imports: loopwind/sdk/compiler');
199
+ console.log('- Production API imports: loopwind/sdk');
200
+ console.log('- Result: Only admin panel includes Babel\n');
201
+ }
202
+
203
+ // ============================================================================
204
+ // Example Integration Code
205
+ // ============================================================================
206
+
207
+ /**
208
+ * Example: Next.js Admin Panel API Route
209
+ */
210
+ async function exampleNextJSAdminRoute(req: any, res: any) {
211
+ const { templateId, code } = req.body;
212
+
213
+ try {
214
+ // Compile JSX
215
+ const compiled = compileTemplate(code);
216
+
217
+ // Save to database
218
+ // await db.templates.update(templateId, { sourceCode: compiled });
219
+
220
+ res.json({ success: true });
221
+ } catch (error) {
222
+ res.status(400).json({
223
+ error: 'Compilation failed',
224
+ message: (error as Error).message,
225
+ });
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Example: Next.js Production API Route
231
+ */
232
+ async function exampleNextJSProductionRoute(req: any, res: any) {
233
+ const { templateId, props } = req.body;
234
+
235
+ try {
236
+ // Load pre-compiled template
237
+ // const template = await db.templates.findById(templateId);
238
+ // const templateDef = defineTemplateFromSource(template.sourceCode);
239
+
240
+ // Render
241
+ // const png = await renderImage(templateDef, props);
242
+
243
+ // res.setHeader('Content-Type', 'image/png');
244
+ // res.send(png);
245
+ } catch (error) {
246
+ res.status(500).json({ error: 'Rendering failed' });
247
+ }
248
+ }
249
+
250
+ // Run the demo
251
+ demo().catch(console.error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopwind",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "description": "A CLI tool for AI code agents and developers for generating images and videos.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,10 @@
15
15
  "./sdk": {
16
16
  "types": "./dist/sdk/index.d.ts",
17
17
  "default": "./dist/sdk/index.js"
18
+ },
19
+ "./sdk/compiler": {
20
+ "types": "./dist/sdk/compiler.d.ts",
21
+ "default": "./dist/sdk/compiler.js"
18
22
  }
19
23
  },
20
24
  "scripts": {
@@ -46,7 +50,6 @@
46
50
  "url": "https://github.com/tomtev/loopwind/issues"
47
51
  },
48
52
  "dependencies": {
49
- "@babel/standalone": "^7.28.5",
50
53
  "@resvg/resvg-js": "^2.6.0",
51
54
  "@vitejs/plugin-react": "^5.1.1",
52
55
  "boxen": "^7.1.1",
@@ -75,6 +78,7 @@
75
78
  "vite": "^7.2.2"
76
79
  },
77
80
  "devDependencies": {
81
+ "@babel/standalone": "^7.28.5",
78
82
  "@types/babel__standalone": "^7.1.9",
79
83
  "@types/chalk-animation": "^1.6.3",
80
84
  "@types/express": "^5.0.5",
@@ -1,10 +1,15 @@
1
1
  /**
2
- * Test JSX support in defineTemplateFromSource
2
+ * Test JSX support with compileTemplate + defineTemplateFromSource
3
+ *
4
+ * This test demonstrates the recommended workflow:
5
+ * 1. compileTemplate() - transforms JSX to JavaScript (admin/build time)
6
+ * 2. defineTemplateFromSource() - loads pre-compiled JavaScript (production)
3
7
  */
4
8
 
5
9
  import { defineTemplateFromSource, renderImage } from './dist/sdk/index.js';
10
+ import { compileTemplate } from './dist/sdk/compiler.js';
6
11
 
7
- console.log('Testing JSX Support in defineTemplateFromSource\n');
12
+ console.log('Testing JSX Support with Pre-compilation\n');
8
13
 
9
14
  // Test 1: Simple JSX template
10
15
  const simpleJSX = `
@@ -22,7 +27,11 @@ export default ({ tw, title }) => (
22
27
  `;
23
28
 
24
29
  try {
25
- const template1 = defineTemplateFromSource(simpleJSX);
30
+ // Step 1: Compile JSX to JavaScript (admin/build time)
31
+ const compiledJS = compileTemplate(simpleJSX);
32
+
33
+ // Step 2: Load compiled template (production runtime)
34
+ const template1 = defineTemplateFromSource(compiledJS);
26
35
  const png1 = await renderImage(template1, { title: 'JSX Works!' });
27
36
  console.log('✓ Simple JSX template works');
28
37
  console.log(' PNG size:', png1.length, 'bytes\n');
@@ -53,7 +62,11 @@ export default ({ tw, title, subtitle, items }) => (
53
62
  `;
54
63
 
55
64
  try {
56
- const template2 = defineTemplateFromSource(nestedJSX);
65
+ // Step 1: Compile JSX to JavaScript
66
+ const compiledJS2 = compileTemplate(nestedJSX);
67
+
68
+ // Step 2: Load compiled template
69
+ const template2 = defineTemplateFromSource(compiledJS2);
57
70
  const png2 = await renderImage(template2, {
58
71
  title: 'Nested JSX',
59
72
  subtitle: 'With multiple levels',
@@ -82,7 +95,11 @@ export default ({ tw }) => (
82
95
  `;
83
96
 
84
97
  try {
85
- const template3 = defineTemplateFromSource(selfClosing);
98
+ // Step 1: Compile JSX to JavaScript
99
+ const compiledJS3 = compileTemplate(selfClosing);
100
+
101
+ // Step 2: Load compiled template
102
+ const template3 = defineTemplateFromSource(compiledJS3);
86
103
  const png3 = await renderImage(template3, {});
87
104
  console.log('✓ Self-closing tags work');
88
105
  console.log(' PNG size:', png3.length, 'bytes\n');
@@ -109,7 +126,11 @@ export default ({ tw, title, count }) => (
109
126
  `;
110
127
 
111
128
  try {
112
- const template4 = defineTemplateFromSource(complexAttrs);
129
+ // Step 1: Compile JSX to JavaScript
130
+ const compiledJS4 = compileTemplate(complexAttrs);
131
+
132
+ // Step 2: Load compiled template
133
+ const template4 = defineTemplateFromSource(compiledJS4);
113
134
  const png4 = await renderImage(template4, { title: 'Complex JSX', count: 42 });
114
135
  console.log('✓ Complex attributes work');
115
136
  console.log(' PNG size:', png4.length, 'bytes\n');
@@ -118,3 +139,8 @@ try {
118
139
  }
119
140
 
120
141
  console.log('✅ All JSX tests completed!');
142
+ console.log('\nWorkflow Summary:');
143
+ console.log('1. compileTemplate() - Transform JSX → JS (admin panel)');
144
+ console.log('2. defineTemplateFromSource() - Load compiled JS (production)');
145
+ console.log('3. renderImage() - Render with props');
146
+ console.log('\nBenefit: No @babel/standalone in production! 🎉');