Merge branch 'master' into improveOutputSize

pull/5702/head
Ivan Hofer 5 years ago
commit 9e6556cf29

@ -1,5 +1,20 @@
# Svelte changelog # Svelte changelog
## 3.30.1
* Support consuming decoded sourcemaps as created by the `source-map` library's `SourceMapGenerator` ([#5722](https://github.com/sveltejs/svelte/issues/5722))
* Actually export `hasContext` ([#5726](https://github.com/sveltejs/svelte/issues/5726))
## 3.30.0
* Add a typed `SvelteComponent` interface ([#5431](https://github.com/sveltejs/svelte/pull/5431))
* Support spread into `<slot>` props ([#5456](https://github.com/sveltejs/svelte/issues/5456))
* Fix setting reactive dependencies which don't appear in the template to `undefined` ([#5538](https://github.com/sveltejs/svelte/issues/5538))
* Support preprocessor sourcemaps during compilation ([#5584](https://github.com/sveltejs/svelte/pull/5584))
* Fix ordering of elements when using `{#if}` inside `{#key}` ([#5680](https://github.com/sveltejs/svelte/issues/5680))
* Add `hasContext` lifecycle function ([#5690](https://github.com/sveltejs/svelte/pull/5690))
* Fix missing `walk` types in `svelte/compiler` ([#5696](https://github.com/sveltejs/svelte/pull/5696))
## 3.29.7 ## 3.29.7
* Include `./register` in exports map ([#5670](https://github.com/sveltejs/svelte/issues/5670)) * Include `./register` in exports map ([#5670](https://github.com/sveltejs/svelte/issues/5670))

2
package-lock.json generated

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.29.7", "version": "3.30.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.29.7", "version": "3.30.1",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",

@ -20,7 +20,7 @@ const ts_plugin = is_publish
const external = id => id.startsWith('svelte/'); const external = id => id.startsWith('svelte/');
fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';`); fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, walk, VERSION } from './types/compiler/index';`);
export default [ export default [
/* runtime */ /* runtime */

@ -55,6 +55,6 @@ Once you've tinkered a bit and understood how everything fits together, you can
npx degit your-name/template my-new-project npx degit your-name/template my-new-project
``` ```
And that's it! Do `npm run build` to create a production-ready version of your app, and check the project template's [README](https://github.com/sveltejs/template/blob/master/README.md) for instructions on how to easily deploy your app to the web with [Now](https://zeit.co/now) or [Surge](http://surge.sh/). And that's it! Do `npm run build` to create a production-ready version of your app, and check the project template's [README](https://github.com/sveltejs/template/blob/master/README.md) for instructions on how to easily deploy your app to the web with [Vercel](https://vercel.com) or [Surge](http://surge.sh/).
You're not restricted to using Rollup — there are also integrations for [webpack](https://github.com/sveltejs/svelte-loader), [Browserify](https://github.com/tehshrike/sveltify) and others, or you can use the [Svelte CLI](https://github.com/sveltejs/svelte-cli) (Update from 2019: with Svelte 3 the CLI was deprecated and we now use [sirv-cli](https://www.npmjs.com/package/sirv-cli) in our template. Feel free to use whatever tool you like!) or the [API](https://github.com/sveltejs/svelte/tree/v2#api) directly. If you make a project template using one of these tools, please share it with the [Svelte Discord chatroom](chat), or via [@sveltejs](https://twitter.com/sveltejs) on Twitter! You're not restricted to using Rollup — there are also integrations for [webpack](https://github.com/sveltejs/svelte-loader), [Browserify](https://github.com/tehshrike/sveltify) and others, or you can use the [Svelte CLI](https://github.com/sveltejs/svelte-cli) (Update from 2019: with Svelte 3 the CLI was deprecated and we now use [sirv-cli](https://www.npmjs.com/package/sirv-cli) in our template. Feel free to use whatever tool you like!) or the [API](https://github.com/sveltejs/svelte/tree/v2#api) directly. If you make a project template using one of these tools, please share it with the [Svelte Discord chatroom](chat), or via [@sveltejs](https://twitter.com/sveltejs) on Twitter!

@ -0,0 +1,71 @@
---
title: What's new in Svelte: December 2020
description: Better tooling, export maps and improvements to slots and context
author: Daniel Sandoval
authorURL: https://desandoval.net
---
It's the last "What's new in Svelte" of the year and there's lots to celebrate! This month's coverage includes updates from `rollup-plugin-svelte`, `Sapper` and `SvelteKit` and a bunch of showcases from the Svelte community!
## New features & impactful bug fixes
1. `$$props`, `$$restProps`, and `$$slots` are all now supported in custom web components (**3.29.5**, [Example](https://svelte.dev/repl/ad8e6f39cd20403dacd1be84d71e498d?version=3.29.5)) and `slot` components now support spread props: `<slot {...foo} />` (**3.30.0**)
2. A new `hasContext` lifecycle function makes it easy to check whether a `key` has been set in the context of a parent component (**3.30.0** & **3.30.1**, [Docs](https://svelte.dev/docs#hasContext))
3. `SvelteComponent` is now typed which makes it easier to add typed classes that extend base Svelte Components. Component library and framework authors rejoice! An example: `export class YourComponent extends SvelteComponent<{aProp: boolean}, {click: MouseEvent}, {default: {aSlot: string}}> {}` (**3.30.0**, [RFC](https://github.com/sveltejs/rfcs/pull/37))
4. Transitions within `{:else}` blocks should now complete successfully (**3.29.5**, [Example](https://svelte.dev/repl/49cef205e5da459594ef2eafcbd41593?version=3.29.5))
5. Svelte now includes an export map, which explicitly states which files can be imported from its npm package (**3.29.5** with some fixes in **3.29.6**, **3.29.7** and **3.30.0**)
6. `rollup-plugin-svelte` had a new [7.0.0 release](https://github.com/sveltejs/rollup-plugin-svelte/blob/master/CHANGELOG.md). The biggest change is that the `css` option was removed. Users who were using that option should add another plugin like `rollup-plugin-css-only` as demonstrated [in the template](https://github.com/sveltejs/template/blob/5b1135c286f7a649daa99825a077586655051649/rollup.config.js#L48)
## What's going on in Sapper?
Lots of new TypeScript definition improvements to make editing Sapper apps even easier! CSS for dynamic imports also should now work in `client.js` files. (Unreleased)
## What's the deal with SvelteKit?
We're glad you asked! If you didn't catch Rich's blog post from early last month, [you can find it here](https://svelte.dev/blog/whats-the-deal-with-sveltekit)!
For all the features and bugfixes see the CHANGELOGs for [Svelte](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md) and [Sapper](https://github.com/sveltejs/sapper/blob/master/CHANGELOG.md).
---
## Community Showcase
**Apps & Sites**
- [narration.studio](https://narration.studio/) (Chrome Only) is an automatic in-browser audio recording & editing platform for voice over narration.
- [Vippet](https://vippet.netlify.app/) is a video recording and editing tool for the browser.
- [Pattern Monster](https://pattern.monster/) is a simple online pattern generator to create repeatable SVG patterns.
- [Plant-based diets](https://planetbaseddiets.panda.org/) is a website from the World Wildlife Foundation (WWF) built with Svelte.
- [johnells.se](https://www.johnells.se/) is a Swedish fashion e-commerce site, built with [Crown](https://crownframework.com/) - a Svelte-powered framework.
- [sentence-length](https://sentence-length.netlify.app/) is a learning and analysis tool to show how some authors play with different lengths, while others stick with one.
- [svelte-presenter](https://github.com/stephane-vanraes/svelte-presenter) lets you quickly make good looking presentations using Svelte and mdsvex.
**Demos**
- [u/loopcake got SSR working in Java Spring Boot](https://www.reddit.com/r/sveltejs/comments/jkh5up/svelte_ssr_but_its_java_spring_boot_and_its_native/) for all the Java shops out there looking to render Svelte server-side.
- [svelte-liquid-swipe](https://github.com/tncrazvan/svelte-liquid-swipe) shows off a fancy interaction pattern using svg paths.
- [Crossfade Link Animation](https://svelte.dev/repl/7f68e148caf04b2787bb6f296208f870?version=3.29.7) demonstrates how to animate between navigation links using a crossfade (made by Blu, from the Discord community)
- [Clip-Path Transitions](https://svelte.dev/repl/b5ad281ae8024b629b545c70c9e8764d?version=3.29.7) showcases how to use clip paths and custom transitions to create magical in-and-out transitions (made by Faber, from the Discord community)
**Learning Resources**
- [lihautan](https://www.youtube.com/channel/UCbmC3HP3FaAFdcZkui8YoMQ/featured) has been making easy-to-follow videos to share his in-depth knowledge of Svelte.
- [Lessons From Building a Static Site Generator](https://nicholasreese.com/lessons-from-building-a-static-site-generator/) shares the backstory and thinking behind Elder.js - and the design decision made along the way.
- [Svelte Tutorial and Projects Course ](https://www.udemy.com/course/svelte-tutorial-and-projects-course/) is a udemy course by John Smilga where students learn Svelte.js by building interesting projects.
- [Building Pastebin on IPFS - with FastAPI, Svelte, and IPFS](https://amalshaji.wtf/building-pastebin-on-ipfs-with-fastapi-svelte-and-ipfs) explains how to make a distributed pastebin-like application.
**Components, Libraries & Tools**
- [svelte-crossword](https://russellgoldenberg.github.io/svelte-crossword/) is a customizable crossword puzzle component for Svelte.
- [svelte-cloudinary](https://github.com/cupcakearmy/svelte-cloudinary) makes it easy to integrate Cloudinary with Svelte (including Typescript and SSR support)
- [Svelte Nova](https://extensions.panic.com/extensions/sb.lao/sb.lao.svelte-nova/) extends the new Nova editor to support Svelte
- [saos](https://github.com/shiryel/saos) is a small svelte component to animate your elements on scroll.
- [Svelte-nStore](https://github.com/lacikawiz/svelte-nStore) is a general purpose store replacement that fulfills the Svelte store contract and adds getter and calculation features.
- [svelte-slimscroll](https://github.com/MelihAltintas/svelte-slimscroll) is a Svelte Action that transforms any div into a scrollable area with a nice scrollbar.
- [svelte-typewriter](https://github.com/henriquehbr/svelte-typewriter) is a simple and reusable typewriter effect for your Svelte applications
- [svelte-store-router](https://github.com/zyxd/svelte-store-router) is a store-based router for Svelte that suggests that routing is just another global state and History API changes are just an optional side-effects of this state.
- [Routify](https://routify.dev/blog/routify-2-released) just released version 2 of its Svelte router.
- [svelte-error-boundary](https://www.npmjs.com/package/@crownframework/svelte-error-boundary) provides a simple error boundary component for Svelte that can be can be used with both DOM and SSR targets.
- [svelte2dts](https://www.npmjs.com/package/svelte2dts) generates d.ts files from svelte files, creating truly sharable and well typed components.
## See you next month!
Got an idea for something to add to the Showcase? Want to get involved more with Svelte? We're always looking for maintainers, contributors and fanatics... Check out the [Svelte Society](https://sveltesociety.dev/), [Reddit](https://www.reddit.com/r/sveltejs/) and [Discord](https://discord.com/invite/yy75DKs) to get involved!
That's all for the year, folks! See you in January 😎

@ -178,6 +178,26 @@ Retrieves the context that belongs to the closest parent component with the spec
</script> </script>
``` ```
#### `hasContext`
```js
hasContext: boolean = hasContext(key: any)
```
---
Checks whether a given `key` has been set in the context of a parent component. Must be called during component initialisation.
```sv
<script>
import { hasContext } from 'svelte';
if (hasContext('answer')) {
// do something
}
</script>
```
#### `createEventDispatcher` #### `createEventDispatcher`
```js ```js

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

@ -15,7 +15,7 @@ export default class Slot extends Element {
super(component, parent, scope, info); super(component, parent, scope, info);
info.attributes.forEach(attr => { info.attributes.forEach(attr => {
if (attr.type !== 'Attribute') { if (attr.type !== 'Attribute' && attr.type !== 'Spread') {
component.error(attr, { component.error(attr, {
code: 'invalid-slot-directive', code: 'invalid-slot-directive',
message: '<slot> cannot have directives' message: '<slot> cannot have directives'

@ -111,8 +111,9 @@ export default class Renderer {
// these determine whether variable is included in initial context // these determine whether variable is included in initial context
// array, so must have the highest priority // array, so must have the highest priority
if (variable.export_name) member.priority += 16; if (variable.is_reactive_dependency && (variable.mutated || variable.reassigned)) member.priority += 16;
if (variable.referenced) member.priority += 32; if (variable.export_name) member.priority += 32;
if (variable.referenced) member.priority += 64;
} else if (member.is_non_contextual) { } else if (member.is_non_contextual) {
// determine whether variable is included in initial context // determine whether variable is included in initial context
// array, so must have the highest priority // array, so must have the highest priority
@ -131,7 +132,7 @@ export default class Renderer {
while (i--) { while (i--) {
const member = this.context[i]; const member = this.context[i];
if (member.variable) { if (member.variable) {
if (member.variable.referenced || member.variable.export_name) break; if (member.variable.referenced || member.variable.export_name || (member.variable.is_reactive_dependency && (member.variable.mutated || member.variable.reassigned))) break;
} else if (member.is_non_contextual) { } else if (member.is_non_contextual) {
break; break;
} }
@ -220,7 +221,7 @@ export default class Renderer {
.reduce((lhs, rhs) => x`${lhs}, ${rhs}`); .reduce((lhs, rhs) => x`${lhs}, ${rhs}`);
} }
dirty(names, is_reactive_declaration = false): Expression { dirty(names: string[], is_reactive_declaration = false): Expression {
const renderer = this; const renderer = this;
const dirty = (is_reactive_declaration const dirty = (is_reactive_declaration

@ -670,7 +670,7 @@ export default class ElementWrapper extends Wrapper {
// handle edge cases for elements // handle edge cases for elements
if (this.node.name === 'select') { if (this.node.name === 'select') {
const dependencies = new Set(); const dependencies = new Set<string>();
for (const attr of this.attributes) { for (const attr of this.attributes) {
for (const dep of attr.node.dependencies) { for (const dep of attr.node.dependencies) {
dependencies.add(dep); dependencies.add(dep);

@ -44,7 +44,7 @@ export default class KeyBlockWrapper extends Wrapper {
renderer, renderer,
this.block, this.block,
node.children, node.children,
parent, this,
strip_whitespace, strip_whitespace,
next_sibling next_sibling
); );

@ -8,7 +8,6 @@ import { sanitize } from '../../../utils/names';
import add_to_set from '../../utils/add_to_set'; import add_to_set from '../../utils/add_to_set';
import get_slot_data from '../../utils/get_slot_data'; import get_slot_data from '../../utils/get_slot_data';
import { is_reserved_keyword } from '../../utils/reserved_keywords'; import { is_reserved_keyword } from '../../utils/reserved_keywords';
import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic'; import is_dynamic from './shared/is_dynamic';
import { Identifier, ObjectExpression } from 'estree'; import { Identifier, ObjectExpression } from 'estree';
import create_debugging_comment from './shared/create_debugging_comment'; import create_debugging_comment from './shared/create_debugging_comment';
@ -82,6 +81,7 @@ export default class SlotWrapper extends Wrapper {
} }
let get_slot_changes_fn; let get_slot_changes_fn;
let get_slot_spread_changes_fn;
let get_slot_context_fn; let get_slot_context_fn;
if (this.node.values.size > 0) { if (this.node.values.size > 0) {
@ -90,32 +90,31 @@ export default class SlotWrapper extends Wrapper {
const changes = x`{}` as ObjectExpression; const changes = x`{}` as ObjectExpression;
const dependencies = new Set(); const spread_dynamic_dependencies = new Set<string>();
this.node.values.forEach(attribute => { this.node.values.forEach(attribute => {
attribute.chunks.forEach(chunk => { if (attribute.type === 'Spread') {
if ((chunk as Expression).dependencies) { add_to_set(spread_dynamic_dependencies, Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name)));
add_to_set(dependencies, (chunk as Expression).contextual_dependencies); } else {
// add_to_set(dependencies, (chunk as Expression).dependencies);
(chunk as Expression).dependencies.forEach(name => {
const variable = renderer.component.var_lookup.get(name);
if (variable && !variable.hoistable) dependencies.add(name);
});
}
});
const dynamic_dependencies = Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name)); const dynamic_dependencies = Array.from(attribute.dependencies).filter((name) => this.is_dependency_dynamic(name));
if (dynamic_dependencies.length > 0) { if (dynamic_dependencies.length > 0) {
changes.properties.push(p`${attribute.name}: ${renderer.dirty(dynamic_dependencies)}`); changes.properties.push(p`${attribute.name}: ${renderer.dirty(dynamic_dependencies)}`);
} }
}
}); });
renderer.blocks.push(b` renderer.blocks.push(b`
const ${get_slot_changes_fn} = #dirty => ${changes}; const ${get_slot_changes_fn} = #dirty => ${changes};
const ${get_slot_context_fn} = #ctx => ${get_slot_data(this.node.values, block)}; const ${get_slot_context_fn} = #ctx => ${get_slot_data(this.node.values, block)};
`); `);
if (spread_dynamic_dependencies.size) {
get_slot_spread_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_spread_changes`);
renderer.blocks.push(b`
const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))} > 0 ? -1 : 0;
`);
}
} else { } else {
get_slot_changes_fn = 'null'; get_slot_changes_fn = 'null';
get_slot_context_fn = 'null'; get_slot_context_fn = 'null';
@ -170,7 +169,11 @@ export default class SlotWrapper extends Wrapper {
? Array.from(this.fallback.dependencies).filter((name) => this.is_dependency_dynamic(name)) ? Array.from(this.fallback.dependencies).filter((name) => this.is_dependency_dynamic(name))
: []; : [];
const slot_update = b` const slot_update = get_slot_spread_changes_fn ? b`
if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) {
@update_slot_spread(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_spread_changes_fn}, ${get_slot_context_fn});
}
`: b`
if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) { if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) {
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn}); @update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn});
} }

@ -9,6 +9,14 @@ export default function get_slot_data(values: Map<string, Attribute>, block: Blo
properties: Array.from(values.values()) properties: Array.from(values.values())
.filter(attribute => attribute.name !== 'name') .filter(attribute => attribute.name !== 'name')
.map(attribute => { .map(attribute => {
if (attribute.is_spread) {
const argument = get_spread_value(block, attribute);
return {
type: 'SpreadElement',
argument
};
}
const value = get_value(block, attribute); const value = get_value(block, attribute);
return p`${attribute.name}: ${value}`; return p`${attribute.name}: ${value}`;
}) })
@ -29,3 +37,7 @@ function get_value(block: Block, attribute: Attribute) {
return value; return value;
} }
function get_spread_value(block: Block, attribute: Attribute) {
return block ? attribute.expression.manipulate(block) : attribute.expression.node;
}

@ -83,6 +83,79 @@ async function replace_async(
return out.concat(final_content); return out.concat(final_content);
} }
/**
* Import decoded sourcemap from mozilla/source-map/SourceMapGenerator
* Forked from source-map/lib/source-map-generator.js
* from methods _serializeMappings and toJSON.
* We cannot use source-map.d.ts types, because we access hidden properties.
*/
function decoded_sourcemap_from_generator(generator: any) {
let previous_generated_line = 1;
const converted_mappings = [[]];
let result_line;
let result_segment;
let mapping;
const source_idx = generator._sources.toArray()
.reduce((acc, val, idx) => (acc[val] = idx, acc), {});
const name_idx = generator._names.toArray()
.reduce((acc, val, idx) => (acc[val] = idx, acc), {});
const mappings = generator._mappings.toArray();
result_line = converted_mappings[0];
for (let i = 0, len = mappings.length; i < len; i++) {
mapping = mappings[i];
if (mapping.generatedLine > previous_generated_line) {
while (mapping.generatedLine > previous_generated_line) {
converted_mappings.push([]);
previous_generated_line++;
}
result_line = converted_mappings[mapping.generatedLine - 1]; // line is one-based
} else if (i > 0) {
const previous_mapping = mappings[i - 1];
if (
// sorted by selectivity
mapping.generatedColumn === previous_mapping.generatedColumn &&
mapping.originalColumn === previous_mapping.originalColumn &&
mapping.name === previous_mapping.name &&
mapping.generatedLine === previous_mapping.generatedLine &&
mapping.originalLine === previous_mapping.originalLine &&
mapping.source === previous_mapping.source
) {
continue;
}
}
result_line.push([mapping.generatedColumn]);
result_segment = result_line[result_line.length - 1];
if (mapping.source != null) {
result_segment.push(...[
source_idx[mapping.source],
mapping.originalLine - 1, // line is one-based
mapping.originalColumn
]);
if (mapping.name != null) {
result_segment.push(name_idx[mapping.name]);
}
}
}
const map = {
version: generator._version,
sources: generator._sources.toArray(),
names: generator._names.toArray(),
mappings: converted_mappings
};
if (generator._file != null) {
(map as any).file = generator._file;
}
// not needed: map.sourcesContent and map.sourceRoot
return map;
}
/** /**
* Convert a preprocessor output and its leading prefix and trailing suffix into StringWithSourceMap * Convert a preprocessor output and its leading prefix and trailing suffix into StringWithSourceMap
*/ */
@ -109,6 +182,10 @@ function get_replacement(
if (typeof(decoded_map.mappings) === 'string') { if (typeof(decoded_map.mappings) === 'string') {
decoded_map.mappings = decode_mappings(decoded_map.mappings); decoded_map.mappings = decode_mappings(decoded_map.mappings);
} }
if ((decoded_map as any)._mappings && decoded_map.constructor.name === 'SourceMapGenerator') {
// import decoded sourcemap from mozilla/source-map/SourceMapGenerator
decoded_map = decoded_sourcemap_from_generator(decoded_map);
}
sourcemap_add_offset(decoded_map, get_location(offset + prefix.length)); sourcemap_add_offset(decoded_map, get_location(offset + prefix.length));
} }
const processed_with_map = StringWithSourcemap.from_processed(processed.code, decoded_map); const processed_with_map = StringWithSourcemap.from_processed(processed.code, decoded_map);
@ -164,7 +241,7 @@ export default async function preprocess(
async function preprocess_tag_content(tag_name: 'style' | 'script', preprocessor: Preprocessor) { async function preprocess_tag_content(tag_name: 'style' | 'script', preprocessor: Preprocessor) {
const get_location = getLocator(source); const get_location = getLocator(source);
const tag_regex = tag_name == 'style' const tag_regex = tag_name === 'style'
? /<!--[^]*?-->|<style(\s[^]*?)?(?:>([^]*?)<\/style>|\/>)/gi ? /<!--[^]*?-->|<style(\s[^]*?)?(?:>([^]*?)<\/style>|\/>)/gi
: /<!--[^]*?-->|<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi; : /<!--[^]*?-->|<script(\s[^]*?)?(?:>([^]*?)<\/script>|\/>)/gi;

@ -7,6 +7,7 @@ export {
afterUpdate, afterUpdate,
setContext, setContext,
getContext, getContext,
hasContext,
tick, tick,
createEventDispatcher, createEventDispatcher,
SvelteComponentDev as SvelteComponent SvelteComponentDev as SvelteComponent

@ -214,16 +214,19 @@ if (typeof HTMLElement === 'function') {
}; };
} }
export class SvelteComponent { export class SvelteComponent<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any
> {
$$: T$$; $$: T$$;
$$set?: ($$props: any) => void; $$set?: ($$props: Partial<Props>) => void;
$destroy() { $destroy() {
destroy_component(this, 1); destroy_component(this, 1);
this.$destroy = noop; this.$destroy = noop;
} }
$on(type, callback) { $on<K extends Extract<keyof Events, string>>(type: K, callback: (e: Events[K]) => void) {
const { $$ } = this; const { $$ } = this;
const callbacks = ($$.callbacks[type] || ($$.callbacks[type] = [])); const callbacks = ($$.callbacks[type] || ($$.callbacks[type] = []));
callbacks.push(callback); callbacks.push(callback);
@ -234,7 +237,7 @@ export class SvelteComponent {
}; };
} }
$set($$props) { $set($$props: Partial<Props>) {
const { $$set, $$ } = this; const { $$set, $$ } = this;
if ($$set && !is_empty($$props)) { if ($$set && !is_empty($$props)) {
$$.skip_bound = true; $$.skip_bound = true;

@ -97,15 +97,44 @@ export function validate_slots(name, slot, keys) {
} }
} }
type Props = Record<string, any>; export interface SvelteComponentDev<
export interface SvelteComponentDev { Props extends Record<string, any> = any,
$set(props?: Props): void; Events extends Record<string, any> = any,
$on<T = any>(event: string, callback: (event: CustomEvent<T>) => void): () => void; Slots extends Record<string, any> = any
> {
$set(props?: Partial<Props>): void;
$on<K extends Extract<keyof Events, string>>(type: K, callback: (e: Events[K]) => void): () => void;
$destroy(): void; $destroy(): void;
[accessor: string]: any; [accessor: string]: any;
} }
export class SvelteComponentDev extends SvelteComponent { export class SvelteComponentDev<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> extends SvelteComponent<Props, Events> {
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$prop_def: Props;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$events_def: Events;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$slot_def: Slots;
constructor(options: { constructor(options: {
target: Element; target: Element;
anchor?: Element; anchor?: Element;

@ -54,6 +54,10 @@ export function getContext<T>(key): T {
return get_current_component().$$.context.get(key); return get_current_component().$$.context.get(key);
} }
export function hasContext(key): boolean {
return get_current_component().$$.context.has(key);
}
// TODO figure out if we still want to support // TODO figure out if we still want to support
// shorthand events, or if we want to implement // shorthand events, or if we want to implement
// a real bubbling mechanism // a real bubbling mechanism

@ -49,7 +49,7 @@ export function transition_in(block, local?: 0 | 1) {
} }
} }
export function transition_out(block, local: 0 | 1, detach: 0 | 1, callback) { export function transition_out(block, local: 0 | 1, detach?: 0 | 1, callback?) {
if (block && block.o) { if (block && block.o) {
if (outroing.has(block)) return; if (outroing.has(block)) return;
outroing.add(block); outroing.add(block);

@ -113,6 +113,14 @@ export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot
} }
} }
export function update_slot_spread(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_spread_changes_fn, get_slot_context_fn) {
const slot_changes = get_slot_spread_changes_fn(dirty) | get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
if (slot_changes) {
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
slot.p(slot_context, slot_changes);
}
}
export function exclude_internal_props(props) { export function exclude_internal_props(props) {
const result = {}; const result = {};
for (const k in props) if (k[0] !== '$') result[k] = props[k]; for (const k in props) if (k[0] !== '$') result[k] = props[k];

@ -62,7 +62,7 @@ function instance($$self, $$props, $$invalidate) {
} }
}; };
return [x]; return [x, y];
} }
class Component extends SvelteComponent { class Component extends SvelteComponent {

@ -12,15 +12,15 @@ function instance($$self, $$props, $$invalidate) {
$$self.$$.update = () => { $$self.$$.update = () => {
if ($$self.$$.dirty & /*x*/ 1) { if ($$self.$$.dirty & /*x*/ 1) {
$: $$invalidate(2, b = x); $: $$invalidate(1, b = x);
} }
if ($$self.$$.dirty & /*b*/ 4) { if ($$self.$$.dirty & /*b*/ 2) {
$: a = b; $: a = b;
} }
}; };
return [x]; return [x, b];
} }
class Component extends SvelteComponent { class Component extends SvelteComponent {

@ -64,7 +64,7 @@ function instance($$self, $$props, $$invalidate) {
}; };
$: x = a * 2; $: x = a * 2;
return [y]; return [y, b];
} }
class Component extends SvelteComponent { class Component extends SvelteComponent {

@ -0,0 +1,7 @@
<script>
export let obj;
export let c;
export let d;
</script>
<slot {c} {...obj} {d} />

@ -0,0 +1,55 @@
export default {
props: {
obj: { a: 1, b: 42 },
c: 5,
d: 10
},
html: `
<p>1</p>
<p>42</p>
<p>5</p>
<p>10</p>
`,
test({ assert, target, component }) {
component.obj = { a: 2, b: 50, c: 30 };
assert.htmlEqual(target.innerHTML, `
<p>2</p>
<p>50</p>
<p>30</p>
<p>10</p>
`);
component.c = 22;
assert.htmlEqual(target.innerHTML, `
<p>2</p>
<p>50</p>
<p>30</p>
<p>10</p>
`);
component.d = 44;
assert.htmlEqual(target.innerHTML, `
<p>2</p>
<p>50</p>
<p>30</p>
<p>44</p>
`);
component.obj = { a: 9, b: 12 };
assert.htmlEqual(target.innerHTML, `
<p>9</p>
<p>12</p>
<p>22</p>
<p>44</p>
`);
component.c = 88;
assert.htmlEqual(target.innerHTML, `
<p>9</p>
<p>12</p>
<p>88</p>
<p>44</p>
`);
}
};

@ -0,0 +1,14 @@
<script>
import Nested from './Nested.svelte';
export let obj;
export let c;
export let d;
</script>
<Nested {obj} {c} {d} let:a let:b let:c let:d>
<p>{a}</p>
<p>{b}</p>
<p>{c}</p>
<p>{d}</p>
</Nested>

@ -0,0 +1,7 @@
<script>
import { hasContext } from 'svelte';
const has = hasContext('test');
</script>
<div>{has}</div>

@ -0,0 +1,11 @@
<script>
import { setContext } from 'svelte';
export let value = '';
if (value) {
setContext('test', value);
}
</script>
<slot></slot>

@ -0,0 +1,6 @@
export default {
html: `
<div>true</div>
<div>false</div>
`
};

@ -0,0 +1,12 @@
<script>
import Nested from "./Nested.svelte";
import Leaf from "./Leaf.svelte";
</script>
<Nested value="bar">
<Leaf />
</Nested>
<Nested>
<Leaf />
</Nested>

@ -0,0 +1,21 @@
export default {
html: `
<section>
<div>Second</div>
</section>
<button>Click</button>
`,
async test({ assert, component, target, window }) {
const button = target.querySelector('button');
await button.dispatchEvent(new window.Event('click'));
assert.htmlEqual(target.innerHTML, `
<section>
<div>First</div>
<div>Second</div>
</section>
<button>Click</button>
`);
}
};

@ -0,0 +1,17 @@
<script>
let slide = 0;
let num = false;
const changeNum = () => num = !num;
</script>
<section>
{#key slide}
{#if num}
<div>First</div>
{/if}
{/key}
<div>Second</div>
</section>
<button on:click={changeNum}>Click</button>

@ -0,0 +1,26 @@
export default {
html: `
<p>42</p>
<p>42</p>
`,
async test({ assert, component, target }) {
await component.updateStore(undefined);
assert.htmlEqual(target.innerHTML, '<p>undefined</p><p>42</p>');
await component.updateStore(33);
assert.htmlEqual(target.innerHTML, '<p>33</p><p>42</p>');
await component.updateStore(undefined);
assert.htmlEqual(target.innerHTML, '<p>undefined</p><p>42</p>');
await component.updateVar(undefined);
assert.htmlEqual(target.innerHTML, '<p>undefined</p><p>undefined</p>');
await component.updateVar(33);
assert.htmlEqual(target.innerHTML, '<p>undefined</p><p>33</p>');
await component.updateVar(undefined);
assert.htmlEqual(target.innerHTML, '<p>undefined</p><p>undefined</p>');
}
};

@ -0,0 +1,19 @@
<script>
import { writable } from 'svelte/store';
let store = writable(42);
let variable = 42;
let value;
let value2;
$: value = $store;
$: value2 = variable;
export function updateStore(value) {
store.set(value);
}
export function updateVar(value) {
variable = value;
}
</script>
<p>{ value }</p>
<p>{ value2 }</p>

@ -0,0 +1,25 @@
import MagicString from 'magic-string';
import { SourceMapConsumer, SourceMapGenerator } from 'source-map';
export default {
preprocess: {
style: async ({ content, filename }) => {
const src = new MagicString(content);
const idx = content.indexOf('baritone');
src.overwrite(idx, idx+'baritone'.length, 'bar');
const map = SourceMapGenerator.fromSourceMap(
await new SourceMapConsumer(
// sourcemap must be encoded for SourceMapConsumer
src.generateMap({
source: filename,
hires: true,
includeContent: false
})
)
);
return { code: src.toString(), map };
}
}
};

@ -0,0 +1,12 @@
<h1>Testing Styles</h1>
<h2>Testing Styles 2</h2>
<script>export const b = 2;</script>
<style>
h1 {
--baritone: red;
}
h2 {
--baz: blue;
}
</style>

@ -0,0 +1 @@
export { test } from '../preprocessed-styles/test';

@ -25,6 +25,7 @@
//"strict": true, //"strict": true,
"noImplicitThis": true, "noImplicitThis": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true "noUnusedParameters": true,
"typeRoots": ["./node_modules/@types"]
} }
} }

Loading…
Cancel
Save