juxscript 1.1.55 → 1.1.57
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/lib/utils/codeparser.d.ts +1 -1
- package/lib/utils/codeparser.d.ts.map +1 -1
- package/lib/utils/codeparser.js +74 -26
- package/lib/utils/codeparser.ts +79 -26
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ export interface ParsedLine {
|
|
|
8
8
|
*/
|
|
9
9
|
declare function escapeHtml(text: string): string;
|
|
10
10
|
/**
|
|
11
|
-
* Parse code into lines
|
|
11
|
+
* Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
|
|
12
12
|
*/
|
|
13
13
|
export declare function parseCode(code: string, language?: string): ParsedLine[];
|
|
14
14
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codeparser.d.ts","sourceRoot":"","sources":["codeparser.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,UAAU,EAAE,CAQrF;AAuFD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAiE9C;;;;;;;AAED,wBAKE"}
|
package/lib/utils/codeparser.js
CHANGED
|
@@ -8,7 +8,9 @@ const KEYWORDS = new Set([
|
|
|
8
8
|
'let', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try',
|
|
9
9
|
'typeof', 'var', 'void', 'while', 'with', 'yield', 'from', 'of',
|
|
10
10
|
'static', 'get', 'set', 'as', 'interface', 'type', 'enum', 'namespace',
|
|
11
|
-
'jux', 'state', 'registry', 'render', 'bind', 'sync'
|
|
11
|
+
'jux', 'state', 'registry', 'render', 'bind', 'sync', ';', '=', '==', '===',
|
|
12
|
+
'!=', '!==', '+', '-', '*', '/', '%', '++', '--', '&&', '||', '!', '<', '>',
|
|
13
|
+
'<=', '>=', '=>', '...', '.', ',', '(', ')', '{', '}', '[', ']', ':', '?'
|
|
12
14
|
]);
|
|
13
15
|
/**
|
|
14
16
|
* Escape HTML entities
|
|
@@ -22,47 +24,90 @@ function escapeHtml(text) {
|
|
|
22
24
|
.replace(/'/g, ''');
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
|
-
* Parse code into lines
|
|
27
|
+
* Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
|
|
26
28
|
*/
|
|
27
29
|
export function parseCode(code, language = 'javascript') {
|
|
28
30
|
const lines = code.split('\n');
|
|
29
31
|
return lines.map((line, index) => ({
|
|
30
32
|
lineNumber: index + 1,
|
|
31
|
-
html:
|
|
33
|
+
html: tokenizeLine(line),
|
|
32
34
|
raw: line
|
|
33
35
|
}));
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
38
|
+
* Tokenize a single line character-by-character
|
|
37
39
|
*/
|
|
38
|
-
function
|
|
40
|
+
function tokenizeLine(line) {
|
|
39
41
|
if (!line.trim())
|
|
40
42
|
return ' ';
|
|
41
|
-
// ✅ Split line into tokens (words, operators, whitespace)
|
|
42
|
-
const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
|
|
43
43
|
let result = '';
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Whitespace - preserve
|
|
49
|
-
if (
|
|
50
|
-
result +=
|
|
51
|
-
|
|
44
|
+
let i = 0;
|
|
45
|
+
const len = line.length;
|
|
46
|
+
while (i < len) {
|
|
47
|
+
const char = line[i];
|
|
48
|
+
// Whitespace - preserve
|
|
49
|
+
if (/\s/.test(char)) {
|
|
50
|
+
result += char;
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
52
53
|
}
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// Word (identifier or keyword)
|
|
55
|
+
if (/[a-zA-Z_$]/.test(char)) {
|
|
56
|
+
const word = consumeWord(line, i);
|
|
57
|
+
const escaped = escapeHtml(word);
|
|
58
|
+
if (KEYWORDS.has(word)) {
|
|
59
|
+
result += `<span class="token-keyword">${escaped}</span>`;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
result += escaped;
|
|
63
|
+
}
|
|
64
|
+
i += word.length;
|
|
65
|
+
continue;
|
|
58
66
|
}
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
// Number
|
|
68
|
+
if (/\d/.test(char)) {
|
|
69
|
+
const num = consumeNumber(line, i);
|
|
70
|
+
result += `<span class="token-number">${escapeHtml(num)}</span>`;
|
|
71
|
+
i += num.length;
|
|
72
|
+
continue;
|
|
62
73
|
}
|
|
63
|
-
|
|
74
|
+
// Everything else - just escape and append
|
|
75
|
+
result += escapeHtml(char);
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
64
78
|
return result || ' ';
|
|
65
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Consume a complete word (identifier)
|
|
82
|
+
*/
|
|
83
|
+
function consumeWord(line, start) {
|
|
84
|
+
let i = start;
|
|
85
|
+
while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
return line.substring(start, i);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Consume a complete number
|
|
92
|
+
*/
|
|
93
|
+
function consumeNumber(line, start) {
|
|
94
|
+
let i = start;
|
|
95
|
+
let hasDot = false;
|
|
96
|
+
while (i < line.length) {
|
|
97
|
+
const char = line[i];
|
|
98
|
+
if (/\d/.test(char)) {
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
else if (char === '.' && !hasDot) {
|
|
102
|
+
hasDot = true;
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return line.substring(start, i);
|
|
110
|
+
}
|
|
66
111
|
/**
|
|
67
112
|
* Render a parsed line
|
|
68
113
|
*/
|
|
@@ -74,7 +119,7 @@ export function renderLineWithTokens(parsedLine) {
|
|
|
74
119
|
*/
|
|
75
120
|
export function getSyntaxHighlightCSS() {
|
|
76
121
|
return `
|
|
77
|
-
/* Simple
|
|
122
|
+
/* Simple Code Highlighting */
|
|
78
123
|
.jux-code {
|
|
79
124
|
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
80
125
|
font-size: 14px;
|
|
@@ -128,10 +173,13 @@ export function getSyntaxHighlightCSS() {
|
|
|
128
173
|
display: none;
|
|
129
174
|
}
|
|
130
175
|
|
|
131
|
-
/* Token colors
|
|
176
|
+
/* Token colors */
|
|
132
177
|
.token-keyword {
|
|
133
178
|
color: #c678dd;
|
|
134
179
|
font-weight: 600;
|
|
180
|
+
}
|
|
181
|
+
.token-number {
|
|
182
|
+
color: #d19a66;
|
|
135
183
|
}
|
|
136
184
|
`;
|
|
137
185
|
}
|
package/lib/utils/codeparser.ts
CHANGED
|
@@ -8,7 +8,9 @@ const KEYWORDS = new Set([
|
|
|
8
8
|
'let', 'new', 'return', 'super', 'switch', 'this', 'throw', 'try',
|
|
9
9
|
'typeof', 'var', 'void', 'while', 'with', 'yield', 'from', 'of',
|
|
10
10
|
'static', 'get', 'set', 'as', 'interface', 'type', 'enum', 'namespace',
|
|
11
|
-
'jux', 'state', 'registry', 'render', 'bind', 'sync'
|
|
11
|
+
'jux', 'state', 'registry', 'render', 'bind', 'sync', ';', '=', '==', '===',
|
|
12
|
+
'!=', '!==', '+', '-', '*', '/', '%', '++', '--', '&&', '||', '!', '<', '>',
|
|
13
|
+
'<=', '>=', '=>', '...', '.', ',', '(', ')', '{', '}', '[', ']', ':', '?'
|
|
12
14
|
]);
|
|
13
15
|
|
|
14
16
|
export interface ParsedLine {
|
|
@@ -30,55 +32,103 @@ function escapeHtml(text: string): string {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
|
-
* Parse code into lines
|
|
35
|
+
* Parse code into lines - CHARACTER-BY-CHARACTER TOKENIZATION
|
|
34
36
|
*/
|
|
35
37
|
export function parseCode(code: string, language: string = 'javascript'): ParsedLine[] {
|
|
36
38
|
const lines = code.split('\n');
|
|
37
39
|
|
|
38
40
|
return lines.map((line, index) => ({
|
|
39
41
|
lineNumber: index + 1,
|
|
40
|
-
html:
|
|
42
|
+
html: tokenizeLine(line),
|
|
41
43
|
raw: line
|
|
42
44
|
}));
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
|
-
*
|
|
48
|
+
* Tokenize a single line character-by-character
|
|
47
49
|
*/
|
|
48
|
-
function
|
|
50
|
+
function tokenizeLine(line: string): string {
|
|
49
51
|
if (!line.trim()) return ' ';
|
|
50
52
|
|
|
51
|
-
// ✅ Split line into tokens (words, operators, whitespace)
|
|
52
|
-
const tokens = line.match(/\w+|[^\w\s]|\s+/g) || [];
|
|
53
|
-
|
|
54
53
|
let result = '';
|
|
54
|
+
let i = 0;
|
|
55
|
+
const len = line.length;
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!token) return;
|
|
57
|
+
while (i < len) {
|
|
58
|
+
const char = line[i];
|
|
59
59
|
|
|
60
|
-
// Whitespace - preserve
|
|
61
|
-
if (
|
|
62
|
-
result +=
|
|
63
|
-
|
|
60
|
+
// Whitespace - preserve
|
|
61
|
+
if (/\s/.test(char)) {
|
|
62
|
+
result += char;
|
|
63
|
+
i++;
|
|
64
|
+
continue;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
//
|
|
67
|
-
|
|
67
|
+
// Word (identifier or keyword)
|
|
68
|
+
if (/[a-zA-Z_$]/.test(char)) {
|
|
69
|
+
const word = consumeWord(line, i);
|
|
70
|
+
const escaped = escapeHtml(word);
|
|
71
|
+
|
|
72
|
+
if (KEYWORDS.has(word)) {
|
|
73
|
+
result += `<span class="token-keyword">${escaped}</span>`;
|
|
74
|
+
} else {
|
|
75
|
+
result += escaped;
|
|
76
|
+
}
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
result += `<span class="token-keyword">${escaped}</span>`;
|
|
78
|
+
i += word.length;
|
|
79
|
+
continue;
|
|
72
80
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
|
|
82
|
+
// Number
|
|
83
|
+
if (/\d/.test(char)) {
|
|
84
|
+
const num = consumeNumber(line, i);
|
|
85
|
+
result += `<span class="token-number">${escapeHtml(num)}</span>`;
|
|
86
|
+
i += num.length;
|
|
87
|
+
continue;
|
|
76
88
|
}
|
|
77
|
-
|
|
89
|
+
|
|
90
|
+
// Everything else - just escape and append
|
|
91
|
+
result += escapeHtml(char);
|
|
92
|
+
i++;
|
|
93
|
+
}
|
|
78
94
|
|
|
79
95
|
return result || ' ';
|
|
80
96
|
}
|
|
81
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Consume a complete word (identifier)
|
|
100
|
+
*/
|
|
101
|
+
function consumeWord(line: string, start: number): string {
|
|
102
|
+
let i = start;
|
|
103
|
+
while (i < line.length && /[a-zA-Z0-9_$]/.test(line[i])) {
|
|
104
|
+
i++;
|
|
105
|
+
}
|
|
106
|
+
return line.substring(start, i);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Consume a complete number
|
|
111
|
+
*/
|
|
112
|
+
function consumeNumber(line: string, start: number): string {
|
|
113
|
+
let i = start;
|
|
114
|
+
let hasDot = false;
|
|
115
|
+
|
|
116
|
+
while (i < line.length) {
|
|
117
|
+
const char = line[i];
|
|
118
|
+
|
|
119
|
+
if (/\d/.test(char)) {
|
|
120
|
+
i++;
|
|
121
|
+
} else if (char === '.' && !hasDot) {
|
|
122
|
+
hasDot = true;
|
|
123
|
+
i++;
|
|
124
|
+
} else {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return line.substring(start, i);
|
|
130
|
+
}
|
|
131
|
+
|
|
82
132
|
/**
|
|
83
133
|
* Render a parsed line
|
|
84
134
|
*/
|
|
@@ -91,7 +141,7 @@ export function renderLineWithTokens(parsedLine: ParsedLine): string {
|
|
|
91
141
|
*/
|
|
92
142
|
export function getSyntaxHighlightCSS(): string {
|
|
93
143
|
return `
|
|
94
|
-
/* Simple
|
|
144
|
+
/* Simple Code Highlighting */
|
|
95
145
|
.jux-code {
|
|
96
146
|
font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
|
|
97
147
|
font-size: 14px;
|
|
@@ -145,10 +195,13 @@ export function getSyntaxHighlightCSS(): string {
|
|
|
145
195
|
display: none;
|
|
146
196
|
}
|
|
147
197
|
|
|
148
|
-
/* Token colors
|
|
198
|
+
/* Token colors */
|
|
149
199
|
.token-keyword {
|
|
150
200
|
color: #c678dd;
|
|
151
201
|
font-weight: 600;
|
|
202
|
+
}
|
|
203
|
+
.token-number {
|
|
204
|
+
color: #d19a66;
|
|
152
205
|
}
|
|
153
206
|
`;
|
|
154
207
|
}
|