rip-lang 2.5.1 → 2.7.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/CHANGELOG.md +33 -10
- package/README.md +133 -289
- package/docs/BROWSER.md +8 -11
- package/docs/GUIDE.md +101 -923
- package/docs/INTERNALS.md +1 -1
- package/docs/PHILOSOPHY.md +22 -77
- package/docs/REACTIVITY.md +288 -0
- package/docs/WHY-YES-RIP.md +39 -177
- package/docs/dist/rip.browser.js +605 -2453
- package/docs/dist/rip.browser.min.js +283 -359
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/repl.html +94 -437
- package/package.json +4 -1
- package/scripts/serve.js +2 -0
- package/src/compiler.js +73 -2160
- package/src/grammar/grammar.rip +22 -57
- package/src/lexer.js +11 -333
- package/src/parser.js +220 -223
- package/src/repl.js +202 -128
- package/src/tags.js +0 -62
package/src/repl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Rip REPL - Interactive Read-Eval-Print Loop
|
|
2
|
+
* Rip REPL - Interactive Read-Eval-Print Loop (Bun-native version)
|
|
3
3
|
*
|
|
4
4
|
* Features:
|
|
5
5
|
* - Multi-line input detection
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
* - Special commands (.help, .clear, .vars, etc.)
|
|
8
8
|
* - Colored output
|
|
9
9
|
* - Persistent context
|
|
10
|
+
* - Full dynamic import() support (Bun-native)
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import * as readline from 'readline';
|
|
13
14
|
import { inspect } from 'util';
|
|
14
|
-
import * as vm from 'vm';
|
|
15
15
|
import * as fs from 'fs';
|
|
16
16
|
import * as path from 'path';
|
|
17
17
|
import * as os from 'os';
|
|
18
|
+
import { pathToFileURL } from 'url';
|
|
18
19
|
import { Compiler } from './compiler.js';
|
|
19
20
|
import packageJson from '../package.json' with { type: 'json' };
|
|
20
21
|
|
|
@@ -34,6 +35,13 @@ const colors = {
|
|
|
34
35
|
gray: '\x1b[90m'
|
|
35
36
|
};
|
|
36
37
|
|
|
38
|
+
// Use globalThis for persistent context (works across async evaluations)
|
|
39
|
+
globalThis.__ripRepl = globalThis.__ripRepl || {
|
|
40
|
+
vars: {},
|
|
41
|
+
tempCounter: 0,
|
|
42
|
+
cwd: process.cwd()
|
|
43
|
+
};
|
|
44
|
+
|
|
37
45
|
export class RipREPL {
|
|
38
46
|
constructor() {
|
|
39
47
|
this.buffer = ''; // Multi-line input buffer
|
|
@@ -41,19 +49,7 @@ export class RipREPL {
|
|
|
41
49
|
this.historyFile = path.join(os.homedir(), '.rip_history');
|
|
42
50
|
this.reactiveVars = new Set(); // Track reactive variables across lines
|
|
43
51
|
|
|
44
|
-
//
|
|
45
|
-
this.context = vm.createContext({
|
|
46
|
-
console,
|
|
47
|
-
process,
|
|
48
|
-
Buffer,
|
|
49
|
-
setTimeout,
|
|
50
|
-
setInterval,
|
|
51
|
-
clearTimeout,
|
|
52
|
-
clearInterval,
|
|
53
|
-
// Add any other globals you want available
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Inject reactive runtime once into context (so all reactive code shares same tracking)
|
|
52
|
+
// Inject reactive runtime into global context
|
|
57
53
|
this.injectReactiveRuntime();
|
|
58
54
|
|
|
59
55
|
this.rl = readline.createInterface({
|
|
@@ -68,6 +64,14 @@ export class RipREPL {
|
|
|
68
64
|
this.loadHistory();
|
|
69
65
|
}
|
|
70
66
|
|
|
67
|
+
get context() {
|
|
68
|
+
return globalThis.__ripRepl.vars;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
set context(val) {
|
|
72
|
+
globalThis.__ripRepl.vars = val;
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
getPrompt() {
|
|
72
76
|
if (this.buffer) {
|
|
73
77
|
return `${colors.dim}....>${colors.reset} `; // Continuation prompt
|
|
@@ -78,11 +82,12 @@ export class RipREPL {
|
|
|
78
82
|
start() {
|
|
79
83
|
this.printWelcome();
|
|
80
84
|
|
|
81
|
-
this.rl.on('line', (line) => {
|
|
82
|
-
this.handleLine(line);
|
|
85
|
+
this.rl.on('line', async (line) => {
|
|
86
|
+
await this.handleLine(line);
|
|
83
87
|
});
|
|
84
88
|
|
|
85
89
|
this.rl.on('close', () => {
|
|
90
|
+
this.cleanup();
|
|
86
91
|
this.saveHistory();
|
|
87
92
|
console.log(`\n${colors.gray}Goodbye!${colors.reset}`);
|
|
88
93
|
process.exit(0);
|
|
@@ -92,89 +97,95 @@ export class RipREPL {
|
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
printWelcome() {
|
|
95
|
-
console.log(`${colors.bright}Rip ${VERSION}${colors.reset} - Interactive REPL`);
|
|
100
|
+
console.log(`${colors.bright}Rip ${VERSION}${colors.reset} - Interactive REPL ${colors.dim}(Bun-native)${colors.reset}`);
|
|
96
101
|
console.log(`${colors.gray}Type ${colors.cyan}.help${colors.gray} for commands, ${colors.cyan}Ctrl+C${colors.gray} to exit${colors.reset}\n`);
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
injectReactiveRuntime() {
|
|
100
|
-
// Inject reactive runtime into
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
105
|
+
// Inject reactive runtime into global context once
|
|
106
|
+
if (globalThis.__ripRepl.runtimeInjected) return;
|
|
107
|
+
|
|
108
|
+
// Define reactive primitives on globalThis so they're available in all evaluations
|
|
109
|
+
globalThis.__currentEffect = null;
|
|
110
|
+
globalThis.__pendingEffects = new Set();
|
|
111
|
+
|
|
112
|
+
globalThis.__state = function(v) {
|
|
113
|
+
const subs = new Set();
|
|
114
|
+
let notifying = false, locked = false, dead = false;
|
|
115
|
+
const s = {
|
|
116
|
+
get value() { if (dead) return v; if (__currentEffect) { subs.add(__currentEffect); __currentEffect.dependencies.add(subs); } return v; },
|
|
117
|
+
set value(n) {
|
|
118
|
+
if (dead || locked || n === v || notifying) return;
|
|
119
|
+
v = n;
|
|
120
|
+
notifying = true;
|
|
121
|
+
for (const sub of subs) if (sub.markDirty) sub.markDirty();
|
|
122
|
+
for (const sub of subs) if (!sub.markDirty) __pendingEffects.add(sub);
|
|
123
|
+
const fx = [...__pendingEffects]; __pendingEffects.clear();
|
|
124
|
+
for (const e of fx) e.run();
|
|
125
|
+
notifying = false;
|
|
126
|
+
},
|
|
127
|
+
read() { return v; },
|
|
128
|
+
lock() { locked = true; return s; },
|
|
129
|
+
free() { subs.clear(); return s; },
|
|
130
|
+
kill() { dead = true; subs.clear(); return v; },
|
|
131
|
+
valueOf() { return this.value; },
|
|
132
|
+
toString() { return String(this.value); },
|
|
133
|
+
[Symbol.toPrimitive](hint) { return hint === 'string' ? this.toString() : this.valueOf(); }
|
|
134
|
+
};
|
|
135
|
+
return s;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
globalThis.__computed = function(fn) {
|
|
139
|
+
let v, dirty = true, locked = false, dead = false;
|
|
140
|
+
const subs = new Set();
|
|
141
|
+
const c = {
|
|
142
|
+
dependencies: new Set(),
|
|
143
|
+
markDirty() {
|
|
144
|
+
if (dead || locked || !dirty) { if (!dead && !locked && !dirty) { dirty = true; for (const s of subs) if (s.markDirty) s.markDirty(); for (const s of subs) if (!s.markDirty) __pendingEffects.add(s); } }
|
|
145
|
+
},
|
|
146
|
+
get value() {
|
|
147
|
+
if (dead) return v;
|
|
148
|
+
if (__currentEffect) { subs.add(__currentEffect); __currentEffect.dependencies.add(subs); }
|
|
149
|
+
if (dirty && !locked) {
|
|
150
|
+
for (const d of c.dependencies) d.delete(c); c.dependencies.clear();
|
|
151
|
+
const prev = __currentEffect; __currentEffect = c;
|
|
152
|
+
try { v = fn(); } finally { __currentEffect = prev; }
|
|
153
|
+
dirty = false;
|
|
154
|
+
}
|
|
155
|
+
return v;
|
|
156
|
+
},
|
|
157
|
+
read() { return dead ? v : c.value; },
|
|
158
|
+
lock() { locked = true; c.value; return c; },
|
|
159
|
+
free() { for (const d of c.dependencies) d.delete(c); c.dependencies.clear(); subs.clear(); return c; },
|
|
160
|
+
kill() { dead = true; const result = v; c.free(); return result; },
|
|
161
|
+
valueOf() { return this.value; },
|
|
162
|
+
toString() { return String(this.value); },
|
|
163
|
+
[Symbol.toPrimitive](hint) { return hint === 'string' ? this.toString() : this.valueOf(); }
|
|
164
|
+
};
|
|
165
|
+
return c;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
globalThis.__effect = function(fn) {
|
|
169
|
+
const e = {
|
|
170
|
+
dependencies: new Set(),
|
|
171
|
+
run() {
|
|
172
|
+
for (const d of e.dependencies) d.delete(e); e.dependencies.clear();
|
|
173
|
+
const prev = __currentEffect; __currentEffect = e;
|
|
174
|
+
try { fn(); } finally { __currentEffect = prev; }
|
|
175
|
+
},
|
|
176
|
+
free() { for (const d of e.dependencies) d.delete(e); e.dependencies.clear(); }
|
|
177
|
+
};
|
|
178
|
+
e.run();
|
|
179
|
+
return () => e.free();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
globalThis.__batch = function(fn) { fn(); };
|
|
183
|
+
globalThis.__readonly = function(v) { return Object.freeze({ value: v }); };
|
|
184
|
+
|
|
185
|
+
globalThis.__ripRepl.runtimeInjected = true;
|
|
175
186
|
}
|
|
176
187
|
|
|
177
|
-
handleLine(line) {
|
|
188
|
+
async handleLine(line) {
|
|
178
189
|
// Handle special commands
|
|
179
190
|
if (line.startsWith('.')) {
|
|
180
191
|
this.handleCommand(line);
|
|
@@ -192,7 +203,7 @@ function __readonly(v) { return Object.freeze({ value: v }); }
|
|
|
192
203
|
|
|
193
204
|
// Check if input is complete
|
|
194
205
|
if (this.isComplete(this.buffer)) {
|
|
195
|
-
this.evaluate(this.buffer);
|
|
206
|
+
await this.evaluate(this.buffer);
|
|
196
207
|
this.buffer = '';
|
|
197
208
|
}
|
|
198
209
|
|
|
@@ -289,14 +300,12 @@ function __readonly(v) { return Object.freeze({ value: v }); }
|
|
|
289
300
|
return true;
|
|
290
301
|
}
|
|
291
302
|
|
|
292
|
-
evaluate(code) {
|
|
303
|
+
async evaluate(code) {
|
|
293
304
|
try {
|
|
294
305
|
// Add to history
|
|
295
306
|
this.history.push(code);
|
|
296
307
|
|
|
297
|
-
// Compile Rip to JavaScript
|
|
298
|
-
// skipReactiveRuntime: runtime is already injected into context
|
|
299
|
-
// reactiveVars: track reactive variables across REPL lines
|
|
308
|
+
// Compile Rip to JavaScript
|
|
300
309
|
const compiler = new Compiler({
|
|
301
310
|
showTokens: this.showTokens,
|
|
302
311
|
showSExpr: this.showSExp,
|
|
@@ -312,7 +321,7 @@ function __readonly(v) { return Object.freeze({ value: v }); }
|
|
|
312
321
|
}
|
|
313
322
|
}
|
|
314
323
|
|
|
315
|
-
|
|
324
|
+
let js = result.code;
|
|
316
325
|
|
|
317
326
|
// Show compiled JS if enabled
|
|
318
327
|
if (this.showJS) {
|
|
@@ -320,18 +329,8 @@ function __readonly(v) { return Object.freeze({ value: v }); }
|
|
|
320
329
|
console.log(`${colors.dim}${js}${colors.reset}\n`);
|
|
321
330
|
}
|
|
322
331
|
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
let processedJs = js;
|
|
326
|
-
|
|
327
|
-
// Replace 'let x, y, z;' with 'var x, y, z;'
|
|
328
|
-
processedJs = processedJs.replace(/^let\s+/m, 'var ');
|
|
329
|
-
|
|
330
|
-
// Replace 'const x =' with 'var x ='
|
|
331
|
-
processedJs = processedJs.replace(/^const\s+/gm, 'var ');
|
|
332
|
-
|
|
333
|
-
// Evaluate in persistent context
|
|
334
|
-
const evalResult = vm.runInContext(processedJs, this.context);
|
|
332
|
+
// Execute using Bun's native async evaluation with import support
|
|
333
|
+
const evalResult = await this.bunEval(js);
|
|
335
334
|
|
|
336
335
|
// Store result in _ for convenience
|
|
337
336
|
if (evalResult !== undefined) {
|
|
@@ -343,6 +342,92 @@ function __readonly(v) { return Object.freeze({ value: v }); }
|
|
|
343
342
|
}
|
|
344
343
|
}
|
|
345
344
|
|
|
345
|
+
async bunEval(js) {
|
|
346
|
+
// Extract variable declarations from the compiled JS
|
|
347
|
+
const varMatches = [...js.matchAll(/^let\s+(\w+)(?:,\s*(\w+))*;$/gm)];
|
|
348
|
+
const declaredVars = new Set();
|
|
349
|
+
for (const match of varMatches) {
|
|
350
|
+
for (let i = 1; i < match.length; i++) {
|
|
351
|
+
if (match[i]) declaredVars.add(match[i]);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Also check for let x = ... pattern
|
|
356
|
+
const assignMatches = [...js.matchAll(/^let\s+(\w+)\s*=/gm)];
|
|
357
|
+
for (const match of assignMatches) {
|
|
358
|
+
declaredVars.add(match[1]);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Build code that restores context
|
|
362
|
+
const ctx = this.context;
|
|
363
|
+
const existingVars = Object.keys(ctx);
|
|
364
|
+
|
|
365
|
+
// Remove let declarations for vars we're restoring from context
|
|
366
|
+
let cleanJs = js;
|
|
367
|
+
for (const v of existingVars) {
|
|
368
|
+
cleanJs = cleanJs.replace(new RegExp(`^let ${v};\\n`, 'm'), '');
|
|
369
|
+
cleanJs = cleanJs.replace(new RegExp(`^let ${v}(\\s*=)`, 'm'), `${v}$1`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Restore existing variables
|
|
373
|
+
const restoreCtx = existingVars
|
|
374
|
+
.filter(k => k !== '_')
|
|
375
|
+
.map(k => `let ${k} = globalThis.__ripRepl.vars["${k}"];`)
|
|
376
|
+
.join('\n');
|
|
377
|
+
|
|
378
|
+
// Find the last expression to capture as result
|
|
379
|
+
const lines = cleanJs.trim().split('\n');
|
|
380
|
+
let lastLine = lines[lines.length - 1];
|
|
381
|
+
|
|
382
|
+
// If it's a simple expression (ends with ;), capture it
|
|
383
|
+
if (lastLine && lastLine.endsWith(';') &&
|
|
384
|
+
!lastLine.startsWith('let ') &&
|
|
385
|
+
!lastLine.startsWith('const ') &&
|
|
386
|
+
!lastLine.startsWith('var ') &&
|
|
387
|
+
!lastLine.includes('import ') &&
|
|
388
|
+
!lastLine.includes('export ')) {
|
|
389
|
+
lines[lines.length - 1] = `globalThis.__ripRepl_result = ${lastLine.slice(0, -1)};`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Save variables back to context
|
|
393
|
+
const allVars = [...new Set([...existingVars, ...declaredVars])].filter(k => k !== '_');
|
|
394
|
+
const saveCtx = allVars.map(v =>
|
|
395
|
+
`if (typeof ${v} !== 'undefined') globalThis.__ripRepl.vars["${v}"] = ${v};`
|
|
396
|
+
).join('\n');
|
|
397
|
+
|
|
398
|
+
const wrapped = `
|
|
399
|
+
${restoreCtx}
|
|
400
|
+
${lines.join('\n')}
|
|
401
|
+
${saveCtx}
|
|
402
|
+
`;
|
|
403
|
+
|
|
404
|
+
// Write to temp file and import it (enables dynamic imports)
|
|
405
|
+
const tempFile = path.join(globalThis.__ripRepl.cwd, `.rip-repl-${globalThis.__ripRepl.tempCounter++}.mjs`);
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
fs.writeFileSync(tempFile, wrapped);
|
|
409
|
+
await import(pathToFileURL(tempFile).href + `?t=${Date.now()}`);
|
|
410
|
+
const result = globalThis.__ripRepl_result;
|
|
411
|
+
delete globalThis.__ripRepl_result;
|
|
412
|
+
return result;
|
|
413
|
+
} finally {
|
|
414
|
+
try { fs.unlinkSync(tempFile); } catch {}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
cleanup() {
|
|
419
|
+
// Clean up any leftover temp files
|
|
420
|
+
const cwd = globalThis.__ripRepl.cwd;
|
|
421
|
+
try {
|
|
422
|
+
const files = fs.readdirSync(cwd);
|
|
423
|
+
for (const f of files) {
|
|
424
|
+
if (f.startsWith('.rip-repl-') && f.endsWith('.mjs')) {
|
|
425
|
+
fs.unlinkSync(path.join(cwd, f));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} catch {}
|
|
429
|
+
}
|
|
430
|
+
|
|
346
431
|
printResult(value) {
|
|
347
432
|
// Pretty print the result
|
|
348
433
|
const formatted = inspect(value, {
|
|
@@ -370,18 +455,8 @@ function __readonly(v) { return Object.freeze({ value: v }); }
|
|
|
370
455
|
break;
|
|
371
456
|
|
|
372
457
|
case '.clear':
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
console,
|
|
376
|
-
process,
|
|
377
|
-
Buffer,
|
|
378
|
-
setTimeout,
|
|
379
|
-
setInterval,
|
|
380
|
-
clearTimeout,
|
|
381
|
-
clearInterval,
|
|
382
|
-
});
|
|
383
|
-
// Re-inject reactive runtime and clear reactive vars
|
|
384
|
-
this.injectReactiveRuntime();
|
|
458
|
+
// Clear all variables
|
|
459
|
+
globalThis.__ripRepl.vars = {};
|
|
385
460
|
this.reactiveVars = new Set();
|
|
386
461
|
this.buffer = '';
|
|
387
462
|
console.log(`${colors.green}Context cleared${colors.reset}`);
|
|
@@ -438,6 +513,7 @@ ${colors.cyan}Debug Toggles:${colors.reset}
|
|
|
438
513
|
|
|
439
514
|
${colors.cyan}Tips:${colors.reset}
|
|
440
515
|
- Multi-line input is supported (press Enter mid-expression)
|
|
516
|
+
- Full import() support: { x } = await import('./file.js')
|
|
441
517
|
- Use Tab for history navigation
|
|
442
518
|
- Previous results stored in _ variable
|
|
443
519
|
- Use Ctrl+C to cancel multi-line input or exit
|
|
@@ -445,10 +521,8 @@ ${colors.cyan}Tips:${colors.reset}
|
|
|
445
521
|
}
|
|
446
522
|
|
|
447
523
|
printVars() {
|
|
448
|
-
// Get all variables from the context
|
|
449
|
-
const
|
|
450
|
-
const allKeys = Object.keys(this.context);
|
|
451
|
-
const userVars = allKeys.filter(k => !builtins.has(k) && !k.startsWith('_'));
|
|
524
|
+
// Get all variables from the context
|
|
525
|
+
const userVars = Object.keys(this.context).filter(k => k !== '_');
|
|
452
526
|
|
|
453
527
|
if (userVars.length === 0) {
|
|
454
528
|
console.log(`${colors.gray}No variables defined${colors.reset}`);
|
package/src/tags.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Shared HTML/SVG Tag Definitions
|
|
3
|
-
// ============================================================================
|
|
4
|
-
// Single source of truth for tag recognition in templates.
|
|
5
|
-
// Used by lexer and codegen at compile time.
|
|
6
|
-
|
|
7
|
-
export const HTML_TAGS = new Set([
|
|
8
|
-
// Document metadata
|
|
9
|
-
'html', 'head', 'title', 'base', 'link', 'meta', 'style',
|
|
10
|
-
// Sectioning
|
|
11
|
-
'body', 'address', 'article', 'aside', 'footer', 'header',
|
|
12
|
-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'main', 'nav', 'section',
|
|
13
|
-
// Grouping
|
|
14
|
-
'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure',
|
|
15
|
-
'hr', 'li', 'ol', 'p', 'pre', 'ul',
|
|
16
|
-
// Text-level
|
|
17
|
-
'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data',
|
|
18
|
-
'dfn', 'em', 'i', 'kbd', 'mark', 'q', 'rp', 'rt', 'ruby', 's',
|
|
19
|
-
'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr',
|
|
20
|
-
// Embedded
|
|
21
|
-
'area', 'audio', 'img', 'map', 'track', 'video',
|
|
22
|
-
'embed', 'iframe', 'object', 'param', 'picture', 'portal', 'source',
|
|
23
|
-
// SVG/Math
|
|
24
|
-
'svg', 'math', 'canvas',
|
|
25
|
-
// Scripting
|
|
26
|
-
'noscript', 'script',
|
|
27
|
-
// Edits
|
|
28
|
-
'del', 'ins',
|
|
29
|
-
// Tables
|
|
30
|
-
'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr',
|
|
31
|
-
// Forms
|
|
32
|
-
'button', 'datalist', 'fieldset', 'form', 'input', 'label', 'legend',
|
|
33
|
-
'meter', 'optgroup', 'option', 'output', 'progress', 'select', 'textarea',
|
|
34
|
-
// Interactive
|
|
35
|
-
'details', 'dialog', 'menu', 'summary',
|
|
36
|
-
// Web components
|
|
37
|
-
'slot', 'template'
|
|
38
|
-
]);
|
|
39
|
-
|
|
40
|
-
export const SVG_TAGS = new Set([
|
|
41
|
-
// Container elements
|
|
42
|
-
'svg', 'g', 'defs', 'symbol', 'use', 'marker', 'clipPath', 'mask', 'pattern',
|
|
43
|
-
// Shape elements
|
|
44
|
-
'circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect',
|
|
45
|
-
// Text elements
|
|
46
|
-
'text', 'textPath', 'tspan',
|
|
47
|
-
// Gradient elements
|
|
48
|
-
'linearGradient', 'radialGradient', 'stop',
|
|
49
|
-
// Filter elements
|
|
50
|
-
'filter', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite',
|
|
51
|
-
'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight',
|
|
52
|
-
'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR',
|
|
53
|
-
'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology',
|
|
54
|
-
'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence',
|
|
55
|
-
// Animation elements
|
|
56
|
-
'animate', 'animateMotion', 'animateTransform', 'set', 'mpath',
|
|
57
|
-
// Other elements
|
|
58
|
-
'desc', 'foreignObject', 'image', 'metadata', 'switch', 'title', 'view'
|
|
59
|
-
]);
|
|
60
|
-
|
|
61
|
-
// Combined set for template element detection (HTML + common SVG)
|
|
62
|
-
export const TEMPLATE_TAGS = new Set([...HTML_TAGS, ...SVG_TAGS]);
|