Skip to content

Commit 9bba8d1

Browse files
committed
implement dynamic components etc
1 parent e85eda8 commit 9bba8d1

File tree

32 files changed

+305
-27
lines changed

32 files changed

+305
-27
lines changed

src/generators/Generator.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ export default class Generator {
875875
this.skip();
876876
}
877877

878-
if (node.type === 'Component' && node.name === ':Component') {
878+
if (node.type === 'Component' && (node.name === ':Component' || node.name === 'svelte:component')) {
879879
node.metadata = contextualise(node.expression, contextDependencies, indexes, false);
880880
}
881881

src/generators/nodes/Component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ export default class Component extends Node {
292292
`;
293293
}
294294

295-
if (this.name === ':Component') {
295+
if (this.name === ':Component' || this.name === 'svelte:component') {
296296
const switch_value = block.getUniqueName('switch_value');
297297
const switch_props = block.getUniqueName('switch_props');
298298

src/generators/server-side-rendering/visitors/Component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default function visitComponent(
8787
.concat(bindingProps)
8888
.join(', ')} }`;
8989

90-
const isDynamicComponent = node.name === ':Component';
90+
const isDynamicComponent = node.name === ':Component' || node.name === 'svelte:component';
9191
if (isDynamicComponent) block.contextualise(node.expression);
9292

9393
const expression = (

src/parse/state/tag.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export default function tag(parser: Parser) {
169169
}
170170
}
171171

172-
if (name === (parser.v2 ? 'svelte:component' : ':Component')) {
172+
if (name === ':Component') {
173173
parser.eat('{', true);
174174
element.expression = readExpression(parser);
175175
parser.allowWhitespace();
@@ -189,6 +189,21 @@ export default function tag(parser: Parser) {
189189
parser.allowWhitespace();
190190
}
191191

192+
if (parser.v2 && name === 'svelte:component') {
193+
// TODO post v2, treat this just as any other attribute
194+
const index = element.attributes.findIndex(attr => attr.name === 'this');
195+
if (!~index) {
196+
parser.error(`<svelte:component> must have a 'this' attribute`, start);
197+
}
198+
199+
const definition = element.attributes.splice(index, 1)[0];
200+
if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') {
201+
parser.error(`invalid component definition`, definition.start);
202+
}
203+
204+
element.expression = definition.value[0].expression;
205+
}
206+
192207
// special cases – top-level <script> and <style>
193208
if (specials.has(name) && parser.stack.length === 1) {
194209
const special = specials.get(name);
@@ -348,8 +363,8 @@ function readAttribute(parser: Parser, uniqueNames: Set<string>) {
348363

349364
parser.allowWhitespace();
350365

351-
const attribute = readDirective(parser, start, name);
352-
if (attribute) return attribute;
366+
const directive = readDirective(parser, start, name);
367+
if (directive) return directive;
353368

354369
let value = parser.eat('=') ? readAttributeValue(parser) : true;
355370

test/helpers.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ export function showOutput(cwd, options = {}, compile = svelte.compile) {
178178
glob.sync('**/*.html', { cwd }).forEach(file => {
179179
if (file[0] === '_') return;
180180

181+
// TODO remove this post-v2
182+
if (/-v2\.html$/.test(file)) {
183+
if (!options.v2) return;
184+
} else if (options.v2 && fs.existsSync(`${cwd}/${file.replace('.html', '-v2.html')}`)) {
185+
return;
186+
}
187+
181188
const name = path.basename(file)
182189
.slice(0, -path.extname(file).length)
183190
.replace(/^\d/, '_$&')
@@ -187,7 +194,8 @@ export function showOutput(cwd, options = {}, compile = svelte.compile) {
187194
fs.readFileSync(`${cwd}/${file}`, 'utf-8'),
188195
Object.assign(options, {
189196
filename: file,
190-
name: capitalise(name)
197+
name: capitalise(name),
198+
parser: options.v2 ? 'v2' : 'v1'
191199
})
192200
);
193201

Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<svelte:component {foo ? Foo : Bar}></svelte:component>
1+
<svelte:component this="{foo ? Foo : Bar}"></svelte:component>

test/parser/samples/component-dynamic/output-v2.json

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
{
2-
"hash": "7yh2k2",
2+
"hash": "2u5ec3",
33
"html": {
44
"start": 0,
5-
"end": 55,
5+
"end": 62,
66
"type": "Fragment",
77
"children": [
88
{
99
"start": 0,
10-
"end": 55,
10+
"end": 62,
1111
"type": "Element",
1212
"name": "svelte:component",
1313
"attributes": [],
1414
"children": [],
1515
"expression": {
1616
"type": "ConditionalExpression",
17-
"start": 19,
18-
"end": 34,
17+
"start": 25,
18+
"end": 40,
1919
"test": {
2020
"type": "Identifier",
21-
"start": 19,
22-
"end": 22,
21+
"start": 25,
22+
"end": 28,
2323
"name": "foo"
2424
},
2525
"consequent": {
2626
"type": "Identifier",
27-
"start": 25,
28-
"end": 28,
27+
"start": 31,
28+
"end": 34,
2929
"name": "Foo"
3030
},
3131
"alternate": {
3232
"type": "Identifier",
33-
"start": 31,
34-
"end": 34,
33+
"start": 37,
34+
"end": 40,
3535
"name": "Bar"
3636
}
3737
}

test/runtime/index.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let compileOptions = null;
2121
let compile = null;
2222

2323
function getName(filename) {
24-
const base = path.basename(filename).replace(".html", "");
24+
const base = path.basename(filename).replace('-v2', '').replace(".html", "");
2525
return base[0].toUpperCase() + base.slice(1);
2626
}
2727

@@ -46,7 +46,7 @@ describe("runtime", () => {
4646

4747
const failed = new Set();
4848

49-
function runTest(dir, shared, hydrate) {
49+
function runTest(dir, shared, hydrate, v2) {
5050
if (dir[0] === ".") return;
5151

5252
const config = loadConfig(`./runtime/samples/${dir}/_config.js`);
@@ -55,7 +55,7 @@ describe("runtime", () => {
5555
throw new Error("Forgot to remove `solo: true` from test");
5656
}
5757

58-
(config.skip ? it.skip : config.solo ? it.only : it)(`${dir} (${shared ? 'shared' : 'inline'} helpers${hydrate ? ' , hydration' : ''})`, () => {
58+
(config.skip ? it.skip : config.solo ? it.only : it)(`${dir} (${shared ? 'shared' : 'inline'} helpers${hydrate ? ', hydration' : ''}${v2 ? ', v2' : ''})`, () => {
5959
if (failed.has(dir)) {
6060
// this makes debugging easier, by only printing compiled output once
6161
throw new Error('skipping test, already failed');
@@ -72,12 +72,13 @@ describe("runtime", () => {
7272
compileOptions.dev = config.dev;
7373
compileOptions.store = !!config.store;
7474
compileOptions.immutable = config.immutable;
75+
compileOptions.parser = v2 ? 'v2' : 'v1';
7576

7677
// check that no ES2015+ syntax slipped in
7778
if (!config.allowES2015) {
7879
try {
7980
const source = fs.readFileSync(
80-
`test/runtime/samples/${dir}/main.html`,
81+
`test/runtime/samples/${dir}/main${v2 ? '-v2' : ''}.html`,
8182
"utf-8"
8283
);
8384
const { code } = compile(source, compileOptions);
@@ -100,7 +101,7 @@ describe("runtime", () => {
100101
if (err.frame) {
101102
console.error(chalk.red(err.frame)); // eslint-disable-line no-console
102103
}
103-
showOutput(cwd, { shared, format: 'cjs', store: !!compileOptions.store }, compile); // eslint-disable-line no-console
104+
showOutput(cwd, { shared, format: 'cjs', store: !!compileOptions.store, v2 }, compile); // eslint-disable-line no-console
104105
throw err;
105106
}
106107
}
@@ -143,7 +144,7 @@ describe("runtime", () => {
143144
};
144145

145146
try {
146-
SvelteComponent = require(`./samples/${dir}/main.html`);
147+
SvelteComponent = require(`./samples/${dir}/main${v2 ? '-v2' : ''}.html`);
147148
} catch (err) {
148149
showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store }, compile); // eslint-disable-line no-console
149150
throw err;
@@ -203,12 +204,12 @@ describe("runtime", () => {
203204
config.error(assert, err);
204205
} else {
205206
failed.add(dir);
206-
showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store }, compile); // eslint-disable-line no-console
207+
showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, v2 }, compile); // eslint-disable-line no-console
207208
throw err;
208209
}
209210
})
210211
.then(() => {
211-
if (config.show) showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store }, compile);
212+
if (config.show) showOutput(cwd, { shared, format: 'cjs', hydratable: hydrate, store: !!compileOptions.store, v2 }, compile);
212213
});
213214
});
214215
}
@@ -218,6 +219,10 @@ describe("runtime", () => {
218219
runTest(dir, shared, false);
219220
runTest(dir, shared, true);
220221
runTest(dir, null, false);
222+
223+
if (fs.existsSync(`test/runtime/samples/${dir}/main-v2.html`)) {
224+
runTest(dir, shared, false, true);
225+
}
221226
});
222227

223228
it("fails if options.target is missing in dev mode", () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<button use:tooltip="tooltip">action</button>
2+
<svelte:window on:keydown="checkForCtrl(event)" on:keyup="checkForCtrl(event)"/>
3+
4+
<script>
5+
export default {
6+
data() {
7+
return { tooltip: 'Perform an Action' };
8+
},
9+
10+
methods: {
11+
checkForCtrl(event) {
12+
if (event.ctrlKey) {
13+
this.set({ tooltip: 'Perform an augmented Action' });
14+
} else {
15+
this.set({ tooltip: 'Perform an Action' });
16+
}
17+
}
18+
},
19+
20+
actions: {
21+
tooltip(node, text) {
22+
let tooltip = null;
23+
24+
function onMouseEnter() {
25+
tooltip = document.createElement('div');
26+
tooltip.classList.add('tooltip');
27+
tooltip.textContent = text;
28+
node.parentNode.appendChild(tooltip);
29+
}
30+
31+
function onMouseLeave() {
32+
if (!tooltip) return;
33+
tooltip.remove();
34+
tooltip = null;
35+
}
36+
37+
node.addEventListener('mouseenter', onMouseEnter);
38+
node.addEventListener('mouseleave', onMouseLeave);
39+
40+
return {
41+
update(text) {
42+
if (tooltip) tooltip.textContent = text;
43+
},
44+
destroy() {
45+
node.removeEventListener('mouseenter', onMouseEnter);
46+
node.removeEventListener('mouseleave', onMouseLeave);
47+
}
48+
}
49+
}
50+
}
51+
}
52+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<svelte:component this="{x ? Foo : Bar}"/>
2+
3+
<script>
4+
import Foo from './Foo.html';
5+
import Bar from './Bar.html';
6+
7+
export default {
8+
components: {
9+
Foo,
10+
Bar
11+
}
12+
};
13+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<svelte:window bind:innerWidth='width'/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>green {foo}</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>red {foo}</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<svelte:component this="{x ? Green : Red}" bind:foo />
2+
3+
<script>
4+
import Green from './Green-v2.html';
5+
import Red from './Red-v2.html';
6+
7+
export default {
8+
data() {
9+
return {
10+
Green,
11+
Red
12+
};
13+
}
14+
};
15+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<svelte:component this="{ x ? Foo : Bar }" bind:y bind:z/>
2+
3+
<script>
4+
import Foo from './Foo.html';
5+
import Bar from './Bar.html';
6+
7+
export default {
8+
data() {
9+
return { Foo, Bar };
10+
}
11+
};
12+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<svelte:component this="{ x ? Foo : Bar }" on:select='set({ selected: event.id })'/>
2+
3+
<script>
4+
import Foo from './Foo.html';
5+
import Bar from './Bar.html';
6+
7+
export default {
8+
data() {
9+
return { Foo, Bar };
10+
}
11+
};
12+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>{x}, therefore Bar</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>{x}, therefore Foo</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div>
2+
<svelte:component this="{ x ? Foo : Bar }" x='{x}'/>
3+
</div>
4+
5+
<script>
6+
import Foo from './Foo-v2.html';
7+
import Bar from './Bar-v2.html';
8+
9+
export default {
10+
data() {
11+
return { Foo, Bar };
12+
}
13+
};
14+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<svelte:component this={foo} ref:test/>
2+
3+
<script>
4+
import Foo from './Foo.html';
5+
6+
export default {
7+
data() {
8+
return { foo: Foo };
9+
}
10+
};
11+
</script>

0 commit comments

Comments
 (0)