docs: add a couple internal JSDocs and cleanup from TS migration (#8940)

pull/8951/head
Ben McCann 1 year ago committed by GitHub
parent 895709c6a2
commit 8601195a85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,16 +3,14 @@ const now = () => 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)
);
}
);
timings.forEach((timing) => {
result[timing.label] = Object.assign(
{
total: timing.end - timing.start
},
timing.children && collapse_timings(timing.children)
);
});
return result;
}

File diff suppressed because it is too large Load Diff

@ -23,7 +23,7 @@ export default {
},
module_script_variable_reactive_declaration: /** @param {string[]} names */ (names) => ({
code: 'module-script-reactive-declaration',
message: `${names.map(/** @param {any} name */ (name) => `"${name}"`).join(', ')} ${
message: `${names.map((name) => `"${name}"`).join(', ')} ${
names.length > 1 ? 'are' : 'is'
} declared in a module script and will not be reactive`
}),
@ -175,7 +175,7 @@ export default {
*/ (role, props) => ({
code: 'a11y-role-has-required-aria-props',
message: `A11y: Elements with the ARIA role "${role}" must have the following attributes defined: ${props
.map(/** @param {any} name */ (name) => `"${name}"`)
.map((name) => `"${name}"`)
.join(', ')}`
}),
a11y_role_supports_aria_props: /**

@ -23,18 +23,8 @@ export default function create_module(
exports_from
) {
const internal_path = `${sveltePath}/internal`;
helpers.sort(
/**
* @param {any} a
* @param {any} b
*/ (a, b) => (a.name < b.name ? -1 : 1)
);
globals.sort(
/**
* @param {any} a
* @param {any} b
*/ (a, b) => (a.name < b.name ? -1 : 1)
);
helpers.sort((a, b) => (a.name < b.name ? -1 : 1));
globals.sort((a, b) => (a.name < b.name ? -1 : 1));
return esm(
program,
name,
@ -73,19 +63,17 @@ function get_internal_globals(globals, helpers) {
type: 'VariableDeclarator',
id: {
type: 'ObjectPattern',
properties: globals.map(
/** @param {any} g */ (g) => ({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: { type: 'Identifier', name: g.name },
value: g.alias,
kind: 'init'
})
)
properties: globals.map((g) => ({
type: 'Property',
method: false,
shorthand: false,
computed: false,
key: { type: 'Identifier', name: g.name },
value: g.alias,
kind: 'init'
}))
},
init: helpers.find(/** @param {any}params_0 */ ({ name }) => name === 'globals').alias
init: helpers.find(({ name }) => name === 'globals').alias
}
]
}
@ -118,13 +106,11 @@ function esm(
) {
const import_declaration = {
type: 'ImportDeclaration',
specifiers: helpers.map(
/** @param {any} h */ (h) => ({
type: 'ImportSpecifier',
local: h.alias,
imported: { type: 'Identifier', name: h.name }
})
),
specifiers: helpers.map((h) => ({
type: 'ImportSpecifier',
local: h.alias,
imported: { type: 'Identifier', name: h.name }
})),
source: { type: 'Literal', value: internal_path }
};
const internal_globals = get_internal_globals(globals, helpers);
@ -142,13 +128,11 @@ function esm(
exports_from.forEach(rewrite_import);
const exports = module_exports.length > 0 && {
type: 'ExportNamedDeclaration',
specifiers: module_exports.map(
/** @param {any} x */ (x) => ({
type: 'Specifier',
local: { type: 'Identifier', name: x.name },
exported: { type: 'Identifier', name: x.as }
})
)
specifiers: module_exports.map((x) => ({
type: 'Specifier',
local: { type: 'Identifier', name: x.name },
exported: { type: 'Identifier', name: x.as }
}))
};
program.body = b`
/* ${banner} */

@ -57,16 +57,14 @@ function validate_options(options, warnings) {
}
const { name, filename, loopGuardTimeout, dev, namespace, css } = options;
Object.keys(options).forEach(
/** @param {any} key */ (key) => {
if (!valid_options.includes(key)) {
const match = fuzzymatch(key, valid_options);
let message = `Unrecognized option '${key}'`;
if (match) message += ` (did you mean '${match}'?)`;
throw new Error(message);
}
Object.keys(options).forEach((key) => {
if (!valid_options.includes(key)) {
const match = fuzzymatch(key, valid_options);
let message = `Unrecognized option '${key}'`;
if (match) message += ` (did you mean '${match}'?)`;
throw new Error(message);
}
);
});
if (name && !regex_valid_identifier.test(name)) {
throw new Error(`options.name must be a valid identifier (got '${name}')`);
}

@ -56,15 +56,13 @@ export default class Attribute extends Node {
this.dependencies = new Set();
this.chunks = this.is_true
? []
: info.value.map(
/** @param {any} node */ (node) => {
if (node.type === 'Text') return node;
this.is_static = false;
const expression = new Expression(component, this, scope, node.expression);
add_to_set(this.dependencies, expression.dependencies);
return expression;
}
);
: info.value.map((node) => {
if (node.type === 'Text') return node;
this.is_static = false;
const expression = new Expression(component, this, scope, node.expression);
add_to_set(this.dependencies, expression.dependencies);
return expression;
});
}
if (this.dependencies.size > 0) {
@ -88,13 +86,11 @@ export default class Attribute extends Node {
/** @type {Set<string>} */
const dependencies = new Set();
this.chunks.forEach(
/** @param {any} chunk */ (chunk) => {
if (chunk.type === 'Expression') {
add_to_set(dependencies, chunk.dynamic_dependencies());
}
this.chunks.forEach((chunk) => {
if (chunk.type === 'Expression') {
add_to_set(dependencies, chunk.dynamic_dependencies());
}
);
});
return Array.from(dependencies);
}
@ -114,12 +110,7 @@ export default class Attribute extends Node {
/** @param {any} chunk */ (chunk) =>
chunk.type === 'Text' ? string_literal(chunk.data) : chunk.manipulate(block)
)
.reduce(
/**
* @param {any} lhs
* @param {any} rhs
*/ (lhs, rhs) => x`${lhs} + ${rhs}`
);
.reduce((lhs, rhs) => x`${lhs} + ${rhs}`);
if (this.chunks[0].type !== 'Text') {
expression = x`"" + ${expression}`;
}

@ -54,8 +54,8 @@ export default class Binding extends Node {
this.expression = new Expression(component, this, scope, info.expression);
this.raw_expression = clone(info.expression);
const { name } = get_object(this.expression.node);
this.is_contextual = Array.from(this.expression.references).some(
/** @param {any} name */ (name) => scope.names.has(name)
this.is_contextual = Array.from(this.expression.references).some((name) =>
scope.names.has(name)
);
if (this.is_contextual) this.validate_binding_rest_properties(scope);
// make sure we track this as a mutable ref
@ -70,14 +70,12 @@ export default class Binding extends Node {
if (scope.is_const(name)) {
component.error(this, compiler_errors.invalid_binding_const);
}
scope.dependencies_for_name.get(name).forEach(
/** @param {any} name */ (name) => {
const variable = component.var_lookup.get(name);
if (variable) {
variable.mutated = true;
}
scope.dependencies_for_name.get(name).forEach((name) => {
const variable = component.var_lookup.get(name);
if (variable) {
variable.mutated = true;
}
);
});
} else {
const variable = component.var_lookup.get(name);
if (!variable || variable.global) {
@ -110,20 +108,18 @@ export default class Binding extends Node {
/** @param {import('./shared/TemplateScope.js').default} scope */
validate_binding_rest_properties(scope) {
this.expression.references.forEach(
/** @param {any} name */ (name) => {
const each_block = scope.get_owner(name);
if (each_block && each_block.type === 'EachBlock') {
const rest_node = each_block.context_rest_properties.get(name);
if (rest_node) {
this.component.warn(
/** @type {any} */ (rest_node),
compiler_warnings.invalid_rest_eachblock_binding(name)
);
}
this.expression.references.forEach((name) => {
const each_block = scope.get_owner(name);
if (each_block && each_block.type === 'EachBlock') {
const rest_node = each_block.context_rest_properties.get(name);
if (rest_node) {
this.component.warn(
/** @type {any} */ (rest_node),
compiler_warnings.invalid_rest_eachblock_binding(name)
);
}
}
);
});
}
}

@ -18,16 +18,14 @@ export default class Body extends Node {
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach(
/** @param {any} node */ (node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
info.attributes.forEach((node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
);
});
}
}

@ -19,12 +19,10 @@ export default class CatchBlock extends AbstractBlock {
super(component, parent, scope, info);
this.scope = scope.child();
if (parent.catch_node) {
parent.catch_contexts.forEach(
/** @param {any} context */ (context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this);
}
);
parent.catch_contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this);
});
}
[this.const_tags, this.children] = get_const_tags(info.children, component, this, parent);
if (!info.skip) {

@ -54,19 +54,16 @@ export default class ConstTag extends Node {
this.node = info;
this.scope = scope;
const { assignees, dependencies } = this;
extract_identifiers(info.expression.left).forEach(
/** @param {any}params_0 */ ({ name }) => {
assignees.add(name);
const owner = this.scope.get_owner(name);
if (owner === parent) {
component.error(info, compiler_errors.invalid_const_declaration(name));
}
extract_identifiers(info.expression.left).forEach(({ name }) => {
assignees.add(name);
const owner = this.scope.get_owner(name);
if (owner === parent) {
component.error(info, compiler_errors.invalid_const_declaration(name));
}
);
});
walk(info.expression.right, {
/**
* @param {any} node
* @param {any} parent
* @type {import('estree-walker').SyncHandler}
*/
enter(node, parent) {
if (
@ -75,7 +72,7 @@ export default class ConstTag extends Node {
/** @type {import('is-reference').NodeWithPropertyDefinition} */ (parent)
)
) {
const identifier = get_object(/** @type {any} */ (node));
const identifier = get_object(node);
const { name } = identifier;
dependencies.add(name);
}
@ -92,18 +89,16 @@ export default class ConstTag extends Node {
context_rest_properties: this.context_rest_properties
});
this.expression = new Expression(this.component, this, this.scope, this.node.expression.right);
this.contexts.forEach(
/** @param {any} context */ (context) => {
if (context.type !== 'DestructuredVariable') return;
const owner = this.scope.get_owner(context.key.name);
if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) {
this.component.error(
this.node,
compiler_errors.invalid_const_declaration(context.key.name)
);
}
this.scope.add(context.key.name, this.expression.dependencies, this);
this.contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
const owner = this.scope.get_owner(context.key.name);
if (owner && owner.type === 'ConstTag' && owner.parent === this.parent) {
this.component.error(
this.node,
compiler_errors.invalid_const_declaration(context.key.name)
);
}
);
this.scope.add(context.key.name, this.expression.dependencies, this);
});
}
}

@ -28,48 +28,46 @@ export default class Document extends Node {
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach(
/** @param {any} node */ (node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Binding') {
if (!~valid_bindings.indexOf(node.name)) {
const match = fuzzymatch(node.name, valid_bindings);
if (match) {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:document>',
` (did you mean '${match}'?)`
)
);
} else {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:document>',
` — valid bindings are ${list(valid_bindings)}`
)
);
}
info.attributes.forEach((node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Binding') {
if (!~valid_bindings.indexOf(node.name)) {
const match = fuzzymatch(node.name, valid_bindings);
if (match) {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:document>',
` (did you mean '${match}'?)`
)
);
} else {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:document>',
` — valid bindings are ${list(valid_bindings)}`
)
);
}
this.bindings.push(new Binding(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
this.bindings.push(new Binding(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
);
});
this.validate();
}
/** @private */
validate() {
const handlers_map = new Set();
this.handlers.forEach(/** @param {any} handler */ (handler) => handlers_map.add(handler.name));
this.handlers.forEach((handler) => handlers_map.add(handler.name));
if (handlers_map.has('mouseenter') || handlers_map.has('mouseleave')) {
this.component.warn(this, compiler_warnings.avoid_mouse_events_on_document);
}

@ -71,12 +71,10 @@ export default class EachBlock extends AbstractBlock {
component,
context_rest_properties: this.context_rest_properties
});
this.contexts.forEach(
/** @param {any} context */ (context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, this.expression.dependencies, this);
}
);
this.contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, this.expression.dependencies, this);
});
if (this.index) {
// index can only change if this is a keyed each block
const dependencies = info.key ? this.expression.dependencies : new Set([]);
@ -86,13 +84,10 @@ export default class EachBlock extends AbstractBlock {
this.has_animation = false;
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
if (this.has_animation) {
this.children = this.children.filter(
/** @param {any} child */ (child) => !isEmptyNode(child) && !isCommentNode(child)
);
this.children = this.children.filter((child) => !isEmptyNode(child) && !isCommentNode(child));
if (this.children.length !== 1) {
const child = this.children.find(
/** @param {any} child */ (child) =>
!!(/** @type {import('./Element.js').default} */ (child).animation)
(child) => !!(/** @type {import('./Element.js').default} */ (child).animation)
);
component.error(
/** @type {import('./Element.js').default} */ (child).animation,

File diff suppressed because it is too large Load Diff

@ -48,7 +48,6 @@ export default class EventHandler extends Node {
if (node.type === 'VariableDeclaration') {
// for `const handleClick = () => {...}`, we want the [arrow] function expression node
const declarator = node.declarations.find(
/** @param {any} d */
(d) => /** @type {import('estree').Identifier} */ (d.id).name === info.expression.name
);
node = declarator && declarator.init;

@ -29,11 +29,9 @@ export default class Head extends Node {
component,
parent,
scope,
info.children.filter(
/** @param {any} child */ (child) => {
return child.type !== 'Text' || regex_non_whitespace_character.test(child.data);
}
)
info.children.filter((child) => {
return child.type !== 'Text' || regex_non_whitespace_character.test(child.data);
})
);
if (this.children.length > 0) {
this.id = `svelte-${hash(this.component.source.slice(this.start, this.end))}`;

@ -101,17 +101,13 @@ export default class InlineComponent extends Node {
this.scope = scope;
this.handlers.forEach(
/** @param {any} handler */ (handler) => {
handler.modifiers.forEach(
/** @param {any} modifier */ (modifier) => {
if (modifier !== 'once') {
return component.error(handler, compiler_errors.invalid_event_modifier_component);
}
}
);
}
);
this.handlers.forEach((handler) => {
handler.modifiers.forEach((modifier) => {
if (modifier !== 'once') {
return component.error(handler, compiler_errors.invalid_event_modifier_component);
}
});
});
const children = [];
for (let i = info.children.length - 1; i >= 0; i--) {
const child = info.children[i];
@ -120,9 +116,7 @@ export default class InlineComponent extends Node {
info.children.splice(i, 1);
} else if (
(child.type === 'Element' || child.type === 'InlineComponent' || child.type === 'Slot') &&
child.attributes.find(
/** @param {any} attribute */ (attribute) => attribute.name === 'slot'
)
child.attributes.find((attribute) => attribute.name === 'slot')
) {
const slot_template = {
start: child.start,
@ -156,7 +150,7 @@ export default class InlineComponent extends Node {
children[children.length - 1].children.unshift(child);
}
}
if (info.children.some(/** @param {any} node */ (node) => not_whitespace_text(node))) {
if (info.children.some((node) => not_whitespace_text(node))) {
children.push({
start: info.start,
end: info.end,
@ -182,9 +176,7 @@ export default class InlineComponent extends Node {
}
get slot_template_name() {
return /** @type {string} */ (
this.attributes
.find(/** @param {any} attribute */ (attribute) => attribute.name === 'slot')
.get_static_value()
this.attributes.find((attribute) => attribute.name === 'slot').get_static_value()
);
}
}

@ -22,23 +22,21 @@ export default class Slot extends Element {
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach(
/** @param {any} attr */ (attr) => {
if (attr.type !== 'Attribute' && attr.type !== 'Spread') {
return component.error(attr, compiler_errors.invalid_slot_directive);
info.attributes.forEach((attr) => {
if (attr.type !== 'Attribute' && attr.type !== 'Spread') {
return component.error(attr, compiler_errors.invalid_slot_directive);
}
if (attr.name === 'name') {
if (attr.value.length !== 1 || attr.value[0].type !== 'Text') {
return component.error(attr, compiler_errors.dynamic_slot_name);
}
if (attr.name === 'name') {
if (attr.value.length !== 1 || attr.value[0].type !== 'Text') {
return component.error(attr, compiler_errors.dynamic_slot_name);
}
this.slot_name = attr.value[0].data;
if (this.slot_name === 'default') {
return component.error(attr, compiler_errors.invalid_slot_name);
}
this.slot_name = attr.value[0].data;
if (this.slot_name === 'default') {
return component.error(attr, compiler_errors.invalid_slot_name);
}
this.values.set(attr.name, new Attribute(component, this, scope, attr));
}
);
this.values.set(attr.name, new Attribute(component, this, scope, attr));
});
if (!this.slot_name) this.slot_name = 'default';
component.slots.set(this.slot_name, this);

@ -34,40 +34,36 @@ export default class SlotTemplate extends Node {
super(component, parent, scope, info);
this.validate_slot_template_placement();
scope = scope.child();
info.attributes.forEach(
/** @param {any} node */ (node) => {
switch (node.type) {
case 'Let': {
const l = new Let(component, this, scope, node);
this.lets.push(l);
const dependencies = new Set([l.name.name]);
l.names.forEach(
/** @param {any} name */ (name) => {
scope.add(name, dependencies, this);
}
);
break;
}
case 'Attribute': {
if (node.name === 'slot') {
this.slot_attribute = new Attribute(component, this, scope, node);
if (!this.slot_attribute.is_static) {
return component.error(node, compiler_errors.invalid_slot_attribute);
}
const value = this.slot_attribute.get_static_value();
if (typeof value === 'boolean') {
return component.error(node, compiler_errors.invalid_slot_attribute_value_missing);
}
this.slot_template_name = /** @type {string} */ (value);
break;
info.attributes.forEach((node) => {
switch (node.type) {
case 'Let': {
const l = new Let(component, this, scope, node);
this.lets.push(l);
const dependencies = new Set([l.name.name]);
l.names.forEach((name) => {
scope.add(name, dependencies, this);
});
break;
}
case 'Attribute': {
if (node.name === 'slot') {
this.slot_attribute = new Attribute(component, this, scope, node);
if (!this.slot_attribute.is_static) {
return component.error(node, compiler_errors.invalid_slot_attribute);
}
throw new Error(`Invalid attribute '${node.name}' in <svelte:fragment>`);
const value = this.slot_attribute.get_static_value();
if (typeof value === 'boolean') {
return component.error(node, compiler_errors.invalid_slot_attribute_value_missing);
}
this.slot_template_name = /** @type {string} */ (value);
break;
}
default:
throw new Error(`Not implemented: ${node.type}`);
throw new Error(`Invalid attribute '${node.name}' in <svelte:fragment>`);
}
default:
throw new Error(`Not implemented: ${node.type}`);
}
);
});
this.scope = scope;
[this.const_tags, this.children] = get_const_tags(info.children, component, this, this);
}

@ -43,12 +43,12 @@ export default class StyleDirective extends Node {
if (info.value === true || (info.value.length === 1 && info.value[0].type === 'MustacheTag')) {
const identifier =
info.value === true
? /** @type {any} */ ({
? {
type: 'Identifier',
start: info.end - info.name.length,
end: info.end,
name: info.name
})
}
: info.value[0].expression;
this.expression = new Expression(component, this, scope, identifier);
this.should_cache = false;

@ -19,12 +19,10 @@ export default class ThenBlock extends AbstractBlock {
super(component, parent, scope, info);
this.scope = scope.child();
if (parent.then_node) {
parent.then_contexts.forEach(
/** @param {any} context */ (context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this);
}
);
parent.then_contexts.forEach((context) => {
if (context.type !== 'DestructuredVariable') return;
this.scope.add(context.key.name, parent.expression.dependencies, this);
});
}
[this.const_tags, this.children] = get_const_tags(info.children, component, this, parent);
if (!info.skip) {

@ -23,13 +23,11 @@ export default class Title extends Node {
component.error(info.attributes[0], compiler_errors.illegal_attribute_title);
return;
}
info.children.forEach(
/** @param {any} child */ (child) => {
if (child.type !== 'Text' && child.type !== 'MustacheTag') {
return component.error(child, compiler_errors.illegal_structure_title);
}
info.children.forEach((child) => {
if (child.type !== 'Text' && child.type !== 'MustacheTag') {
return component.error(child, compiler_errors.illegal_structure_title);
}
);
});
this.should_cache =
info.children.length === 1
? info.children[0].type !== 'Identifier' || scope.names.has(info.children[0].name)

@ -37,50 +37,48 @@ export default class Window extends Node {
*/
constructor(component, parent, scope, info) {
super(component, parent, scope, info);
info.attributes.forEach(
/** @param {any} node */ (node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Binding') {
if (node.expression.type !== 'Identifier') {
const { parts } = flatten_reference(node.expression);
// TODO is this constraint necessary?
return component.error(node.expression, compiler_errors.invalid_binding_window(parts));
}
if (!~valid_bindings.indexOf(node.name)) {
const match =
node.name === 'width'
? 'innerWidth'
: node.name === 'height'
? 'innerHeight'
: fuzzymatch(node.name, valid_bindings);
if (match) {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:window>',
` (did you mean '${match}'?)`
)
);
} else {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:window>',
` — valid bindings are ${list(valid_bindings)}`
)
);
}
info.attributes.forEach((node) => {
if (node.type === 'EventHandler') {
this.handlers.push(new EventHandler(component, this, scope, node));
} else if (node.type === 'Binding') {
if (node.expression.type !== 'Identifier') {
const { parts } = flatten_reference(node.expression);
// TODO is this constraint necessary?
return component.error(node.expression, compiler_errors.invalid_binding_window(parts));
}
if (!~valid_bindings.indexOf(node.name)) {
const match =
node.name === 'width'
? 'innerWidth'
: node.name === 'height'
? 'innerHeight'
: fuzzymatch(node.name, valid_bindings);
if (match) {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:window>',
` (did you mean '${match}'?)`
)
);
} else {
return component.error(
node,
compiler_errors.invalid_binding_on(
node.name,
'<svelte:window>',
` — valid bindings are ${list(valid_bindings)}`
)
);
}
this.bindings.push(new Binding(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
this.bindings.push(new Binding(component, this, scope, node));
} else if (node.type === 'Action') {
this.actions.push(new Action(component, this, scope, node));
} else {
// TODO there shouldn't be anything else here...
}
);
});
}
}

@ -31,10 +31,16 @@ export default class Expression {
/** @type {Set<string>} */
references = new Set();
/** @type {Set<string>} */
/**
* Dependencies declared in the script block
* @type {Set<string>}
*/
dependencies = new Set();
/** @type {Set<string>} */
/**
* Dependencies declared in the HTML-like template section
* @type {Set<string>}
*/
contextual_dependencies = new Set();
/** @type {import('./TemplateScope.js').default} */
@ -82,12 +88,12 @@ export default class Expression {
walk(info, {
/**
* @param {any} node
* @param {any} parent
* @param {import('estree').Node} parent
* @param {string} key
*/
enter(node, parent, key) {
// don't manipulate shorthand props twice
if (key === 'key' && parent.shorthand) return;
if (key === 'key' && /** @type {import('estree').Property} */ (parent).shorthand) return;
// don't manipulate `import.meta`, `new.target`
if (node.type === 'MetaProperty') return this.skip();
if (map.has(node)) {
@ -119,7 +125,7 @@ export default class Expression {
if (!lazy || is_index) {
template_scope.dependencies_for_name
.get(name)
.forEach(/** @param {any} name */ (name) => dependencies.add(name));
.forEach((name) => dependencies.add(name));
}
} else {
if (!lazy) {
@ -143,49 +149,48 @@ export default class Expression {
}
}
if (names) {
names.forEach(
/** @param {any} name */ (name) => {
if (template_scope.names.has(name)) {
if (template_scope.is_const(name)) {
component.error(node, compiler_errors.invalid_const_update(name));
}
template_scope.dependencies_for_name.get(name).forEach(
/** @param {any} name */ (name) => {
const variable = component.var_lookup.get(name);
if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
}
);
const each_block = template_scope.get_owner(name);
/** @type {import('../EachBlock.js').default} */ (each_block).has_binding = true;
} else {
component.add_reference(node, name);
names.forEach((name) => {
if (template_scope.names.has(name)) {
if (template_scope.is_const(name)) {
component.error(node, compiler_errors.invalid_const_update(name));
}
template_scope.dependencies_for_name.get(name).forEach((name) => {
const variable = component.var_lookup.get(name);
if (variable) {
variable[deep ? 'mutated' : 'reassigned'] = true;
}
if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
});
const each_block = template_scope.get_owner(name);
/** @type {import('../EachBlock.js').default} */ (each_block).has_binding = true;
} else {
component.add_reference(node, name);
const variable = component.var_lookup.get(name);
if (variable) {
variable[deep ? 'mutated' : 'reassigned'] = true;
}
/** @type {any} */
const declaration = scope.find_owner(name)?.declarations.get(name);
if (declaration) {
if (declaration.kind === 'const' && !deep) {
component.error(node, {
code: 'assignment-to-const',
message: 'You are assigning to a const'
});
}
} else if (variable && variable.writable === false && !deep) {
const declaration = scope.find_owner(name)?.declarations.get(name);
if (declaration) {
if (
/** @type {import('estree').VariableDeclaration} */ (declaration).kind ===
'const' &&
!deep
) {
component.error(node, {
code: 'assignment-to-const',
message: 'You are assigning to a const'
});
}
} else if (variable && variable.writable === false && !deep) {
component.error(node, {
code: 'assignment-to-const',
message: 'You are assigning to a const'
});
}
}
);
});
}
},
/** @param {import('estree').Node} node */
/** @type {import('estree-walker').SyncHandler} */
leave(node) {
if (map.has(node)) {
scope = scope.parent;
@ -197,27 +202,22 @@ export default class Expression {
});
}
dynamic_dependencies() {
return Array.from(this.dependencies).filter(
/** @param {any} name */ (name) => {
if (this.template_scope.is_let(name)) return true;
if (is_reserved_keyword(name)) return true;
const variable = this.component.var_lookup.get(name);
return is_dynamic(variable);
}
);
return Array.from(this.dependencies).filter((name) => {
if (this.template_scope.is_let(name)) return true;
if (is_reserved_keyword(name)) return true;
const variable = this.component.var_lookup.get(name);
return is_dynamic(variable);
});
}
dynamic_contextual_dependencies() {
return Array.from(this.contextual_dependencies).filter(
/** @param {any} name */ (name) => {
return Array.from(this.template_scope.dependencies_for_name.get(name)).some(
/** @param {any} variable_name */
(variable_name) => {
const variable = this.component.var_lookup.get(variable_name);
return is_dynamic(variable);
}
);
}
);
return Array.from(this.contextual_dependencies).filter((name) => {
return Array.from(this.template_scope.dependencies_for_name.get(name)).some(
(variable_name) => {
const variable = this.component.var_lookup.get(variable_name);
return is_dynamic(variable);
}
);
});
}
// TODO move this into a render-dom wrapper?
@ -239,10 +239,7 @@ export default class Expression {
/** @type {Set<string>} */
let contextual_dependencies;
const node = walk(this.node, {
/**
* @param {any} node
* @param {any} parent
*/
/** @type {import('estree-walker').SyncHandler} */
enter(node, parent) {
if (node.type === 'Property' && node.shorthand) {
node.value = clone(node.value);
@ -257,11 +254,9 @@ export default class Expression {
if (function_expression) {
if (template_scope.names.has(name)) {
contextual_dependencies.add(name);
template_scope.dependencies_for_name.get(name).forEach(
/** @param {any} dependency */ (dependency) => {
dependencies.add(dependency);
}
);
template_scope.dependencies_for_name.get(name).forEach((dependency) => {
dependencies.add(dependency);
});
} else {
dependencies.add(name);
component.add_reference(node, name); // TODO is this redundant/misplaced?
@ -284,10 +279,7 @@ export default class Expression {
}
},
/**
* @param {import('estree').Node} node
* @param {import('estree').Node} parent
*/
/** @type {import('estree-walker').SyncHandler} */
leave(node, parent) {
if (map.has(node)) scope = scope.parent;
if (node === function_expression) {
@ -299,18 +291,15 @@ export default class Expression {
const has_args = function_expression.params.length > 0;
function_expression.params = [
...deps.map(
/** @param {any} name */ (name) =>
/** @type {import('estree').Identifier} */ ({ type: 'Identifier', name })
(name) => /** @type {import('estree').Identifier} */ ({ type: 'Identifier', name })
),
...function_expression.params
];
const context_args = deps.map(
/** @param {any} name */ (name) => block.renderer.reference(name, ctx)
);
const context_args = deps.map((name) => block.renderer.reference(name, ctx));
component.partly_hoisted.push(declaration);
block.renderer.add_to_context(id.name);
const callee = block.renderer.reference(id);
this.replace(/** @type {any} */ (id));
this.replace(id);
const func_declaration = has_args
? b`function ${id}(...args) {
return ${callee}(${context_args}, ...args);
@ -325,10 +314,7 @@ export default class Expression {
if (contextual_dependencies.size === 0) {
let child_scope = scope;
walk(node, {
/**
* @param {import('estree').Node} node
* @param {any} parent
*/
/** @type {import('estree-walker').SyncHandler} */
enter(node, parent) {
if (map.has(node)) child_scope = map.get(node);
if (node.type === 'Identifier' && is_reference(node, parent)) {
@ -349,7 +335,7 @@ export default class Expression {
} else if (dependencies.size === 0 && contextual_dependencies.size === 0) {
// we can hoist this out of the component completely
component.fully_hoisted.push(declaration);
this.replace(/** @type {any} */ (id));
this.replace(id);
component.add_var(node, {
name: id.name,
internal: true,
@ -366,9 +352,7 @@ export default class Expression {
const { deps, func_declaration } = extract_functions();
if (owner.type === 'Attribute' && owner.parent.name === 'slot') {
/** @type {Set<import('../interfaces.js').INode>} */
const dep_scopes = new Set(
deps.map(/** @param {any} name */ (name) => template_scope.get_owner(name))
);
const dep_scopes = new Set(deps.map((name) => template_scope.get_owner(name)));
// find the nearest scopes
/** @type {import('../interfaces.js').INode} */
@ -402,7 +386,7 @@ export default class Expression {
type: 'DestructuredVariable',
key: func_id,
modifier: () => func_expression,
default_modifier: /** @param {any} node */ (node) => node
default_modifier: (node) => node
});
this.replace(block.renderer.reference(func_id));
}
@ -429,16 +413,14 @@ export default class Expression {
/** @type {Set<string>} */
const traced = new Set();
names.forEach(
/** @param {any} name */ (name) => {
const dependencies = template_scope.dependencies_for_name.get(name);
if (dependencies) {
dependencies.forEach(/** @param {any} name */ (name) => traced.add(name));
} else {
traced.add(name);
}
names.forEach((name) => {
const dependencies = template_scope.dependencies_for_name.get(name);
if (dependencies) {
dependencies.forEach((name) => traced.add(name));
} else {
traced.add(name);
}
);
});
const context = block.bindings.get(object_name);
if (context) {
// for `{#each array as item}`
@ -463,19 +445,17 @@ export default class Expression {
if (declarations.length > 0) {
block.maintain_context = true;
declarations.forEach(
/** @param {any} declaration */ (declaration) => {
block.chunks.init.push(declaration);
}
);
declarations.forEach((declaration) => {
block.chunks.init.push(declaration);
});
}
return (this.manipulated = /** @type {import('estree').Node} */ (node));
}
}
/**
* @param {any} _node
* @param {any} parent
* @param {import('estree').Node} _node
* @param {import('../interfaces.js').INode} parent
*/
function get_function_name(_node, parent) {
if (parent.type === 'EventHandler') {

@ -1,3 +1,4 @@
/** The scope of constructs within the Svelte template */
export default class TemplateScope {
/**
* @typedef {import('../EachBlock').default

@ -23,11 +23,9 @@ export default function get_const_tags(children, component, node, parent) {
others.push(child);
}
}
const consts_nodes = const_tags.map(
/** @param {any} tag */ (tag) => new ConstTag(component, node, node.scope, tag)
);
const consts_nodes = const_tags.map((tag) => new ConstTag(component, node, node.scope, tag));
const sorted_consts_nodes = sort_consts_nodes(consts_nodes, component);
sorted_consts_nodes.forEach(/** @param {any} node */ (node) => node.parse_expression());
sorted_consts_nodes.forEach((node) => node.parse_expression());
const children_nodes = map_children(component, parent, node.scope, others);
return [
sorted_consts_nodes,
@ -46,49 +44,33 @@ function sort_consts_nodes(consts_nodes, component) {
const sorted_consts_nodes = [];
/** @type {ConstNode[]} */
const unsorted_consts_nodes = consts_nodes.map(
/** @param {any} node */ (node) => {
return {
assignees: node.assignees,
dependencies: node.dependencies,
node
};
}
);
const unsorted_consts_nodes = consts_nodes.map((node) => {
return {
assignees: node.assignees,
dependencies: node.dependencies,
node
};
});
const lookup = new Map();
unsorted_consts_nodes.forEach(
/** @param {any} node */ (node) => {
node.assignees.forEach(
/** @param {any} name */ (name) => {
if (!lookup.has(name)) {
lookup.set(name, []);
}
lookup.get(name).push(node);
}
);
}
);
unsorted_consts_nodes.forEach((node) => {
node.assignees.forEach((name) => {
if (!lookup.has(name)) {
lookup.set(name, []);
}
lookup.get(name).push(node);
});
});
const cycle = check_graph_for_cycles(
unsorted_consts_nodes.reduce(
/**
* @param {any} acc
* @param {any} node
*/ (acc, node) => {
node.assignees.forEach(
/** @param {any} v */ (v) => {
node.dependencies.forEach(
/** @param {any} w */ (w) => {
if (!node.assignees.has(w)) {
acc.push([v, w]);
}
}
);
unsorted_consts_nodes.reduce((acc, node) => {
node.assignees.forEach((v) => {
node.dependencies.forEach((w) => {
if (!node.assignees.has(w)) {
acc.push([v, w]);
}
);
return acc;
},
[]
)
});
});
return acc;
}, [])
);
if (cycle && cycle.length) {
const nodeList = lookup.get(cycle[0]);
@ -99,17 +81,15 @@ function sort_consts_nodes(consts_nodes, component) {
/** @param {ConstNode} node */
const add_node = (node) => {
if (sorted_consts_nodes.includes(node)) return;
node.dependencies.forEach(
/** @param {any} name */ (name) => {
if (node.assignees.has(name)) return;
const earlier_nodes = lookup.get(name);
if (earlier_nodes) {
earlier_nodes.forEach(add_node);
}
node.dependencies.forEach((name) => {
if (node.assignees.has(name)) return;
const earlier_nodes = lookup.get(name);
if (earlier_nodes) {
earlier_nodes.forEach(add_node);
}
);
});
sorted_consts_nodes.push(node);
};
unsorted_consts_nodes.forEach(add_node);
return sorted_consts_nodes.map(/** @param {any} node */ (node) => node.node);
return sorted_consts_nodes.map((node) => node.node);
}

@ -79,20 +79,18 @@ function get_constructor(type) {
export default function map_children(component, parent, scope, children) {
let last = null;
let ignores = [];
return children.map(
/** @param {any} child */ (child) => {
const constructor = get_constructor(child.type);
const use_ignores = child.type !== 'Text' && child.type !== 'Comment' && ignores.length;
if (use_ignores) component.push_ignores(ignores);
const node = new constructor(component, parent, scope, child);
if (use_ignores) component.pop_ignores(), (ignores = []);
if (node.type === 'Comment' && node.ignores.length) {
push_array(ignores, node.ignores);
}
if (last) last.next = node;
node.prev = last;
last = node;
return node;
return children.map((child) => {
const constructor = get_constructor(child.type);
const use_ignores = child.type !== 'Text' && child.type !== 'Comment' && ignores.length;
if (use_ignores) component.push_ignores(ignores);
const node = new constructor(component, parent, scope, child);
if (use_ignores) component.pop_ignores(), (ignores = []);
if (node.type === 'Comment' && node.ignores.length) {
push_array(ignores, node.ignores);
}
);
if (last) last.next = node;
node.prev = last;
last = node;
return node;
});
}

@ -73,9 +73,7 @@ export default function read_context(parser) {
space_with_newline =
space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);
return /** @type {any} */ (
parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, start - 1)
).left;
return parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, start - 1).left;
} catch (error) {
parser.acorn_error(error);
}

@ -588,14 +588,12 @@ export function init_binding_group_dynamic(group, indexes) {
};
}
/**
* @returns {number} */
/** @returns {number} */
export function to_number(value) {
return value === '' ? null : +value;
}
/**
* @returns {any[]} */
/** @returns {any[]} */
export function time_ranges_to_array(ranges) {
const array = [];
for (let i = 0; i < ranges.length; i += 1) {

Loading…
Cancel
Save