rip-lang 3.13.83 → 3.13.85

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.83",
3
+ "version": "3.13.85",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/browser.js CHANGED
@@ -26,9 +26,11 @@ const dedent = s => {
26
26
  return s.replace(RegExp(`^[ \t]{${i}}`, 'gm'), '').trim();
27
27
  }
28
28
 
29
- // Browser runtime: collect all <script type="text/rip"> sources (inline + src)
30
- // plus any data-src URLs on the runtime tag, compile them all with shared-scope
31
- // options, and execute as one async IIFE. Then handle data-launch for server mode.
29
+ // Browser runtime: collect all sources (inline scripts, data-src files, bundles),
30
+ // compile them in a shared scope, and execute as one async IIFE.
31
+ //
32
+ // data-src URLs ending in .rip are fetched as individual source files.
33
+ // All other URLs are fetched as JSON bundles containing multiple files.
32
34
  async function processRipScripts() {
33
35
  const sources = [];
34
36
 
@@ -51,99 +53,141 @@ async function processRipScripts() {
51
53
  }
52
54
  }
53
55
 
54
- // Step 3: Fetch externals, compile all, execute in shared scope
56
+ // Step 3: Fetch externals .rip URLs as source text, others as JSON bundles
55
57
  if (sources.length > 0) {
56
- await Promise.all(sources.map(async (s) => {
58
+ const results = await Promise.allSettled(sources.map(async (s) => {
57
59
  if (!s.url) return;
58
- try {
59
- const res = await fetch(s.url);
60
- if (!res.ok) {
61
- console.error(`Rip: failed to fetch ${s.url} (${res.status})`);
62
- return;
63
- }
60
+ const res = await fetch(s.url);
61
+ if (!res.ok) throw new Error(`${s.url} (${res.status})`);
62
+ if (s.url.endsWith('.rip')) {
64
63
  s.code = await res.text();
65
- } catch (e) {
66
- console.error(`Rip: failed to fetch ${s.url}:`, e.message);
64
+ } else {
65
+ const bundle = await res.json();
66
+ s.bundle = bundle;
67
67
  }
68
68
  }));
69
+ for (const r of results) {
70
+ if (r.status === 'rejected') console.warn('Rip: fetch failed:', r.reason.message);
71
+ }
69
72
 
70
- const opts = { skipRuntimes: true, skipExports: true };
71
- const compiled = [];
73
+ // Separate bundles from individual sources
74
+ const bundles = [];
75
+ const individual = [];
72
76
  for (const s of sources) {
73
- if (!s.code) continue;
74
- try {
75
- const js = compileToJS(s.code, opts);
76
- compiled.push({ js, url: s.url || 'inline' });
77
- } catch (e) {
78
- console.error(`Rip compile error in ${s.url || 'inline'}:`, e.message);
79
- }
77
+ if (s.bundle) bundles.push(s.bundle);
78
+ else if (s.code) individual.push(s);
80
79
  }
81
80
 
82
- // Step 3b: Create app stash for data-src mode (skip if data-launch will handle it)
83
- if (!globalThis.__ripApp && runtimeTag && !document.querySelector('script[data-launch]')) {
84
- const stashFn = globalThis.stash;
85
- if (stashFn) {
86
- let initial = {};
87
- const stateAttr = runtimeTag.getAttribute('data-state');
88
- if (stateAttr) {
89
- try { initial = JSON.parse(stateAttr); }
90
- catch (e) { console.error('Rip: invalid data-state JSON:', e.message); }
81
+ const routerAttr = runtimeTag?.getAttribute('data-router');
82
+ const hasRouter = routerAttr != null;
83
+
84
+ // Step 3b: If data-router is present and we have a bundle, use launch()
85
+ // for full routing support. Otherwise compile everything upfront.
86
+ if (hasRouter && bundles.length > 0) {
87
+ // Compile non-bundle sources (inline scripts, individual .rip files)
88
+ const opts = { skipRuntimes: true, skipExports: true };
89
+ if (individual.length > 0) {
90
+ let js = '';
91
+ for (const s of individual) {
92
+ try { js += compileToJS(s.code, opts) + '\n'; }
93
+ catch (e) { console.error(`Rip compile error in ${s.url || 'inline'}:`, e.message); }
94
+ }
95
+ if (js) {
96
+ try { await (0, eval)(`(async()=>{\n${js}\n})()`); }
97
+ catch (e) { console.error('Rip runtime error:', e); }
91
98
  }
92
- const app = stashFn({ data: initial });
93
- globalThis.__ripApp = app;
94
- if (typeof window !== 'undefined') window.app = app;
99
+ }
95
100
 
101
+ // Launch with the last bundle (app bundle) — handles router, renderer, stash
102
+ const ui = importRip.modules?.['ui.rip'];
103
+ if (ui?.launch) {
104
+ const appBundle = bundles[bundles.length - 1];
96
105
  const persistAttr = runtimeTag.getAttribute('data-persist');
97
- if (persistAttr != null && globalThis.persistStash) {
98
- globalThis.persistStash(app, { local: persistAttr === 'local' });
106
+ const launchOpts = { bundle: appBundle, hash: routerAttr === 'hash' };
107
+ if (persistAttr != null) launchOpts.persist = persistAttr === 'local' ? 'local' : true;
108
+ await ui.launch('', launchOpts);
109
+ }
110
+ } else {
111
+ // No routing — expand bundles into individual sources, compile everything
112
+ const expanded = [];
113
+ for (const b of bundles) {
114
+ for (const [name, code] of Object.entries(b.components || {})) {
115
+ expanded.push({ code, url: name });
116
+ }
117
+ if (b.data) {
118
+ const stateAttr = runtimeTag?.getAttribute('data-state');
119
+ let initial = {};
120
+ if (stateAttr) { try { initial = JSON.parse(stateAttr); } catch {} }
121
+ Object.assign(initial, b.data);
122
+ runtimeTag?.setAttribute('data-state', JSON.stringify(initial));
99
123
  }
100
124
  }
101
- }
102
-
103
- if (compiled.length > 0) {
104
- let js = compiled.map(c => c.js).join('\n');
125
+ expanded.push(...individual);
105
126
 
106
- // Step 4: Append data-mount call inside the shared IIFE
107
- const mount = runtimeTag?.getAttribute('data-mount');
108
- if (mount) {
109
- const target = runtimeTag.getAttribute('data-target') || 'body';
110
- js += `\n${mount}.mount(${JSON.stringify(target)});`;
127
+ const opts = { skipRuntimes: true, skipExports: true };
128
+ const compiled = [];
129
+ for (const s of expanded) {
130
+ if (!s.code) continue;
131
+ try {
132
+ const js = compileToJS(s.code, opts);
133
+ compiled.push({ js, url: s.url || 'inline' });
134
+ } catch (e) {
135
+ console.error(`Rip compile error in ${s.url || 'inline'}:`, e.message);
136
+ }
111
137
  }
112
138
 
113
- try {
114
- await (0, eval)(`(async()=>{\n${js}\n})()`);
115
- document.body.classList.add('ready');
116
- } catch (e) {
117
- if (e instanceof SyntaxError) {
118
- console.error(`Rip syntax error in combined output: ${e.message}`);
119
- for (const c of compiled) {
120
- try { new Function(`(async()=>{\n${c.js}\n})()`); }
121
- catch (e2) { console.error(` → source: ${c.url}`, e2.message); }
139
+ // Create app stash
140
+ if (!globalThis.__ripApp && runtimeTag) {
141
+ const stashFn = globalThis.stash;
142
+ if (stashFn) {
143
+ let initial = {};
144
+ const stateAttr = runtimeTag.getAttribute('data-state');
145
+ if (stateAttr) {
146
+ try { initial = JSON.parse(stateAttr); }
147
+ catch (e) { console.error('Rip: invalid data-state JSON:', e.message); }
148
+ }
149
+ const app = stashFn({ data: initial });
150
+ globalThis.__ripApp = app;
151
+ if (typeof window !== 'undefined') window.app = app;
152
+
153
+ const persistAttr = runtimeTag.getAttribute('data-persist');
154
+ if (persistAttr != null && globalThis.persistStash) {
155
+ globalThis.persistStash(app, { local: persistAttr === 'local' });
122
156
  }
123
- } else {
124
- console.error('Rip runtime error:', e);
125
157
  }
126
158
  }
127
- }
128
- }
129
159
 
130
- // Step 5: data-launch triggers launch() for server mode
131
- const cfg = document.querySelector('script[data-launch]');
132
- if (cfg && !globalThis.__ripLaunched) {
133
- const ui = importRip.modules?.['ui.rip'];
134
- if (ui?.launch) {
135
- const url = cfg.getAttribute('data-launch') || '';
136
- const hash = cfg.getAttribute('data-hash');
137
- const persist = cfg.getAttribute('data-persist');
138
- const opts = { hash: hash !== 'false' };
139
- if (url) opts.bundleUrl = url;
140
- if (persist != null) opts.persist = persist === 'local' ? 'local' : true;
141
- await ui.launch('', opts);
160
+ // Execute all compiled code in shared scope
161
+ if (compiled.length > 0) {
162
+ let js = compiled.map(c => c.js).join('\n');
163
+
164
+ const mount = runtimeTag?.getAttribute('data-mount');
165
+ if (mount) {
166
+ const target = runtimeTag.getAttribute('data-target') || 'body';
167
+ js += `\n${mount}.mount(${JSON.stringify(target)});`;
168
+ }
169
+
170
+ try {
171
+ await (0, eval)(`(async()=>{\n${js}\n})()`);
172
+ document.body.classList.add('ready');
173
+ } catch (e) {
174
+ if (e instanceof SyntaxError) {
175
+ console.error(`Rip syntax error in combined output: ${e.message}`);
176
+ for (const c of compiled) {
177
+ try { new Function(`(async()=>{\n${c.js}\n})()`); }
178
+ catch (e2) { console.error(` → source: ${c.url}`, e2.message); }
179
+ }
180
+ } else {
181
+ console.error('Rip runtime error:', e);
182
+ }
183
+ }
184
+ }
142
185
  }
143
186
  }
144
187
 
145
188
  // Step 6: data-reload enables SSE hot-reload from dev server
146
- if (runtimeTag?.hasAttribute('data-reload')) {
189
+ // Skip if launch() was called — it connects its own SSE watch.
190
+ if (runtimeTag?.hasAttribute('data-reload') && !globalThis.__ripLaunched) {
147
191
  let ready = false;
148
192
  const es = new EventSource('/watch');
149
193
  es.addEventListener('connected', () => {
@@ -233,9 +277,8 @@ if (typeof globalThis !== 'undefined') {
233
277
  globalThis.__ripExports = { compile, compileToJS, formatSExpr, getStdlibCode, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
234
278
  }
235
279
 
236
- // Auto-process <script type="text/rip"> blocks and handle data-launch.
237
- // Deferred via queueMicrotask so bundled entry code (e.g. rip.min.js registering
238
- // importRip.modules) runs before script processing begins.
280
+ // Auto-process <script type="text/rip"> blocks and data-src sources.
281
+ // Deferred via queueMicrotask so bundled entry code runs before script processing.
239
282
  if (typeof document !== 'undefined') {
240
283
  globalThis.__ripScriptsReady = new Promise(resolve => {
241
284
  const run = () => processRipScripts().then(resolve);