@tsrx/vue 0.0.10
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 +43 -0
- package/src/error-boundary.js +173 -0
- package/src/index.js +60 -0
- package/src/transform.js +714 -0
- package/types/index.d.ts +21 -0
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
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tsrx/vue",
|
|
3
|
+
"description": "Vue compiler built on @tsrx/core",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Dominic Gannaway",
|
|
6
|
+
"version": "0.0.10",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/Ripple-TS/ripple.git",
|
|
14
|
+
"directory": "packages/tsrx-vue"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./types/index.d.ts",
|
|
19
|
+
"default": "./src/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./error-boundary": {
|
|
22
|
+
"default": "./src/error-boundary.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"esrap": "^2.1.0",
|
|
27
|
+
"zimmerframe": "^1.1.2",
|
|
28
|
+
"@tsrx/core": "0.0.15"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"vue": ">=3.5",
|
|
32
|
+
"vue-jsx-vapor": ">=3.2.10"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/estree": "^1.0.8",
|
|
36
|
+
"@types/estree-jsx": "^1.0.5",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"types"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as Vue from 'vue';
|
|
2
|
+
import { getCurrentInstance, onErrorCaptured, shallowRef } from 'vue';
|
|
3
|
+
|
|
4
|
+
const boundary_states = new WeakMap();
|
|
5
|
+
|
|
6
|
+
/** @typedef {any} BoundaryValue */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {BoundaryValue} nodes
|
|
10
|
+
* @param {Node} [anchor]
|
|
11
|
+
* @returns {BoundaryValue}
|
|
12
|
+
*/
|
|
13
|
+
function create_fragment(nodes, anchor = document.createTextNode('')) {
|
|
14
|
+
const fragment = new /** @type {any} */ (Vue).VaporFragment(nodes);
|
|
15
|
+
fragment.anchor = anchor;
|
|
16
|
+
return fragment;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {BoundaryValue} value
|
|
21
|
+
* @returns {void}
|
|
22
|
+
*/
|
|
23
|
+
function track_dynamic_values(value) {
|
|
24
|
+
if (!value || typeof value !== 'object') return;
|
|
25
|
+
|
|
26
|
+
for (const key of Object.keys(value)) {
|
|
27
|
+
const child = value[key];
|
|
28
|
+
if (key === 'content' || key === 'fallback' || key === 'children' || key === 'default')
|
|
29
|
+
continue;
|
|
30
|
+
if (key === '$' || key.startsWith('on')) continue;
|
|
31
|
+
if (typeof child === 'function') {
|
|
32
|
+
child();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {BoundaryValue} node
|
|
39
|
+
* @param {Node | undefined} anchor
|
|
40
|
+
* @returns {BoundaryValue}
|
|
41
|
+
*/
|
|
42
|
+
function normalize_block(node, anchor) {
|
|
43
|
+
if (node instanceof Node || /** @type {any} */ (Vue).isFragment(node)) return node;
|
|
44
|
+
if (/** @type {any} */ (Vue).isVaporComponent(node)) {
|
|
45
|
+
if (!(node.rawProps && 'content' in node.rawProps && 'fallback' in node.rawProps)) {
|
|
46
|
+
track_dynamic_values(node.rawProps);
|
|
47
|
+
}
|
|
48
|
+
return create_fragment(node, anchor);
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(node))
|
|
51
|
+
return create_fragment(
|
|
52
|
+
node.map((item) => normalize_block(item, undefined)),
|
|
53
|
+
anchor,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const result = node == null || typeof node === 'boolean' ? '' : String(node);
|
|
57
|
+
if (anchor) {
|
|
58
|
+
anchor.textContent = result;
|
|
59
|
+
return anchor;
|
|
60
|
+
}
|
|
61
|
+
return document.createTextNode(result);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {BoundaryValue} current
|
|
66
|
+
* @param {BoundaryValue} value
|
|
67
|
+
* @param {Node | undefined} anchor
|
|
68
|
+
* @returns {BoundaryValue}
|
|
69
|
+
*/
|
|
70
|
+
function resolve_value(current, value, anchor) {
|
|
71
|
+
anchor = anchor || (current instanceof Node && current.nodeType === 3 ? current : undefined);
|
|
72
|
+
const node = normalize_block(value, anchor);
|
|
73
|
+
|
|
74
|
+
if (current) {
|
|
75
|
+
if (/** @type {any} */ (Vue).isFragment(current)) {
|
|
76
|
+
if (current.anchor && current.anchor.parentNode) {
|
|
77
|
+
/** @type {any} */ (Vue).remove(current.nodes, current.anchor.parentNode);
|
|
78
|
+
/** @type {any} */ (Vue).insert(node, current.anchor.parentNode, current.anchor);
|
|
79
|
+
if (current.scope) current.scope.stop();
|
|
80
|
+
}
|
|
81
|
+
} else if (current instanceof Node) {
|
|
82
|
+
if (current.nodeType === 3 && (!(node instanceof Node) || node.nodeType !== 3)) {
|
|
83
|
+
current.textContent = '';
|
|
84
|
+
}
|
|
85
|
+
if (/** @type {any} */ (Vue).isFragment(node) && current.parentNode) {
|
|
86
|
+
/** @type {any} */ (Vue).insert(node, current.parentNode, current);
|
|
87
|
+
if (current.nodeType !== 3) current.parentNode.removeChild(current);
|
|
88
|
+
} else if (node instanceof Node) {
|
|
89
|
+
if (current.nodeType === 3 && node.nodeType === 3) {
|
|
90
|
+
current.textContent = node.textContent;
|
|
91
|
+
return current;
|
|
92
|
+
}
|
|
93
|
+
if (current.parentNode) current.parentNode.replaceChild(node, current);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return node;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {() => BoundaryValue} render
|
|
103
|
+
* @returns {BoundaryValue[]}
|
|
104
|
+
*/
|
|
105
|
+
function create_boundary_nodes(render) {
|
|
106
|
+
/** @type {BoundaryValue[]} */
|
|
107
|
+
const nodes = [];
|
|
108
|
+
/** @type {any} */
|
|
109
|
+
let scope;
|
|
110
|
+
|
|
111
|
+
/** @type {any} */ (Vue).renderEffect(() => {
|
|
112
|
+
if (scope) scope.stop();
|
|
113
|
+
scope = new /** @type {any} */ (Vue).EffectScope();
|
|
114
|
+
nodes[0] = scope.run(() => resolve_value(nodes[0], render(), undefined));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return nodes;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A reusable Vue error boundary component.
|
|
122
|
+
*
|
|
123
|
+
* Used by the `@tsrx/vue` compiler to implement `try/catch` blocks.
|
|
124
|
+
* The `fallback` prop receives the caught error and a `reset` function
|
|
125
|
+
* that clears the error state to re-render the children.
|
|
126
|
+
*/
|
|
127
|
+
/**
|
|
128
|
+
* @param {{ content: () => any, fallback: (error: unknown, reset: () => void) => any }} props
|
|
129
|
+
* @returns {any}
|
|
130
|
+
*/
|
|
131
|
+
export function TsrxErrorBoundary(props) {
|
|
132
|
+
const instance = getCurrentInstance();
|
|
133
|
+
const state = instance ? boundary_states.get(instance) : undefined;
|
|
134
|
+
const error = state?.error ?? shallowRef(/** @type {unknown} */ (null));
|
|
135
|
+
const reset =
|
|
136
|
+
state?.reset ??
|
|
137
|
+
(() => {
|
|
138
|
+
error.value = null;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return create_boundary_nodes(() => {
|
|
142
|
+
if (error.value !== null) {
|
|
143
|
+
return props.fallback(error.value, reset);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
return props.content();
|
|
148
|
+
} catch (caught_error) {
|
|
149
|
+
error.value = caught_error;
|
|
150
|
+
return props.fallback(caught_error, reset);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @returns {void} */
|
|
156
|
+
TsrxErrorBoundary.__setup = function setup() {
|
|
157
|
+
const instance = getCurrentInstance();
|
|
158
|
+
if (!instance || boundary_states.has(instance)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const error = shallowRef(/** @type {unknown} */ (null));
|
|
163
|
+
const reset = () => {
|
|
164
|
+
error.value = null;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
boundary_states.set(instance, { error, reset });
|
|
168
|
+
|
|
169
|
+
onErrorCaptured((captured_error) => {
|
|
170
|
+
error.value = captured_error;
|
|
171
|
+
return false;
|
|
172
|
+
});
|
|
173
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** @import * as AST from 'estree' */
|
|
2
|
+
/** @import { CompileError, ParseOptions } from '@tsrx/core/types' */
|
|
3
|
+
|
|
4
|
+
import { createVolarMappingsResult, dedupeMappings, parseModule } from '@tsrx/core';
|
|
5
|
+
import { transform } from './transform.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse tsrx-vue source code to an ESTree AST.
|
|
9
|
+
* @param {string} source
|
|
10
|
+
* @param {string} [filename]
|
|
11
|
+
* @param {ParseOptions} [options]
|
|
12
|
+
* @returns {AST.Program}
|
|
13
|
+
*/
|
|
14
|
+
export function parse(source, filename, options) {
|
|
15
|
+
return parseModule(source, filename, options);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Compile tsrx-vue source code to a TSX module suitable for consumption by
|
|
20
|
+
* vue-jsx-vapor or another Vue JSX transform.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} source
|
|
23
|
+
* @param {string} [filename]
|
|
24
|
+
* @param {{ loose?: boolean }} [options]
|
|
25
|
+
* @returns {{ code: string, map: any, css: { code: string, hash: string } | null, errors: CompileError[] }}
|
|
26
|
+
*/
|
|
27
|
+
export function compile(source, filename, options) {
|
|
28
|
+
const errors = /** @type {CompileError[]} */ ([]);
|
|
29
|
+
const collect = !!options?.loose;
|
|
30
|
+
const ast = parseModule(source, filename, collect ? { loose: true, errors } : undefined);
|
|
31
|
+
const { ast: _ast, ...result } = transform(ast, source, filename);
|
|
32
|
+
return { ...result, errors };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compile tsrx-vue source to virtual TSX plus Volar mappings for editor tooling.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} source
|
|
39
|
+
* @param {string} [filename]
|
|
40
|
+
* @param {ParseOptions} [options]
|
|
41
|
+
* @returns {import('@tsrx/core/types').VolarMappingsResult}
|
|
42
|
+
*/
|
|
43
|
+
export function compile_to_volar_mappings(source, filename, options) {
|
|
44
|
+
const errors = /** @type {import('@tsrx/core/types').CompileError[]} */ ([]);
|
|
45
|
+
const ast = parseModule(source, filename, { ...options, errors });
|
|
46
|
+
const transformed = transform(ast, source, filename);
|
|
47
|
+
const result = createVolarMappingsResult({
|
|
48
|
+
ast: transformed.ast,
|
|
49
|
+
ast_from_source: ast,
|
|
50
|
+
source,
|
|
51
|
+
generated_code: transformed.code,
|
|
52
|
+
source_map: transformed.map,
|
|
53
|
+
errors,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
...result,
|
|
58
|
+
mappings: dedupeMappings(result.mappings),
|
|
59
|
+
};
|
|
60
|
+
}
|
package/src/transform.js
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
/** @import { JsxPlatform } from '@tsrx/core/types' */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
builders,
|
|
5
|
+
clone_expression_node,
|
|
6
|
+
clone_identifier,
|
|
7
|
+
componentToFunctionDeclaration,
|
|
8
|
+
createJsxTransform,
|
|
9
|
+
create_compile_error,
|
|
10
|
+
identifier_to_jsx_name,
|
|
11
|
+
setLocation,
|
|
12
|
+
} from '@tsrx/core';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Minimal Vue platform descriptor consumed by `createJsxTransform`.
|
|
16
|
+
*
|
|
17
|
+
* Vue largely reuses the shared JSX lowering while wrapping compiled
|
|
18
|
+
* components in `defineVaporComponent(...)` and handling its extra imports.
|
|
19
|
+
* Async component bodies still stay explicitly unsupported.
|
|
20
|
+
*
|
|
21
|
+
* @type {JsxPlatform}
|
|
22
|
+
*/
|
|
23
|
+
const vue_platform = {
|
|
24
|
+
name: 'Vue',
|
|
25
|
+
imports: {
|
|
26
|
+
suspense: 'vue',
|
|
27
|
+
errorBoundary: '@tsrx/vue/error-boundary',
|
|
28
|
+
},
|
|
29
|
+
jsx: {
|
|
30
|
+
rewriteClassAttr: false,
|
|
31
|
+
acceptedTsxKinds: ['vue'],
|
|
32
|
+
},
|
|
33
|
+
validation: {
|
|
34
|
+
requireUseServerForAwait: true,
|
|
35
|
+
scanUseServerDirectiveForAwaitWithCustomValidator: false,
|
|
36
|
+
unsupportedTryPendingMessage:
|
|
37
|
+
'Vue TSRX does not support `pending` blocks in component templates yet. Vue Suspense uses fallback slots rather than a `fallback` prop, so `try { ... } pending { ... }` cannot be lowered correctly for this target yet.',
|
|
38
|
+
},
|
|
39
|
+
hooks: {
|
|
40
|
+
initialState: () => ({
|
|
41
|
+
needs_define_vapor_component: false,
|
|
42
|
+
}),
|
|
43
|
+
isTopLevelSetupCall(call_expression) {
|
|
44
|
+
return is_vue_setup_call(call_expression);
|
|
45
|
+
},
|
|
46
|
+
wrapHelperComponent(helper_fn, helper_id, ctx, source_node) {
|
|
47
|
+
ctx.needs_define_vapor_component = true;
|
|
48
|
+
return wrap_helper_component(helper_fn, helper_id, source_node);
|
|
49
|
+
},
|
|
50
|
+
canHoistStaticNode(node) {
|
|
51
|
+
return !contains_component_jsx(node);
|
|
52
|
+
},
|
|
53
|
+
preprocessElementAttributes(attrs, ctx, element) {
|
|
54
|
+
return preprocess_ref_attributes(attrs, element, ctx);
|
|
55
|
+
},
|
|
56
|
+
renderForOf: (node, loop_params, body_statements) =>
|
|
57
|
+
render_for_of_as_vapor_template(node, loop_params, body_statements),
|
|
58
|
+
createErrorBoundaryContent(try_content) {
|
|
59
|
+
return {
|
|
60
|
+
type: 'ArrowFunctionExpression',
|
|
61
|
+
params: [],
|
|
62
|
+
body: try_content.expression,
|
|
63
|
+
async: false,
|
|
64
|
+
generator: false,
|
|
65
|
+
expression: true,
|
|
66
|
+
metadata: { path: [] },
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
transformElementChildren(node, walked_children, raw_children, attributes) {
|
|
70
|
+
return rewrite_host_text_or_html_children(node, walked_children, raw_children, attributes);
|
|
71
|
+
},
|
|
72
|
+
validateComponentAwait(await_expression) {
|
|
73
|
+
throw create_compile_error(
|
|
74
|
+
await_expression,
|
|
75
|
+
'`await` is not yet supported in Vue TSRX components.',
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
componentToFunction(component, ctx, helper_state) {
|
|
79
|
+
ctx.needs_define_vapor_component = true;
|
|
80
|
+
return component_to_vapor_component_declaration(component, ctx, helper_state);
|
|
81
|
+
},
|
|
82
|
+
injectImports(program, ctx) {
|
|
83
|
+
inject_vue_imports(program, ctx);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const transform = createJsxTransform(vue_platform);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {any} component
|
|
92
|
+
* @param {any} transform_context
|
|
93
|
+
* @param {any} helper_state
|
|
94
|
+
* @returns {any}
|
|
95
|
+
*/
|
|
96
|
+
function component_to_vapor_component_declaration(component, transform_context, helper_state) {
|
|
97
|
+
const fn = componentToFunctionDeclaration(component, transform_context, helper_state);
|
|
98
|
+
const generated_helpers = helper_state?.helpers || [];
|
|
99
|
+
const generated_statics = helper_state?.statics || [];
|
|
100
|
+
const call = create_define_vapor_component_call(
|
|
101
|
+
function_declaration_to_expression(fn),
|
|
102
|
+
generated_helpers,
|
|
103
|
+
generated_statics,
|
|
104
|
+
component,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (component.default || !component.id) {
|
|
108
|
+
return call;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const component_id = clone_identifier(component.id);
|
|
112
|
+
component_id.metadata = {
|
|
113
|
+
...component_id.metadata,
|
|
114
|
+
...(fn.id?.metadata || {}),
|
|
115
|
+
path: component_id.metadata?.path || [],
|
|
116
|
+
};
|
|
117
|
+
/** @type {any} */ (component_id.metadata).hover = create_component_hover_replacement(fn.params);
|
|
118
|
+
|
|
119
|
+
return setLocation(
|
|
120
|
+
/** @type {any} */ ({
|
|
121
|
+
type: 'VariableDeclaration',
|
|
122
|
+
kind: 'const',
|
|
123
|
+
declarations: [
|
|
124
|
+
{
|
|
125
|
+
type: 'VariableDeclarator',
|
|
126
|
+
id: component_id,
|
|
127
|
+
init: call,
|
|
128
|
+
metadata: { path: [] },
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
metadata: {
|
|
132
|
+
path: [],
|
|
133
|
+
generated_helpers,
|
|
134
|
+
generated_statics,
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
137
|
+
component,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {any} helper_fn
|
|
143
|
+
* @param {any} helper_id
|
|
144
|
+
* @param {any} source_node
|
|
145
|
+
* @returns {any}
|
|
146
|
+
*/
|
|
147
|
+
function wrap_helper_component(helper_fn, helper_id, source_node) {
|
|
148
|
+
return setLocation(
|
|
149
|
+
/** @type {any} */ ({
|
|
150
|
+
type: 'VariableDeclaration',
|
|
151
|
+
kind: 'const',
|
|
152
|
+
declarations: [
|
|
153
|
+
{
|
|
154
|
+
type: 'VariableDeclarator',
|
|
155
|
+
id: clone_identifier(helper_id),
|
|
156
|
+
init: create_define_vapor_component_call(
|
|
157
|
+
function_declaration_to_expression(helper_fn),
|
|
158
|
+
[],
|
|
159
|
+
[],
|
|
160
|
+
source_node,
|
|
161
|
+
),
|
|
162
|
+
metadata: { path: [] },
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
metadata: { path: [] },
|
|
166
|
+
}),
|
|
167
|
+
source_node,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {any} fn_expression
|
|
173
|
+
* @param {any[]} generated_helpers
|
|
174
|
+
* @param {any[]} generated_statics
|
|
175
|
+
* @param {any} source_node
|
|
176
|
+
* @returns {any}
|
|
177
|
+
*/
|
|
178
|
+
function create_define_vapor_component_call(
|
|
179
|
+
fn_expression,
|
|
180
|
+
generated_helpers,
|
|
181
|
+
generated_statics,
|
|
182
|
+
source_node,
|
|
183
|
+
) {
|
|
184
|
+
return setLocation(
|
|
185
|
+
/** @type {any} */ ({
|
|
186
|
+
type: 'CallExpression',
|
|
187
|
+
callee: {
|
|
188
|
+
type: 'Identifier',
|
|
189
|
+
name: 'defineVaporComponent',
|
|
190
|
+
metadata: { path: [] },
|
|
191
|
+
},
|
|
192
|
+
arguments: [fn_expression],
|
|
193
|
+
optional: false,
|
|
194
|
+
metadata: {
|
|
195
|
+
path: [],
|
|
196
|
+
generated_helpers,
|
|
197
|
+
generated_statics,
|
|
198
|
+
},
|
|
199
|
+
}),
|
|
200
|
+
source_node,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {any} node
|
|
206
|
+
* @param {any[]} loop_params
|
|
207
|
+
* @param {any[]} body_statements
|
|
208
|
+
* @returns {any | null}
|
|
209
|
+
*/
|
|
210
|
+
function render_for_of_as_vapor_template(node, loop_params, body_statements) {
|
|
211
|
+
if (body_statements.length !== 1) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const statement = body_statements[0];
|
|
216
|
+
if (statement?.type !== 'ReturnStatement' || !statement.argument) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const rendered = statement.argument;
|
|
221
|
+
const key_expression = node.key
|
|
222
|
+
? clone_expression_node(node.key)
|
|
223
|
+
: find_jsx_key_expression(rendered);
|
|
224
|
+
strip_top_level_jsx_keys(rendered);
|
|
225
|
+
const children = rendered.type === 'JSXFragment' ? rendered.children : [rendered];
|
|
226
|
+
const attributes = [
|
|
227
|
+
{
|
|
228
|
+
type: 'JSXAttribute',
|
|
229
|
+
name: { type: 'JSXIdentifier', name: 'v-for', metadata: { path: [] } },
|
|
230
|
+
value: to_jsx_expression_container({
|
|
231
|
+
type: 'BinaryExpression',
|
|
232
|
+
operator: 'in',
|
|
233
|
+
left: create_v_for_left(loop_params),
|
|
234
|
+
right: clone_expression_node(node.right),
|
|
235
|
+
metadata: { path: [] },
|
|
236
|
+
}),
|
|
237
|
+
metadata: { path: [] },
|
|
238
|
+
},
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
if (key_expression) {
|
|
242
|
+
attributes.push({
|
|
243
|
+
type: 'JSXAttribute',
|
|
244
|
+
name: { type: 'JSXIdentifier', name: 'key', metadata: { path: [] } },
|
|
245
|
+
value: to_jsx_expression_container(key_expression),
|
|
246
|
+
metadata: { path: [] },
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return to_jsx_expression_container({
|
|
251
|
+
type: 'JSXElement',
|
|
252
|
+
openingElement: {
|
|
253
|
+
type: 'JSXOpeningElement',
|
|
254
|
+
name: { type: 'JSXIdentifier', name: 'template', metadata: { path: [] } },
|
|
255
|
+
attributes,
|
|
256
|
+
selfClosing: false,
|
|
257
|
+
metadata: { path: [] },
|
|
258
|
+
},
|
|
259
|
+
closingElement: {
|
|
260
|
+
type: 'JSXClosingElement',
|
|
261
|
+
name: { type: 'JSXIdentifier', name: 'template', metadata: { path: [] } },
|
|
262
|
+
metadata: { path: [] },
|
|
263
|
+
},
|
|
264
|
+
children,
|
|
265
|
+
metadata: { path: [] },
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {any[]} loop_params
|
|
271
|
+
* @returns {any}
|
|
272
|
+
*/
|
|
273
|
+
function create_v_for_left(loop_params) {
|
|
274
|
+
if (loop_params.length === 1) {
|
|
275
|
+
return clone_expression_node(loop_params[0]);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
type: 'SequenceExpression',
|
|
280
|
+
expressions: loop_params.map((param) => clone_expression_node(param)),
|
|
281
|
+
metadata: { path: [] },
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @param {any} node
|
|
287
|
+
* @returns {any | null}
|
|
288
|
+
*/
|
|
289
|
+
function find_jsx_key_expression(node) {
|
|
290
|
+
if (node?.type !== 'JSXElement') {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
for (const attr of node.openingElement?.attributes || []) {
|
|
295
|
+
if (
|
|
296
|
+
attr.type === 'JSXAttribute' &&
|
|
297
|
+
attr.name?.type === 'JSXIdentifier' &&
|
|
298
|
+
attr.name.name === 'key'
|
|
299
|
+
) {
|
|
300
|
+
return attr.value?.type === 'JSXExpressionContainer'
|
|
301
|
+
? clone_expression_node(attr.value.expression)
|
|
302
|
+
: clone_expression_node(attr.value);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* @param {any} node
|
|
311
|
+
* @returns {void}
|
|
312
|
+
*/
|
|
313
|
+
function strip_top_level_jsx_keys(node) {
|
|
314
|
+
if (node?.type === 'JSXElement') {
|
|
315
|
+
node.openingElement.attributes = (node.openingElement.attributes || []).filter(
|
|
316
|
+
(/** @type {any} */ attr) =>
|
|
317
|
+
!(
|
|
318
|
+
attr.type === 'JSXAttribute' &&
|
|
319
|
+
attr.name?.type === 'JSXIdentifier' &&
|
|
320
|
+
attr.name.name === 'key'
|
|
321
|
+
),
|
|
322
|
+
);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (node?.type === 'JSXFragment') {
|
|
327
|
+
for (const child of node.children || []) {
|
|
328
|
+
strip_top_level_jsx_keys(child);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @param {any} fn
|
|
335
|
+
* @returns {any}
|
|
336
|
+
*/
|
|
337
|
+
function function_declaration_to_expression(fn) {
|
|
338
|
+
return {
|
|
339
|
+
...fn,
|
|
340
|
+
type: 'FunctionExpression',
|
|
341
|
+
metadata: {
|
|
342
|
+
...(fn.metadata || {}),
|
|
343
|
+
path: fn.metadata?.path || [],
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const VUE_COMPONENT_HOVER_LABEL_REGEX = /(function|\((property|method)\))/;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @param {any[]} [params]
|
|
352
|
+
* @returns {(content: string) => string}
|
|
353
|
+
*/
|
|
354
|
+
function create_component_hover_replacement(params) {
|
|
355
|
+
const lazy_param_regexes = (params || [])
|
|
356
|
+
.filter((param) => param.type === 'Identifier' && /^__lazy\d+$/.test(param.name))
|
|
357
|
+
.map((param) => new RegExp(`\\b${param.name}\\s*:\\s*`, 'g'));
|
|
358
|
+
|
|
359
|
+
return (content) => {
|
|
360
|
+
let next = content.replace(VUE_COMPONENT_HOVER_LABEL_REGEX, (_, fn, kind) => {
|
|
361
|
+
if (fn === 'function') return 'component';
|
|
362
|
+
return `(component ${kind})`;
|
|
363
|
+
});
|
|
364
|
+
for (const regex of lazy_param_regexes) {
|
|
365
|
+
next = next.replace(regex, '&');
|
|
366
|
+
}
|
|
367
|
+
return next;
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const VUE_SETUP_CALLS = new Set([
|
|
372
|
+
'ref',
|
|
373
|
+
'shallowRef',
|
|
374
|
+
'computed',
|
|
375
|
+
'reactive',
|
|
376
|
+
'shallowReactive',
|
|
377
|
+
'customRef',
|
|
378
|
+
'toRef',
|
|
379
|
+
'toRefs',
|
|
380
|
+
'useTemplateRef',
|
|
381
|
+
]);
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* @param {any} call_expression
|
|
385
|
+
* @returns {boolean}
|
|
386
|
+
*/
|
|
387
|
+
function is_vue_setup_call(call_expression) {
|
|
388
|
+
const callee = call_expression?.callee;
|
|
389
|
+
if (!callee) return false;
|
|
390
|
+
|
|
391
|
+
if (callee.type === 'Identifier') {
|
|
392
|
+
return VUE_SETUP_CALLS.has(callee.name);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (
|
|
396
|
+
callee.type === 'MemberExpression' &&
|
|
397
|
+
callee.computed === false &&
|
|
398
|
+
callee.property?.type === 'Identifier'
|
|
399
|
+
) {
|
|
400
|
+
return VUE_SETUP_CALLS.has(callee.property.name);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @param {any[]} attrs
|
|
408
|
+
* @param {any} element
|
|
409
|
+
* @param {any} transform_context
|
|
410
|
+
* @returns {any[]}
|
|
411
|
+
*/
|
|
412
|
+
function preprocess_ref_attributes(attrs, element, transform_context) {
|
|
413
|
+
/** @type {any[]} */
|
|
414
|
+
const result = [];
|
|
415
|
+
/** @type {any[]} */
|
|
416
|
+
const ref_attrs = [];
|
|
417
|
+
|
|
418
|
+
for (const attr of attrs) {
|
|
419
|
+
if (!attr) continue;
|
|
420
|
+
if (attr.type === 'RefAttribute') {
|
|
421
|
+
ref_attrs.push(attr);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
result.push(attr);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (ref_attrs.length > 0 && is_component_like_element(element)) {
|
|
428
|
+
throw create_compile_error(
|
|
429
|
+
ref_attrs[0],
|
|
430
|
+
'`{ref ...}` on the Vue target is only supported on host elements. Vue component refs resolve to component instances rather than the rendered DOM node, so Ripple-style component refs are not supported here.',
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (ref_attrs.length === 1) {
|
|
435
|
+
result.push(ref_attrs[0]);
|
|
436
|
+
} else if (ref_attrs.length > 1) {
|
|
437
|
+
result.push({
|
|
438
|
+
type: 'RefAttribute',
|
|
439
|
+
argument: create_combined_ref_callback(ref_attrs),
|
|
440
|
+
loc: ref_attrs[0].loc,
|
|
441
|
+
metadata: { path: [] },
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @param {any[]} ref_attrs
|
|
450
|
+
* @returns {any}
|
|
451
|
+
*/
|
|
452
|
+
function create_combined_ref_callback(ref_attrs) {
|
|
453
|
+
const node_id = builders.id('node');
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
type: 'ArrowFunctionExpression',
|
|
457
|
+
params: [node_id],
|
|
458
|
+
body: {
|
|
459
|
+
type: 'BlockStatement',
|
|
460
|
+
body: ref_attrs.map((attr) => ({
|
|
461
|
+
type: 'ExpressionStatement',
|
|
462
|
+
expression: {
|
|
463
|
+
type: 'CallExpression',
|
|
464
|
+
callee: attr.argument,
|
|
465
|
+
arguments: [clone_identifier(node_id)],
|
|
466
|
+
optional: false,
|
|
467
|
+
metadata: { path: [] },
|
|
468
|
+
},
|
|
469
|
+
metadata: { path: [] },
|
|
470
|
+
})),
|
|
471
|
+
metadata: { path: [] },
|
|
472
|
+
},
|
|
473
|
+
expression: false,
|
|
474
|
+
async: false,
|
|
475
|
+
generator: false,
|
|
476
|
+
metadata: { path: [] },
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* @param {any} node
|
|
482
|
+
* @param {any[]} walked_children
|
|
483
|
+
* @param {any[]} raw_children
|
|
484
|
+
* @param {any[]} attributes
|
|
485
|
+
* @returns {{ children: any[]; selfClosing?: boolean } | null}
|
|
486
|
+
*/
|
|
487
|
+
function rewrite_host_text_or_html_children(node, walked_children, raw_children, attributes) {
|
|
488
|
+
const source_children = raw_children || walked_children;
|
|
489
|
+
const is_composite = is_component_like_element(node);
|
|
490
|
+
const html_children = source_children.filter((child) => child?.type === 'Html');
|
|
491
|
+
|
|
492
|
+
if (html_children.length > 0) {
|
|
493
|
+
if (
|
|
494
|
+
is_composite ||
|
|
495
|
+
source_children.length !== 1 ||
|
|
496
|
+
has_dom_content_attribute(attributes, 'innerHTML') ||
|
|
497
|
+
has_dom_content_attribute(attributes, 'textContent')
|
|
498
|
+
) {
|
|
499
|
+
throw create_compile_error(
|
|
500
|
+
html_children[0],
|
|
501
|
+
'`{html ...}` on the Vue target is only supported as the sole child of a host element. Use `innerHTML={...}` as an element attribute when you need the explicit prop form.',
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const walked_html = walked_children[0] || html_children[0];
|
|
506
|
+
attributes.push(
|
|
507
|
+
create_jsx_attribute(
|
|
508
|
+
'innerHTML',
|
|
509
|
+
to_jsx_expression_container(walked_html.expression, html_children[0]),
|
|
510
|
+
html_children[0],
|
|
511
|
+
),
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
return { children: [], selfClosing: true };
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (!is_composite && source_children.length === 1 && source_children[0]?.type === 'Text') {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* @param {any} node
|
|
526
|
+
* @returns {boolean}
|
|
527
|
+
*/
|
|
528
|
+
function is_component_like_element(node) {
|
|
529
|
+
const id = node?.id;
|
|
530
|
+
if (!id) return false;
|
|
531
|
+
if (id.type === 'Identifier') return /^[A-Z]/.test(id.name);
|
|
532
|
+
if (id.type === 'MemberExpression') return true;
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* @param {any[]} attributes
|
|
538
|
+
* @param {string} name
|
|
539
|
+
* @returns {boolean}
|
|
540
|
+
*/
|
|
541
|
+
function has_dom_content_attribute(attributes, name) {
|
|
542
|
+
return attributes.some(
|
|
543
|
+
(/** @type {any} */ attribute) =>
|
|
544
|
+
attribute &&
|
|
545
|
+
(attribute.type === 'JSXSpreadAttribute' ||
|
|
546
|
+
(attribute.type === 'JSXAttribute' &&
|
|
547
|
+
attribute.name?.type === 'JSXIdentifier' &&
|
|
548
|
+
attribute.name.name === name)),
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @param {string} name
|
|
554
|
+
* @param {any} value
|
|
555
|
+
* @param {any} source_node
|
|
556
|
+
* @returns {any}
|
|
557
|
+
*/
|
|
558
|
+
function create_jsx_attribute(name, value, source_node) {
|
|
559
|
+
return setLocation(
|
|
560
|
+
/** @type {any} */ ({
|
|
561
|
+
type: 'JSXAttribute',
|
|
562
|
+
name: identifier_to_jsx_name(builders.id(name)),
|
|
563
|
+
value,
|
|
564
|
+
shorthand: false,
|
|
565
|
+
metadata: { path: [] },
|
|
566
|
+
}),
|
|
567
|
+
source_node,
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* @param {any} expression
|
|
573
|
+
* @param {any} source_node
|
|
574
|
+
* @returns {any}
|
|
575
|
+
*/
|
|
576
|
+
function to_jsx_expression_container(expression, source_node = expression) {
|
|
577
|
+
void source_node;
|
|
578
|
+
return {
|
|
579
|
+
type: 'JSXExpressionContainer',
|
|
580
|
+
expression,
|
|
581
|
+
metadata: { path: [] },
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* @param {any} node
|
|
587
|
+
* @returns {boolean}
|
|
588
|
+
*/
|
|
589
|
+
function contains_component_jsx(node) {
|
|
590
|
+
if (!node || typeof node !== 'object') {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (node.type === 'JSXElement') {
|
|
595
|
+
if (is_component_jsx_name(node.openingElement?.name)) {
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return node.children.some(contains_component_jsx);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (node.type === 'JSXFragment') {
|
|
603
|
+
return node.children.some(contains_component_jsx);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (node.type === 'JSXExpressionContainer') {
|
|
607
|
+
return contains_component_jsx(node.expression);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (Array.isArray(node)) {
|
|
611
|
+
return node.some(contains_component_jsx);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* @param {any} name
|
|
619
|
+
* @returns {boolean}
|
|
620
|
+
*/
|
|
621
|
+
function is_component_jsx_name(name) {
|
|
622
|
+
if (!name || typeof name !== 'object') {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (name.type === 'JSXIdentifier') {
|
|
627
|
+
const first = name.name?.[0];
|
|
628
|
+
return first != null && first >= 'A' && first <= 'Z';
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (name.type === 'JSXMemberExpression') {
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @param {import('estree').Program} program
|
|
640
|
+
* @param {any} transform_context
|
|
641
|
+
* @returns {void}
|
|
642
|
+
*/
|
|
643
|
+
function inject_vue_imports(program, transform_context) {
|
|
644
|
+
if (transform_context.needs_define_vapor_component) {
|
|
645
|
+
ensure_named_import(program, 'vue-jsx-vapor', 'defineVaporComponent');
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (transform_context.needs_suspense) {
|
|
649
|
+
ensure_named_import(program, 'vue', 'Suspense');
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (transform_context.needs_error_boundary) {
|
|
653
|
+
ensure_named_import(program, '@tsrx/vue/error-boundary', 'TsrxErrorBoundary');
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* @param {import('estree').Program} program
|
|
659
|
+
* @param {string} source
|
|
660
|
+
* @param {string} name
|
|
661
|
+
* @returns {void}
|
|
662
|
+
*/
|
|
663
|
+
function ensure_named_import(program, source, name) {
|
|
664
|
+
for (const statement of program.body) {
|
|
665
|
+
if (statement.type !== 'ImportDeclaration' || statement.source?.value !== source) {
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const has_specifier = statement.specifiers.some(
|
|
670
|
+
(/** @type {any} */ specifier) =>
|
|
671
|
+
specifier.type === 'ImportSpecifier' &&
|
|
672
|
+
specifier.imported?.type === 'Identifier' &&
|
|
673
|
+
specifier.imported.name === name,
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
if (!has_specifier) {
|
|
677
|
+
statement.specifiers.push(create_import_specifier(name));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
program.body.unshift(create_import_declaration(source, [create_import_specifier(name)]));
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* @param {string} name
|
|
688
|
+
* @returns {any}
|
|
689
|
+
*/
|
|
690
|
+
function create_import_specifier(name) {
|
|
691
|
+
return {
|
|
692
|
+
type: 'ImportSpecifier',
|
|
693
|
+
imported: builders.id(name),
|
|
694
|
+
local: builders.id(name),
|
|
695
|
+
importKind: 'value',
|
|
696
|
+
metadata: { path: [] },
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* @param {string} source
|
|
702
|
+
* @param {any[]} specifiers
|
|
703
|
+
* @returns {any}
|
|
704
|
+
*/
|
|
705
|
+
function create_import_declaration(source, specifiers) {
|
|
706
|
+
return {
|
|
707
|
+
type: 'ImportDeclaration',
|
|
708
|
+
attributes: [],
|
|
709
|
+
specifiers,
|
|
710
|
+
importKind: 'value',
|
|
711
|
+
source: builders.literal(source),
|
|
712
|
+
metadata: { path: [] },
|
|
713
|
+
};
|
|
714
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Program } from 'estree';
|
|
2
|
+
import type { CompileError, ParseOptions, VolarMappingsResult } from '@tsrx/core/types';
|
|
3
|
+
|
|
4
|
+
export function parse(source: string, filename?: string, options?: ParseOptions): Program;
|
|
5
|
+
|
|
6
|
+
export function compile(
|
|
7
|
+
source: string,
|
|
8
|
+
filename?: string,
|
|
9
|
+
options?: { loose?: boolean },
|
|
10
|
+
): {
|
|
11
|
+
code: string;
|
|
12
|
+
map: unknown;
|
|
13
|
+
css: { code: string; hash: string } | null;
|
|
14
|
+
errors: CompileError[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function compile_to_volar_mappings(
|
|
18
|
+
source: string,
|
|
19
|
+
filename?: string,
|
|
20
|
+
options?: ParseOptions,
|
|
21
|
+
): VolarMappingsResult;
|