parametric runes

config
Rich Harris 2 months ago
parent 848d1dcff0
commit aaf405b119

@ -34,7 +34,8 @@ export function compile(source, options) {
const combined_options = {
...validated,
...parsed_options,
customElementOptions
customElementOptions,
runes: 'runes' in parsed_options ? () => parsed_options.runes : validated.runes
};
if (parsed.metadata.ts) {

@ -142,10 +142,11 @@ export function migrate(source, { filename, use_ts } = {}) {
/** @type {ValidatedCompileOptions} */
const combined_options = {
...validate_component_options({}, ''),
...validate_component_options({}),
...parsed_options,
customElementOptions,
filename: filename ?? UNKNOWN_FILENAME,
runes: 'runes' in parsed_options ? () => parsed_options.runes : () => undefined,
experimental: {
async: true
}

@ -345,6 +345,8 @@ export function analyze_component(root, source, options) {
let synthetic_stores_legacy_check = [];
const runes_option = options.runes?.({ filename: options.filename });
// create synthetic bindings for store subscriptions
for (const [name, references] of module.scope.references) {
if (name[0] !== '$' || RESERVED.includes(name)) continue;
@ -359,7 +361,7 @@ export function analyze_component(root, source, options) {
// If we're not in legacy mode through the compiler option, assume the user
// is referencing a rune and not a global store.
if (
options.runes === false ||
runes_option === false ||
!is_rune(name) ||
(declaration !== null &&
// const state = $state(0) is valid
@ -395,7 +397,7 @@ export function analyze_component(root, source, options) {
e.store_invalid_scoped_subscription(is_nested_store_subscription_node);
}
if (options.runes !== false) {
if (runes_option !== false) {
if (declaration === null && /[a-z]/.test(store_name[0])) {
e.global_reference_invalid(references[0].node, name);
} else if (declaration !== null && is_rune(name)) {
@ -447,7 +449,7 @@ export function analyze_component(root, source, options) {
const component_name = get_component_name(options.filename);
const runes =
options.runes ??
runes_option ??
(has_await || instance.has_await || Array.from(module.scope.references.keys()).some(is_rune));
if (!runes) {
@ -491,7 +493,7 @@ export function analyze_component(root, source, options) {
maybe_runes:
!runes &&
// if they explicitly disabled runes, use the legacy behavior
options.runes !== false &&
runes_option !== false &&
![...module.scope.references.keys()].some((name) =>
['$$props', '$$restProps'].includes(name)
) &&

@ -142,7 +142,7 @@ export interface CompileOptions extends ModuleCompileOptions {
* which is likely not what you want. If you're using Vite, consider using [dynamicCompileOptions](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#dynamiccompileoptions) instead.
* @default undefined
*/
runes?: boolean | undefined;
runes?: boolean | undefined | ((options: { filename: string }) => boolean | undefined);
/**
* If `true`, exposes the Svelte major version in the browser by adding it to a `Set` stored in the global `window.__svelte.v`.
*
@ -259,7 +259,7 @@ export type ValidatedCompileOptions = ValidatedModuleCompileOptions &
cssOutputFilename: CompileOptions['cssOutputFilename'];
sourcemap: CompileOptions['sourcemap'];
compatibility: Required<Required<CompileOptions>['compatibility']>;
runes: CompileOptions['runes'];
runes: (options: { filename: string }) => boolean | undefined;
customElementOptions: AST.SvelteOptions['customElement'];
hmr: CompileOptions['hmr'];
};

@ -1,6 +1,6 @@
/** @import { Validator } from '@sveltejs/config/validate' */
/** @import { ModuleCompileOptions, ValidatedModuleCompileOptions, CompileOptions, ValidatedCompileOptions } from '#compiler' */
import { string, boolean, object, validator, ValidateError } from '@sveltejs/config/validate';
import { string, boolean, object, fn, validator, ValidateError } from '@sveltejs/config/validate';
import * as e from './errors.js';
import * as w from './warnings.js';
@ -37,7 +37,7 @@ const common_options = {
return /** @type {'client' | 'server' | false} */ (input);
}),
warningFilter: fun(() => true),
warningFilter: fn(() => true),
experimental: object({
async: boolean(false)
@ -67,7 +67,7 @@ const component_options = {
return input;
}),
cssHash: fun(({ css, filename, hash }) => {
cssHash: fn(({ css, filename, hash }) => {
return `svelte-${hash(filename === '(unknown)' ? css : filename ?? css)}`;
}),
@ -104,7 +104,8 @@ const component_options = {
preserveWhitespace: boolean(false),
runes: boolean(undefined),
/** @type {Validator<boolean | undefined | () => boolean | undefined, () => boolean | undefined>} */
runes: parametric(() => /** @type {boolean | undefined} */ (undefined)),
hmr: boolean(false),
@ -261,15 +262,20 @@ function list(options, fallback = options[0]) {
}
/**
* @param {(...args: any) => any} fallback
* @returns {Validator<(...args: any) => any>}
* @template {(...args: any[]) => any} F
* @param {F} fallback
* @returns {Validator<ReturnType<F> | F, F>}
*/
function fun(fallback) {
return validator(fallback, (input, keypath) => {
if (typeof input !== 'function') {
throw_error(`${keypath} should be a function, if specified`);
function parametric(fallback) {
return validator(fallback, (input) => {
if (typeof input === 'function') {
return /** @type {F} */ (input);
}
return input;
/** @type {(...args: Parameters<F>) => ReturnType<F>} */
const normalized = (..._args) => /** @type {ReturnType<F>} */ (input);
return /** @type {F} */ (/** @type {unknown} */ (normalized));
});
}

Loading…
Cancel
Save