Comparing version 5.15.0 to 5.16.0
@@ -5,3 +5,3 @@ { | ||
"license": "MIT", | ||
"version": "5.15.0", | ||
"version": "5.16.0", | ||
"type": "module", | ||
@@ -147,2 +147,3 @@ "types": "./types/index.d.ts", | ||
"axobject-query": "^4.1.0", | ||
"clsx": "^2.1.1", | ||
"esm-env": "^1.2.1", | ||
@@ -149,0 +150,0 @@ "esrap": "^1.3.2", |
@@ -31,2 +31,10 @@ /** @import { ComponentAnalysis } from '../../types.js' */ | ||
/** | ||
* | ||
* @param {Array<AST.CSS.Node>} path | ||
*/ | ||
function is_in_global_block(path) { | ||
return path.some((node) => node.type === 'Rule' && node.metadata.is_global_block); | ||
} | ||
/** @type {CssVisitors} */ | ||
@@ -36,3 +44,3 @@ const css_visitors = { | ||
if (is_keyframes_node(node)) { | ||
if (!node.prelude.startsWith('-global-')) { | ||
if (!node.prelude.startsWith('-global-') && !is_in_global_block(context.path)) { | ||
context.state.keyframes.push(node.prelude); | ||
@@ -39,0 +47,0 @@ } |
@@ -734,3 +734,3 @@ /** @import * as Compiler from '#compiler' */ | ||
for (const chunk of chunks) { | ||
const current_possible_values = get_possible_values(chunk); | ||
const current_possible_values = get_possible_values(chunk, name === 'class'); | ||
@@ -788,3 +788,3 @@ // impossible to find out all combinations | ||
}); | ||
if (prev_values.length < current_possible_values.size) { | ||
if (prev_values.length < current_possible_values.length) { | ||
prev_values.push(' '); | ||
@@ -791,0 +791,0 @@ } |
@@ -7,10 +7,70 @@ /** @import { AST } from '#compiler' */ | ||
* @param {Node} node | ||
* @param {boolean} is_class | ||
* @param {Set<any>} set | ||
* @param {boolean} is_nested | ||
*/ | ||
function gather_possible_values(node, set) { | ||
function gather_possible_values(node, is_class, set, is_nested = false) { | ||
if (set.has(UNKNOWN)) { | ||
// no point traversing any further | ||
return; | ||
} | ||
if (node.type === 'Literal') { | ||
set.add(String(node.value)); | ||
} else if (node.type === 'ConditionalExpression') { | ||
gather_possible_values(node.consequent, set); | ||
gather_possible_values(node.alternate, set); | ||
gather_possible_values(node.consequent, is_class, set, is_nested); | ||
gather_possible_values(node.alternate, is_class, set, is_nested); | ||
} else if (node.type === 'LogicalExpression') { | ||
if (node.operator === '&&') { | ||
// && is a special case, because the only way the left | ||
// hand value can be included is if it's falsy. this is | ||
// a bit of extra work but it's worth it because | ||
// `class={[condition && 'blah']}` is common, | ||
// and we don't want to deopt on `condition` | ||
const left = new Set(); | ||
gather_possible_values(node.left, is_class, left, is_nested); | ||
if (left.has(UNKNOWN)) { | ||
// add all non-nullish falsy values, unless this is a `class` attribute that | ||
// will be processed by cslx, in which case falsy values are removed, unless | ||
// they're not inside an array/object (TODO 6.0 remove that last part) | ||
if (!is_class || !is_nested) { | ||
set.add(''); | ||
set.add(false); | ||
set.add(NaN); | ||
set.add(0); // -0 and 0n are also falsy, but stringify to '0' | ||
} | ||
} else { | ||
for (const value of left) { | ||
if (!value && value != undefined && (!is_class || !is_nested)) { | ||
set.add(value); | ||
} | ||
} | ||
} | ||
gather_possible_values(node.right, is_class, set, is_nested); | ||
} else { | ||
gather_possible_values(node.left, is_class, set, is_nested); | ||
gather_possible_values(node.right, is_class, set, is_nested); | ||
} | ||
} else if (is_class && node.type === 'ArrayExpression') { | ||
for (const entry of node.elements) { | ||
if (entry) { | ||
gather_possible_values(entry, is_class, set, true); | ||
} | ||
} | ||
} else if (is_class && node.type === 'ObjectExpression') { | ||
for (const property of node.properties) { | ||
if ( | ||
property.type === 'Property' && | ||
!property.computed && | ||
(property.key.type === 'Identifier' || property.key.type === 'Literal') | ||
) { | ||
set.add( | ||
property.key.type === 'Identifier' ? property.key.name : String(property.key.value) | ||
); | ||
} else { | ||
set.add(UNKNOWN); | ||
} | ||
} | ||
} else { | ||
@@ -23,5 +83,6 @@ set.add(UNKNOWN); | ||
* @param {AST.Text | AST.ExpressionTag} chunk | ||
* @returns {Set<string> | null} | ||
* @param {boolean} is_class | ||
* @returns {string[] | null} | ||
*/ | ||
export function get_possible_values(chunk) { | ||
export function get_possible_values(chunk, is_class) { | ||
const values = new Set(); | ||
@@ -32,7 +93,7 @@ | ||
} else { | ||
gather_possible_values(chunk.expression, values); | ||
gather_possible_values(chunk.expression, is_class, values); | ||
} | ||
if (values.has(UNKNOWN)) return null; | ||
return values; | ||
return [...values].map((value) => String(value)); | ||
} | ||
@@ -39,0 +100,0 @@ |
@@ -776,2 +776,4 @@ /** @import { Expression, Node, Program } from 'estree' */ | ||
if (attribute.name.toLowerCase() !== 'class') continue; | ||
// The dynamic class method appends the hash to the end of the class attribute on its own | ||
if (attribute.metadata.needs_clsx) continue outer; | ||
@@ -778,0 +780,0 @@ class_attribute = attribute; |
@@ -41,2 +41,15 @@ /** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression } from 'estree' */ | ||
// class={[...]} or class={{...}} or `class={x}` need clsx to resolve the classes | ||
if ( | ||
node.name === 'class' && | ||
!Array.isArray(node.value) && | ||
node.value !== true && | ||
node.value.expression.type !== 'Literal' && | ||
node.value.expression.type !== 'TemplateLiteral' && | ||
node.value.expression.type !== 'BinaryExpression' | ||
) { | ||
mark_subtree_dynamic(context.path); | ||
node.metadata.needs_clsx = true; | ||
} | ||
if (node.value !== true) { | ||
@@ -43,0 +56,0 @@ for (const chunk of get_attribute_chunks(node.value)) { |
@@ -556,2 +556,6 @@ /** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ | ||
if (name === 'class') { | ||
if (attribute.metadata.needs_clsx) { | ||
value = b.call('$.clsx', value); | ||
} | ||
if (attribute.metadata.expression.has_state && has_call) { | ||
@@ -565,2 +569,3 @@ // ensure we're not creating a separate template effect for this so that | ||
} | ||
update = b.stmt( | ||
@@ -570,3 +575,4 @@ b.call( | ||
node_id, | ||
value | ||
value, | ||
attribute.metadata.needs_clsx ? b.literal(context.state.analysis.css.hash) : undefined | ||
) | ||
@@ -573,0 +579,0 @@ ); |
@@ -89,6 +89,31 @@ /** @import { Expression, Literal } from 'estree' */ | ||
class_index = attributes.length; | ||
} else if (attribute.name === 'style') { | ||
style_index = attributes.length; | ||
if (attribute.metadata.needs_clsx) { | ||
const clsx_value = b.call( | ||
'$.clsx', | ||
/** @type {AST.ExpressionTag} */ (attribute.value).expression | ||
); | ||
attributes.push({ | ||
...attribute, | ||
value: { | ||
.../** @type {AST.ExpressionTag} */ (attribute.value), | ||
expression: context.state.analysis.css.hash | ||
? b.binary( | ||
'+', | ||
b.binary('+', clsx_value, b.literal(' ')), | ||
b.literal(context.state.analysis.css.hash) | ||
) | ||
: clsx_value | ||
} | ||
}); | ||
} else { | ||
attributes.push(attribute); | ||
} | ||
} else { | ||
if (attribute.name === 'style') { | ||
style_index = attributes.length; | ||
} | ||
attributes.push(attribute); | ||
} | ||
attributes.push(attribute); | ||
} | ||
@@ -95,0 +120,0 @@ } else if (attribute.type === 'BindDirective') { |
@@ -48,3 +48,4 @@ /** @import { AST, ExpressionMetadata } from '#compiler' */ | ||
expression: create_expression_metadata(), | ||
delegated: null | ||
delegated: null, | ||
needs_clsx: false | ||
} | ||
@@ -51,0 +52,0 @@ }; |
@@ -485,2 +485,4 @@ import type { Binding, ExpressionMetadata } from '#compiler'; | ||
delegated: null | DelegatedEvent; | ||
/** May be `true` if this is a `class` attribute that needs `clsx` */ | ||
needs_clsx: boolean; | ||
}; | ||
@@ -487,0 +489,0 @@ } |
@@ -171,7 +171,7 @@ /* This file is generated by scripts/process-messages/index.js. Do not edit! */ | ||
/** | ||
* Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details | ||
* Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate | ||
* @param {null | NodeLike} node | ||
*/ | ||
export function a11y_click_events_have_key_events(node) { | ||
w(node, "a11y_click_events_have_key_events", `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details\nhttps://svelte.dev/e/a11y_click_events_have_key_events`); | ||
w(node, "a11y_click_events_have_key_events", `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`); | ||
} | ||
@@ -178,0 +178,0 @@ |
@@ -16,2 +16,3 @@ import { DEV } from 'esm-env'; | ||
} from '../../runtime.js'; | ||
import { clsx } from '../../../shared/attributes.js'; | ||
@@ -271,2 +272,6 @@ /** | ||
if (next.class) { | ||
next.class = clsx(next.class); | ||
} | ||
if (css_hash !== undefined) { | ||
@@ -273,0 +278,0 @@ next.class = next.class ? next.class + ' ' + css_hash : css_hash; |
@@ -6,8 +6,9 @@ import { hydrating } from '../hydration.js'; | ||
* @param {string} value | ||
* @param {string} [hash] | ||
* @returns {void} | ||
*/ | ||
export function set_svg_class(dom, value) { | ||
export function set_svg_class(dom, value, hash) { | ||
// @ts-expect-error need to add __className to patched prototype | ||
var prev_class_name = dom.__className; | ||
var next_class_name = to_class(value); | ||
var next_class_name = to_class(value, hash); | ||
@@ -36,8 +37,9 @@ if (hydrating && dom.getAttribute('class') === next_class_name) { | ||
* @param {string} value | ||
* @param {string} [hash] | ||
* @returns {void} | ||
*/ | ||
export function set_mathml_class(dom, value) { | ||
export function set_mathml_class(dom, value, hash) { | ||
// @ts-expect-error need to add __className to patched prototype | ||
var prev_class_name = dom.__className; | ||
var next_class_name = to_class(value); | ||
var next_class_name = to_class(value, hash); | ||
@@ -66,8 +68,9 @@ if (hydrating && dom.getAttribute('class') === next_class_name) { | ||
* @param {string} value | ||
* @param {string} [hash] | ||
* @returns {void} | ||
*/ | ||
export function set_class(dom, value) { | ||
export function set_class(dom, value, hash) { | ||
// @ts-expect-error need to add __className to patched prototype | ||
var prev_class_name = dom.__className; | ||
var next_class_name = to_class(value); | ||
var next_class_name = to_class(value, hash); | ||
@@ -85,3 +88,3 @@ if (hydrating && dom.className === next_class_name) { | ||
// we should only remove the class if the the value is nullish. | ||
if (value == null) { | ||
if (value == null && !hash) { | ||
dom.removeAttribute('class'); | ||
@@ -100,6 +103,7 @@ } else { | ||
* @param {V} value | ||
* @param {string} [hash] | ||
* @returns {string | V} | ||
*/ | ||
function to_class(value) { | ||
return value == null ? '' : value; | ||
function to_class(value, hash) { | ||
return (value == null ? '' : value) + (hash ? ' ' + hash : ''); | ||
} | ||
@@ -106,0 +110,0 @@ |
@@ -57,3 +57,3 @@ /* This file is generated by scripts/process-messages/index.js. Do not edit! */ | ||
/** | ||
* %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information | ||
* %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 | ||
* @param {string} parent | ||
@@ -66,3 +66,3 @@ * @param {string} method | ||
if (DEV) { | ||
const error = new Error(`component_api_changed\n${parent} called \`${method}\` on an instance of ${component}, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information\nhttps://svelte.dev/e/component_api_changed`); | ||
const error = new Error(`component_api_changed\n${parent} called \`${method}\` on an instance of ${component}, which is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`); | ||
@@ -77,3 +77,3 @@ error.name = 'Svelte error'; | ||
/** | ||
* Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information | ||
* Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. | ||
* @param {string} component | ||
@@ -85,3 +85,3 @@ * @param {string} name | ||
if (DEV) { | ||
const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`compatibility.componentApi\` compiler option to \`4\` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information\nhttps://svelte.dev/e/component_api_invalid_new`); | ||
const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`compatibility.componentApi\` compiler option to \`4\` to keep it working.\nhttps://svelte.dev/e/component_api_invalid_new`); | ||
@@ -88,0 +88,0 @@ error.name = 'Svelte error'; |
@@ -164,3 +164,3 @@ export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js'; | ||
} from './dom/operations.js'; | ||
export { attr } from '../shared/attributes.js'; | ||
export { attr, clsx } from '../shared/attributes.js'; | ||
export { snapshot } from '../shared/clone.js'; | ||
@@ -167,0 +167,0 @@ export { noop, fallback } from '../shared/utils.js'; |
@@ -5,3 +5,3 @@ /** @import { ComponentType, SvelteComponent } from 'svelte' */ | ||
export { FILENAME, HMR } from '../../constants.js'; | ||
import { attr } from '../shared/attributes.js'; | ||
import { attr, clsx } from '../shared/attributes.js'; | ||
import { is_promise, noop } from '../shared/utils.js'; | ||
@@ -199,2 +199,6 @@ import { subscribe_to_store } from '../../store/utils.js'; | ||
if (attrs.class) { | ||
attrs.class = clsx(attrs.class); | ||
} | ||
if (classes) { | ||
@@ -527,3 +531,3 @@ const classlist = attrs.class ? [attrs.class] : []; | ||
export { attr }; | ||
export { attr, clsx }; | ||
@@ -530,0 +534,0 @@ export { html } from './blocks/html.js'; |
import { escape_html } from '../../escaping.js'; | ||
import { clsx as _clsx } from 'clsx'; | ||
@@ -29,1 +30,14 @@ /** | ||
} | ||
/** | ||
* Small wrapper around clsx to preserve Svelte's (weird) handling of falsy values. | ||
* TODO Svelte 6 revisit this, and likely turn all falsy values into the empty string (what clsx also does) | ||
* @param {any} value | ||
*/ | ||
export function clsx(value) { | ||
if (typeof value === 'object') { | ||
return _clsx(value); | ||
} else { | ||
return value ?? ''; | ||
} | ||
} |
@@ -9,3 +9,3 @@ // generated during release, do not modify | ||
*/ | ||
export const VERSION = '5.15.0'; | ||
export const VERSION = '5.16.0'; | ||
export const PUBLIC_VERSION = '5'; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2475402
54626
14
33
55
0
+ Addedclsx@^2.1.1
+ Addedclsx@2.1.1(transitive)