diff --git a/.gitignore b/.gitignore
index 963ae9f26e..c2cac230a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,17 +4,17 @@
node_modules
*.map
/src/compiler/compile/internal_exports.js
+/src/shared/version.js
/compiler.d.ts
-/compiler.*js
-/index.*js
-/ssr.*js
-/action
-/internal
-/store
-/easing
-/motion
-/transition
-/animate
+/compiler.cjs
+/index.d.ts
+/action.d.ts
+/internal.d.ts
+/store.d.ts
+/easing.d.ts
+/motion.d.ts
+/transition.d.ts
+/animate.d.ts
/scratch/
/test/*/samples/_
/test/runtime/shards
diff --git a/.prettierignore b/.prettierignore
index 58dfd819cd..519034cd2e 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -3,6 +3,7 @@
!/scripts
!/src
src/compiler/compile/internal_exports.js
+src/shared/version.js
!/test
!documentation
!sites
@@ -12,4 +13,5 @@ src/compiler/compile/internal_exports.js
/test/**/expected*
/test/**/_output
/types
+!rollup.config.js
!vitest.config.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3eac6d40e..299e4d7dce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
* **breaking** Bundlers must specify the `browser` condition when building a frontend bundle for the browser ([#8516](https://github.com/sveltejs/svelte/pull/8516))
* **breaking** Minimum supported vite-plugin-svelte version is now 2.1.1. SvelteKit users can upgrade to 1.15.9 or newer to ensure a compatible version ([#8516](https://github.com/sveltejs/svelte/pull/8516))
* **breaking** Minimum supported TypeScript version is now TypeScript 5 (it will likely work with lower versions, but we make no guarantees about that) ([#8488](https://github.com/sveltejs/svelte/pull/8488))
+* **breaking** Remove `svelte/register` hook, CJS runtime version and CJS compiler output ([#8613](https://github.com/sveltejs/svelte/pull/8613))
* **breaking** Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
* **breaking** Stricter types for `Action` and `ActionReturn` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
* **breaking** Stricter types for `onMount` - now throws a type error when returning a function asynchronously to catch potential mistakes around callback functions (see PR for migration instructions) ([#8136](https://github.com/sveltejs/svelte/pull/8136))
@@ -14,6 +15,8 @@
* **breaking** Deprecate `SvelteComponentTyped`, use `SvelteComponent` instead ([#8512](https://github.com/sveltejs/svelte/pull/8512))
* **breaking** Error on falsy values instead of stores passed to `derived` ([#7947](https://github.com/sveltejs/svelte/pull/7947))
* **breaking** Custom store implementers now need to pass an `update` function additionally to the `set` function ([#6750](https://github.com/sveltejs/svelte/pull/6750))
+* **breaking** Change order in which preprocessors are applied ([#8618](https://github.com/sveltejs/svelte/pull/8618))
+* Add a way to modify attributes for script/style preprocessors ([#8618](https://github.com/sveltejs/svelte/pull/8618))
* Improve hydration speed by adding `data-svelte-h` attribute to detect unchanged HTML elements ([#7426](https://github.com/sveltejs/svelte/pull/7426))
* Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391))
* Add `a11y-no-static-element-interactions`rule ([#8251](https://github.com/sveltejs/svelte/pull/8251))
diff --git a/action/index.d.ts b/action/index.d.ts
new file mode 100644
index 0000000000..229aeb9859
--- /dev/null
+++ b/action/index.d.ts
@@ -0,0 +1 @@
+export * from '../types/runtime/action/index.js';
\ No newline at end of file
diff --git a/action/index.js b/action/index.js
new file mode 100644
index 0000000000..eb109abbed
--- /dev/null
+++ b/action/index.js
@@ -0,0 +1,2 @@
+'use strict';
+
diff --git a/action/index.mjs b/action/index.mjs
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/action/index.mjs
@@ -0,0 +1 @@
+
diff --git a/action/package.json b/action/package.json
new file mode 100644
index 0000000000..598aeeaf51
--- /dev/null
+++ b/action/package.json
@@ -0,0 +1,5 @@
+{
+ "main": "./index",
+ "module": "./index.mjs",
+ "types": "./index.d.ts"
+}
\ No newline at end of file
diff --git a/animate/index.d.ts b/animate/index.d.ts
new file mode 100644
index 0000000000..c6671ee75d
--- /dev/null
+++ b/animate/index.d.ts
@@ -0,0 +1 @@
+export * from '../types/runtime/animate/index.js';
\ No newline at end of file
diff --git a/animate/index.js b/animate/index.js
new file mode 100644
index 0000000000..6e52923a2f
--- /dev/null
+++ b/animate/index.js
@@ -0,0 +1,33 @@
+'use strict';
+
+var easing = require('../easing/index.js');
+var Component = require('../internal/Component-9c4b98a2.js');
+
+/**
+ * @param {Element} node
+ * @param {{ from: DOMRect; to: DOMRect }} fromTo
+ * @param {import('./public.js').FlipParams} params
+ * @returns {import('./public.js').AnimationConfig}
+ */
+function flip(node, { from, to }, params = {}) {
+ const style = getComputedStyle(node);
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
+ const dx = from.left + (from.width * ox) / to.width - (to.left + ox);
+ const dy = from.top + (from.height * oy) / to.height - (to.top + oy);
+ const { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing: easing$1 = easing.cubicOut } = params;
+ return {
+ delay,
+ duration: Component.is_function(duration) ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
+ easing: easing$1,
+ css: (t, u) => {
+ const x = u * dx;
+ const y = u * dy;
+ const sx = t + (u * from.width) / to.width;
+ const sy = t + (u * from.height) / to.height;
+ return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`;
+ }
+ };
+}
+
+exports.flip = flip;
diff --git a/animate/index.mjs b/animate/index.mjs
new file mode 100644
index 0000000000..5add71c114
--- /dev/null
+++ b/animate/index.mjs
@@ -0,0 +1,31 @@
+import { cubicOut } from '../easing/index.mjs';
+import { is_function } from '../internal/Component-cd97939e.mjs';
+
+/**
+ * @param {Element} node
+ * @param {{ from: DOMRect; to: DOMRect }} fromTo
+ * @param {import('./public.js').FlipParams} params
+ * @returns {import('./public.js').AnimationConfig}
+ */
+function flip(node, { from, to }, params = {}) {
+ const style = getComputedStyle(node);
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
+ const dx = from.left + (from.width * ox) / to.width - (to.left + ox);
+ const dy = from.top + (from.height * oy) / to.height - (to.top + oy);
+ const { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params;
+ return {
+ delay,
+ duration: is_function(duration) ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
+ easing,
+ css: (t, u) => {
+ const x = u * dx;
+ const y = u * dy;
+ const sx = t + (u * from.width) / to.width;
+ const sy = t + (u * from.height) / to.height;
+ return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`;
+ }
+ };
+}
+
+export { flip };
diff --git a/animate/package.json b/animate/package.json
new file mode 100644
index 0000000000..598aeeaf51
--- /dev/null
+++ b/animate/package.json
@@ -0,0 +1,5 @@
+{
+ "main": "./index",
+ "module": "./index.mjs",
+ "types": "./index.d.ts"
+}
\ No newline at end of file
diff --git a/check_publish_env.js b/check_publish_env.js
deleted file mode 100644
index ce6f066cff..0000000000
--- a/check_publish_env.js
+++ /dev/null
@@ -1,4 +0,0 @@
-if (!process.env.PUBLISH) {
- console.error('npm publish must be run with the PUBLISH environment variable set');
- process.exit(1);
-}
diff --git a/codemod-lazy-props.mjs b/codemod-lazy-props.mjs
deleted file mode 100644
index d1a92c4348..0000000000
--- a/codemod-lazy-props.mjs
+++ /dev/null
@@ -1,47 +0,0 @@
-import { existsSync, fstat, readFileSync, readdirSync, writeFileSync } from 'fs';
-import { resolve } from 'path';
-import { parse } from 'acorn';
-import { walk } from 'estree-walker';
-import { inspect } from 'util';
-
-import { p, print } from 'code-red';
-
-const samples = resolve(`vitest/runtime/runtime/samples`);
-
-for (const dir of readdirSync(samples)) {
- const cwd = resolve(samples, dir);
- const file = resolve(cwd, '_config.js');
-
- if (!existsSync(file)) continue;
- const contents = readFileSync(file, 'utf-8');
- const ast = parse(contents, {
- sourceType: 'module',
- ecmaVersion: 'latest',
- sourceFile: file,
- ranges: true
- });
-
- walk(ast, {
- enter(node) {
- if (
- node.type === 'ExportDefaultDeclaration' &&
- node.declaration.type === 'ObjectExpression'
- ) {
- this.skip();
-
- const props = node.declaration.properties.find((prop) => prop.key.name === 'props');
- if (!props) return;
- const { range } = props;
-
- const [start, end] = range;
-
- const code =
- contents.slice(0, start) +
- print(p`get ${props.key}() { return ${props.value}}`).code +
- contents.slice(end);
-
- writeFileSync(file, code);
- }
- }
- });
-}
diff --git a/compiler.js b/compiler.js
new file mode 100644
index 0000000000..a31b2644db
--- /dev/null
+++ b/compiler.js
@@ -0,0 +1,43153 @@
+'use strict';
+
+var acorn = require('acorn');
+var cssTree = require('css-tree');
+var tokenizer = require('css-tree/tokenizer');
+var MagicString = require('magic-string');
+
+function _interopNamespaceDefault(e) {
+ var n = Object.create(null);
+ if (e) {
+ Object.keys(e).forEach(function (k) {
+ if (k !== 'default') {
+ var d = Object.getOwnPropertyDescriptor(e, k);
+ Object.defineProperty(n, k, d.get ? d : {
+ enumerable: true,
+ get: function () { return e[k]; }
+ });
+ }
+ });
+ }
+ n.default = e;
+ return Object.freeze(n);
+}
+
+var acorn__namespace = /*#__PURE__*/_interopNamespaceDefault(acorn);
+
+const now =
+ typeof process !== 'undefined' && process.hrtime
+ ? () => {
+ const t = process.hrtime();
+ return t[0] * 1e3 + t[1] / 1e6;
+ }
+ : () => self.performance.now();
+
+/** @param {any} timings */
+function collapse_timings(timings) {
+ const result = {};
+ timings.forEach(
+ /** @param {any} timing */ (timing) => {
+ result[timing.label] = Object.assign(
+ {
+ total: timing.end - timing.start
+ },
+ timing.children && collapse_timings(timing.children)
+ );
+ }
+ );
+ return result;
+}
+
+class Stats {
+ /**
+ * @typedef {Object} Timing
+ * @property {string} label
+ * @property {number} start
+ * @property {number} end
+ * @property {Timing[]} children
+ */
+
+ /** @type {number} */
+
+
+ /** @type {Timing} */
+
+
+ /** @type {Timing[]} */
+
+
+ /** @type {Timing[]} */
+
+
+ /** @type {Timing[]} */
+
+ constructor() {
+ this.start_time = now();
+ this.stack = [];
+ this.current_children = this.timings = [];
+ }
+
+ /** @param {any} label */
+ start(label) {
+ const timing = {
+ label,
+ start: now(),
+ end: null,
+ children: []
+ };
+ this.current_children.push(timing);
+ this.stack.push(timing);
+ this.current_timing = timing;
+ this.current_children = timing.children;
+ }
+
+ /** @param {any} label */
+ stop(label) {
+ if (label !== this.current_timing.label) {
+ throw new Error(
+ `Mismatched timing labels (expected ${this.current_timing.label}, got ${label})`
+ );
+ }
+ this.current_timing.end = now();
+ this.stack.pop();
+ this.current_timing = this.stack[this.stack.length - 1];
+ this.current_children = this.current_timing ? this.current_timing.children : this.timings;
+ }
+ render() {
+ const timings = Object.assign(
+ {
+ total: now() - this.start_time
+ },
+ collapse_timings(this.timings)
+ );
+ return {
+ timings
+ };
+ }
+}
+
+/**
+ * @template T
+ * @overload
+ * @param {T[][]} nodes
+ * @param {T[]} [target]
+ * @returns {T[]}
+ */
+
+/**
+ * @template T
+ * @overload
+ * @param {T[]} nodes
+ * @param {T[]} [target]
+ * @returns {T[]}
+ */
+
+/**
+ * @param {any[]} nodes
+ * @param {any[]} [target]
+ * @returns {any[]}
+ */
+function flatten$1(nodes, target = []) {
+ for (let i = 0; i < nodes.length; i += 1) {
+ const node = nodes[i];
+ if (Array.isArray(node)) {
+ flatten$1(node, target);
+ } else {
+ target.push(node);
+ }
+ }
+
+ return target;
+}
+
+const regex_whitespace = /\s/;
+const regex_whitespaces = /\s+/;
+const regex_starts_with_whitespace = /^\s/;
+const regex_starts_with_whitespaces = /^[ \t\r\n]*/;
+const regex_ends_with_whitespace = /\s$/;
+const regex_ends_with_whitespaces = /[ \t\r\n]*$/;
+const regex_only_whitespaces = /^[ \t\n\r\f]+$/;
+
+const regex_whitespace_characters = /\s/g;
+const regex_non_whitespace_character = /\S/;
+
+const regex_starts_with_newline = /^\r?\n/;
+const regex_not_newline_characters = /[^\n]/g;
+
+const regex_double_quotes = /"/g;
+
+const regex_backslashes = /\\/g;
+
+const regex_starts_with_underscore = /^_/;
+const regex_ends_with_underscore = /_$/;
+
+const regex_dimensions = /^(?:offset|client)(?:Width|Height)$/;
+
+const regex_content_rect = /^(?:contentRect)$/;
+const regex_content_box_size = /^(?:contentBoxSize)$/;
+const regex_border_box_size = /^(?:borderBoxSize)$/;
+const regex_device_pixel_content_box_size = /^(?:devicePixelContentBoxSize)$/;
+const regex_box_size =
+ /^(?:contentRect|contentBoxSize|borderBoxSize|devicePixelContentBoxSize)$/;
+
+const regex_svelte_ignore = /^\s*svelte-ignore\s+([\s\S]+)\s*$/m;
+
+/**
+ * @param {string} text
+ * @returns {string[]}
+ */
+function extract_svelte_ignore(text) {
+ const match = regex_svelte_ignore.exec(text);
+ return match
+ ? match[1]
+ .split(regex_whitespace)
+ .map((x) => x.trim())
+ .filter(Boolean)
+ : [];
+}
+
+/**
+ * @param {import('estree').Node} node
+ * @returns {string[]}
+ */
+function extract_svelte_ignore_from_comments(node) {
+ return flatten$1(
+ (node.leadingComments || []).map((comment) => extract_svelte_ignore(comment.value))
+ );
+}
+
+/**
+ * @param {number} position
+ * @param {import('../interfaces.js').TemplateNode[]} template_nodes
+ * @returns {string[]}
+ */
+function extract_ignores_above_position(position, template_nodes) {
+ const previous_node_idx = template_nodes.findIndex((child) => child.end === position);
+ if (previous_node_idx === -1) {
+ return [];
+ }
+ for (let i = previous_node_idx; i >= 0; i--) {
+ const node = template_nodes[i];
+ if (node.type !== 'Comment' && node.type !== 'Text') {
+ return [];
+ }
+ if (node.type === 'Comment') {
+ if (node.ignores.length) {
+ return node.ignores;
+ }
+ }
+ }
+ return [];
+}
+
+/**
+ * @param {import('../compile/nodes/interfaces.js').INode} node
+ * @returns {string[]}
+ */
+function extract_ignores_above_node(node) {
+ /**
+ * This utilizes the fact that node has a prev and a next attribute
+ * which means that it can find svelte-ignores along
+ * the nodes on the same level as itself who share the same parent.
+ */
+ let cur_node = node.prev;
+ while (cur_node) {
+ if (cur_node.type !== 'Comment' && cur_node.type !== 'Text') {
+ return [];
+ }
+ if (cur_node.type === 'Comment' && cur_node.ignores.length) {
+ return cur_node.ignores;
+ }
+ cur_node = cur_node.prev;
+ }
+ return [];
+}
+
+/**
+ * @param {string} name
+ * @param {string[]} names
+ */
+function fuzzymatch(name, names) {
+ const set = new FuzzySet(names);
+ const matches = set.get(name);
+ return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null;
+}
+
+// adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js
+// BSD Licensed
+const GRAM_SIZE_LOWER = 2;
+const GRAM_SIZE_UPPER = 3;
+// return an edit distance from 0 to 1
+
+/**
+ * @param {string} str1
+ * @param {string} str2
+ */
+function _distance(str1, str2) {
+ if (str1 === null && str2 === null) {
+ throw 'Trying to compare two null values';
+ }
+ if (str1 === null || str2 === null) return 0;
+ str1 = String(str1);
+ str2 = String(str2);
+ const distance = levenshtein(str1, str2);
+ if (str1.length > str2.length) {
+ return 1 - distance / str1.length;
+ } else {
+ return 1 - distance / str2.length;
+ }
+}
+
+// helper functions
+
+/**
+ * @param {string} str1
+ * @param {string} str2
+ */
+function levenshtein(str1, str2) {
+ /**
+ * @type {number[]}
+ */
+ const current = [];
+ let prev;
+ let value;
+ for (let i = 0; i <= str2.length; i++) {
+ for (let j = 0; j <= str1.length; j++) {
+ if (i && j) {
+ if (str1.charAt(j - 1) === str2.charAt(i - 1)) {
+ value = prev;
+ } else {
+ value = Math.min(current[j], current[j - 1], prev) + 1;
+ }
+ } else {
+ value = i + j;
+ }
+ prev = current[j];
+ current[j] = value;
+ }
+ }
+ return current.pop();
+}
+
+const non_word_regex = /[^\w, ]+/;
+
+/**
+ * @param {string} value
+ * @param {any} gram_size
+ */
+function iterate_grams(value, gram_size = 2) {
+ const simplified = '-' + value.toLowerCase().replace(non_word_regex, '') + '-';
+ const len_diff = gram_size - simplified.length;
+ const results = [];
+ if (len_diff > 0) {
+ for (let i = 0; i < len_diff; ++i) {
+ value += '-';
+ }
+ }
+ for (let i = 0; i < simplified.length - gram_size + 1; ++i) {
+ results.push(simplified.slice(i, i + gram_size));
+ }
+ return results;
+}
+
+/**
+ * @param {string} value
+ * @param {any} gram_size
+ */
+function gram_counter(value, gram_size = 2) {
+ // return an object where key=gram, value=number of occurrences
+ const result = {};
+ const grams = iterate_grams(value, gram_size);
+ let i = 0;
+ for (i; i < grams.length; ++i) {
+ if (grams[i] in result) {
+ result[grams[i]] += 1;
+ } else {
+ result[grams[i]] = 1;
+ }
+ }
+ return result;
+}
+
+/**
+ * @param {any} a
+ * @param {any} b
+ */
+function sort_descending(a, b) {
+ return b[0] - a[0];
+}
+
+class FuzzySet {
+ __init() {this.exact_set = {};}
+ __init2() {this.match_dict = {};}
+ __init3() {this.items = {};}
+
+ /**
+ * @param {string[]} arr
+ */
+ constructor(arr) {FuzzySet.prototype.__init.call(this);FuzzySet.prototype.__init2.call(this);FuzzySet.prototype.__init3.call(this);
+ // initialization
+ for (let i = GRAM_SIZE_LOWER; i < GRAM_SIZE_UPPER + 1; ++i) {
+ this.items[i] = [];
+ }
+ // add all the items to the set
+ for (let i = 0; i < arr.length; ++i) {
+ this.add(arr[i]);
+ }
+ }
+
+ /**
+ * @param {string} value
+ */
+ add(value) {
+ const normalized_value = value.toLowerCase();
+ if (normalized_value in this.exact_set) {
+ return false;
+ }
+ let i = GRAM_SIZE_LOWER;
+ for (i; i < GRAM_SIZE_UPPER + 1; ++i) {
+ this._add(value, i);
+ }
+ }
+
+ /**
+ * @param {string} value
+ * @param {number} gram_size
+ */
+ _add(value, gram_size) {
+ const normalized_value = value.toLowerCase();
+ const items = this.items[gram_size] || [];
+ const index = items.length;
+ items.push(0);
+ const gram_counts = gram_counter(normalized_value, gram_size);
+ let sum_of_square_gram_counts = 0;
+ let gram;
+ let gram_count;
+ for (gram in gram_counts) {
+ gram_count = gram_counts[gram];
+ sum_of_square_gram_counts += Math.pow(gram_count, 2);
+ if (gram in this.match_dict) {
+ this.match_dict[gram].push([index, gram_count]);
+ } else {
+ this.match_dict[gram] = [[index, gram_count]];
+ }
+ }
+ const vector_normal = Math.sqrt(sum_of_square_gram_counts);
+ items[index] = [vector_normal, normalized_value];
+ this.items[gram_size] = items;
+ this.exact_set[normalized_value] = value;
+ }
+
+ /**
+ * @param {string} value
+ */
+ get(value) {
+ const normalized_value = value.toLowerCase();
+ const result = this.exact_set[normalized_value];
+ if (result) {
+ return [[1, result]];
+ }
+ let results = [];
+ // start with high gram size and if there are no results, go to lower gram sizes
+ for (let gram_size = GRAM_SIZE_UPPER; gram_size >= GRAM_SIZE_LOWER; --gram_size) {
+ results = this.__get(value, gram_size);
+ if (results) {
+ return results;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param {string} value
+ * @param {number} gram_size
+ */
+ __get(value, gram_size) {
+ const normalized_value = value.toLowerCase();
+ const matches = {};
+ const gram_counts = gram_counter(normalized_value, gram_size);
+ const items = this.items[gram_size];
+ let sum_of_square_gram_counts = 0;
+ let gram;
+ let gram_count;
+ let i;
+ let index;
+ let other_gram_count;
+ for (gram in gram_counts) {
+ gram_count = gram_counts[gram];
+ sum_of_square_gram_counts += Math.pow(gram_count, 2);
+ if (gram in this.match_dict) {
+ for (i = 0; i < this.match_dict[gram].length; ++i) {
+ index = this.match_dict[gram][i][0];
+ other_gram_count = this.match_dict[gram][i][1];
+ if (index in matches) {
+ matches[index] += gram_count * other_gram_count;
+ } else {
+ matches[index] = gram_count * other_gram_count;
+ }
+ }
+ }
+ }
+ const vector_normal = Math.sqrt(sum_of_square_gram_counts);
+ let results = [];
+ let match_score;
+ // build a results list of [score, str]
+ for (const match_index in matches) {
+ match_score = matches[match_index];
+ results.push([match_score / (vector_normal * items[match_index][0]), items[match_index][1]]);
+ }
+ results.sort(sort_descending);
+ let new_results = [];
+ const end_index = Math.min(50, results.length);
+ // truncate somewhat arbitrarily to 50
+ for (let i = 0; i < end_index; ++i) {
+ new_results.push([_distance(results[i][1], normalized_value), results[i][1]]);
+ }
+ results = new_results;
+ results.sort(sort_descending);
+ new_results = [];
+ for (let i = 0; i < results.length; ++i) {
+ if (results[i][0] == results[0][0]) {
+ new_results.push([results[i][0], this.exact_set[results[i][1]]]);
+ }
+ }
+ return new_results;
+ }
+}
+
+/** regex of all html void element names */
+const void_element_names =
+ /^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
+
+/** regex of all html element names. svg and math are omitted because they belong to the svg elements namespace */
+const html_element_names =
+ /^(?:a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr)$/;
+
+/** regex of all svg element names */
+const svg$1 =
+ /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|svg|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/;
+
+/**
+ * @param {string} name
+ * @returns {boolean}
+ */
+function is_void(name) {
+ return void_element_names.test(name) || name.toLowerCase() === '!doctype';
+}
+
+/**
+ * @param {string} name
+ * @returns {boolean}
+ */
+function is_html(name) {
+ return html_element_names.test(name);
+}
+
+/**
+ * @param {string} name
+ * @returns {boolean}
+ */
+function is_svg(name) {
+ return svg$1.test(name);
+}
+
+/**
+ * @param {string[]} items
+ * @param {string} [conjunction]
+ */
+function list(items, conjunction = 'or') {
+ if (items.length === 1) return items[0];
+ return `${items.slice(0, -1).join(', ')} ${conjunction} ${items[items.length - 1]}`;
+}
+
+// All parser errors should be listed and accessed from here
+
+/**
+ * @internal
+ */
+var parser_errors = {
+ /**
+ * @param {string} message
+ */
+ css_syntax_error: (message) => ({
+ code: 'css-syntax-error',
+ message
+ }),
+ duplicate_attribute: {
+ code: 'duplicate-attribute',
+ message: 'Attributes need to be unique'
+ },
+ /**
+ * @param {string} slug
+ * @param {string} name
+ */
+ duplicate_element: (slug, name) => ({
+ code: `duplicate-${slug}`,
+ message: `A component can only have one <${name}> tag`
+ }),
+ duplicate_style: {
+ code: 'duplicate-style',
+ message: 'You can only have one top-level
\ No newline at end of file
diff --git a/test/preprocess/samples/script/_config.js b/test/preprocess/samples/script/_config.js
index 1a21045465..535917ade6 100644
--- a/test/preprocess/samples/script/_config.js
+++ b/test/preprocess/samples/script/_config.js
@@ -1,8 +1,17 @@
+import MagicString from 'magic-string';
+
export default {
preprocess: {
- script: ({ content }) => {
+ script: ({ content, filename }) => {
+ const s = new MagicString(content);
+ s.overwrite(
+ content.indexOf('__THE_ANSWER__'),
+ content.indexOf('__THE_ANSWER__') + '__THE_ANSWER__'.length,
+ '42'
+ );
return {
- code: content.replace('__THE_ANSWER__', '42')
+ code: s.toString(),
+ map: s.generateMap({ hires: true, file: filename })
};
}
}
diff --git a/test/preprocess/samples/script/expected_map.json b/test/preprocess/samples/script/expected_map.json
new file mode 100644
index 0000000000..d5bf98483f
--- /dev/null
+++ b/test/preprocess/samples/script/expected_map.json
@@ -0,0 +1,8 @@
+{
+ "version": 3,
+ "mappings": "AAAA,CAAC,MAAM,CAAC;AACR,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAc,CAAC,CAAC;AAC7B,CAAC,CAAC,MAAM",
+ "names": [],
+ "sources": [
+ "input.svelte"
+ ]
+}
\ No newline at end of file
diff --git a/test/preprocess/samples/style-attributes-modified-longer/_config.js b/test/preprocess/samples/style-attributes-modified-longer/_config.js
new file mode 100644
index 0000000000..1fbb8b3d23
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified-longer/_config.js
@@ -0,0 +1,12 @@
+import * as assert from 'node:assert';
+
+export default {
+ preprocess: {
+ style: ({ attributes }) => {
+ assert.deepEqual(attributes, {
+ lang: 'scss'
+ });
+ return { code: 'PROCESSED', attributes: { sth: 'wayyyyyyyyyyyyy looooooonger' } };
+ }
+ }
+};
diff --git a/test/preprocess/samples/style-attributes-modified-longer/expected_map.json b/test/preprocess/samples/style-attributes-modified-longer/expected_map.json
new file mode 100644
index 0000000000..28397f95d9
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified-longer/expected_map.json
@@ -0,0 +1,8 @@
+{
+ "version": 3,
+ "mappings": "AAAA;;AAEA,MAAM,mCAAa,UAAM,CAAC,CAAC,KAAK;;AAEhC",
+ "names": [],
+ "sources": [
+ "input.svelte"
+ ]
+}
\ No newline at end of file
diff --git a/test/preprocess/samples/style-attributes-modified-longer/input.svelte b/test/preprocess/samples/style-attributes-modified-longer/input.svelte
new file mode 100644
index 0000000000..8b2713472a
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified-longer/input.svelte
@@ -0,0 +1,5 @@
+foo
+
+
+
+bar
diff --git a/test/preprocess/samples/style-attributes-modified-longer/output.svelte b/test/preprocess/samples/style-attributes-modified-longer/output.svelte
new file mode 100644
index 0000000000..e9971f7f18
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified-longer/output.svelte
@@ -0,0 +1,5 @@
+foo
+
+
+
+bar
diff --git a/test/preprocess/samples/style-attributes-modified/_config.js b/test/preprocess/samples/style-attributes-modified/_config.js
new file mode 100644
index 0000000000..cfeae0c027
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified/_config.js
@@ -0,0 +1,14 @@
+import * as assert from 'node:assert';
+
+export default {
+ preprocess: {
+ style: ({ attributes }) => {
+ assert.deepEqual(attributes, {
+ lang: 'scss',
+ 'data-foo': 'bar',
+ bool: true
+ });
+ return { code: 'PROCESSED', attributes: { sth: 'else' } };
+ }
+ }
+};
diff --git a/test/preprocess/samples/style-attributes-modified/expected_map.json b/test/preprocess/samples/style-attributes-modified/expected_map.json
new file mode 100644
index 0000000000..65f340c1c0
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified/expected_map.json
@@ -0,0 +1,8 @@
+{
+ "version": 3,
+ "mappings": "AAAA;;AAEA,MAAM,WAAiC,UAAM,CAAC,CAAC,KAAK;;AAEpD",
+ "names": [],
+ "sources": [
+ "input.svelte"
+ ]
+}
\ No newline at end of file
diff --git a/test/preprocess/samples/style-attributes-modified/input.svelte b/test/preprocess/samples/style-attributes-modified/input.svelte
new file mode 100644
index 0000000000..2b2b0e4423
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified/input.svelte
@@ -0,0 +1,5 @@
+foo
+
+
+
+bar
diff --git a/test/preprocess/samples/style-attributes-modified/output.svelte b/test/preprocess/samples/style-attributes-modified/output.svelte
new file mode 100644
index 0000000000..ff8ca98bf9
--- /dev/null
+++ b/test/preprocess/samples/style-attributes-modified/output.svelte
@@ -0,0 +1,5 @@
+foo
+
+
+
+bar
diff --git a/test/preprocess/samples/style-attributes/expected_map.json b/test/preprocess/samples/style-attributes/expected_map.json
new file mode 100644
index 0000000000..2eca3ec763
--- /dev/null
+++ b/test/preprocess/samples/style-attributes/expected_map.json
@@ -0,0 +1,8 @@
+{
+ "version": 3,
+ "mappings": "AAAA,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,UAAO,CAAC,CAAC,KAAK",
+ "names": [],
+ "sources": [
+ "input.svelte"
+ ]
+}
\ No newline at end of file
diff --git a/test/preprocess/samples/style-attributes/input.svelte b/test/preprocess/samples/style-attributes/input.svelte
index 3a55a5a3f6..c513b8bf21 100644
--- a/test/preprocess/samples/style-attributes/input.svelte
+++ b/test/preprocess/samples/style-attributes/input.svelte
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/test/runtime/runtime.shared.js b/test/runtime/runtime.shared.js
index 95a648771b..0d317fb360 100644
--- a/test/runtime/runtime.shared.js
+++ b/test/runtime/runtime.shared.js
@@ -56,7 +56,6 @@ async function run_test(dir) {
const cwd = path.resolve(`${__dirname}/samples/${dir}`);
const compileOptions = Object.assign({}, config.compileOptions || {}, {
- format: 'cjs',
hydratable: hydrate,
immutable: config.immutable,
accessors: 'accessors' in config ? config.accessors : true
diff --git a/test/runtime/runtime_base.test.js b/test/runtime/runtime_base.test.js
index 756c63c187..4f912eb444 100644
--- a/test/runtime/runtime_base.test.js
+++ b/test/runtime/runtime_base.test.js
@@ -3,7 +3,7 @@
import { create_loader } from '../helpers';
import { assert, it } from 'vitest';
-const load = create_loader({ generate: 'dom', dev: true, format: 'cjs' }, __dirname);
+const load = create_loader({ generate: 'dom', dev: true }, __dirname);
const { default: App } = await load('App.svelte');
it('fails if options.target is missing in dev mode', async () => {
diff --git a/test/runtime/samples/component-slot-duplicate-error-2/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error-2/Nested.svelte
deleted file mode 100644
index 32eee1534a..0000000000
--- a/test/runtime/samples/component-slot-duplicate-error-2/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-duplicate-error-3/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error-3/Nested.svelte
deleted file mode 100644
index 32eee1534a..0000000000
--- a/test/runtime/samples/component-slot-duplicate-error-3/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-duplicate-error-4/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error-4/Nested.svelte
deleted file mode 100644
index 0385342cef..0000000000
--- a/test/runtime/samples/component-slot-duplicate-error-4/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-duplicate-error/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error/Nested.svelte
deleted file mode 100644
index 32eee1534a..0000000000
--- a/test/runtime/samples/component-slot-duplicate-error/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-nested-error-2/Nested.svelte b/test/runtime/samples/component-slot-nested-error-2/Nested.svelte
deleted file mode 100644
index c6f086d96c..0000000000
--- a/test/runtime/samples/component-slot-nested-error-2/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-nested-error-3/Nested.svelte b/test/runtime/samples/component-slot-nested-error-3/Nested.svelte
deleted file mode 100644
index c6f086d96c..0000000000
--- a/test/runtime/samples/component-slot-nested-error-3/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-nested-error/Nested.svelte b/test/runtime/samples/component-slot-nested-error/Nested.svelte
deleted file mode 100644
index c6f086d96c..0000000000
--- a/test/runtime/samples/component-slot-nested-error/Nested.svelte
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/test/runtime/samples/store-imported-module-b/foo.js b/test/runtime/samples/store-imported-module-b/foo.js
deleted file mode 100644
index b88c1cc577..0000000000
--- a/test/runtime/samples/store-imported-module-b/foo.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { writable } from 'svelte/store';
-
-export default writable(42);
diff --git a/test/server-side-rendering/ssr-1.test.js b/test/server-side-rendering/ssr-1.test.js
index 9eb19aae41..43732e8eb7 100644
--- a/test/server-side-rendering/ssr-1.test.js
+++ b/test/server-side-rendering/ssr-1.test.js
@@ -30,8 +30,7 @@ describe('ssr', async () => {
const compileOptions = {
...config.compileOptions,
- generate: 'ssr',
- format: 'cjs'
+ generate: 'ssr'
};
const load = create_loader(compileOptions, dir);
diff --git a/test/server-side-rendering/ssr-2.test.js b/test/server-side-rendering/ssr-2.test.js
index acf823d0b0..f24edfc2c7 100644
--- a/test/server-side-rendering/ssr-2.test.js
+++ b/test/server-side-rendering/ssr-2.test.js
@@ -33,8 +33,7 @@ function run_runtime_samples(suite) {
it_fn(dir, async () => {
const compileOptions = {
...config.compileOptions,
- generate: 'ssr',
- format: 'cjs'
+ generate: 'ssr'
};
const load = create_loader(compileOptions, cwd);
diff --git a/transition/index.d.ts b/transition/index.d.ts
new file mode 100644
index 0000000000..ac81403395
--- /dev/null
+++ b/transition/index.d.ts
@@ -0,0 +1 @@
+export * from '../types/runtime/transition/index.js';
\ No newline at end of file
diff --git a/transition/index.js b/transition/index.js
new file mode 100644
index 0000000000..b981c3b717
--- /dev/null
+++ b/transition/index.js
@@ -0,0 +1,244 @@
+'use strict';
+
+var easing = require('../easing/index.js');
+var Component = require('../internal/Component-9c4b98a2.js');
+
+/**
+ * @param {Element} node
+ * @param {import('./public').BlurParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function blur(
+ node,
+ { delay = 0, duration = 400, easing: easing$1 = easing.cubicInOut, amount = 5, opacity = 0 } = {}
+) {
+ const style = getComputedStyle(node);
+ const target_opacity = +style.opacity;
+ const f = style.filter === 'none' ? '' : style.filter;
+ const od = target_opacity * (1 - opacity);
+ const [value, unit] = Component.split_css_unit(amount);
+ return {
+ delay,
+ duration,
+ easing: easing$1,
+ css: (_t, u) => `opacity: ${target_opacity - od * u}; filter: ${f} blur(${u * value}${unit});`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').FadeParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function fade(node, { delay = 0, duration = 400, easing = Component.identity } = {}) {
+ const o = +getComputedStyle(node).opacity;
+ return {
+ delay,
+ duration,
+ easing,
+ css: (t) => `opacity: ${t * o}`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').FlyParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function fly(
+ node,
+ { delay = 0, duration = 400, easing: easing$1 = easing.cubicOut, x = 0, y = 0, opacity = 0 } = {}
+) {
+ const style = getComputedStyle(node);
+ const target_opacity = +style.opacity;
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const od = target_opacity * (1 - opacity);
+ const [xValue, xUnit] = Component.split_css_unit(x);
+ const [yValue, yUnit] = Component.split_css_unit(y);
+ return {
+ delay,
+ duration,
+ easing: easing$1,
+ css: (t, u) => `
+ transform: ${transform} translate(${(1 - t) * xValue}${xUnit}, ${(1 - t) * yValue}${yUnit});
+ opacity: ${target_opacity - od * u}`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').SlideParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function slide(node, { delay = 0, duration = 400, easing: easing$1 = easing.cubicOut, axis = 'y' } = {}) {
+ const style = getComputedStyle(node);
+ const opacity = +style.opacity;
+ const primary_property = axis === 'y' ? 'height' : 'width';
+ const primary_property_value = parseFloat(style[primary_property]);
+ const secondary_properties = axis === 'y' ? ['top', 'bottom'] : ['left', 'right'];
+ const capitalized_secondary_properties = secondary_properties.map(
+ (e) => `${e[0].toUpperCase()}${e.slice(1)}`
+ );
+ const padding_start_value = parseFloat(style[`padding${capitalized_secondary_properties[0]}`]);
+ const padding_end_value = parseFloat(style[`padding${capitalized_secondary_properties[1]}`]);
+ const margin_start_value = parseFloat(style[`margin${capitalized_secondary_properties[0]}`]);
+ const margin_end_value = parseFloat(style[`margin${capitalized_secondary_properties[1]}`]);
+ const border_width_start_value = parseFloat(
+ style[`border${capitalized_secondary_properties[0]}Width`]
+ );
+ const border_width_end_value = parseFloat(
+ style[`border${capitalized_secondary_properties[1]}Width`]
+ );
+ return {
+ delay,
+ duration,
+ easing: easing$1,
+ css: (t) =>
+ 'overflow: hidden;' +
+ `opacity: ${Math.min(t * 20, 1) * opacity};` +
+ `${primary_property}: ${t * primary_property_value}px;` +
+ `padding-${secondary_properties[0]}: ${t * padding_start_value}px;` +
+ `padding-${secondary_properties[1]}: ${t * padding_end_value}px;` +
+ `margin-${secondary_properties[0]}: ${t * margin_start_value}px;` +
+ `margin-${secondary_properties[1]}: ${t * margin_end_value}px;` +
+ `border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` +
+ `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').ScaleParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function scale(
+ node,
+ { delay = 0, duration = 400, easing: easing$1 = easing.cubicOut, start = 0, opacity = 0 } = {}
+) {
+ const style = getComputedStyle(node);
+ const target_opacity = +style.opacity;
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const sd = 1 - start;
+ const od = target_opacity * (1 - opacity);
+ return {
+ delay,
+ duration,
+ easing: easing$1,
+ css: (_t, u) => `
+ transform: ${transform} scale(${1 - sd * u});
+ opacity: ${target_opacity - od * u}
+ `
+ };
+}
+
+/**
+ * @param {SVGElement & { getTotalLength(): number }} node
+ * @param {import('./public').DrawParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function draw(node, { delay = 0, speed, duration, easing: easing$1 = easing.cubicInOut } = {}) {
+ let len = node.getTotalLength();
+ const style = getComputedStyle(node);
+ if (style.strokeLinecap !== 'butt') {
+ len += parseInt(style.strokeWidth);
+ }
+ if (duration === undefined) {
+ if (speed === undefined) {
+ duration = 800;
+ } else {
+ duration = len / speed;
+ }
+ } else if (typeof duration === 'function') {
+ duration = duration(len);
+ }
+ return {
+ delay,
+ duration,
+ easing: easing$1,
+ css: (_, u) => `
+ stroke-dasharray: ${len};
+ stroke-dashoffset: ${u * len};
+ `
+ };
+}
+
+/**
+ * @param {import('./public').CrossfadeParams & {
+ * fallback?: (node: Element, params: import('./public').CrossfadeParams, intro: boolean) => import('./public').TransitionConfig;
+ * }} params
+ * @returns {[(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig, (node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig]}
+ */
+function crossfade({ fallback, ...defaults }) {
+ /** @type {Map} */
+ const to_receive = new Map();
+ /** @type {Map} */
+ const to_send = new Map();
+ /**
+ * @param {Element} from_node
+ * @param {Element} node
+ * @param {import('./public').CrossfadeParams} params
+ * @returns {import('./public').TransitionConfig}
+ */
+ function crossfade(from_node, node, params) {
+ const {
+ delay = 0,
+ duration = (d) => Math.sqrt(d) * 30,
+ easing: easing$1 = easing.cubicOut
+ } = Component.assign(Component.assign({}, defaults), params);
+ const from = from_node.getBoundingClientRect();
+ const to = node.getBoundingClientRect();
+ const dx = from.left - to.left;
+ const dy = from.top - to.top;
+ const dw = from.width / to.width;
+ const dh = from.height / to.height;
+ const d = Math.sqrt(dx * dx + dy * dy);
+ const style = getComputedStyle(node);
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const opacity = +style.opacity;
+ return {
+ delay,
+ duration: Component.is_function(duration) ? duration(d) : duration,
+ easing: easing$1,
+ css: (t, u) => `
+ opacity: ${t * opacity};
+ transform-origin: top left;
+ transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${
+ t + (1 - t) * dh
+ });
+ `
+ };
+ }
+
+ /**
+ * @param {Map} items
+ * @param {Map} counterparts
+ * @param {boolean} intro
+ * @returns {(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig}
+ */
+ function transition(items, counterparts, intro) {
+ return (node, params) => {
+ items.set(params.key, node);
+ return () => {
+ if (counterparts.has(params.key)) {
+ const other_node = counterparts.get(params.key);
+ counterparts.delete(params.key);
+ return crossfade(other_node, node, params);
+ }
+ // if the node is disappearing altogether
+ // (i.e. wasn't claimed by the other list)
+ // then we need to supply an outro
+ items.delete(params.key);
+ return fallback && fallback(node, params, intro);
+ };
+ };
+ }
+ return [transition(to_send, to_receive, false), transition(to_receive, to_send, true)];
+}
+
+exports.blur = blur;
+exports.crossfade = crossfade;
+exports.draw = draw;
+exports.fade = fade;
+exports.fly = fly;
+exports.scale = scale;
+exports.slide = slide;
diff --git a/transition/index.mjs b/transition/index.mjs
new file mode 100644
index 0000000000..5168c39a17
--- /dev/null
+++ b/transition/index.mjs
@@ -0,0 +1,236 @@
+import { cubicInOut, cubicOut } from '../easing/index.mjs';
+import { split_css_unit, identity, assign, is_function } from '../internal/Component-cd97939e.mjs';
+
+/**
+ * @param {Element} node
+ * @param {import('./public').BlurParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function blur(
+ node,
+ { delay = 0, duration = 400, easing = cubicInOut, amount = 5, opacity = 0 } = {}
+) {
+ const style = getComputedStyle(node);
+ const target_opacity = +style.opacity;
+ const f = style.filter === 'none' ? '' : style.filter;
+ const od = target_opacity * (1 - opacity);
+ const [value, unit] = split_css_unit(amount);
+ return {
+ delay,
+ duration,
+ easing,
+ css: (_t, u) => `opacity: ${target_opacity - od * u}; filter: ${f} blur(${u * value}${unit});`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').FadeParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function fade(node, { delay = 0, duration = 400, easing = identity } = {}) {
+ const o = +getComputedStyle(node).opacity;
+ return {
+ delay,
+ duration,
+ easing,
+ css: (t) => `opacity: ${t * o}`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').FlyParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function fly(
+ node,
+ { delay = 0, duration = 400, easing = cubicOut, x = 0, y = 0, opacity = 0 } = {}
+) {
+ const style = getComputedStyle(node);
+ const target_opacity = +style.opacity;
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const od = target_opacity * (1 - opacity);
+ const [xValue, xUnit] = split_css_unit(x);
+ const [yValue, yUnit] = split_css_unit(y);
+ return {
+ delay,
+ duration,
+ easing,
+ css: (t, u) => `
+ transform: ${transform} translate(${(1 - t) * xValue}${xUnit}, ${(1 - t) * yValue}${yUnit});
+ opacity: ${target_opacity - od * u}`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').SlideParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function slide(node, { delay = 0, duration = 400, easing = cubicOut, axis = 'y' } = {}) {
+ const style = getComputedStyle(node);
+ const opacity = +style.opacity;
+ const primary_property = axis === 'y' ? 'height' : 'width';
+ const primary_property_value = parseFloat(style[primary_property]);
+ const secondary_properties = axis === 'y' ? ['top', 'bottom'] : ['left', 'right'];
+ const capitalized_secondary_properties = secondary_properties.map(
+ (e) => `${e[0].toUpperCase()}${e.slice(1)}`
+ );
+ const padding_start_value = parseFloat(style[`padding${capitalized_secondary_properties[0]}`]);
+ const padding_end_value = parseFloat(style[`padding${capitalized_secondary_properties[1]}`]);
+ const margin_start_value = parseFloat(style[`margin${capitalized_secondary_properties[0]}`]);
+ const margin_end_value = parseFloat(style[`margin${capitalized_secondary_properties[1]}`]);
+ const border_width_start_value = parseFloat(
+ style[`border${capitalized_secondary_properties[0]}Width`]
+ );
+ const border_width_end_value = parseFloat(
+ style[`border${capitalized_secondary_properties[1]}Width`]
+ );
+ return {
+ delay,
+ duration,
+ easing,
+ css: (t) =>
+ 'overflow: hidden;' +
+ `opacity: ${Math.min(t * 20, 1) * opacity};` +
+ `${primary_property}: ${t * primary_property_value}px;` +
+ `padding-${secondary_properties[0]}: ${t * padding_start_value}px;` +
+ `padding-${secondary_properties[1]}: ${t * padding_end_value}px;` +
+ `margin-${secondary_properties[0]}: ${t * margin_start_value}px;` +
+ `margin-${secondary_properties[1]}: ${t * margin_end_value}px;` +
+ `border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` +
+ `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;`
+ };
+}
+
+/**
+ * @param {Element} node
+ * @param {import('./public').ScaleParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function scale(
+ node,
+ { delay = 0, duration = 400, easing = cubicOut, start = 0, opacity = 0 } = {}
+) {
+ const style = getComputedStyle(node);
+ const target_opacity = +style.opacity;
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const sd = 1 - start;
+ const od = target_opacity * (1 - opacity);
+ return {
+ delay,
+ duration,
+ easing,
+ css: (_t, u) => `
+ transform: ${transform} scale(${1 - sd * u});
+ opacity: ${target_opacity - od * u}
+ `
+ };
+}
+
+/**
+ * @param {SVGElement & { getTotalLength(): number }} node
+ * @param {import('./public').DrawParams} [params]
+ * @returns {import('./public').TransitionConfig}
+ */
+function draw(node, { delay = 0, speed, duration, easing = cubicInOut } = {}) {
+ let len = node.getTotalLength();
+ const style = getComputedStyle(node);
+ if (style.strokeLinecap !== 'butt') {
+ len += parseInt(style.strokeWidth);
+ }
+ if (duration === undefined) {
+ if (speed === undefined) {
+ duration = 800;
+ } else {
+ duration = len / speed;
+ }
+ } else if (typeof duration === 'function') {
+ duration = duration(len);
+ }
+ return {
+ delay,
+ duration,
+ easing,
+ css: (_, u) => `
+ stroke-dasharray: ${len};
+ stroke-dashoffset: ${u * len};
+ `
+ };
+}
+
+/**
+ * @param {import('./public').CrossfadeParams & {
+ * fallback?: (node: Element, params: import('./public').CrossfadeParams, intro: boolean) => import('./public').TransitionConfig;
+ * }} params
+ * @returns {[(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig, (node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig]}
+ */
+function crossfade({ fallback, ...defaults }) {
+ /** @type {Map} */
+ const to_receive = new Map();
+ /** @type {Map} */
+ const to_send = new Map();
+ /**
+ * @param {Element} from_node
+ * @param {Element} node
+ * @param {import('./public').CrossfadeParams} params
+ * @returns {import('./public').TransitionConfig}
+ */
+ function crossfade(from_node, node, params) {
+ const {
+ delay = 0,
+ duration = (d) => Math.sqrt(d) * 30,
+ easing = cubicOut
+ } = assign(assign({}, defaults), params);
+ const from = from_node.getBoundingClientRect();
+ const to = node.getBoundingClientRect();
+ const dx = from.left - to.left;
+ const dy = from.top - to.top;
+ const dw = from.width / to.width;
+ const dh = from.height / to.height;
+ const d = Math.sqrt(dx * dx + dy * dy);
+ const style = getComputedStyle(node);
+ const transform = style.transform === 'none' ? '' : style.transform;
+ const opacity = +style.opacity;
+ return {
+ delay,
+ duration: is_function(duration) ? duration(d) : duration,
+ easing,
+ css: (t, u) => `
+ opacity: ${t * opacity};
+ transform-origin: top left;
+ transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${
+ t + (1 - t) * dh
+ });
+ `
+ };
+ }
+
+ /**
+ * @param {Map} items
+ * @param {Map} counterparts
+ * @param {boolean} intro
+ * @returns {(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig}
+ */
+ function transition(items, counterparts, intro) {
+ return (node, params) => {
+ items.set(params.key, node);
+ return () => {
+ if (counterparts.has(params.key)) {
+ const other_node = counterparts.get(params.key);
+ counterparts.delete(params.key);
+ return crossfade(other_node, node, params);
+ }
+ // if the node is disappearing altogether
+ // (i.e. wasn't claimed by the other list)
+ // then we need to supply an outro
+ items.delete(params.key);
+ return fallback && fallback(node, params, intro);
+ };
+ };
+ }
+ return [transition(to_send, to_receive, false), transition(to_receive, to_send, true)];
+}
+
+export { blur, crossfade, draw, fade, fly, scale, slide };
diff --git a/transition/package.json b/transition/package.json
new file mode 100644
index 0000000000..598aeeaf51
--- /dev/null
+++ b/transition/package.json
@@ -0,0 +1,5 @@
+{
+ "main": "./index",
+ "module": "./index.mjs",
+ "types": "./index.d.ts"
+}
\ No newline at end of file