chore: run test suite on both `functional` and `string` templating

templateless-template-generation
paoloricciuti 5 days ago
parent 8424395ac4
commit c75c42949b

@ -149,9 +149,9 @@
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/estree": "^1.0.5",
"acorn": "^8.12.1",
"@sveltejs/acorn-typescript": "^1.0.5",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",

@ -22,7 +22,7 @@ import { current_component } from './context.js';
let parent = null;
/** @type {Set<string>} */
let seen;
export let seen;
/**
* @param {Element} element

@ -58,15 +58,17 @@ export function create_deferred() {
* @param {Partial<CompileOptions>} compileOptions
* @param {boolean} [output_map]
* @param {any} [preprocessor]
* @param {import('./suite').TemplatingMode} [templating_mode]
*/
export async function compile_directory(
cwd,
generate,
compileOptions = {},
output_map = false,
preprocessor
preprocessor,
templating_mode
) {
const output_dir = `${cwd}/_output/${generate}`;
const output_dir = `${cwd}/_output/${generate}${templating_mode === 'functional' ? `-${templating_mode}` : ''}`;
fs.rmSync(output_dir, { recursive: true, force: true });
@ -77,7 +79,8 @@ export async function compile_directory(
let opts = {
filename: path.join(cwd, file),
...compileOptions,
generate
generate,
preventTemplateCloning: templating_mode === 'functional'
};
if (file.endsWith('.js')) {

@ -41,10 +41,24 @@ function read(path: string): string | void {
return fs.existsSync(path) ? fs.readFileSync(path, 'utf-8') : undefined;
}
const { test, run } = suite<HydrationTest>(async (config, cwd) => {
const { test, run } = suite<HydrationTest>(async (config, cwd, templating_mode) => {
if (!config.load_compiled) {
await compile_directory(cwd, 'client', { accessors: true, ...config.compileOptions });
await compile_directory(cwd, 'server', config.compileOptions);
await compile_directory(
cwd,
'client',
{ accessors: true, ...config.compileOptions },
undefined,
undefined,
templating_mode
);
await compile_directory(
cwd,
'server',
config.compileOptions,
undefined,
undefined,
templating_mode
);
}
const target = window.document.body;
@ -102,7 +116,11 @@ const { test, run } = suite<HydrationTest>(async (config, cwd) => {
};
const component = createClassComponent({
component: (await import(`${cwd}/_output/client/main.svelte.js`)).default,
component: (
await import(
`${cwd}/_output/client${templating_mode === 'functional' ? '-functional' : ''}/main.svelte.js`
)
).default,
target,
hydrate: true,
props: config.props,
@ -169,4 +187,5 @@ const { test, run } = suite<HydrationTest>(async (config, cwd) => {
});
export { test, assert_ok };
await run(__dirname);
await run(__dirname, 'string');
await run(__dirname, 'functional');

@ -4,7 +4,7 @@ import * as fs from 'node:fs';
import * as path from 'node:path';
import { compile } from 'svelte/compiler';
import { afterAll, assert, beforeAll, describe } from 'vitest';
import { suite, suite_with_variants } from '../suite';
import { suite, suite_with_variants, type TemplatingMode } from '../suite';
import { write } from '../helpers';
import type { Warning } from '#compiler';
@ -35,27 +35,41 @@ const { run: run_browser_tests } = suite_with_variants<
return false;
},
() => {},
async (config, test_dir, variant) => {
await run_test(test_dir, config, variant === 'hydrate');
async (config, test_dir, variant, _, templating_mode) => {
await run_test(test_dir, config, variant === 'hydrate', templating_mode);
}
);
describe.concurrent(
'runtime-browser',
() => run_browser_tests(__dirname),
() => run_browser_tests(__dirname, 'string'),
// Browser tests are brittle and slow on CI
{ timeout: 20000, retry: process.env.CI ? 1 : 0 }
);
describe.concurrent(
'runtime-browser-functional',
() => run_browser_tests(__dirname, 'functional'),
// Browser tests are brittle and slow on CI
{ timeout: 20000, retry: process.env.CI ? 1 : 0 }
);
const { run: run_ce_tests } = suite<ReturnType<typeof import('./assert').test>>(
async (config, test_dir) => {
await run_test(test_dir, config, false);
async (config, test_dir, templating_mode) => {
await run_test(test_dir, config, false, templating_mode);
}
);
describe.concurrent(
'custom-elements',
() => run_ce_tests(__dirname, 'custom-elements-samples'),
() => run_ce_tests(__dirname, 'string', 'custom-elements-samples'),
// Browser tests are brittle and slow on CI
{ timeout: 20000, retry: process.env.CI ? 1 : 0 }
);
describe.concurrent(
'custom-elements',
() => run_ce_tests(__dirname, 'functional', 'custom-elements-samples'),
// Browser tests are brittle and slow on CI
{ timeout: 20000, retry: process.env.CI ? 1 : 0 }
);
@ -63,7 +77,8 @@ describe.concurrent(
async function run_test(
test_dir: string,
config: ReturnType<typeof import('./assert').test>,
hydrate: boolean
hydrate: boolean,
templating_mode: TemplatingMode
) {
const warnings: any[] = [];
@ -90,10 +105,14 @@ async function run_test(
...config.compileOptions,
immutable: config.immutable,
customElement: test_dir.includes('custom-elements-samples'),
accessors: 'accessors' in config ? config.accessors : true
accessors: 'accessors' in config ? config.accessors : true,
preventTemplateCloning: templating_mode === 'functional'
});
write(`${test_dir}/_output/client/${path.basename(args.path)}.js`, compiled.js.code);
write(
`${test_dir}/_output/client${templating_mode === 'functional' ? '-functional' : ''}/${path.basename(args.path)}.js`,
compiled.js.code
);
compiled.warnings.forEach((warning) => {
if (warning.code === 'options_deprecated_accessors') return;
@ -103,7 +122,7 @@ async function run_test(
if (compiled.css !== null) {
compiled.js.code += `document.head.innerHTML += \`<style>${compiled.css.code}</style>\``;
write(
`${test_dir}/_output/client/${path.basename(args.path)}.css`,
`${test_dir}/_output/${templating_mode === 'functional' ? '-functional' : ''}/${path.basename(args.path)}.css`,
compiled.css.code
);
}
@ -151,7 +170,8 @@ async function run_test(
...config.compileOptions,
immutable: config.immutable,
customElement: test_dir.includes('custom-elements-samples'),
accessors: 'accessors' in config ? config.accessors : true
accessors: 'accessors' in config ? config.accessors : true,
preventTemplateCloning: templating_mode === 'functional'
});
return {

@ -18,8 +18,9 @@
this.innerHTML = 'Hello ' + this._obj.text + '!';
}
}
window.customElements.define('my-custom-element', MyCustomElement);
if(!window.customElements.get('my-custom-element')) {
window.customElements.define('my-custom-element', MyCustomElement);
}
</script>
<my-custom-element camelCase={{ text: 'World' }} />

@ -26,8 +26,10 @@
}
class Extended extends MyCustomElement {}
window.customElements.define('my-custom-inheritance-element', Extended);
if(!window.customElements.get('my-custom-inheritance-element')) {
window.customElements.define('my-custom-inheritance-element', Extended);
}
</script>
<my-custom-inheritance-element camelCase={{ text: 'World' }} text="!" />

@ -9,7 +9,8 @@ export default test({
Object.defineProperties(window, {
scrollY: {
value: 0,
configurable: true
configurable: true,
writable: true
}
});
original_scrollTo = window.scrollTo;

@ -10,7 +10,8 @@ import { compile_directory } from '../helpers.js';
import { setup_html_equal } from '../html_equal.js';
import { raf } from '../animation-helpers.js';
import type { CompileOptions } from '#compiler';
import { suite_with_variants, type BaseTest } from '../suite.js';
import { suite_with_variants, type BaseTest, type TemplatingMode } from '../suite.js';
import { seen } from '../../src/internal/server/dev.js';
type Assert = typeof import('vitest').assert & {
htmlEqual(a: string, b: string, description?: string): void;
@ -141,16 +142,21 @@ export function runtime_suite(runes: boolean) {
return false;
},
(config, cwd) => {
return common_setup(cwd, runes, config);
(config, cwd, templating_mode) => {
return common_setup(cwd, runes, config, templating_mode);
},
async (config, cwd, variant, common) => {
await run_test_variant(cwd, config, variant, common, runes);
async (config, cwd, variant, common, templating_mode) => {
await run_test_variant(cwd, config, variant, common, runes, templating_mode);
}
);
}
async function common_setup(cwd: string, runes: boolean | undefined, config: RuntimeTest) {
async function common_setup(
cwd: string,
runes: boolean | undefined,
config: RuntimeTest,
templating_mode: TemplatingMode
) {
const force_hmr = process.env.HMR && config.compileOptions?.dev !== false && !config.error;
const compileOptions: CompileOptions = {
@ -161,13 +167,14 @@ async function common_setup(cwd: string, runes: boolean | undefined, config: Run
...config.compileOptions,
immutable: config.immutable,
accessors: 'accessors' in config ? config.accessors : true,
runes
runes,
preventTemplateCloning: templating_mode === 'functional'
};
// load_compiled can be used for debugging a test. It means the compiler will not run on the input
// so you can manipulate the output manually to see what fixes it, adding console.logs etc.
if (!config.load_compiled) {
await compile_directory(cwd, 'client', compileOptions);
await compile_directory(cwd, 'client', compileOptions, undefined, undefined, templating_mode);
await compile_directory(cwd, 'server', compileOptions);
}
@ -179,7 +186,8 @@ async function run_test_variant(
config: RuntimeTest,
variant: 'dom' | 'hydrate' | 'ssr',
compileOptions: CompileOptions,
runes: boolean
runes: boolean,
templating_mode: TemplatingMode
) {
let unintended_error = false;
@ -257,8 +265,15 @@ async function run_test_variant(
raf.reset();
// Put things we need on window for testing
const styles = globSync('**/*.css', { cwd: `${cwd}/_output/client` })
.map((file) => fs.readFileSync(`${cwd}/_output/client/${file}`, 'utf-8'))
const styles = globSync('**/*.css', {
cwd: `${cwd}/_output/client${templating_mode === 'functional' ? '-functional' : ''}`
})
.map((file) =>
fs.readFileSync(
`${cwd}/_output/client${templating_mode === 'functional' ? '-functional' : ''}/${file}`,
'utf-8'
)
)
.join('\n')
.replace(/\/\*<\/?style>\*\//g, '');
@ -274,7 +289,9 @@ async function run_test_variant(
globalThis.requestAnimationFrame = globalThis.setTimeout;
let mod = await import(`${cwd}/_output/client/main.svelte.js`);
let mod = await import(
`${cwd}/_output/client${templating_mode === 'functional' ? '-functional' : ''}/main.svelte.js`
);
const target = window.document.querySelector('main') as HTMLElement;
@ -282,6 +299,8 @@ async function run_test_variant(
if (variant === 'hydrate' || variant === 'ssr') {
config.before_test?.();
// we need to clear the seen messages between tests
seen?.clear?.();
// ssr into target
const SsrSvelteComponent = (await import(`${cwd}/_output/server/main.svelte.js`)).default;
const { html, head } = render(SsrSvelteComponent, {
@ -289,11 +308,17 @@ async function run_test_variant(
idPrefix: config.id_prefix
});
fs.writeFileSync(`${cwd}/_output/rendered.html`, html);
fs.writeFileSync(
`${cwd}/_output/rendered${templating_mode === 'functional' ? '-functional' : ''}.html`,
html
);
target.innerHTML = html;
if (head) {
fs.writeFileSync(`${cwd}/_output/rendered_head.html`, head);
fs.writeFileSync(
`${cwd}/_output/rendered_head${templating_mode === 'functional' ? '-functional' : ''}.html`,
head
);
window.document.head.innerHTML = window.document.head.innerHTML + head;
}

@ -11,4 +11,5 @@ const { test, run } = runtime_suite(false);
export { test, ok };
await run(__dirname);
await run(__dirname, 'string');
await run(__dirname, 'functional');

@ -1,18 +1,20 @@
<script module>
customElements.define('value-element', class extends HTMLElement {
if(!customElements.get('value-element')) {
customElements.define('value-element', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
set value(v) {
if (this.__value !== v) {
this.__value = v;
this.shadowRoot.innerHTML = `<span>${v}</span>`;
}
}
});
set value(v) {
if (this.__value !== v) {
this.__value = v;
this.shadowRoot.innerHTML = `<span>${v}</span>`;
}
}
});
}
</script>
<my-element string="test" object={{ test: true }}></my-element>

@ -5,4 +5,5 @@ const { test, run } = runtime_suite(true);
export { test, ok };
await run(__dirname);
await run(__dirname, 'string');
await run(__dirname, 'functional');

@ -0,0 +1,52 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
function increment(_, counter) {
counter.count += 1;
}
var root = $.template_fn(
() => {
var button = document.createElement('button');
var text = document.createTextNode(' ');
button.insertBefore(text, undefined)
var text_1 = document.createTextNode(' ');
var comment = document.createComment('');
var text_2 = document.createTextNode(' ');
var fragment = document.createDocumentFragment();
fragment.append(button, text_1, comment, text_2)
return fragment;
},
1
);
export default function Await_block_scope($$anchor) {
let counter = $.proxy({ count: 0 });
const promise = $.derived(() => Promise.resolve(counter));
var fragment = root();
var button = $.first_child(fragment);
button.__click = [increment, counter];
var text = $.child(button);
$.reset(button);
var node = $.sibling(button, 2);
$.await(node, () => $.get(promise), null, ($$anchor, counter) => {});
var text_1 = $.sibling(node);
$.template_effect(() => {
$.set_text(text, `clicks: ${counter.count ?? ''}`);
$.set_text(text_1, ` ${counter.count ?? ''}`);
});
$.append($$anchor, fragment);
}
$.delegate(['click']);

@ -0,0 +1,45 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
import TextInput from './Child.svelte';
const snippet = ($$anchor) => {
$.next();
var text = $.text('Something');
$.append($$anchor, text);
};
var root = $.template_fn(
() => {
var comment = document.createComment('');
var comment_1 = document.createComment('');
var text = document.createTextNode(' ');
var fragment = document.createDocumentFragment();
fragment.append(comment, comment_1, text)
return fragment;
},
1
);
export default function Bind_component_snippet($$anchor) {
let value = $.state('');
const _snippet = snippet;
var fragment = root();
var node = $.first_child(fragment);
TextInput(node, {
get value() {
return $.get(value);
},
set value($$value) {
$.set(value, $.proxy($$value));
}
});
var text_1 = $.sibling(node);
$.template_effect(() => $.set_text(text_1, ` value: ${$.get(value) ?? ''}`));
$.append($$anchor, fragment);
}

@ -0,0 +1,7 @@
import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';
export default function Bind_this($$anchor) {
$.bind_this(Foo($$anchor, { $$legacy: true }), ($$value) => foo = $$value, () => foo);
}

@ -0,0 +1,27 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
export default function Class_state_field_constructor_assignment($$anchor, $$props) {
$.push($$props, true);
class Foo {
#a = $.state();
get a() {
return $.get(this.#a);
}
set a(value) {
$.set(this.#a, $.proxy(value));
}
#b = $.state();
constructor() {
this.a = 1;
this.#b.v = 2;
}
}
$.pop();
}

@ -0,0 +1,16 @@
/* index.svelte.js generated by Svelte VERSION */
import * as $ from 'svelte/internal/client';
let a = $.state(1);
let b = $.state(2);
let c = 3;
let d = 4;
export function update(array) {
(
$.set(a, $.proxy(array[0])),
$.set(b, $.proxy(array[1]))
);
[c, d] = array;
}

@ -0,0 +1,53 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
var root = $.template_fn(
() => {
var div = document.createElement('div');
var text = document.createTextNode(' ');
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var text_1 = document.createTextNode(' ');
var custom_element = document.createElement('custom-element');
var text_2 = document.createTextNode(' ');
var div_1 = document.createElement('div');
var text_3 = document.createTextNode(' ');
var svg_1 = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var text_4 = document.createTextNode(' ');
var custom_element_1 = document.createElement('custom-element');
var fragment = document.createDocumentFragment();
fragment.append(div, text, svg, text_1, custom_element, text_2, div_1, text_3, svg_1, text_4, custom_element_1)
return fragment;
},
3
);
export default function Main($$anchor) {
// needs to be a snapshot test because jsdom does auto-correct the attribute casing
let x = 'test';
let y = () => 'test';
var fragment = root();
var div = $.first_child(fragment);
var svg = $.sibling(div, 2);
var custom_element = $.sibling(svg, 2);
$.template_effect(() => $.set_custom_element_data(custom_element, 'fooBar', x));
var div_1 = $.sibling(custom_element, 2);
var svg_1 = $.sibling(div_1, 2);
var custom_element_1 = $.sibling(svg_1, 2);
$.template_effect(() => $.set_custom_element_data(custom_element_1, 'fooBar', y()));
$.template_effect(
($0, $1) => {
$.set_attribute(div, 'foobar', x);
$.set_attribute(svg, 'viewBox', x);
$.set_attribute(div_1, 'foobar', $0);
$.set_attribute(svg_1, 'viewBox', $1);
},
[y, y]
);
$.append($$anchor, fragment);
}

@ -0,0 +1,19 @@
import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';
export default function Each_string_template($$anchor) {
var fragment = $.comment();
var node = $.first_child(fragment);
$.each(node, 0, () => ['foo', 'bar', 'baz'], $.index, ($$anchor, thing) => {
$.next();
var text = $.text();
$.template_effect(() => $.set_text(text, `${thing ?? ''}, `));
$.append($$anchor, text);
});
$.append($$anchor, fragment);
}

@ -0,0 +1,4 @@
/* index.svelte.js generated by Svelte VERSION */
import * as $ from 'svelte/internal/client';
export const object = $.proxy({ ok: true });

@ -0,0 +1,27 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
export default function Function_prop_no_getter($$anchor) {
let count = $.state(0);
function onmouseup() {
$.set(count, $.get(count) + 2);
}
const plusOne = (num) => num + 1;
Button($$anchor, {
onmousedown: () => $.set(count, $.get(count) + 1),
onmouseup,
onmouseenter: () => $.set(count, $.proxy(plusOne($.get(count)))),
children: ($$anchor, $$slotProps) => {
$.next();
var text = $.text();
$.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ''}`));
$.append($$anchor, text);
},
$$slots: { default: true }
});
}

@ -0,0 +1,21 @@
import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';
var root = $.template_fn(() => {
var h1 = document.createElement('h1');
var text = document.createTextNode('hello world');
h1.insertBefore(text, undefined)
var fragment = document.createDocumentFragment();
fragment.append(h1)
return fragment;
});
export default function Hello_world($$anchor) {
var h1 = root();
$.append($$anchor, h1);
}

@ -0,0 +1,32 @@
import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';
var root = $.template_fn(() => {
var h1 = document.createElement('h1');
var text = document.createTextNode('hello world');
h1.insertBefore(text, undefined)
var fragment = document.createDocumentFragment();
fragment.append(h1)
return fragment;
});
function Hmr($$anchor) {
var h1 = root();
$.append($$anchor, h1);
}
if (import.meta.hot) {
Hmr = $.hmr(Hmr, () => Hmr[$.HMR].source);
import.meta.hot.accept((module) => {
module.default[$.HMR].source = Hmr[$.HMR].source;
$.set(Hmr[$.HMR].source, module.default[$.HMR].original);
});
}
export default Hmr;

@ -0,0 +1,8 @@
import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';
import { random } from './module.svelte';
export default function Imports_in_modules($$anchor) {
}

@ -0,0 +1,5 @@
/* module.svelte.js generated by Svelte VERSION */
import * as $ from 'svelte/internal/client';
import { random } from './export';
export { random };

@ -0,0 +1,54 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
var on_click = (_, count) => $.update(count);
var root = $.template_fn(
() => {
var h1 = document.createElement('h1');
var text = document.createTextNode(' ');
var b = document.createElement('b');
var text_1 = document.createTextNode(' ');
var button = document.createElement('button');
var text_2 = document.createTextNode(' ');
button.insertBefore(text_2, undefined)
var text_3 = document.createTextNode(' ');
var h1_1 = document.createElement('h1');
var fragment = document.createDocumentFragment();
fragment.append(h1, text, b, text_1, button, text_3, h1_1)
return fragment;
},
1
);
export default function Nullish_coallescence_omittance($$anchor) {
let name = 'world';
let count = $.state(0);
var fragment = root();
var h1 = $.first_child(fragment);
h1.textContent = `Hello, ${name ?? ''}!`;
var b = $.sibling(h1, 2);
b.textContent = `${1 ?? 'stuff'}${2 ?? 'more stuff'}${3 ?? 'even more stuff'}`;
var button = $.sibling(b, 2);
button.__click = [on_click, count];
var text = $.child(button);
$.reset(button);
var h1_1 = $.sibling(button, 2);
h1_1.textContent = `Hello, ${name ?? 'earth' ?? ''}`;
$.template_effect(() => $.set_text(text, `Count is ${$.get(count) ?? ''}`));
$.append($$anchor, fragment);
}
$.delegate(['click']);

@ -0,0 +1,17 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
export default function Props_identifier($$anchor, $$props) {
$.push($$props, true);
let props = $.rest_props($$props, ['$$slots', '$$events', '$$legacy']);
$$props.a;
props[a];
$$props.a.b;
$$props.a.b = true;
props.a = true;
props[a] = true;
props;
$.pop();
}

@ -0,0 +1,34 @@
import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';
var root = $.template_fn(
() => {
var p = document.createElement('p');
var text = document.createTextNode(' ');
var p_1 = document.createElement('p');
var text_1 = document.createTextNode(' ');
var comment = document.createComment('');
var fragment = document.createDocumentFragment();
fragment.append(p, text, p_1, text_1, comment)
return fragment;
},
1
);
export default function Purity($$anchor) {
var fragment = root();
var p = $.first_child(fragment);
p.textContent = Math.max(0, Math.min(0, 100));
var p_1 = $.sibling(p, 2);
p_1.textContent = location.href;
var node = $.sibling(p_1, 2);
Child(node, { prop: encodeURIComponent('hello') });
$.append($$anchor, fragment);
}

@ -0,0 +1,283 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
var root = $.template_fn(
() => {
var header = document.createElement('header');
var nav = document.createElement('nav');
header.insertBefore(nav, undefined)
var a = document.createElement('a');
nav.insertBefore(a, undefined)
a.setAttribute('href', '/')
var text = document.createTextNode('Home');
a.insertBefore(text, undefined)
var text_1 = document.createTextNode(' ');
nav.insertBefore(text_1, undefined)
var a_1 = document.createElement('a');
nav.insertBefore(a_1, undefined)
a_1.setAttribute('href', '/away')
var text_2 = document.createTextNode('Away');
a_1.insertBefore(text_2, undefined)
var text_3 = document.createTextNode(' ');
var main = document.createElement('main');
var h1 = document.createElement('h1');
main.insertBefore(h1, undefined)
var text_4 = document.createTextNode(' ');
h1.insertBefore(text_4, undefined)
var text_5 = document.createTextNode(' ');
main.insertBefore(text_5, undefined)
var div = document.createElement('div');
main.insertBefore(div, undefined)
div.setAttribute('class', 'static')
var p = document.createElement('p');
div.insertBefore(p, undefined)
var text_6 = document.createTextNode('we don\'t need to traverse these nodes');
p.insertBefore(text_6, undefined)
var text_7 = document.createTextNode(' ');
main.insertBefore(text_7, undefined)
var p_1 = document.createElement('p');
main.insertBefore(p_1, undefined)
var text_8 = document.createTextNode('or');
p_1.insertBefore(text_8, undefined)
var text_9 = document.createTextNode(' ');
main.insertBefore(text_9, undefined)
var p_2 = document.createElement('p');
main.insertBefore(p_2, undefined)
var text_10 = document.createTextNode('these');
p_2.insertBefore(text_10, undefined)
var text_11 = document.createTextNode(' ');
main.insertBefore(text_11, undefined)
var p_3 = document.createElement('p');
main.insertBefore(p_3, undefined)
var text_12 = document.createTextNode('ones');
p_3.insertBefore(text_12, undefined)
var text_13 = document.createTextNode(' ');
main.insertBefore(text_13, undefined)
var comment = document.createComment('');
main.insertBefore(comment, undefined)
var text_14 = document.createTextNode(' ');
main.insertBefore(text_14, undefined)
var p_4 = document.createElement('p');
main.insertBefore(p_4, undefined)
var text_15 = document.createTextNode('these');
p_4.insertBefore(text_15, undefined)
var text_16 = document.createTextNode(' ');
main.insertBefore(text_16, undefined)
var p_5 = document.createElement('p');
main.insertBefore(p_5, undefined)
var text_17 = document.createTextNode('trailing');
p_5.insertBefore(text_17, undefined)
var text_18 = document.createTextNode(' ');
main.insertBefore(text_18, undefined)
var p_6 = document.createElement('p');
main.insertBefore(p_6, undefined)
var text_19 = document.createTextNode('nodes');
p_6.insertBefore(text_19, undefined)
var text_20 = document.createTextNode(' ');
main.insertBefore(text_20, undefined)
var p_7 = document.createElement('p');
main.insertBefore(p_7, undefined)
var text_21 = document.createTextNode('can');
p_7.insertBefore(text_21, undefined)
var text_22 = document.createTextNode(' ');
main.insertBefore(text_22, undefined)
var p_8 = document.createElement('p');
main.insertBefore(p_8, undefined)
var text_23 = document.createTextNode('be');
p_8.insertBefore(text_23, undefined)
var text_24 = document.createTextNode(' ');
main.insertBefore(text_24, undefined)
var p_9 = document.createElement('p');
main.insertBefore(p_9, undefined)
var text_25 = document.createTextNode('completely');
p_9.insertBefore(text_25, undefined)
var text_26 = document.createTextNode(' ');
main.insertBefore(text_26, undefined)
var p_10 = document.createElement('p');
main.insertBefore(p_10, undefined)
var text_27 = document.createTextNode('ignored');
p_10.insertBefore(text_27, undefined)
var text_28 = document.createTextNode(' ');
var cant_skip = document.createElement('cant-skip');
var custom_elements = document.createElement('custom-elements');
cant_skip.insertBefore(custom_elements, undefined)
var text_29 = document.createTextNode(' ');
var div_1 = document.createElement('div');
var input = document.createElement('input');
div_1.insertBefore(input, undefined)
var text_30 = document.createTextNode(' ');
var div_2 = document.createElement('div');
var source = document.createElement('source');
div_2.insertBefore(source, undefined)
var text_31 = document.createTextNode(' ');
var select = document.createElement('select');
var option = document.createElement('option');
select.insertBefore(option, undefined)
var text_32 = document.createTextNode('a');
option.insertBefore(text_32, undefined)
var text_33 = document.createTextNode(' ');
var img = document.createElement('img');
img.setAttribute('src', '...')
img.setAttribute('alt', '')
img.setAttribute('loading', 'lazy')
var text_34 = document.createTextNode(' ');
var div_3 = document.createElement('div');
var img_1 = document.createElement('img');
div_3.insertBefore(img_1, undefined)
img_1.setAttribute('src', '...')
img_1.setAttribute('alt', '')
img_1.setAttribute('loading', 'lazy')
var fragment = document.createDocumentFragment();
fragment.append(header, text_3, main, text_28, cant_skip, text_29, div_1, text_30, div_2, text_31, select, text_33, img, text_34, div_3)
return fragment;
},
3
);
export default function Skip_static_subtree($$anchor, $$props) {
var fragment = root();
var main = $.sibling($.first_child(fragment), 2);
var h1 = $.child(main);
var text = $.child(h1, true);
$.reset(h1);
var node = $.sibling(h1, 10);
$.html(node, () => $$props.content, false, false);
$.next(14);
$.reset(main);
var cant_skip = $.sibling(main, 2);
var custom_elements = $.child(cant_skip);
$.set_custom_element_data(custom_elements, 'with', 'attributes');
$.reset(cant_skip);
var div = $.sibling(cant_skip, 2);
var input = $.child(div);
$.autofocus(input, true);
$.reset(div);
var div_1 = $.sibling(div, 2);
var source = $.child(div_1);
source.muted = true;
$.reset(div_1);
var select = $.sibling(div_1, 2);
var option = $.child(select);
option.value = null == (option.__value = 'a') ? '' : 'a';
$.reset(select);
var img = $.sibling(select, 2);
$.next(2);
$.template_effect(() => $.set_text(text, $$props.title));
$.append($$anchor, fragment);
}

@ -0,0 +1,50 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
function reset(_, str, tpl) {
$.set(str, '');
$.set(str, ``);
$.set(tpl, '');
$.set(tpl, ``);
}
var root = $.template_fn(
() => {
var input = document.createElement('input');
var text = document.createTextNode(' ');
var input_1 = document.createElement('input');
var text_1 = document.createTextNode(' ');
var button = document.createElement('button');
var text_2 = document.createTextNode('reset');
button.insertBefore(text_2, undefined)
var fragment = document.createDocumentFragment();
fragment.append(input, text, input_1, text_1, button)
return fragment;
},
1
);
export default function State_proxy_literal($$anchor) {
let str = $.state('');
let tpl = $.state(``);
var fragment = root();
var input = $.first_child(fragment);
$.remove_input_defaults(input);
var input_1 = $.sibling(input, 2);
$.remove_input_defaults(input_1);
var button = $.sibling(input_1, 2);
button.__click = [reset, str, tpl];
$.bind_value(input, () => $.get(str), ($$value) => $.set(str, $$value));
$.bind_value(input_1, () => $.get(tpl), ($$value) => $.set(tpl, $$value));
$.append($$anchor, fragment);
}
$.delegate(['click']);

@ -0,0 +1,11 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
export default function Svelte_element($$anchor, $$props) {
let tag = $.prop($$props, 'tag', 3, 'hr');
var fragment = $.comment();
var node = $.first_child(fragment);
$.element(node, tag, false);
$.append($$anchor, fragment);
}

@ -0,0 +1,34 @@
import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';
var root = $.template_fn(() => {
var p = document.createElement('p');
var text = document.createTextNode(' ');
p.insertBefore(text, undefined)
var fragment = document.createDocumentFragment();
fragment.append(p)
return fragment;
});
export default function Text_nodes_deriveds($$anchor) {
let count1 = 0;
let count2 = 0;
function text1() {
return count1;
}
function text2() {
return count2;
}
var p = root();
var text = $.child(p);
$.reset(p);
$.template_effect(($0, $1) => $.set_text(text, `${$0 ?? ''}${$1 ?? ''}`), [text1, text2]);
$.append($$anchor, p);
}

@ -9,8 +9,15 @@ interface SnapshotTest extends BaseTest {
compileOptions?: Partial<import('#compiler').CompileOptions>;
}
const { test, run } = suite<SnapshotTest>(async (config, cwd) => {
await compile_directory(cwd, 'client', config.compileOptions);
const { test, run } = suite<SnapshotTest>(async (config, cwd, templating_mode) => {
await compile_directory(
cwd,
'client',
config.compileOptions,
undefined,
undefined,
templating_mode
);
await compile_directory(cwd, 'server', config.compileOptions);
// run `UPDATE_SNAPSHOTS=true pnpm test snapshot` to update snapshot tests
@ -41,4 +48,5 @@ const { test, run } = suite<SnapshotTest>(async (config, cwd) => {
export { test };
await run(__dirname);
await run(__dirname, 'string');
await run(__dirname, 'functional');

@ -6,6 +6,8 @@ export interface BaseTest {
solo?: boolean;
}
export type TemplatingMode = 'string' | 'functional';
/**
* To filter tests, run one of these:
*
@ -20,14 +22,22 @@ const filter = process.env.FILTER
)
: /./;
export function suite<Test extends BaseTest>(fn: (config: Test, test_dir: string) => void) {
export function suite<Test extends BaseTest>(
fn: (config: Test, test_dir: string, templating_mode: TemplatingMode) => void
) {
return {
test: (config: Test) => config,
run: async (cwd: string, samples_dir = 'samples') => {
run: async (
cwd: string,
templating_mode: TemplatingMode = 'string',
samples_dir = 'samples'
) => {
await for_each_dir<Test>(cwd, samples_dir, (config, dir) => {
let it_fn = config.skip ? it.skip : config.solo ? it.only : it;
it_fn(dir, () => fn(config, `${cwd}/${samples_dir}/${dir}`));
it_fn(`${dir} (${templating_mode})`, () =>
fn(config, `${cwd}/${samples_dir}/${dir}`, templating_mode)
);
});
}
};
@ -36,12 +46,26 @@ export function suite<Test extends BaseTest>(fn: (config: Test, test_dir: string
export function suite_with_variants<Test extends BaseTest, Variants extends string, Common>(
variants: Variants[],
should_skip_variant: (variant: Variants, config: Test) => boolean | 'no-test',
common_setup: (config: Test, test_dir: string) => Promise<Common> | Common,
fn: (config: Test, test_dir: string, variant: Variants, common: Common) => void
common_setup: (
config: Test,
test_dir: string,
templating_mode: TemplatingMode
) => Promise<Common> | Common,
fn: (
config: Test,
test_dir: string,
variant: Variants,
common: Common,
templating_mode: TemplatingMode
) => void
) {
return {
test: (config: Test) => config,
run: async (cwd: string, samples_dir = 'samples') => {
run: async (
cwd: string,
templating_mode: TemplatingMode = 'string',
samples_dir = 'samples'
) => {
await for_each_dir<Test>(cwd, samples_dir, (config, dir) => {
let called_common = false;
let common: any = undefined;
@ -54,12 +78,12 @@ export function suite_with_variants<Test extends BaseTest, Variants extends stri
const solo = config.solo;
let it_fn = skip ? it.skip : solo ? it.only : it;
it_fn(`${dir} (${variant})`, async () => {
it_fn(`${dir} (${templating_mode}-${variant})`, async () => {
if (!called_common) {
called_common = true;
common = await common_setup(config, `${cwd}/${samples_dir}/${dir}`);
common = await common_setup(config, `${cwd}/${samples_dir}/${dir}`, templating_mode);
}
return fn(config, `${cwd}/${samples_dir}/${dir}`, variant, common);
return fn(config, `${cwd}/${samples_dir}/${dir}`, variant, common, templating_mode);
});
}
});

@ -916,7 +916,7 @@ packages:
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
concat-map@0.0.1:
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
cross-spawn@5.1.0:
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}

Loading…
Cancel
Save