ripple 0.2.196 → 0.2.197
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/package.json +2 -2
- package/src/compiler/comment-utils.js +91 -0
- package/src/compiler/errors.js +1 -1
- package/src/compiler/identifier-utils.js +23 -18
- package/src/compiler/index.d.ts +7 -2
- package/src/compiler/index.js +13 -6
- package/src/compiler/phases/1-parse/index.js +508 -206
- package/src/compiler/phases/2-analyze/index.js +47 -15
- package/src/compiler/phases/3-transform/client/index.js +389 -251
- package/src/compiler/phases/3-transform/segments.js +111 -90
- package/src/compiler/phases/3-transform/server/index.js +31 -2
- package/src/compiler/types/index.d.ts +40 -15
- package/src/compiler/types/parse.d.ts +16 -4
- package/src/compiler/utils.js +28 -3
- package/src/jsx-runtime.d.ts +852 -13
- package/src/runtime/internal/client/render.js +0 -2
- package/src/utils/builders.js +61 -43
- package/tests/client/array/array.derived.test.ripple +7 -7
- package/tests/client/array/array.static.test.ripple +37 -35
- package/tests/client/array/array.to-methods.test.ripple +4 -4
- package/tests/client/basic/basic.attributes.test.ripple +9 -9
- package/tests/client/basic/basic.components.test.ripple +34 -12
- package/tests/client/boundaries.test.ripple +2 -2
- package/tests/client/compiler/compiler.basic.test.ripple +10 -10
- package/tests/client/composite/composite.generics.test.ripple +6 -7
- package/tests/client/dynamic-elements.test.ripple +17 -10
- package/tests/client/events.test.ripple +17 -14
- package/tests/client/input-value.test.ripple +54 -20
- package/tests/client/object.test.ripple +3 -2
- package/tests/client/ref.test.ripple +6 -4
- package/tests/client/svg.test.ripple +8 -7
- package/tests/client/typescript-generics.test.ripple +61 -51
- package/tests/client.d.ts +13 -0
- package/tests/server/basic.test.ripple +7 -1
- package/tests/server/composite.test.ripple +5 -5
- package/types/index.d.ts +10 -7
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.197",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"vscode-languageserver-types": "^3.17.5"
|
|
93
93
|
},
|
|
94
94
|
"peerDependencies": {
|
|
95
|
-
"ripple": "0.2.
|
|
95
|
+
"ripple": "0.2.197"
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import * as AST from 'estree'
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if a comment is a TypeScript pragma (line comment)
|
|
7
|
+
* @param {AST.CommentWithLocation} comment
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
export function is_ts_pragma(comment) {
|
|
11
|
+
if (comment.type !== 'Line') return false;
|
|
12
|
+
|
|
13
|
+
const pragmas = ['@ts-ignore', '@ts-expect-error', '@ts-nocheck', '@ts-check'];
|
|
14
|
+
return pragmas.some((pragma) => comment.value.includes(pragma));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if a comment is a triple-slash directive
|
|
19
|
+
* /// <reference path="..." />
|
|
20
|
+
* @param {AST.CommentWithLocation} comment
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
export function is_triple_slash_directive(comment) {
|
|
24
|
+
if (comment.type !== 'Line') return false;
|
|
25
|
+
|
|
26
|
+
// Triple slash directives start with / after the // is stripped
|
|
27
|
+
// So the value should start with / followed by <reference, <amd-module, or <amd-dependency
|
|
28
|
+
const value = comment.value.trim();
|
|
29
|
+
return /^\/\s*<(reference|amd-module|amd-dependency)/.test(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if a comment is a JSDoc comment with TypeScript annotations
|
|
34
|
+
* Examples: block comments containing `@type`, `@typedef`, `@param`, `@returns`, etc.
|
|
35
|
+
* @param {AST.CommentWithLocation} comment
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
export function is_jsdoc_ts_annotation(comment) {
|
|
39
|
+
if (comment.type !== 'Block') return false;
|
|
40
|
+
|
|
41
|
+
// JSDoc comments start with /** which means the value starts with * after /* is stripped
|
|
42
|
+
if (!comment.value.startsWith('*')) return false;
|
|
43
|
+
|
|
44
|
+
// Check if it contains TypeScript-relevant tags
|
|
45
|
+
const tsAnnotations = [
|
|
46
|
+
'@type',
|
|
47
|
+
'@typedef',
|
|
48
|
+
'@param',
|
|
49
|
+
'@returns',
|
|
50
|
+
'@template',
|
|
51
|
+
'@extends',
|
|
52
|
+
'@implements',
|
|
53
|
+
'@satisfies',
|
|
54
|
+
'@overload',
|
|
55
|
+
'@import',
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
return tsAnnotations.some((annotation) => comment.value.includes(annotation));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if a comment should be preserved in to_ts mode
|
|
63
|
+
* @param {AST.CommentWithLocation} comment
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export function should_preserve_comment(comment) {
|
|
67
|
+
return (
|
|
68
|
+
is_ts_pragma(comment) || is_triple_slash_directive(comment) || is_jsdoc_ts_annotation(comment)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format a comment for output
|
|
74
|
+
* @param {AST.CommentWithLocation} comment
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
export function format_comment(comment) {
|
|
78
|
+
if (comment.type === 'Line') {
|
|
79
|
+
// Check if it's a triple-slash directive (value starts with /)
|
|
80
|
+
if (comment.value.trimStart().startsWith('/')) {
|
|
81
|
+
return `/// ${comment.value.trimStart().slice(1)}`;
|
|
82
|
+
}
|
|
83
|
+
return `// ${comment.value.trim()}`;
|
|
84
|
+
} else {
|
|
85
|
+
// Block comment - check if it's a JSDoc (value starts with *)
|
|
86
|
+
if (comment.value.startsWith('*')) {
|
|
87
|
+
return `/** ${comment.value.trim().slice(1)} */`;
|
|
88
|
+
}
|
|
89
|
+
return `/* ${comment.value.trim()} */`;
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/compiler/errors.js
CHANGED
|
@@ -13,11 +13,26 @@ function encode_utf16_char(char) {
|
|
|
13
13
|
return `_u${('0000' + char.charCodeAt(0).toString(16)).slice(-4)}_`;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Finds the next uppercase character or returns name.length
|
|
18
|
+
* @param {string} name
|
|
19
|
+
* @param {number} start
|
|
20
|
+
* @returns {number}
|
|
21
|
+
*/
|
|
22
|
+
function find_next_uppercase(name, start) {
|
|
23
|
+
for (let i = start; i < name.length; i++) {
|
|
24
|
+
if (name[i] === name[i].toUpperCase()) {
|
|
25
|
+
return i;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return name.length;
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
/**
|
|
17
32
|
* @param {string} encoded
|
|
18
33
|
* @returns {string}
|
|
19
34
|
*/
|
|
20
|
-
function
|
|
35
|
+
function decode_utf16_string(encoded) {
|
|
21
36
|
return encoded.replace(DECODE_UTF16_REGEX, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
22
37
|
}
|
|
23
38
|
|
|
@@ -26,8 +41,13 @@ function decoded_utf16_string(encoded) {
|
|
|
26
41
|
* @returns {string}
|
|
27
42
|
*/
|
|
28
43
|
export function obfuscate_identifier(name) {
|
|
44
|
+
const first_char = name[0];
|
|
29
45
|
let start = 0;
|
|
30
|
-
if (
|
|
46
|
+
if (first_char === '@' || first_char === '#') {
|
|
47
|
+
const encoded = encode_utf16_char(first_char);
|
|
48
|
+
name = encoded + name.slice(1);
|
|
49
|
+
start = encoded.length;
|
|
50
|
+
} else if (first_char === first_char.toUpperCase()) {
|
|
31
51
|
start = 1;
|
|
32
52
|
}
|
|
33
53
|
const index = find_next_uppercase(name, start);
|
|
@@ -56,20 +76,5 @@ export function is_identifier_obfuscated(name) {
|
|
|
56
76
|
export function deobfuscate_identifier(name) {
|
|
57
77
|
name = name.replaceAll(IDENTIFIER_OBFUSCATION_PREFIX, '');
|
|
58
78
|
const parts = name.split('__');
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Finds the next uppercase character or returns name.length
|
|
64
|
-
* @param {string} name
|
|
65
|
-
* @param {number} start
|
|
66
|
-
* @returns {number}
|
|
67
|
-
*/
|
|
68
|
-
function find_next_uppercase(name, start) {
|
|
69
|
-
for (let i = start; i < name.length; i++) {
|
|
70
|
-
if (name[i] === name[i].toUpperCase()) {
|
|
71
|
-
return i;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return name.length;
|
|
79
|
+
return decode_utf16_string((parts[1] ? parts[1] : '') + parts[0]);
|
|
75
80
|
}
|
package/src/compiler/index.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export interface RippleCompileError extends Error {
|
|
|
87
87
|
raisedAt: number | undefined;
|
|
88
88
|
end: number | undefined;
|
|
89
89
|
loc: AST.SourceLocation | undefined;
|
|
90
|
-
fileName: string |
|
|
90
|
+
fileName: string | null;
|
|
91
91
|
type: 'fatal' | 'usage';
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -104,13 +104,18 @@ export interface CompileOptions extends SharedCompileOptions {
|
|
|
104
104
|
|
|
105
105
|
export interface ParseOptions {
|
|
106
106
|
loose?: boolean;
|
|
107
|
+
errors?: RippleCompileError[];
|
|
108
|
+
comments?: AST.CommentWithLocation[];
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
export interface AnalyzeOptions extends ParseOptions, Pick<CompileOptions, 'mode'> {
|
|
112
|
+
errors?: RippleCompileError[];
|
|
110
113
|
to_ts?: boolean;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
export interface VolarCompileOptions
|
|
116
|
+
export interface VolarCompileOptions
|
|
117
|
+
extends Omit<ParseOptions, 'errors' | 'comments'>,
|
|
118
|
+
SharedCompileOptions {}
|
|
114
119
|
|
|
115
120
|
export function parse(source: string, options?: ParseOptions): AST.Program;
|
|
116
121
|
|
package/src/compiler/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { convert_source_map_to_mappings } from './phases/3-transform/segments.js
|
|
|
12
12
|
* @returns {AST.Program}
|
|
13
13
|
*/
|
|
14
14
|
export function parse(source) {
|
|
15
|
-
return parse_module(source, undefined);
|
|
15
|
+
return parse_module(source, undefined, undefined);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -23,7 +23,7 @@ export function parse(source) {
|
|
|
23
23
|
* @returns {object}
|
|
24
24
|
*/
|
|
25
25
|
export function compile(source, filename, options = {}) {
|
|
26
|
-
const ast = parse_module(source, undefined);
|
|
26
|
+
const ast = parse_module(source, filename, undefined);
|
|
27
27
|
const analysis = analyze(ast, filename, options);
|
|
28
28
|
const result =
|
|
29
29
|
options.mode === 'server'
|
|
@@ -34,7 +34,7 @@ export function compile(source, filename, options = {}) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/** @import { PostProcessingChanges, LineOffsets } from './phases/3-transform/client/index.js' */
|
|
37
|
-
/** @import { VolarMappingsResult, VolarCompileOptions, CompileOptions } from 'ripple/compiler' */
|
|
37
|
+
/** @import { VolarMappingsResult, VolarCompileOptions, CompileOptions, RippleCompileError } from 'ripple/compiler' */
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Compile Ripple component to Volar virtual code with TypeScript mappings
|
|
@@ -43,9 +43,16 @@ export function compile(source, filename, options = {}) {
|
|
|
43
43
|
* @param {VolarCompileOptions} [options] - Compiler options
|
|
44
44
|
* @returns {VolarMappingsResult} Volar mappings object
|
|
45
45
|
*/
|
|
46
|
-
export function compile_to_volar_mappings(source, filename, options) {
|
|
47
|
-
const
|
|
48
|
-
const
|
|
46
|
+
export function compile_to_volar_mappings(source, filename, options = {}) {
|
|
47
|
+
const errors = /** @type {RippleCompileError[]} */ ([]);
|
|
48
|
+
const comments = /** @type {AST.CommentWithLocation[]} */ ([]);
|
|
49
|
+
const ast = parse_module(source, filename, { ...options, errors, comments });
|
|
50
|
+
const analysis = analyze(ast, filename, {
|
|
51
|
+
to_ts: true,
|
|
52
|
+
loose: !!options?.loose,
|
|
53
|
+
errors,
|
|
54
|
+
comments,
|
|
55
|
+
});
|
|
49
56
|
const transformed = transform_client(
|
|
50
57
|
filename,
|
|
51
58
|
source,
|