rip-lang 3.16.0 → 3.16.2
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 +3 -4
- package/bin/rip +201 -18
- package/bin/rip-schema +175 -0
- package/docs/AGENTS.md +1 -1
- package/docs/RIP-APP.md +200 -19
- package/docs/RIP-DUCKDB.md +64 -1
- package/docs/RIP-INTRO.md +4 -4
- package/docs/RIP-LANG.md +36 -38
- package/docs/RIP-SCHEMA.md +1204 -364
- package/docs/RIP-TYPES.md +74 -103
- package/docs/demo/README.md +4 -3
- package/docs/dist/rip.js +4195 -966
- package/docs/dist/rip.min.js +1161 -284
- 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/vscode/print/print-1.0.14.vsix +0 -0
- package/docs/extensions/vscode/print/print-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/index.html +2 -1
- package/docs/extensions/vscode/rip/rip-0.5.15.vsix +0 -0
- package/docs/extensions/vscode/rip/rip-latest.vsix +0 -0
- package/docs/extensions/vscode/rip/vscode-rip-0.6.0.vsix +0 -0
- package/docs/index.html +1 -1
- package/docs/ui/bundle.json +55 -55
- package/docs/ui/bundle.json.br +0 -0
- package/docs/ui/hljs-rip.js +1 -1
- package/docs/ui/index.html +1 -1
- package/package.json +15 -7
- package/rip-loader.js +59 -2
- package/src/AGENTS.md +43 -12
- package/src/browser.js +52 -11
- package/src/compiler.js +538 -80
- package/src/components.js +488 -48
- package/src/dts.js +80 -50
- package/src/grammar/README.md +29 -170
- package/src/grammar/grammar.rip +17 -12
- package/src/grammar/solar.rip +4 -17
- package/src/lexer.js +82 -32
- package/src/parser.js +229 -229
- package/src/schema/dts.js +328 -54
- package/src/schema/loader-server.js +2 -1
- package/src/schema/runtime-browser-stubs.js +20 -9
- package/src/schema/runtime-ddl.js +161 -44
- package/src/schema/runtime-migrate.js +681 -0
- package/src/schema/runtime-orm.js +698 -54
- package/src/schema/runtime-validate.js +808 -24
- package/src/schema/runtime.generated.js +2395 -135
- package/src/schema/schema.js +1054 -94
- package/src/typecheck.js +1610 -127
- package/src/types.js +90 -6
- package/src/grammar/lunar.rip +0 -2412
- /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/docs/ui/bundle.json.br
CHANGED
|
Binary file
|
package/docs/ui/hljs-rip.js
CHANGED
|
@@ -165,7 +165,7 @@ export default function(hljs) {
|
|
|
165
165
|
|
|
166
166
|
const OPERATORS = {
|
|
167
167
|
className: 'operator',
|
|
168
|
-
begin:
|
|
168
|
+
begin: /::|:=|~=|~>|<=>|\.=|=!|!\?|\?!|=~|\?\?=|\?\?|\?\.|\.\.\.|\.\.|=>|->|\*\*|\/\/|%%|===|!==|==|!=|<=|>=|&&|\|\||[+\-*\/%&|^~<>=!?]/,
|
|
169
169
|
relevance: 0,
|
|
170
170
|
};
|
|
171
171
|
|
package/docs/ui/index.html
CHANGED
|
@@ -103,7 +103,7 @@ export WidgetGallery = component
|
|
|
103
103
|
return unless entry
|
|
104
104
|
sourceName = entry.name
|
|
105
105
|
sourceLines = entry.lines
|
|
106
|
-
src = window.__RIP__?.components?.read("
|
|
106
|
+
src = window.__RIP__?.components?.read("_pkg/ui/#{id}.rip")
|
|
107
107
|
return unless src
|
|
108
108
|
sourceCode = src
|
|
109
109
|
_closeSource: -> sourceCode = null
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rip-lang",
|
|
3
|
-
"version": "3.16.
|
|
3
|
+
"version": "3.16.2",
|
|
4
4
|
"description": "A modern language that compiles to JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/compiler.js",
|
|
7
7
|
"workspaces": [
|
|
8
|
+
"examples/*",
|
|
8
9
|
"packages/*"
|
|
9
10
|
],
|
|
11
|
+
"catalog": {
|
|
12
|
+
"typescript": "5.9.3"
|
|
13
|
+
},
|
|
10
14
|
"browser": "docs/dist/rip.min.js",
|
|
11
15
|
"exports": {
|
|
12
16
|
".": {
|
|
@@ -35,8 +39,8 @@
|
|
|
35
39
|
"bump": "bun scripts/bump.js",
|
|
36
40
|
"gen:dom": "bun scripts/gen-dom.js",
|
|
37
41
|
"gallery": "bun scripts/gallery.js",
|
|
38
|
-
"bundle:demo": "bun scripts/bundle-app.js docs/demo -o docs/example/index.json -t 'Rip App Demo'",
|
|
39
|
-
"bundle:ui": "bun scripts/bundle-app.js packages/ui/browser -o docs/ui/bundle.json -t 'Rip UI'",
|
|
42
|
+
"bundle:demo": "bun scripts/bundle-app.js docs/demo/routes --prefix _route --css docs/demo/css -o docs/example/index.json -t 'Rip App Demo'",
|
|
43
|
+
"bundle:ui": "bun scripts/bundle-app.js packages/ui/browser/components --prefix _pkg/ui -o docs/ui/bundle.json -t 'Rip UI'",
|
|
40
44
|
"parser": "bun src/grammar/solar.rip -o src/parser.js src/grammar/grammar.rip",
|
|
41
45
|
"postinstall": "node scripts/postinstall.js --quiet",
|
|
42
46
|
"link-local": "bun scripts/link-local.js",
|
|
@@ -45,14 +49,17 @@
|
|
|
45
49
|
"serve": "bun scripts/serve.js",
|
|
46
50
|
"test": "bun test/runner.js",
|
|
47
51
|
"test:types": "bun test/types/runner.js",
|
|
48
|
-
"test:schema": "bun test/schema/singleton.test.js && bun test/schema/errors.test.js && bun test/schema/modes.test.js && bun test/schema/build.test.js",
|
|
52
|
+
"test:schema": "bun test/schema/singleton.test.js && bun test/schema/errors.test.js && bun test/schema/modes.test.js && bun test/schema/build.test.js && bun test/schema/extract.test.js && bun test/schema/infer.test.js",
|
|
49
53
|
"test:schema-fresh": "bun scripts/build-schema-runtime.js --check",
|
|
54
|
+
"test:docs": "bun scripts/doctest.js",
|
|
50
55
|
"build:schema-runtime": "bun scripts/build-schema-runtime.js",
|
|
51
56
|
"test:bundle": "bun test/bundle.test.js",
|
|
57
|
+
"test:loader": "bun test/loader-reexport.test.js",
|
|
52
58
|
"test:graph": "bun scripts/check-bundle-graph.js",
|
|
53
59
|
"test:server": "./bin/rip packages/server/tests/runner.rip",
|
|
54
60
|
"test:time": "bun run --cwd packages/time test",
|
|
55
|
-
"test:
|
|
61
|
+
"test:validate": "./bin/rip packages/validate/test.rip",
|
|
62
|
+
"test:all": "bun run test && bun run test:types && bun run test:schema && bun run test:schema-fresh && bun run test:docs && bun run test:loader && bun run test:graph && bun run test:bundle && bun run test:server && bun run test:time && bun run test:validate",
|
|
56
63
|
"test:ui": "bun run --cwd packages/ui test:e2e",
|
|
57
64
|
"test:ui:chromium": "bun run --cwd packages/ui test:e2e:chromium",
|
|
58
65
|
"test:ui:axe": "bun run --cwd packages/ui test:e2e:axe",
|
|
@@ -86,7 +93,8 @@
|
|
|
86
93
|
"homepage": "https://github.com/shreeve/rip-lang#readme",
|
|
87
94
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
88
95
|
"license": "MIT",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
96
|
+
"dependencies": {
|
|
97
|
+
"@types/bun": "1.3.14",
|
|
98
|
+
"typescript": "catalog:"
|
|
91
99
|
}
|
|
92
100
|
}
|
package/rip-loader.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { plugin } from "bun";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
|
+
import { dirname, resolve as resolvePath } from "path";
|
|
6
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
7
|
import { compileToJS, formatError } from "./src/compiler.js";
|
|
6
8
|
// Register the full schema runtime provider so .rip files containing
|
|
7
9
|
// `schema :model` blocks compile correctly inside spawned workers.
|
|
@@ -9,11 +11,65 @@ import { compileToJS, formatError } from "./src/compiler.js";
|
|
|
9
11
|
// would call compileToJS without ever registering a provider.
|
|
10
12
|
import "./src/schema/loader-server.js";
|
|
11
13
|
|
|
14
|
+
// ── Undeclared-import diagnostic ────────────────────────────────────────
|
|
15
|
+
// Walk up from an importer to its nearest package.json, then verify that any
|
|
16
|
+
// `@rip-lang/<pkg>` specifier is declared in dependencies/devDependencies/
|
|
17
|
+
// peerDependencies/optionalDependencies (or is the package's own self-import).
|
|
18
|
+
//
|
|
19
|
+
// Throws a clear error before `import.meta.resolve` is even attempted, so
|
|
20
|
+
// "works on my machine" failures rooted in link-global rescue surface loudly
|
|
21
|
+
// instead of silently shipping.
|
|
22
|
+
const declarationCache = new Map(); // importerDir → { pkgName, declared } | null
|
|
23
|
+
|
|
24
|
+
function getDeclarationInfo(importerPath) {
|
|
25
|
+
const start = dirname(importerPath);
|
|
26
|
+
if (declarationCache.has(start)) return declarationCache.get(start);
|
|
27
|
+
let cur = start;
|
|
28
|
+
let info = null;
|
|
29
|
+
while (true) {
|
|
30
|
+
const pkgPath = resolvePath(cur, 'package.json');
|
|
31
|
+
if (existsSync(pkgPath)) {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
34
|
+
info = {
|
|
35
|
+
pkgName: pkg.name || null,
|
|
36
|
+
declared: new Set([
|
|
37
|
+
...Object.keys(pkg.dependencies || {}),
|
|
38
|
+
...Object.keys(pkg.devDependencies || {}),
|
|
39
|
+
...Object.keys(pkg.peerDependencies || {}),
|
|
40
|
+
...Object.keys(pkg.optionalDependencies || {}),
|
|
41
|
+
]),
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
info = { pkgName: null, declared: new Set() };
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
const parent = dirname(cur);
|
|
49
|
+
if (parent === cur) break;
|
|
50
|
+
cur = parent;
|
|
51
|
+
}
|
|
52
|
+
declarationCache.set(start, info);
|
|
53
|
+
return info;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function assertDeclaredRipImport(importerPath, specifier) {
|
|
57
|
+
const m = specifier.match(/^(@rip-lang\/[^\/]+)/);
|
|
58
|
+
if (!m) return;
|
|
59
|
+
const pkgKey = m[1];
|
|
60
|
+
const info = getDeclarationInfo(importerPath);
|
|
61
|
+
if (!info) return; // ad-hoc script outside any package — don't block
|
|
62
|
+
if (info.pkgName === pkgKey) return; // self-import
|
|
63
|
+
if (info.declared.has(pkgKey)) return;
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Import of '${pkgKey}' is not declared in package.json. ` +
|
|
66
|
+
`Run \`bun add ${pkgKey}\` (or use \`workspace:*\` inside this monorepo).`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
12
70
|
await plugin({
|
|
13
71
|
name: "rip-loader",
|
|
14
72
|
async setup(build) {
|
|
15
|
-
const { readFileSync } = await import("fs");
|
|
16
|
-
|
|
17
73
|
// Handle .rip files
|
|
18
74
|
build.onLoad({ filter: /\.rip$/ }, async (args) => {
|
|
19
75
|
try {
|
|
@@ -25,6 +81,7 @@ await plugin({
|
|
|
25
81
|
// is broken in plugin handlers — so we use import.meta.resolve, which
|
|
26
82
|
// resolves from this file's location (inside the global node_modules tree).
|
|
27
83
|
js = js.replace(/(from\s+|import\s*\()(['"])(@rip-lang\/[^'"]+)\2/g, (match, prefix, quote, specifier) => {
|
|
84
|
+
assertDeclaredRipImport(args.path, specifier);
|
|
28
85
|
try {
|
|
29
86
|
return `${prefix}${quote}${fileURLToPath(import.meta.resolve(specifier))}${quote}`;
|
|
30
87
|
} catch {
|
package/src/AGENTS.md
CHANGED
|
@@ -27,6 +27,7 @@ The browser bundle (`docs/dist/rip.min.js`) is built from `src/browser.js` plus
|
|
|
27
27
|
| `src/schema/runtime-db-naming.js` | source for `db-naming` fragment (server + migration) |
|
|
28
28
|
| `src/schema/runtime-orm.js` | source for `orm` fragment (server + migration) |
|
|
29
29
|
| `src/schema/runtime-ddl.js` | source for `ddl` fragment (migration only) |
|
|
30
|
+
| `src/schema/runtime-migrate.js` | source for `migrate` fragment (migration only) — introspection, differ, status/make/migrate |
|
|
30
31
|
| `src/schema/runtime-browser-stubs.js` | source for `browser-stubs` fragment (browser only) |
|
|
31
32
|
| `src/types.js` | yes | only `installTypeSupport(Lexer)` — token-stream type stripper |
|
|
32
33
|
| `src/error.js` | yes | runtime error formatting |
|
|
@@ -40,6 +41,8 @@ The browser bundle (`docs/dist/rip.min.js`) is built from `src/browser.js` plus
|
|
|
40
41
|
|
|
41
42
|
The forbidden list in `scripts/check-bundle-graph.js` enforces this. If a code change would put a forbidden module on the browser graph, `bun run build` aborts before the bundler runs.
|
|
42
43
|
|
|
44
|
+
**Decision record — "core Rip" vs "type-enabled Rip" (June 2026):** this module graph *is* the split. Tier 0 is the erasure core (everything reachable from `src/browser.js` — types stripped, never checked); tier 1 adds `.d.ts` emission (`dts.js`, registered via `setTypesEmitter`); tier 2 adds type checking (`typecheck.js` + the TypeScript service, loaded only by `rip check`). A package-level split (`@rip-lang/core` + a typed variant) was considered and rejected: it would add publishing and versioning friction to express a boundary the import graph and the bundle-graph guard already enforce mechanically. Don't re-propose the package split; if the boundary needs strengthening, extend the forbidden list or route new type-aware behavior through a registration hook instead.
|
|
45
|
+
|
|
43
46
|
### Registration-hook pattern (`setTypesEmitter`, `setSchemaRuntimeProvider`)
|
|
44
47
|
|
|
45
48
|
The same pattern is used twice — once for `.d.ts` emission, once for the schema runtime body. Both make the bundler's tree-shaker keep CLI/server-only code out of the browser bundle.
|
|
@@ -72,7 +75,7 @@ The mode matrix exposed by `getSchemaRuntime({ mode })`:
|
|
|
72
75
|
| `validate` | VALIDATE | isomorphic validate-only contexts |
|
|
73
76
|
| `browser` | VALIDATE + BROWSER_STUBS | the `<script type="text/rip">` runtime |
|
|
74
77
|
| `server` | VALIDATE + DB_NAMING + ORM | `@rip-lang/server` and friends |
|
|
75
|
-
| `migration` | VALIDATE + DB_NAMING + ORM + DDL | CLI / migration tool / tests (default) |
|
|
78
|
+
| `migration` | VALIDATE + DB_NAMING + ORM + DDL + MIGRATE | CLI / migration tool / tests (default) |
|
|
76
79
|
|
|
77
80
|
Edits to `src/schema/runtime-*.js` require running `bun run build:schema-runtime` to regenerate `runtime.generated.js`. CI fails (`bun run test:schema-fresh`) if the generated file is stale.
|
|
78
81
|
|
|
@@ -236,20 +239,44 @@ Complete node reference:
|
|
|
236
239
|
['render', body]
|
|
237
240
|
```
|
|
238
241
|
|
|
242
|
+
## Rewriter Pipeline (canonical order)
|
|
243
|
+
|
|
244
|
+
The single source of truth is `rewrite()` in `src/lexer.js`. Current order, with
|
|
245
|
+
the passes contributed by sidecar files marked:
|
|
246
|
+
|
|
247
|
+
1. `removeLeadingNewlines`
|
|
248
|
+
2. `rewriteDottedPicks` — must run before `rewriteMapLiterals` so pick bodies see raw `{` `}` for depth counting
|
|
249
|
+
3. `rewriteMapLiterals`
|
|
250
|
+
4. `closeMergeAssignments`
|
|
251
|
+
5. `closeOpenCalls`
|
|
252
|
+
6. `closeOpenIndexes`
|
|
253
|
+
7. `rewriteTypes` — installed by `types.js`; must run before `normalizeLines`, otherwise a type-arrow `=>` inside `(...) => T` is treated as a single-liner function and wrapped in spurious INDENT/OUTDENT, derailing the type collector
|
|
254
|
+
8. `normalizeLines`
|
|
255
|
+
9. `rewriteRender?.()` — installed by `components.js` (optional sidecar)
|
|
256
|
+
10. `rewriteSchema?.()` — installed by `schema/schema.js` (optional sidecar)
|
|
257
|
+
11. `tagPostfixConditionals`
|
|
258
|
+
12. `rewriteTaggedTemplates`
|
|
259
|
+
13. `addImplicitBracesAndParens`
|
|
260
|
+
14. `addImplicitCallCommas`
|
|
261
|
+
|
|
262
|
+
When pass order changes in `rewrite()`, update this list — other sections of
|
|
263
|
+
this file describe pass positions relative to their neighbors and assume this
|
|
264
|
+
listing is accurate.
|
|
265
|
+
|
|
239
266
|
## Lexer Token Format
|
|
240
267
|
|
|
241
268
|
Tokens are `[tag, val]` arrays with extra properties:
|
|
242
269
|
|
|
243
270
|
- `.pre` — whitespace count before token
|
|
244
|
-
- `.data` — metadata like `{ await,
|
|
271
|
+
- `.data` — metadata like `{ await, optional, quote, invert, parsedValue }`
|
|
245
272
|
- `.loc` — `{ r, c, n }`
|
|
246
273
|
- `.spaced` — sugar for `.pre > 0`
|
|
247
274
|
- `.newLine` — whether preceded by newline
|
|
248
275
|
|
|
249
276
|
Identifier suffixes:
|
|
250
277
|
|
|
251
|
-
- `!` sets `.data.
|
|
252
|
-
- `?` sets `.data.
|
|
278
|
+
- `!` sets `.data.bang = true` — a neutral "trailing `!`" flag resolved by context downstream: dammit/`await` at a call site (`fetch!` → `await fetch()`), or the void marker at a function definition (`foo! = ->`, `def foo!` → no implicit return). Void-ness is stamped onto the function node as `isVoid` by `applyVoidMarker` and read locally by the arrow emitters; `def` reads `meta(name, 'bang')` directly.
|
|
279
|
+
- `?` sets `.data.optional = true` (existence check on values; optional marker on prop/type-field names)
|
|
253
280
|
- `as!` in loops emits `FORASAWAIT` for `for await`
|
|
254
281
|
|
|
255
282
|
Tagged template bridge:
|
|
@@ -356,7 +383,7 @@ The component system is a compiler sidecar. `installComponentSupport(CodeEmitter
|
|
|
356
383
|
|
|
357
384
|
### Render Rewriter
|
|
358
385
|
|
|
359
|
-
`rewriteRender()` runs after `normalizeLines` and before `
|
|
386
|
+
`rewriteRender()` runs after `normalizeLines` and before `rewriteSchema()` (see "Rewriter Pipeline" above for the full order).
|
|
360
387
|
|
|
361
388
|
Inside `render` blocks it rewrites template syntax into function-call syntax:
|
|
362
389
|
|
|
@@ -415,7 +442,7 @@ Block factories need locals and `ctx.member` references instead of `this._elN` a
|
|
|
415
442
|
- `_factoryVars` — variables that need local `let` declarations
|
|
416
443
|
- `_fragChildren` — fragment-to-children tracking for removals
|
|
417
444
|
- `_pushEffect(body)` — emits `__effect(...)` or `disposers.push(__effect(...))`
|
|
418
|
-
- `_loopVarStack` — threads loop variables through nested factories
|
|
445
|
+
- `_loopVarStack` — threads loop variables through nested factories. Each frame is `{ itemVar, indexVar, reactiveSource }`; `reactiveSource` is computed once when the loop is emitted (via `hasReactiveDeps(collection)`) and tells `hasReactiveDeps` to treat direct member access rooted at `itemVar`/`indexVar` (`item.foo`, `item[0]`, `item.a.b`) as reactive. Alias and destructuring forms are not tracked.
|
|
419
446
|
|
|
420
447
|
Factory mode is entered in `emitConditionBranch` and `emitTemplateLoop` via save/restore of `[_createLines, _setupLines, _factoryMode, _factoryVars]`.
|
|
421
448
|
|
|
@@ -653,7 +680,7 @@ enum Status
|
|
|
653
680
|
Type emission is split across two files by execution context:
|
|
654
681
|
|
|
655
682
|
- `types.js` (browser-side, ~21 KB) — `installTypeSupport(Lexer)` adds `rewriteTypes()` to strip type annotations from the token stream so user-typed Rip parses. This is the only thing the browser needs from type machinery.
|
|
656
|
-
- `dts.js` (CLI/LSP only, ~38 KB) — `emitTypes(tokens, sexpr, source)` generates `.d.ts`, plus `
|
|
683
|
+
- `dts.js` (CLI/LSP only, ~38 KB) — `emitTypes(tokens, sexpr, source, schemaBehavior)` generates `.d.ts`, plus `tsType`, `emitComponentTypes`, and the intrinsic declaration tables (`INTRINSIC_TYPE_DECLS`, `SIGNAL_*`, `COMPUTED_*`, `EFFECT_*`, etc.). Registers itself with the compiler at module load via `setTypesEmitter()`. `schemaBehavior` (the generator's per-compile buffer of compiled `~>`/`!>` bodies, populated only in `inlineTypes`/shadow mode) drives computed/derived return-type inference.
|
|
657
684
|
|
|
658
685
|
`emitEnum` (runtime JS for `enum` blocks) lives in `compiler.js` next to the rest of the codegen dispatch — it's not type machinery, it's real runtime emission.
|
|
659
686
|
|
|
@@ -700,8 +727,10 @@ several files by execution context:
|
|
|
700
727
|
### Lexer path
|
|
701
728
|
|
|
702
729
|
- `installSchemaSupport(Lexer, CodeEmitter)` adds `rewriteSchema()` to the
|
|
703
|
-
Lexer prototype. It runs
|
|
704
|
-
the rewriter pipeline
|
|
730
|
+
Lexer prototype. It runs immediately after `rewriteRender()` and before
|
|
731
|
+
`tagPostfixConditionals()` in the rewriter pipeline (see "Rewriter
|
|
732
|
+
Pipeline" earlier in this file for the full order; note `rewriteTypes()`
|
|
733
|
+
is not adjacent — it runs much earlier, before `normalizeLines()`).
|
|
705
734
|
- `rewriteSchema()` detects a contextual `schema` identifier at expression-
|
|
706
735
|
start positions followed by either a `:kind` SYMBOL or a direct INDENT.
|
|
707
736
|
The matching INDENT...OUTDENT range is parsed by a schema-specific
|
|
@@ -798,9 +827,11 @@ signatures both enforce this.
|
|
|
798
827
|
|
|
799
828
|
### Shadow TS
|
|
800
829
|
|
|
801
|
-
`emitSchemaTypes(sexpr, lines)` walks the parsed s-expression
|
|
802
|
-
schema declarations, emits mixins first so intersections resolve, then
|
|
803
|
-
emits type aliases and `declare const` per kind
|
|
830
|
+
`emitSchemaTypes(sexpr, lines, schemaBehavior)` walks the parsed s-expression
|
|
831
|
+
for named schema declarations, emits mixins first so intersections resolve, then
|
|
832
|
+
emits type aliases and `declare const` per kind (`schemaBehavior`, when set in
|
|
833
|
+
shadow mode, anchors computed/derived members to
|
|
834
|
+
`ReturnType<typeof __<Name>__behavior.field>`):
|
|
804
835
|
|
|
805
836
|
- `:input` → `Schema<ValueType, ValueType>`
|
|
806
837
|
- `:shape` → `Schema<ShapeInstance, ShapeData>` (or `Schema<Data, Data>` when
|
package/src/browser.js
CHANGED
|
@@ -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.
|
|
@@ -127,15 +161,17 @@ async function processRipScripts() {
|
|
|
127
161
|
let lastBundle;
|
|
128
162
|
|
|
129
163
|
// Step 1: Collect data-src URLs from the runtime script tag
|
|
130
|
-
// When data-src is omitted, default to '/app' (auto-scanned bundle from
|
|
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.
|
|
131
167
|
const runtimeTag = document.querySelector('script[src$="rip.min.js"], script[src$="rip.js"]');
|
|
132
168
|
const dataSrc = runtimeTag?.getAttribute('data-src');
|
|
133
169
|
if (dataSrc !== null && dataSrc !== undefined) {
|
|
134
170
|
for (const url of dataSrc.trim().split(/\s+/)) {
|
|
135
171
|
if (url) sources.push({ url });
|
|
136
172
|
}
|
|
137
|
-
} else if (runtimeTag) {
|
|
138
|
-
sources.push({ url: '/app' });
|
|
173
|
+
} else if (runtimeTag && /^https?:$/.test(location.protocol)) {
|
|
174
|
+
sources.push({ url: '/app', optional: true });
|
|
139
175
|
}
|
|
140
176
|
|
|
141
177
|
// Step 2: Collect all <script type="text/rip"> tags (inline and external)
|
|
@@ -161,8 +197,11 @@ async function processRipScripts() {
|
|
|
161
197
|
s.bundle = bundle;
|
|
162
198
|
}
|
|
163
199
|
}));
|
|
164
|
-
for (
|
|
165
|
-
|
|
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
|
+
}
|
|
166
205
|
}
|
|
167
206
|
|
|
168
207
|
// Separate bundles from individual sources
|
|
@@ -209,7 +248,7 @@ async function processRipScripts() {
|
|
|
209
248
|
? { ...baseOpts, sourceMap: 'inline', filename: ripName }
|
|
210
249
|
: baseOpts;
|
|
211
250
|
let js;
|
|
212
|
-
try { js = compileToJS(s.code, opts); }
|
|
251
|
+
try { js = rewriteRipPkgImports(compileToJS(s.code, opts)); }
|
|
213
252
|
catch (e) { console.error(_formatError(e, { source: s.code, file: ripName, color: false })); continue; }
|
|
214
253
|
try { await (0, eval)(debug ? wrapForEval(js, ripName) : `(async()=>{\n${js}\n})()`); }
|
|
215
254
|
catch (e) { console.error(`Rip runtime error in ${ripName}:`, e); }
|
|
@@ -227,7 +266,8 @@ async function processRipScripts() {
|
|
|
227
266
|
// No routing — expand bundles into individual sources, compile everything
|
|
228
267
|
const expanded = [];
|
|
229
268
|
for (const b of bundles) {
|
|
230
|
-
|
|
269
|
+
const mods = b.modules || b.components || {};
|
|
270
|
+
for (const [name, code] of Object.entries(mods)) {
|
|
231
271
|
expanded.push({ code, url: name });
|
|
232
272
|
}
|
|
233
273
|
if (b.data) {
|
|
@@ -247,7 +287,8 @@ async function processRipScripts() {
|
|
|
247
287
|
if (bundles.length > 0 && typeof globalThis.createComponents === 'function') {
|
|
248
288
|
const sourceStore = globalThis.createComponents();
|
|
249
289
|
for (const b of bundles) {
|
|
250
|
-
|
|
290
|
+
const mods = b.modules || b.components;
|
|
291
|
+
if (mods) sourceStore.load(mods);
|
|
251
292
|
}
|
|
252
293
|
if (typeof window !== 'undefined') {
|
|
253
294
|
if (!window.__RIP__) window.__RIP__ = {};
|
|
@@ -280,7 +321,7 @@ async function processRipScripts() {
|
|
|
280
321
|
? { ...baseOpts, sourceMap: 'inline', filename: ripName }
|
|
281
322
|
: baseOpts;
|
|
282
323
|
try {
|
|
283
|
-
const js = compileToJS(s.code, opts);
|
|
324
|
+
const js = rewriteRipPkgImports(compileToJS(s.code, opts));
|
|
284
325
|
compiled.push({ js, url: ripName });
|
|
285
326
|
} catch (e) {
|
|
286
327
|
console.error(_formatError(e, { source: s.code, file: ripName, color: false }));
|
|
@@ -289,7 +330,7 @@ async function processRipScripts() {
|
|
|
289
330
|
|
|
290
331
|
// Create app stash
|
|
291
332
|
if (!globalThis.__ripApp && runtimeTag) {
|
|
292
|
-
const stashFn = globalThis.
|
|
333
|
+
const stashFn = globalThis.createStash;
|
|
293
334
|
if (stashFn) {
|
|
294
335
|
let initial = {};
|
|
295
336
|
const stateAttr = runtimeTag.getAttribute('data-state');
|
|
@@ -414,7 +455,7 @@ export async function importRip(url) {
|
|
|
414
455
|
if (!r.ok) throw new Error(`importRip: ${url} (${r.status})`);
|
|
415
456
|
return r.text();
|
|
416
457
|
});
|
|
417
|
-
const js = compileToJS(source);
|
|
458
|
+
const js = rewriteRipPkgImports(compileToJS(source));
|
|
418
459
|
const header = `// ${url}\n`;
|
|
419
460
|
const blob = new Blob([header + js], { type: 'application/javascript' });
|
|
420
461
|
const blobUrl = URL.createObjectURL(blob);
|