mirror of https://github.com/sveltejs/svelte
parent
09696b9ebb
commit
a670a89bc3
@ -0,0 +1,18 @@
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { Context } from '../types' */
|
||||
import { mark_subtree_dynamic } from './shared/fragment.js';
|
||||
|
||||
/**
|
||||
* @param {AST.PortalBlock} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function PortalBlock(node, context) {
|
||||
mark_subtree_dynamic(context.path);
|
||||
|
||||
context.visit(node.expression, {
|
||||
...context.state,
|
||||
expression: node.metadata.expression
|
||||
});
|
||||
|
||||
context.visit(node.fragment);
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { Context } from '../types' */
|
||||
import { validate_opening_tag } from './shared/utils.js';
|
||||
import { mark_subtree_dynamic } from './shared/fragment.js';
|
||||
|
||||
/**
|
||||
* @param {AST.PortalTag} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function PortalTag(node, context) {
|
||||
validate_opening_tag(node, context.state, '@');
|
||||
node.metadata.path = [...context.path];
|
||||
mark_subtree_dynamic(context.path);
|
||||
|
||||
context.visit(node.expression, {
|
||||
...context.state,
|
||||
expression: node.metadata.expression
|
||||
});
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { Context } from '../types' */
|
||||
import { visit_component } from './shared/component.js';
|
||||
import * as e from '../../../errors.js';
|
||||
import * as w from '../../../warnings.js';
|
||||
import { filename } from '../../../state.js';
|
||||
|
||||
/**
|
||||
* @param {AST.SveltePortal} node
|
||||
* @param {Context} context
|
||||
*/
|
||||
export function SveltePortal(node, context) {
|
||||
// TODO validation for attributes etc
|
||||
context.next();
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
/** @import { BlockStatement } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
import { build_expression } from './shared/utils.js';
|
||||
|
||||
/**
|
||||
* @param {AST.PortalBlock} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function PortalBlock(node, context) {
|
||||
context.state.template.push_comment();
|
||||
|
||||
const value = build_expression(context, node.expression, node.metadata.expression);
|
||||
const body = /** @type {BlockStatement} */ (
|
||||
context.visit(node.fragment, { ...context.state, transform: { ...context.state.transform } })
|
||||
);
|
||||
const portal = b.call('$.portal', value, b.arrow([b.id('$$anchor')], body));
|
||||
|
||||
context.state.init.push(
|
||||
b.stmt(node.metadata.expression.has_state ? b.call('$.render_effect', b.thunk(portal)) : portal)
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
import { build_expression } from './shared/utils.js';
|
||||
|
||||
/**
|
||||
* @param {AST.PortalTag} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function PortalTag(node, context) {
|
||||
context.state.template.push_comment();
|
||||
|
||||
const value = build_expression(context, node.expression, node.metadata.expression);
|
||||
context.state.init.push(b.stmt(b.call('$.portal_outlet', context.state.node, b.thunk(value))));
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/** @import { BlockStatement } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types' */
|
||||
import * as b from '../../../../utils/builders.js';
|
||||
import { build_attribute_value } from './shared/element.js';
|
||||
|
||||
/**
|
||||
* @param {AST.SveltePortal} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function SveltePortal(node, context) {
|
||||
const _for = node.attributes.find((attr) => attr.type === 'Attribute' && attr.name === 'for');
|
||||
const target = node.attributes.find(
|
||||
(attr) => attr.type === 'Attribute' && attr.name === 'target'
|
||||
);
|
||||
|
||||
context.state.template.push_comment();
|
||||
|
||||
if (target) {
|
||||
const { value, has_state } = build_attribute_value(
|
||||
/** @type {AST.Attribute} */ (target).value,
|
||||
context
|
||||
);
|
||||
const body = /** @type {BlockStatement} */ (
|
||||
context.visit(node.fragment, { ...context.state, transform: { ...context.state.transform } })
|
||||
);
|
||||
const portal = b.call('$.portal', value, b.arrow([b.id('$$anchor')], body));
|
||||
context.state.init.push(
|
||||
b.stmt(has_state ? b.call('$.render_effect', b.thunk(portal)) : portal)
|
||||
);
|
||||
} else {
|
||||
// TODO reactive sources? Doesn't really make sense IMHO
|
||||
const value = build_attribute_value(/** @type {AST.Attribute} */ (_for).value, context);
|
||||
context.state.init.push(b.stmt(b.call('$.portal_outlet', context.state.node, value.value)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
/** @import { BlockStatement } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types.js' */
|
||||
import * as b from '#compiler/builders';
|
||||
|
||||
/**
|
||||
* @param {AST.PortalBlock} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function PortalBlock(node, context) {
|
||||
const value = /** @type {import('estree').Expression} */ (context.visit(node.expression));
|
||||
const body = /** @type {BlockStatement} */ (context.visit(node.fragment, context.state));
|
||||
|
||||
context.state.template.push(
|
||||
b.stmt(b.call('$.portal', b.id('$$renderer'), value, b.arrow([b.id('$$renderer')], body)))
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/** @import { Expression } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types.js' */
|
||||
import * as b from '#compiler/builders';
|
||||
import { PromiseOptimiser } from './shared/utils.js';
|
||||
|
||||
/**
|
||||
* @param {AST.PortalTag} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function PortalTag(node, context) {
|
||||
const optimiser = new PromiseOptimiser();
|
||||
const value = optimiser.transform(
|
||||
/** @type {Expression} */ (context.visit(node.expression)),
|
||||
node.metadata.expression
|
||||
);
|
||||
|
||||
context.state.template.push(
|
||||
...optimiser.render_block([b.stmt(b.call('$.portal_outlet', b.id('$$renderer'), value))])
|
||||
);
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/** @import { BlockStatement } from 'estree' */
|
||||
/** @import { AST } from '#compiler' */
|
||||
/** @import { ComponentContext } from '../types.js' */
|
||||
import * as b from '../../../../utils/builders.js';
|
||||
import { build_attribute_value } from './shared/utils.js';
|
||||
|
||||
/**
|
||||
* @param {AST.SveltePortal} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function SveltePortal(node, context) {
|
||||
const _for = node.attributes.find((attr) => attr.type === 'Attribute' && attr.name === 'for');
|
||||
const target = node.attributes.find(
|
||||
(attr) => attr.type === 'Attribute' && attr.name === 'target'
|
||||
);
|
||||
|
||||
if (target) {
|
||||
const value = build_attribute_value(
|
||||
/** @type {AST.Attribute} */ (target).value,
|
||||
context,
|
||||
(expression) => expression
|
||||
);
|
||||
const body = /** @type {BlockStatement} */ (context.visit(node.fragment, context.state));
|
||||
context.state.template.push(
|
||||
b.stmt(b.call('$.portal', b.id('$$renderer'), value, b.arrow([b.id('$$renderer')], body)))
|
||||
);
|
||||
} else {
|
||||
const value = build_attribute_value(
|
||||
/** @type {AST.Attribute} */ (_for).value,
|
||||
context,
|
||||
(expression) => expression
|
||||
);
|
||||
context.state.template.push(b.stmt(b.call('$.portal_outlet', b.id('$$renderer'), value)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { createPortalKey } from 'svelte';
|
||||
|
||||
const key = createPortalKey('example');
|
||||
</script>
|
||||
|
||||
{@portal key}
|
||||
|
||||
{#portal key}
|
||||
<p>portaled</p>
|
||||
{/portal}
|
||||
@ -0,0 +1,327 @@
|
||||
{
|
||||
"css": null,
|
||||
"js": [],
|
||||
"start": 0,
|
||||
"end": 160,
|
||||
"type": "Root",
|
||||
"fragment": {
|
||||
"type": "Fragment",
|
||||
"nodes": [
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 103,
|
||||
"end": 105,
|
||||
"raw": "\n\n",
|
||||
"data": "\n\n"
|
||||
},
|
||||
{
|
||||
"type": "PortalTag",
|
||||
"start": 105,
|
||||
"end": 118,
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start": 114,
|
||||
"end": 117,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 7,
|
||||
"column": 9
|
||||
},
|
||||
"end": {
|
||||
"line": 7,
|
||||
"column": 12
|
||||
}
|
||||
},
|
||||
"name": "key"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 118,
|
||||
"end": 120,
|
||||
"raw": "\n\n",
|
||||
"data": "\n\n"
|
||||
},
|
||||
{
|
||||
"type": "PortalBlock",
|
||||
"start": 120,
|
||||
"end": 160,
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start": 129,
|
||||
"end": 132,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 9,
|
||||
"column": 9
|
||||
},
|
||||
"end": {
|
||||
"line": 9,
|
||||
"column": 12
|
||||
}
|
||||
},
|
||||
"name": "key"
|
||||
},
|
||||
"fragment": {
|
||||
"type": "Fragment",
|
||||
"nodes": [
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 133,
|
||||
"end": 135,
|
||||
"raw": "\n\t",
|
||||
"data": "\n\t"
|
||||
},
|
||||
{
|
||||
"type": "RegularElement",
|
||||
"start": 135,
|
||||
"end": 150,
|
||||
"name": "p",
|
||||
"name_loc": {
|
||||
"start": {
|
||||
"line": 10,
|
||||
"column": 2,
|
||||
"character": 136
|
||||
},
|
||||
"end": {
|
||||
"line": 10,
|
||||
"column": 3,
|
||||
"character": 137
|
||||
}
|
||||
},
|
||||
"attributes": [],
|
||||
"fragment": {
|
||||
"type": "Fragment",
|
||||
"nodes": [
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 138,
|
||||
"end": 146,
|
||||
"raw": "portaled",
|
||||
"data": "portaled"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"start": 150,
|
||||
"end": 151,
|
||||
"raw": "\n",
|
||||
"data": "\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": null,
|
||||
"comments": [],
|
||||
"instance": {
|
||||
"type": "Script",
|
||||
"start": 0,
|
||||
"end": 103,
|
||||
"context": "default",
|
||||
"content": {
|
||||
"type": "Program",
|
||||
"start": 8,
|
||||
"end": 94,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 5,
|
||||
"column": 9
|
||||
}
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ImportDeclaration",
|
||||
"start": 10,
|
||||
"end": 51,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 42
|
||||
}
|
||||
},
|
||||
"specifiers": [
|
||||
{
|
||||
"type": "ImportSpecifier",
|
||||
"start": 19,
|
||||
"end": 34,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 25
|
||||
}
|
||||
},
|
||||
"imported": {
|
||||
"type": "Identifier",
|
||||
"start": 19,
|
||||
"end": 34,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 25
|
||||
}
|
||||
},
|
||||
"name": "createPortalKey"
|
||||
},
|
||||
"local": {
|
||||
"type": "Identifier",
|
||||
"start": 19,
|
||||
"end": 34,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 25
|
||||
}
|
||||
},
|
||||
"name": "createPortalKey"
|
||||
}
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"type": "Literal",
|
||||
"start": 42,
|
||||
"end": 50,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"column": 33
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"column": 41
|
||||
}
|
||||
},
|
||||
"value": "svelte",
|
||||
"raw": "'svelte'"
|
||||
},
|
||||
"attributes": []
|
||||
},
|
||||
{
|
||||
"type": "VariableDeclaration",
|
||||
"start": 54,
|
||||
"end": 93,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 4,
|
||||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 40
|
||||
}
|
||||
},
|
||||
"declarations": [
|
||||
{
|
||||
"type": "VariableDeclarator",
|
||||
"start": 60,
|
||||
"end": 92,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 4,
|
||||
"column": 7
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 39
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start": 60,
|
||||
"end": 63,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 4,
|
||||
"column": 7
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 10
|
||||
}
|
||||
},
|
||||
"name": "key"
|
||||
},
|
||||
"init": {
|
||||
"type": "CallExpression",
|
||||
"start": 66,
|
||||
"end": 92,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 4,
|
||||
"column": 13
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 39
|
||||
}
|
||||
},
|
||||
"callee": {
|
||||
"type": "Identifier",
|
||||
"start": 66,
|
||||
"end": 81,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 4,
|
||||
"column": 13
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 28
|
||||
}
|
||||
},
|
||||
"name": "createPortalKey"
|
||||
},
|
||||
"arguments": [
|
||||
{
|
||||
"type": "Literal",
|
||||
"start": 82,
|
||||
"end": 91,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 4,
|
||||
"column": 29
|
||||
},
|
||||
"end": {
|
||||
"line": 4,
|
||||
"column": 38
|
||||
}
|
||||
},
|
||||
"value": "example",
|
||||
"raw": "'example'"
|
||||
}
|
||||
],
|
||||
"optional": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"kind": "const"
|
||||
}
|
||||
],
|
||||
"sourceType": "module"
|
||||
},
|
||||
"attributes": []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
mode: ['client'],
|
||||
html: `<main><p>portaled</p></main>`
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import { createPortalKey } from 'svelte';
|
||||
|
||||
const key = createPortalKey('example');
|
||||
let target = $state(null);
|
||||
</script>
|
||||
|
||||
<main bind:this={target}></main>
|
||||
|
||||
{#portal target}
|
||||
<p>portaled</p>
|
||||
{/portal}
|
||||
@ -0,0 +1,5 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<main><p>portaled</p></main>`
|
||||
});
|
||||
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import { createPortalKey } from 'svelte';
|
||||
|
||||
const key = createPortalKey('example');
|
||||
</script>
|
||||
|
||||
<main>
|
||||
{@portal key}
|
||||
</main>
|
||||
|
||||
{#portal key}
|
||||
<p>portaled</p>
|
||||
{/portal}
|
||||
Loading…
Reference in new issue