ripple 0.1.1 → 0.2.0
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/LICENSE +21 -0
- package/package.json +56 -24
- package/src/ai.js +292 -0
- package/src/compiler/errors.js +26 -0
- package/src/compiler/index.js +26 -0
- package/src/compiler/phases/1-parse/index.js +543 -0
- package/src/compiler/phases/1-parse/style.js +566 -0
- package/src/compiler/phases/2-analyze/index.js +509 -0
- package/src/compiler/phases/2-analyze/prune.js +572 -0
- package/src/compiler/phases/3-transform/index.js +1572 -0
- package/src/compiler/phases/3-transform/segments.js +91 -0
- package/src/compiler/phases/3-transform/stylesheet.js +372 -0
- package/src/compiler/scope.js +421 -0
- package/src/compiler/utils.js +552 -0
- package/src/constants.js +4 -0
- package/src/jsx-runtime.d.ts +94 -0
- package/src/jsx-runtime.js +46 -0
- package/src/runtime/array.js +215 -0
- package/src/runtime/index.js +39 -0
- package/src/runtime/internal/client/blocks.js +247 -0
- package/src/runtime/internal/client/constants.js +23 -0
- package/src/runtime/internal/client/events.js +223 -0
- package/src/runtime/internal/client/for.js +388 -0
- package/src/runtime/internal/client/if.js +35 -0
- package/src/runtime/internal/client/index.js +53 -0
- package/src/runtime/internal/client/operations.js +72 -0
- package/src/runtime/internal/client/portal.js +33 -0
- package/src/runtime/internal/client/render.js +156 -0
- package/src/runtime/internal/client/runtime.js +909 -0
- package/src/runtime/internal/client/template.js +51 -0
- package/src/runtime/internal/client/try.js +139 -0
- package/src/runtime/internal/client/utils.js +16 -0
- package/src/utils/ast.js +214 -0
- package/src/utils/builders.js +733 -0
- package/src/utils/patterns.js +23 -0
- package/src/utils/sanitize_template_string.js +7 -0
- package/test-mappings.js +0 -0
- package/types/index.d.ts +2 -0
- package/.npmignore +0 -2
- package/History.md +0 -3
- package/Readme.md +0 -151
- package/lib/exec/index.js +0 -60
- package/ripple.js +0 -645
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dominic Gannaway
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,30 +1,62 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
"name": "ripple",
|
|
3
|
+
"description": "Ripple is a TypeScript UI framework for the web",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Dominic Gannaway",
|
|
6
|
+
"version": "0.2.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"module": "src/runtime/index.js",
|
|
9
|
+
"main": "src/runtime/index.js",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/trueadm/ripple.git",
|
|
13
|
+
"directory": "packages/ripple"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/trueadm/ripple/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ripple",
|
|
20
|
+
"UI",
|
|
21
|
+
"framework"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./types/index.d.ts",
|
|
26
|
+
"worker": "./src/runtime/index.js",
|
|
27
|
+
"browser": "./src/runtime/index.js",
|
|
28
|
+
"default": "./src/runtime/index.js"
|
|
19
29
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
"./compiler": {
|
|
32
|
+
"types": "./types/index.d.ts",
|
|
33
|
+
"require": "./compiler/index.js",
|
|
34
|
+
"default": "./src/compiler/index.js"
|
|
22
35
|
},
|
|
23
|
-
"
|
|
24
|
-
|
|
36
|
+
"./validator": {
|
|
37
|
+
"types": "./types/index.d.ts",
|
|
38
|
+
"require": "./validator/index.js",
|
|
39
|
+
"default": "./src/validator/index.js"
|
|
25
40
|
},
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
"./internal/client": {
|
|
42
|
+
"default": "./src/runtime/internal/client/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./jsx-runtime": {
|
|
45
|
+
"types": "./src/jsx-runtime.d.ts",
|
|
46
|
+
"import": "./src/jsx-runtime.js",
|
|
47
|
+
"default": "./src/jsx-runtime.js"
|
|
29
48
|
}
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@ai-sdk/anthropic": "^2.0.5",
|
|
52
|
+
"@jridgewell/sourcemap-codec": "^1.5.5",
|
|
53
|
+
"@types/estree": "^1.0.8",
|
|
54
|
+
"acorn": "^8.15.0",
|
|
55
|
+
"acorn-typescript": "^1.4.13",
|
|
56
|
+
"esrap": "^2.1.0",
|
|
57
|
+
"is-reference": "^3.0.3",
|
|
58
|
+
"magic-string": "^0.30.17",
|
|
59
|
+
"muggle-string": "^0.4.1",
|
|
60
|
+
"zimmerframe": "^1.1.2"
|
|
61
|
+
}
|
|
30
62
|
}
|
package/src/ai.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// import { anthropic } from '@ai-sdk/anthropic';
|
|
2
|
+
// import { generateText } from 'ai';
|
|
3
|
+
|
|
4
|
+
// const default_prompt = `
|
|
5
|
+
// Ripple is a web-based JavaScript framework for building user interfaces. It's syntax and design is inspired by React and Svelte 5.
|
|
6
|
+
// It uses JSX for templating inside '.ripple' modules. These modules allow for custom syntax that is not JavaScript compliant.
|
|
7
|
+
|
|
8
|
+
// One of the core differences is that it allows for a new type of JavaScript declaration which is a 'component', which is like a 'function' but is only allowed in '.ripple' modules:
|
|
9
|
+
|
|
10
|
+
// \`\`\`js
|
|
11
|
+
// component HelloComponent(props) {
|
|
12
|
+
// const title = 'Hello ';
|
|
13
|
+
|
|
14
|
+
// <div>{title + props.name}</div>;
|
|
15
|
+
// }
|
|
16
|
+
// \`\`\`
|
|
17
|
+
|
|
18
|
+
// Another difference is that 'component' declaration bodies allow for JSX templating. Except this JSX templating isn't "expression" based, but rather "statement" based. That
|
|
19
|
+
// means that 'return' is not valid syntax in a component declaration body. Nor is creating a variable that references JSX. Instead, JSX is directly written in the body of the component declaration.
|
|
20
|
+
// This means that the ordering is important, as JSX that is written first will be rendered first. This is different from React.
|
|
21
|
+
|
|
22
|
+
// Another difference from JSX in other frameworks is that JSXText is not allowed here. That's because JSX is now statement based, and not expression based. This means that all JSX must be wrapped in a JSXExpressionContainer.
|
|
23
|
+
|
|
24
|
+
// For example, this is invalid Ripple code:
|
|
25
|
+
|
|
26
|
+
// \`\`\`js
|
|
27
|
+
// <button>=</button>
|
|
28
|
+
// \`\`\`
|
|
29
|
+
|
|
30
|
+
// The correct version is:
|
|
31
|
+
|
|
32
|
+
// \`\`\`js
|
|
33
|
+
// <button>{"="}</button>
|
|
34
|
+
// \`\`\`
|
|
35
|
+
|
|
36
|
+
// Another core difference is that Ripple defines reactive variables by their usage of a "$" prefix. If the variable declaration does not have a dollar prefix, it is not reactive.
|
|
37
|
+
|
|
38
|
+
// \`\`\`js
|
|
39
|
+
// component HelloComponent(props) {
|
|
40
|
+
// let $count = 0;
|
|
41
|
+
|
|
42
|
+
// <div>{$count}</div>;
|
|
43
|
+
// <button onClick={() => $count++}>{"Increment"}</button>;
|
|
44
|
+
// }
|
|
45
|
+
// \`\`\`
|
|
46
|
+
|
|
47
|
+
// Object properties can also be reactive if the property name starts with a "$" prefix.
|
|
48
|
+
|
|
49
|
+
// \`\`\`js
|
|
50
|
+
// component HelloComponent(props) {
|
|
51
|
+
// let state = { $count: 0 };
|
|
52
|
+
|
|
53
|
+
// <div>{state.$count}</div>;
|
|
54
|
+
// <button onClick={() => state.$count++}>{"Increment"}</button>;
|
|
55
|
+
// }
|
|
56
|
+
// \`\`\`
|
|
57
|
+
|
|
58
|
+
// Ripple doesn't allow for inline expressions with JSX for conditionals or for collections such as arrays or objects.
|
|
59
|
+
// Instead, prefer using normal JavaScript logic where you have a "if" or "for" statement that wraps the JSX.
|
|
60
|
+
|
|
61
|
+
// Here is valid Ripple code:
|
|
62
|
+
|
|
63
|
+
// \`\`\`js
|
|
64
|
+
// export component Counter() {
|
|
65
|
+
// let $count = 0;
|
|
66
|
+
|
|
67
|
+
// if ($count > 5) {
|
|
68
|
+
// <div>{$count}</div>;
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
// <div>
|
|
72
|
+
// if ($count > 5) {
|
|
73
|
+
// <div>{$count}</div>;
|
|
74
|
+
// }
|
|
75
|
+
// </div>;
|
|
76
|
+
|
|
77
|
+
// for (const item of items) {
|
|
78
|
+
// <div>{item}</div>;
|
|
79
|
+
// }
|
|
80
|
+
|
|
81
|
+
// <ul>
|
|
82
|
+
// for (const item of items) {
|
|
83
|
+
// <li>{item}</li>;
|
|
84
|
+
// }
|
|
85
|
+
// </ul>;
|
|
86
|
+
// }
|
|
87
|
+
// \`\`\`
|
|
88
|
+
|
|
89
|
+
// Ripple allows for shorthand props on components, so '<Child state={state} />' can be written as '<Child {state} />'.
|
|
90
|
+
|
|
91
|
+
// Ripple also allows for a singular "<style>" JSX element at the top level of the component declaration body. This is used for styling any JSX elements within the component.
|
|
92
|
+
// The style element can contain any valid CSS, and can also contain CSS variables. CSS variables are defined with a "--" prefix. This is the preferred way of doing styling over inline styles.
|
|
93
|
+
|
|
94
|
+
// If inline styles are to be used, then they should be done using the HTML style attribute approach rather than the JSX style attribute property approach.
|
|
95
|
+
|
|
96
|
+
// In Ripple variables that are created with an identifier that starts with a "$" prefix are considered reactive. If declaration init expression also references reactive variables, or function expressions, then
|
|
97
|
+
// this type of variable is considered "computed". Computed reactive declarations will re-run when any of the reactive variables they reference change. If this is not desired then the "untrack" function call should
|
|
98
|
+
// be used to prevent reactivity.
|
|
99
|
+
|
|
100
|
+
// \`\`\`js
|
|
101
|
+
// import { untrack } from 'ripple';
|
|
102
|
+
|
|
103
|
+
// component Counter({ $initial }) {
|
|
104
|
+
// let $count = untrack(() => $initial);
|
|
105
|
+
// }
|
|
106
|
+
// \`\`\`
|
|
107
|
+
|
|
108
|
+
// An important part of Ripple's reactivity model is that passing reactivity between boundaries can only happen via two ways:
|
|
109
|
+
// - the usage of closures, where a value is referenced in a function or property getter
|
|
110
|
+
// - the usage of objects and/or arrays, where the object or array is passed as a property with a "$" prefix so its reactivity is kept
|
|
111
|
+
|
|
112
|
+
// For example if you were to create a typical Ripple hook function, then you should pass any reactive values through using objects. Otherwise, the
|
|
113
|
+
// hook will act as a computed function and re-run every time the reactive value changes – which is likely not the desired behaviour of a "hook" function.
|
|
114
|
+
|
|
115
|
+
// \`\`\`js
|
|
116
|
+
// function useCounter(initial) {
|
|
117
|
+
// let $count = initial;
|
|
118
|
+
// const $double = $count * 2;
|
|
119
|
+
|
|
120
|
+
// const increment = () => $count++;
|
|
121
|
+
|
|
122
|
+
// return { $double, increment };
|
|
123
|
+
// }
|
|
124
|
+
|
|
125
|
+
// component Counter({ $count }) {
|
|
126
|
+
// const { $double, increment } = useCounter($count);
|
|
127
|
+
|
|
128
|
+
// <button onClick={increment}>{"Increment"}</button>;
|
|
129
|
+
// <div>{$double}</div>;
|
|
130
|
+
// }
|
|
131
|
+
// \`\`\`
|
|
132
|
+
|
|
133
|
+
// If a value needs to be mutated from within a hook, then it should be referenced by the hook in its object form instead:
|
|
134
|
+
|
|
135
|
+
// \`\`\`js
|
|
136
|
+
// function useCounter(state) {
|
|
137
|
+
// const $double = state.$count * 2;
|
|
138
|
+
|
|
139
|
+
// const increment = () => state.$count++;
|
|
140
|
+
|
|
141
|
+
// return { $double, increment };
|
|
142
|
+
// }
|
|
143
|
+
|
|
144
|
+
// component Counter({ $count }) {
|
|
145
|
+
// let $count = 0;
|
|
146
|
+
|
|
147
|
+
// const { $double, increment } = useCounter({ $count });
|
|
148
|
+
|
|
149
|
+
// <button onClick={increment}>{"Increment"}</button>;
|
|
150
|
+
// <div>{$double}</div>;
|
|
151
|
+
// }
|
|
152
|
+
// \`\`\`
|
|
153
|
+
|
|
154
|
+
// It should be noted that in this example, the "$count" inside the "Counter" component will not be mutated by the "increment" function.
|
|
155
|
+
|
|
156
|
+
// If this is desired, then the call to "useCounter" needs to provide a getter and setter for the "$count" value:
|
|
157
|
+
|
|
158
|
+
// \`\`\`js
|
|
159
|
+
// function useCounter(state) {
|
|
160
|
+
// const $double = state.$count * 2;
|
|
161
|
+
|
|
162
|
+
// const increment = () => state.$count++;
|
|
163
|
+
|
|
164
|
+
// return { $double, increment };
|
|
165
|
+
// }
|
|
166
|
+
|
|
167
|
+
// component Counter({ $count }) {
|
|
168
|
+
// let $count = 0;
|
|
169
|
+
|
|
170
|
+
// const { $double, increment } = useCounter({ get $count() { return $count }, set $count(value) { $count = value } });
|
|
171
|
+
|
|
172
|
+
// <button onClick={increment}>{"Increment"}</button>;
|
|
173
|
+
// <div>{$double}</div>;
|
|
174
|
+
// }
|
|
175
|
+
// \`\`\`
|
|
176
|
+
|
|
177
|
+
// Normally, you shouldn't provide getters/setters in the object returned from a hook, especially if the usage site intends to destruct the object.
|
|
178
|
+
|
|
179
|
+
// Ripple also provides a way of handling Suspense and asynchronous data fetching. This requires two parts:
|
|
180
|
+
// - a "try" block, that has an "async" block that shows the fallback pending UI. These blocks can only be used inside Ripple components
|
|
181
|
+
// - an "await" that must happen at the top-level of the component body
|
|
182
|
+
|
|
183
|
+
// Here is an example:
|
|
184
|
+
|
|
185
|
+
// \`\`\`js
|
|
186
|
+
// export component App() {
|
|
187
|
+
// try {
|
|
188
|
+
// <Child />;
|
|
189
|
+
// } async {
|
|
190
|
+
// <div>{"Loading..."}</div>;
|
|
191
|
+
// }
|
|
192
|
+
// }
|
|
193
|
+
|
|
194
|
+
// component Child() {
|
|
195
|
+
// const $pokemons = await fetch('https://pokeapi.co/api/v2/pokemon/').then((res) => res.json());
|
|
196
|
+
|
|
197
|
+
// for (const pokemon of $pokemons.results) {
|
|
198
|
+
// <div>{pokemon.name}</div>;
|
|
199
|
+
// }
|
|
200
|
+
// }
|
|
201
|
+
// \`\`\`
|
|
202
|
+
|
|
203
|
+
// It's important that the transformed code never uses an async fetch() call inside an effect function. This is an anti-pattern, instead the "await" expression should be used
|
|
204
|
+
// directly inside the fragment or component body. Also when using "await" then loading states should be handled using the "try" and "async" blocks, so this isn't required in the
|
|
205
|
+
// output code.
|
|
206
|
+
|
|
207
|
+
// Ripple also supports "fragment" syntax, which is similar to the "component" syntax but allows for multiple arguments:
|
|
208
|
+
|
|
209
|
+
// \`\`\`js
|
|
210
|
+
// fragment foo() {
|
|
211
|
+
// <div>{"Hello World"}</div>;
|
|
212
|
+
// }
|
|
213
|
+
|
|
214
|
+
// component App() {
|
|
215
|
+
// {fragment foo()};
|
|
216
|
+
// }
|
|
217
|
+
// \`\`\`
|
|
218
|
+
|
|
219
|
+
// Fragments can be seen as reactive functions that can take arguments and using the "{@fragment fragment(...args)}" syntax, they can be rendered as if they were JSX elements.
|
|
220
|
+
|
|
221
|
+
// Ripple denotes attributes and properties on JSX elements as being reactive when they also have a "$" prefix. This means that if a property is reactive, then the element will re-render when the property changes.
|
|
222
|
+
|
|
223
|
+
// Ripple does not support both a non-reactive and reactive version of a prop – so having "$ref" and "ref" is not allowed. If a prop could be possibly reactive, then it should always have a "$" prefix to ensure maximum compatibility.
|
|
224
|
+
|
|
225
|
+
// There are also some special attributes that such as "$ref" and "$children" that always start with a "$" prefix.
|
|
226
|
+
|
|
227
|
+
// When creating an implicit children fragment from a JSX component, such as:
|
|
228
|
+
|
|
229
|
+
// \`\`\`js
|
|
230
|
+
// <ChildComponent>
|
|
231
|
+
// {"Hello World"}
|
|
232
|
+
// </ChildComponent>
|
|
233
|
+
// \`\`\`
|
|
234
|
+
|
|
235
|
+
// This can also be written as:
|
|
236
|
+
|
|
237
|
+
// \`\`\`js
|
|
238
|
+
// fragment $children() {
|
|
239
|
+
// {"Hello World"};
|
|
240
|
+
// }
|
|
241
|
+
|
|
242
|
+
// <ChildComponent {$children} />;
|
|
243
|
+
// \`\`\`
|
|
244
|
+
|
|
245
|
+
// Which is the same as the previous example.
|
|
246
|
+
|
|
247
|
+
// The "Hello world" will be passed as a "$children" prop to the "ChildComponent" and it will be of the type of "Fragment". Which means that it's not a string, or JSX element, but rather a special kind of thing.
|
|
248
|
+
|
|
249
|
+
// To render a type of "Fragment" the {@fragment thing()} syntax should be used. This will render the "thing" as if it was a JSX element. Here's an example:
|
|
250
|
+
|
|
251
|
+
// \`\`\`js
|
|
252
|
+
// component Child({ $children }) {
|
|
253
|
+
// <div>
|
|
254
|
+
// {@fragment $children()};
|
|
255
|
+
// </div>;
|
|
256
|
+
// }
|
|
257
|
+
// \`\`\`
|
|
258
|
+
|
|
259
|
+
// Ripple uses for...of blocks for templating over collections or lists. While loops, standard for loops and while loops are not permitted in Ripple components or fragments.
|
|
260
|
+
|
|
261
|
+
// For example, to render a list of items:
|
|
262
|
+
|
|
263
|
+
// \`\`\`js
|
|
264
|
+
// <ul>
|
|
265
|
+
// for (const num of [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) {
|
|
266
|
+
// <li>{num}</li>;
|
|
267
|
+
// }
|
|
268
|
+
// </ul>;
|
|
269
|
+
// \`\`\`
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
// `;
|
|
273
|
+
|
|
274
|
+
// export async function validate_with_ai(source) {
|
|
275
|
+
// const { text } = await generateText({
|
|
276
|
+
// model: anthropic('claude-3-7-sonnet-20250219'),
|
|
277
|
+
// messages: [
|
|
278
|
+
// {
|
|
279
|
+
// role: 'user',
|
|
280
|
+
// content: default_prompt,
|
|
281
|
+
// providerOptions: {
|
|
282
|
+
// anthropic: { cacheControl: { type: 'ephemeral' } }
|
|
283
|
+
// }
|
|
284
|
+
// },
|
|
285
|
+
// {
|
|
286
|
+
// role: 'user',
|
|
287
|
+
// content: `Please validate the following Ripple code and provide feedback on any issues:\n\n${source}`
|
|
288
|
+
// }
|
|
289
|
+
// ]
|
|
290
|
+
// });
|
|
291
|
+
// return text;
|
|
292
|
+
// }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {string} message
|
|
6
|
+
* @param {string} filename
|
|
7
|
+
* @param {any} node
|
|
8
|
+
*/
|
|
9
|
+
export function error(message, filename, node) {
|
|
10
|
+
let errorMessage = message;
|
|
11
|
+
|
|
12
|
+
if (node && node.loc) {
|
|
13
|
+
// Use GitHub-style range format: filename#L39C24-L39C32
|
|
14
|
+
const startLine = node.loc.start.line;
|
|
15
|
+
const startColumn = node.loc.start.column;
|
|
16
|
+
const endLine = node.loc.end.line;
|
|
17
|
+
const endColumn = node.loc.end.column;
|
|
18
|
+
|
|
19
|
+
const rangeInfo = `${filename}#L${startLine}C${startColumn}-L${endLine}C${endColumn}`;
|
|
20
|
+
errorMessage += ` (${rangeInfo})`;
|
|
21
|
+
} else {
|
|
22
|
+
errorMessage += ` (${filename})`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
throw new Error(errorMessage);
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { parse as parse_module } from './phases/1-parse/index.js';
|
|
2
|
+
import { analyze } from './phases/2-analyze/index.js';
|
|
3
|
+
import { transform } from './phases/3-transform/index.js';
|
|
4
|
+
import { convert_source_map_to_mappings } from './phases/3-transform/segments.js';
|
|
5
|
+
|
|
6
|
+
export function parse(source) {
|
|
7
|
+
return parse_module(source);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function compile(source, filename) {
|
|
11
|
+
const ast = parse_module(source);
|
|
12
|
+
const analysis = analyze(ast, filename);
|
|
13
|
+
const result = transform(filename, source, analysis, false);
|
|
14
|
+
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function compile_to_volar_mappings(source, filename) {
|
|
19
|
+
// Parse and transform to get the esrap sourcemap
|
|
20
|
+
const ast = parse_module(source);
|
|
21
|
+
const analysis = analyze(ast, filename);
|
|
22
|
+
const transformed = transform(filename, source, analysis, true);
|
|
23
|
+
|
|
24
|
+
// Use Svelte's approach to convert esrap sourcemap to Volar mappings
|
|
25
|
+
return convert_source_map_to_mappings(transformed.js.map, source, transformed.js.code);
|
|
26
|
+
}
|