rip-lang 3.15.4 → 3.16.1
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 +6 -4
- package/bin/rip +167 -12
- package/docs/AGENTS.md +1 -1
- package/docs/RIP-APP.md +808 -0
- package/docs/RIP-DUCKDB.md +477 -0
- package/docs/RIP-INTRO.md +396 -0
- package/docs/RIP-LANG.md +59 -5
- package/docs/RIP-SCHEMA.md +191 -8
- package/docs/RIP-TYPES.md +74 -103
- package/docs/demo/README.md +4 -3
- package/docs/dist/rip.js +3627 -1470
- package/docs/dist/rip.min.js +671 -244
- package/docs/dist/rip.min.js.br +0 -0
- package/docs/example/index.json +7 -7
- package/docs/example/index.json.br +0 -0
- package/docs/extensions/duckdb/manifest.json +1 -1
- package/docs/extensions/duckdb/v1.5.2/linux_amd64/ripdb.duckdb_extension.gz +0 -0
- package/docs/extensions/duckdb/v1.5.2/osx_arm64/ripdb.duckdb_extension.gz +0 -0
- package/docs/extensions/vscode/print/index.html +2 -1
- package/docs/extensions/vscode/print/print-1.0.13.vsix +0 -0
- package/docs/extensions/vscode/print/print-1.0.14.vsix +0 -0
- package/docs/extensions/vscode/print/print-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-0.5.15.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-latest.vsix +0 -0
- package/docs/ui/bundle.json +61 -0
- package/docs/ui/bundle.json.br +0 -0
- package/docs/ui/hljs-rip.js +0 -7
- package/docs/ui/index.css +66 -23
- package/docs/ui/index.html +6 -6
- package/package.json +9 -3
- package/rip-loader.js +64 -2
- package/src/AGENTS.md +63 -36
- package/src/browser.js +96 -14
- package/src/compiler.js +960 -143
- package/src/components.js +794 -88
- package/src/{types-emit.js → dts.js} +181 -71
- package/src/grammar/README.md +1 -1
- package/src/grammar/grammar.rip +111 -97
- package/src/lexer.js +132 -18
- package/src/parser.js +203 -205
- package/src/repl.js +74 -6
- package/src/schema/runtime-orm.js +168 -4
- package/src/schema/runtime-validate.js +146 -2
- package/src/schema/runtime.generated.js +314 -6
- package/src/schema/schema.js +5 -5
- package/src/sourcemaps.js +277 -1
- package/src/stdlib.js +253 -0
- package/src/typecheck.js +2023 -106
- package/src/types.js +127 -7
- package/docs/ui/accordion.rip +0 -103
- package/docs/ui/alert-dialog.rip +0 -53
- package/docs/ui/autocomplete.rip +0 -115
- package/docs/ui/avatar.rip +0 -37
- package/docs/ui/badge.rip +0 -15
- package/docs/ui/breadcrumb.rip +0 -47
- package/docs/ui/button-group.rip +0 -26
- package/docs/ui/button.rip +0 -23
- package/docs/ui/card.rip +0 -25
- package/docs/ui/carousel.rip +0 -110
- package/docs/ui/checkbox-group.rip +0 -61
- package/docs/ui/checkbox.rip +0 -33
- package/docs/ui/collapsible.rip +0 -50
- package/docs/ui/combobox.rip +0 -130
- package/docs/ui/context-menu.rip +0 -88
- package/docs/ui/date-picker.rip +0 -206
- package/docs/ui/dialog.rip +0 -60
- package/docs/ui/drawer.rip +0 -58
- package/docs/ui/editable-value.rip +0 -82
- package/docs/ui/field.rip +0 -53
- package/docs/ui/fieldset.rip +0 -22
- package/docs/ui/form.rip +0 -39
- package/docs/ui/grid.rip +0 -901
- package/docs/ui/input-group.rip +0 -28
- package/docs/ui/input.rip +0 -36
- package/docs/ui/label.rip +0 -16
- package/docs/ui/menu.rip +0 -134
- package/docs/ui/menubar.rip +0 -151
- package/docs/ui/meter.rip +0 -36
- package/docs/ui/multi-select.rip +0 -203
- package/docs/ui/native-select.rip +0 -33
- package/docs/ui/nav-menu.rip +0 -126
- package/docs/ui/number-field.rip +0 -162
- package/docs/ui/otp-field.rip +0 -89
- package/docs/ui/pagination.rip +0 -123
- package/docs/ui/popover.rip +0 -93
- package/docs/ui/preview-card.rip +0 -75
- package/docs/ui/progress.rip +0 -25
- package/docs/ui/radio-group.rip +0 -57
- package/docs/ui/resizable.rip +0 -123
- package/docs/ui/scroll-area.rip +0 -145
- package/docs/ui/select.rip +0 -151
- package/docs/ui/separator.rip +0 -17
- package/docs/ui/skeleton.rip +0 -22
- package/docs/ui/slider.rip +0 -165
- package/docs/ui/spinner.rip +0 -17
- package/docs/ui/table.rip +0 -27
- package/docs/ui/tabs.rip +0 -113
- package/docs/ui/textarea.rip +0 -48
- package/docs/ui/toast.rip +0 -87
- package/docs/ui/toggle-group.rip +0 -71
- package/docs/ui/toggle.rip +0 -24
- package/docs/ui/toolbar.rip +0 -38
- package/docs/ui/tooltip.rip +0 -85
- package/src/app.rip +0 -1571
- package/src/sourcemap-merge.js +0 -287
- /package/docs/demo/{components → routes}/_layout.rip +0 -0
- /package/docs/demo/{components → routes}/about.rip +0 -0
- /package/docs/demo/{components → routes}/card.rip +0 -0
- /package/docs/demo/{components → routes}/counter.rip +0 -0
- /package/docs/demo/{components → routes}/index.rip +0 -0
- /package/docs/demo/{components → routes}/todos.rip +0 -0
- /package/src/schema/{dts-emit.js → dts.js} +0 -0
package/src/browser.js
CHANGED
|
@@ -10,7 +10,7 @@ import './schema/loader-browser.js';
|
|
|
10
10
|
export { Lexer } from './lexer.js';
|
|
11
11
|
export { parser } from './parser.js';
|
|
12
12
|
export { CodeEmitter, Compiler, compile, compileToJS, formatSExpr, getStdlibCode, getReactiveRuntime, getComponentRuntime, RipError, formatError, formatErrorHTML } from './compiler.js';
|
|
13
|
-
import { mergeChunksWithInlineMap } from './
|
|
13
|
+
import { mergeChunksWithInlineMap } from './sourcemaps.js';
|
|
14
14
|
import { getStdlibCode, formatError as _formatError } from './compiler.js';
|
|
15
15
|
|
|
16
16
|
// Version info (replaced during build)
|
|
@@ -48,6 +48,40 @@ const dedent = s => {
|
|
|
48
48
|
const sanitizeSourceURL = (url) =>
|
|
49
49
|
String(url).replace(/[\r\n]/g, '').replace(/\s+$/g, '');
|
|
50
50
|
|
|
51
|
+
// Rewrite `import { … } from '@rip-lang/<pkg>'` into a `globalThis`
|
|
52
|
+
// destructure. The browser bundle copies every function export from
|
|
53
|
+
// `@rip-lang/app` (and friends) onto `globalThis` at startup (see
|
|
54
|
+
// `_entry.js` in scripts/build.js), so consumers can import them by name
|
|
55
|
+
// in source while the runtime form is a plain destructure. Named imports
|
|
56
|
+
// only — default / namespace forms warn and pass through.
|
|
57
|
+
function rewriteRipPkgImports(js) {
|
|
58
|
+
const re = /^(\s*)import\s+([^'"]+?)\s+from\s+['"](@rip-lang\/[^'"]+)['"];?\s*$/gm;
|
|
59
|
+
return js.replace(re, (full, indent, clause, spec) => {
|
|
60
|
+
const trimmed = clause.trim();
|
|
61
|
+
if (trimmed.startsWith('type ')) return `${indent}// type-only import erased: ${spec}`;
|
|
62
|
+
const open = trimmed.indexOf('{');
|
|
63
|
+
const close = trimmed.lastIndexOf('}');
|
|
64
|
+
if (open < 0 || close <= open) {
|
|
65
|
+
console.warn(`[Rip] Skipping non-named import from ${spec}; only \`import { … } from '@rip-lang/*'\` is supported in browser bundles.`);
|
|
66
|
+
return full;
|
|
67
|
+
}
|
|
68
|
+
const inside = trimmed.slice(open + 1, close);
|
|
69
|
+
const parts = [];
|
|
70
|
+
for (let raw of inside.split(',')) {
|
|
71
|
+
let name = raw.trim().replace(/^type\s+/, '');
|
|
72
|
+
if (!name) continue;
|
|
73
|
+
if (/\s+as\s+/.test(name)) {
|
|
74
|
+
const [orig, alias] = name.split(/\s+as\s+/).map(s => s.trim());
|
|
75
|
+
parts.push(`${orig}: ${alias}`);
|
|
76
|
+
} else {
|
|
77
|
+
parts.push(name);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (parts.length === 0) return `${indent}// import erased: ${spec}`;
|
|
81
|
+
return `${indent}const { ${parts.join(', ')} } = globalThis;`;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
51
85
|
// Insert `//# sourceURL=<name>` BEFORE the existing `//# sourceMappingURL=...`
|
|
52
86
|
// comment (or append at end if none). NEVER prepend — that would shift every
|
|
53
87
|
// generated-line mapping by 1 line, breaking line-only source maps.
|
|
@@ -124,14 +158,20 @@ if (typeof globalThis !== 'undefined') {
|
|
|
124
158
|
// All other URLs are fetched as JSON bundles containing multiple files.
|
|
125
159
|
async function processRipScripts() {
|
|
126
160
|
const sources = [];
|
|
161
|
+
let lastBundle;
|
|
127
162
|
|
|
128
163
|
// Step 1: Collect data-src URLs from the runtime script tag
|
|
164
|
+
// When data-src is omitted, default to '/app' (auto-scanned bundle from
|
|
165
|
+
// serve middleware). The default is silent on failure — only explicit
|
|
166
|
+
// data-src URLs warn — so static / file:// pages aren't noisy.
|
|
129
167
|
const runtimeTag = document.querySelector('script[src$="rip.min.js"], script[src$="rip.js"]');
|
|
130
168
|
const dataSrc = runtimeTag?.getAttribute('data-src');
|
|
131
|
-
if (dataSrc) {
|
|
169
|
+
if (dataSrc !== null && dataSrc !== undefined) {
|
|
132
170
|
for (const url of dataSrc.trim().split(/\s+/)) {
|
|
133
171
|
if (url) sources.push({ url });
|
|
134
172
|
}
|
|
173
|
+
} else if (runtimeTag && /^https?:$/.test(location.protocol)) {
|
|
174
|
+
sources.push({ url: '/app', optional: true });
|
|
135
175
|
}
|
|
136
176
|
|
|
137
177
|
// Step 2: Collect all <script type="text/rip"> tags (inline and external)
|
|
@@ -157,8 +197,11 @@ async function processRipScripts() {
|
|
|
157
197
|
s.bundle = bundle;
|
|
158
198
|
}
|
|
159
199
|
}));
|
|
160
|
-
for (
|
|
161
|
-
|
|
200
|
+
for (let i = 0; i < results.length; i++) {
|
|
201
|
+
const r = results[i];
|
|
202
|
+
if (r.status === 'rejected' && !sources[i].optional) {
|
|
203
|
+
console.warn('Rip: fetch failed:', r.reason.message);
|
|
204
|
+
}
|
|
162
205
|
}
|
|
163
206
|
|
|
164
207
|
// Separate bundles from individual sources
|
|
@@ -170,7 +213,21 @@ async function processRipScripts() {
|
|
|
170
213
|
}
|
|
171
214
|
|
|
172
215
|
const routerAttr = runtimeTag?.getAttribute('data-router');
|
|
173
|
-
|
|
216
|
+
lastBundle = bundles[bundles.length - 1];
|
|
217
|
+
// data-router auto-infers from bundle.data.router (set by the server when
|
|
218
|
+
// the app has a routes/ directory). Explicit attribute always wins:
|
|
219
|
+
// data-router → enabled
|
|
220
|
+
// data-router="hash" → hash routing
|
|
221
|
+
// data-router="false" → explicitly disabled
|
|
222
|
+
// (omitted) → fall back to bundle.data.router
|
|
223
|
+
let hasRouter, hashRouter;
|
|
224
|
+
if (routerAttr != null) {
|
|
225
|
+
hasRouter = routerAttr !== 'false';
|
|
226
|
+
hashRouter = routerAttr === 'hash';
|
|
227
|
+
} else {
|
|
228
|
+
hasRouter = !!(lastBundle?.data?.router);
|
|
229
|
+
hashRouter = false;
|
|
230
|
+
}
|
|
174
231
|
|
|
175
232
|
// Step 3b: If data-router is present and we have a bundle, use launch()
|
|
176
233
|
// for full routing support. Otherwise compile everything upfront.
|
|
@@ -191,7 +248,7 @@ async function processRipScripts() {
|
|
|
191
248
|
? { ...baseOpts, sourceMap: 'inline', filename: ripName }
|
|
192
249
|
: baseOpts;
|
|
193
250
|
let js;
|
|
194
|
-
try { js = compileToJS(s.code, opts); }
|
|
251
|
+
try { js = rewriteRipPkgImports(compileToJS(s.code, opts)); }
|
|
195
252
|
catch (e) { console.error(_formatError(e, { source: s.code, file: ripName, color: false })); continue; }
|
|
196
253
|
try { await (0, eval)(debug ? wrapForEval(js, ripName) : `(async()=>{\n${js}\n})()`); }
|
|
197
254
|
catch (e) { console.error(`Rip runtime error in ${ripName}:`, e); }
|
|
@@ -200,17 +257,17 @@ async function processRipScripts() {
|
|
|
200
257
|
// Launch with the last bundle (app bundle) — handles router, renderer, stash
|
|
201
258
|
const app = importRip.modules?.['app.rip'];
|
|
202
259
|
if (app?.launch) {
|
|
203
|
-
const appBundle = bundles[bundles.length - 1];
|
|
204
260
|
const persistAttr = runtimeTag.getAttribute('data-persist');
|
|
205
|
-
const launchOpts = { bundle:
|
|
261
|
+
const launchOpts = { bundle: lastBundle, hash: hashRouter };
|
|
206
262
|
if (persistAttr != null) launchOpts.persist = persistAttr === 'local' ? 'local' : true;
|
|
207
|
-
await app.launch(
|
|
263
|
+
await app.launch(launchOpts);
|
|
208
264
|
}
|
|
209
265
|
} else {
|
|
210
266
|
// No routing — expand bundles into individual sources, compile everything
|
|
211
267
|
const expanded = [];
|
|
212
268
|
for (const b of bundles) {
|
|
213
|
-
|
|
269
|
+
const mods = b.modules || b.components || {};
|
|
270
|
+
for (const [name, code] of Object.entries(mods)) {
|
|
214
271
|
expanded.push({ code, url: name });
|
|
215
272
|
}
|
|
216
273
|
if (b.data) {
|
|
@@ -223,6 +280,22 @@ async function processRipScripts() {
|
|
|
223
280
|
}
|
|
224
281
|
expanded.push(...individual);
|
|
225
282
|
|
|
283
|
+
// Expose the components-source store on window.__RIP__ so source-text
|
|
284
|
+
// access (e.g. view-source UIs) works the same on the no-router path
|
|
285
|
+
// as it does after launch(). Mirrors what launch() sets up — minus
|
|
286
|
+
// app/router/renderer, since those don't exist on this path.
|
|
287
|
+
if (bundles.length > 0 && typeof globalThis.createComponents === 'function') {
|
|
288
|
+
const sourceStore = globalThis.createComponents();
|
|
289
|
+
for (const b of bundles) {
|
|
290
|
+
const mods = b.modules || b.components;
|
|
291
|
+
if (mods) sourceStore.load(mods);
|
|
292
|
+
}
|
|
293
|
+
if (typeof window !== 'undefined') {
|
|
294
|
+
if (!window.__RIP__) window.__RIP__ = {};
|
|
295
|
+
window.__RIP__.components = sourceStore;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
226
299
|
// Bundle / multi-source path. Components defined in one .rip file
|
|
227
300
|
// need to be visible to siblings (e.g. `WidgetGallery` referencing
|
|
228
301
|
// `Toast`, `Dialog`, `Menu`). To make that work we concatenate
|
|
@@ -248,7 +321,7 @@ async function processRipScripts() {
|
|
|
248
321
|
? { ...baseOpts, sourceMap: 'inline', filename: ripName }
|
|
249
322
|
: baseOpts;
|
|
250
323
|
try {
|
|
251
|
-
const js = compileToJS(s.code, opts);
|
|
324
|
+
const js = rewriteRipPkgImports(compileToJS(s.code, opts));
|
|
252
325
|
compiled.push({ js, url: ripName });
|
|
253
326
|
} catch (e) {
|
|
254
327
|
console.error(_formatError(e, { source: s.code, file: ripName, color: false }));
|
|
@@ -257,7 +330,7 @@ async function processRipScripts() {
|
|
|
257
330
|
|
|
258
331
|
// Create app stash
|
|
259
332
|
if (!globalThis.__ripApp && runtimeTag) {
|
|
260
|
-
const stashFn = globalThis.
|
|
333
|
+
const stashFn = globalThis.createStash;
|
|
261
334
|
if (stashFn) {
|
|
262
335
|
let initial = {};
|
|
263
336
|
const stateAttr = runtimeTag.getAttribute('data-state');
|
|
@@ -315,10 +388,19 @@ async function processRipScripts() {
|
|
|
315
388
|
|
|
316
389
|
// Step 6: data-reload enables SSE hot-reload from dev server
|
|
317
390
|
// Skip if launch() was called — it connects its own SSE watch.
|
|
391
|
+
// Auto-infers from bundle.data.watch (set by the server when --watch is on).
|
|
392
|
+
// Explicit attribute always wins:
|
|
393
|
+
// data-reload → enabled
|
|
394
|
+
// data-reload="false" → explicitly disabled
|
|
395
|
+
// (omitted) → fall back to bundle.data.watch
|
|
318
396
|
// Uses exponential backoff: 1s → 2s → 4s → … → 30s (then 30s forever).
|
|
319
397
|
// The retry delay only affects reconnection to a DOWN server — once connected,
|
|
320
398
|
// the server pushes reload notifications instantly regardless of this value.
|
|
321
|
-
|
|
399
|
+
const reloadAttr = runtimeTag?.getAttribute('data-reload');
|
|
400
|
+
const shouldReload = reloadAttr != null
|
|
401
|
+
? reloadAttr !== 'false'
|
|
402
|
+
: !!(lastBundle?.data?.watch);
|
|
403
|
+
if (shouldReload && !globalThis.__ripLaunched) {
|
|
322
404
|
let ready = false;
|
|
323
405
|
let retryDelay = 1000;
|
|
324
406
|
const maxDelay = 30000;
|
|
@@ -373,7 +455,7 @@ export async function importRip(url) {
|
|
|
373
455
|
if (!r.ok) throw new Error(`importRip: ${url} (${r.status})`);
|
|
374
456
|
return r.text();
|
|
375
457
|
});
|
|
376
|
-
const js = compileToJS(source);
|
|
458
|
+
const js = rewriteRipPkgImports(compileToJS(source));
|
|
377
459
|
const header = `// ${url}\n`;
|
|
378
460
|
const blob = new Blob([header + js], { type: 'application/javascript' });
|
|
379
461
|
const blobUrl = URL.createObjectURL(blob);
|