-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
328 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
import * as parser from 'svelte/compiler'; | ||
import { print } from 'code-red'; | ||
|
||
export function compile( | ||
code = ` | ||
<div id="12" name={3+2} label="1{2}"></div> | ||
<button disabled={!clickable}>...</button> | ||
<button disabled>can't touch this</button> | ||
<input type=checkbox /> | ||
<input required={false} placeholder="This input field is not required" /> | ||
<div title={null}>This div has no title attribute</div> | ||
<button disabled="{number !== 42}">...</button> | ||
<button {disabled}>...</button> | ||
<Widget foo={bar} answer={42} text="hello" /> | ||
<p>{a} + {b} = {a + b}.</p> | ||
<div>{(/^[A-Za-z ]+$/).test(value) ? x : y}</div> | ||
{#if expression}...{:else}...{/if} | ||
{#if answer === 42} | ||
<p>what was the question?</p> | ||
{/if} | ||
----- | ||
{#if porridge.temperature > 100} | ||
<p>too hot!</p> | ||
{:else if 80 > porridge.temperature} | ||
<p>too cold!</p> | ||
{:else} | ||
<p>just right!</p> | ||
{/if} | ||
--- | ||
{#each expression as name}...{/each} | ||
<ul> | ||
{#each items as item} | ||
<li>{item.name} x {item.qty}</li> | ||
{/each} | ||
</ul> | ||
{#each items as item, i} | ||
<li>{i + 1}: {item.name} x {item.qty}</li> | ||
{/each} | ||
{#each items as item (item.id)} | ||
<li>{item.name} x {item.qty}</li> | ||
{/each} | ||
{#each items as item, i (item.id)} | ||
<li>{i + 1}: {item.name} x {item.qty}</li> | ||
{/each} | ||
<MyComponent {...rest} /> | ||
<button on:click={handleClick}> | ||
count: {count} | ||
</button> | ||
<div class="name"></div> | ||
<div class={isActive ? 'active' : ''} >...</div> | ||
<div class:active={isActive}>...</div> | ||
<div class:active class:inactive={!active} class:isAdmin>...</div> | ||
<div style:color={myColor}>...</div> | ||
`, | ||
) { | ||
// code-red | ||
const result = parser.compile(code, { | ||
dev: true, | ||
preserveWhitespace: true, | ||
preserveComments: false, | ||
}); | ||
|
||
console.log('ast', JSON.stringify(result.ast.html.children, null, 2)); | ||
|
||
return result.ast.html.children.map(transform).join(','); | ||
} | ||
|
||
function transformElement(element) { | ||
const modifiers = element.attributes.filter( | ||
(el) => el.type === 'EventHandler' || el.type === 'StyleDirective', | ||
); | ||
const properties = element.attributes.filter( | ||
(el) => | ||
el.type === 'Class' || (el.type === 'Attribute' && el.name === 'class'), | ||
); | ||
const attributes = element.attributes.filter( | ||
(el) => !modifiers.includes(el) && !properties.includes(el), | ||
); | ||
const compiledProperties = `[${properties | ||
.map((el) => { | ||
if (el.type === 'Class') { | ||
return `['',()=>${print(el.expression).code} ? ${escapeText(el.name)} : '']`; | ||
} | ||
if (Array.isArray(el.value)) { | ||
if (el.value.length !== 1) { | ||
throw new Error('Unknown attribute type'); | ||
} | ||
if (el.value[0].type === 'MustacheTag') { | ||
return `['', () => ${transform(el.value[0])}]`; | ||
} | ||
return `['', ${transform(el.value[0])}]`; | ||
} else { | ||
throw new Error('Unknown attribute type'); | ||
} | ||
}) | ||
.join(',')}]`; | ||
const compiledModifiers = `[${modifiers | ||
.map((el) => { | ||
if (el.type === 'StyleDirective') { | ||
return `[2, ${escapeText(el.name)}, () => ${print(el.value[0].expression).code}]`; | ||
} | ||
return `[1,${escapeText(el.name)},${print(el.expression).code}]`; | ||
}) | ||
.join(',')}]`; | ||
return `$_tag(${escapeText( | ||
element.name, | ||
)}, [${compiledProperties}, [${attributes.map( | ||
transformAttribute, | ||
)}], ${compiledModifiers}], [${element.children.map((node) => { | ||
if (node.type === 'MustacheTag') { | ||
return `()=>${transform(node)}`; | ||
} else { | ||
return transform(node); | ||
} | ||
})}], this)`; | ||
} | ||
function transformArgument(attribute) { | ||
if (!Array.isArray(attribute.value)) { | ||
if (attribute.type === 'Spread') { | ||
return `...${print(attribute.expression).code}`; | ||
} | ||
return `${escapeText(attribute.name)}:${transform(attribute.value)}`; | ||
} | ||
if (attribute.value.length === 1) { | ||
const node = attribute.value[0]; | ||
if (node.type === 'MustacheTag') { | ||
return `${escapeText(attribute.name)}:()=>${transform(node)}]`; | ||
} else { | ||
return `${escapeText(attribute.name)}:${transform(node)}]`; | ||
} | ||
} else { | ||
return `${escapeText(attribute.name)}:() => [${attribute.value.map((el) => { | ||
return transform(el); | ||
})}].join('')`; | ||
} | ||
} | ||
function transformAttribute(attribute) { | ||
if (!Array.isArray(attribute.value)) { | ||
return `[${escapeText(attribute.name)},${transform(attribute.value)}]`; | ||
} | ||
if (attribute.value.length === 1) { | ||
const node = attribute.value[0]; | ||
if (node.type === 'MustacheTag') { | ||
return `[${escapeText(attribute.name)},()=>${transform(node)}]`; | ||
} else { | ||
return `[${escapeText(attribute.name)},${transform(node)}]`; | ||
} | ||
} else { | ||
return `[${escapeText(attribute.name)},() => [${attribute.value.map( | ||
(el) => { | ||
return transform(el); | ||
}, | ||
)}].join('')]`; | ||
} | ||
} | ||
|
||
function transformMustacheTag(expression) { | ||
return print(expression).code; | ||
} | ||
|
||
function escapeText(text) { | ||
return JSON.stringify(text); | ||
} | ||
|
||
function transformInlineComponent(node) { | ||
const argsArray = node.attributes.map((attr) => { | ||
return transformArgument(attr); | ||
}); | ||
argsArray.push(`$slots: {default: () => [${node.children.map(transform)}]}`); | ||
return `$_c(${node.name}, {${argsArray.join(',')}}, this)`; | ||
} | ||
function transformAttributeShorthand(node) { | ||
return `()=>${print(node.expression).code}`; | ||
} | ||
|
||
function transformIfBlock(node) { | ||
return `$_if(()=>${print(node.expression).code}, () => [${node.children.map( | ||
transform, | ||
)}], ${node.else ? `${transform(node.else)}` : null})`; | ||
} | ||
function transformElseBlock(node) { | ||
return `()=>[${node.children.map(transform).join(',')}]`; | ||
} | ||
|
||
function transformEachBlock(node) { | ||
const item = print(node.context).code; | ||
return `$_eachSync(()=>${print(node.expression).code},(${item},${ | ||
node.index ? node.index : '$index' | ||
}, ${ | ||
node.key | ||
? `(${item})=>${print(node.key).code}` | ||
: `${escapeText('@identity')}` | ||
})=>[${node.children.map(transform).join(',')}], this)`; | ||
} | ||
|
||
function transform(node) { | ||
if (typeof node !== 'object') { | ||
return node; | ||
} else if (node.type === 'Element') { | ||
return transformElement(node); | ||
} else if (node.type === 'Attribute') { | ||
return transformAttribute(node); | ||
} else if (node.type === 'Text') { | ||
return escapeText(node.data); | ||
} else if (node.type === 'MustacheTag') { | ||
return transformMustacheTag(node.expression); | ||
} else if (node.type === 'AttributeShorthand') { | ||
return transformAttributeShorthand(node); | ||
} else if (node.type === 'InlineComponent') { | ||
return transformInlineComponent(node); | ||
} else if (node.type === 'IfBlock') { | ||
return transformIfBlock(node); | ||
} else if (node.type === 'ElseBlock') { | ||
return transformElseBlock(node); | ||
} else if (node.type === 'EachBlock') { | ||
return transformEachBlock(node); | ||
} | ||
|
||
throw new Error(`Unknown node type: ${node.type}`); | ||
} | ||
|
||
console.log(compile()); |
Oops, something went wrong.