centralise stuff

pull/11302/head
Rich Harris 2 years ago
parent 34e8562f33
commit 4cc013fa6f

@ -8,6 +8,7 @@ import { remove_typescript_nodes } from './phases/1-parse/remove_typescript_node
import { analyze_component, analyze_module } from './phases/2-analyze/index.js';
import { transform_component, transform_module } from './phases/3-transform/index.js';
import { validate_component_options, validate_module_options } from './validate-options.js';
import { reset_warnings } from './warnings.js';
export { default as preprocess } from './preprocess/index.js';
/**
@ -20,6 +21,7 @@ export { default as preprocess } from './preprocess/index.js';
*/
export function compile(source, options) {
try {
const warnings = reset_warnings({ source, filename: options.filename });
const validated = validate_component_options(options, '');
let parsed = _parse(source);
@ -44,6 +46,7 @@ export function compile(source, options) {
const analysis = analyze_component(parsed, source, combined_options);
const result = transform_component(analysis, source, combined_options);
result.warnings = warnings;
result.ast = to_public_ast(source, parsed, options.modernAst);
return result;
} catch (e) {
@ -65,9 +68,12 @@ export function compile(source, options) {
*/
export function compileModule(source, options) {
try {
const warnings = reset_warnings({ source, filename: options.filename });
const validated = validate_module_options(options, '');
const analysis = analyze_module(parse_acorn(source, false), validated);
return transform_module(analysis, source, validated);
const result = transform_module(analysis, source, validated);
result.warnings = warnings;
return result;
} catch (e) {
if (e instanceof CompileError) {
handle_compile_error(e, options.filename, source);

@ -672,15 +672,6 @@ function check_element(node, state) {
// foreign namespace means elements can have completely different meanings, therefore we don't check them
if (state.options.namespace === 'foreign') return;
/**
* @template {keyof import('../../warnings-tmp.js').AllWarnings} T
* @param {{ start?: number, end?: number }} node
* @param {T} code
* @param {Parameters<import('../../warnings-tmp.js').AllWarnings[T]>} args
* @returns {void}
*/
const push_warning = (node, code, ...args) => warn(state.analysis.warnings, node, code, ...args);
/** @type {Map<string, import('#compiler').Attribute>} */
const attribute_map = new Map();
@ -727,17 +718,17 @@ function check_element(node, state) {
if (name.startsWith('aria-')) {
if (invisible_elements.includes(node.name)) {
// aria-unsupported-elements
push_warning(attribute, 'a11y-aria-attributes', node.name);
warn(attribute, 'a11y-aria-attributes', node.name);
}
const type = name.slice(5);
if (!aria_attributes.includes(type)) {
const match = fuzzymatch(type, aria_attributes);
push_warning(attribute, 'a11y-unknown-aria-attribute', type, match);
warn(attribute, 'a11y-unknown-aria-attribute', type, match);
}
if (name === 'aria-hidden' && regex_heading_tags.test(node.name)) {
push_warning(attribute, 'a11y-hidden', node.name);
warn(attribute, 'a11y-hidden', node.name);
}
// aria-proptypes
@ -747,7 +738,7 @@ function check_element(node, state) {
if (value !== null && value !== undefined) {
const schema = aria.get(/** @type {import('aria-query').ARIAProperty} */ (name));
if (schema !== undefined && !is_valid_aria_attribute_value(schema, value)) {
push_warning(attribute, 'a11y-incorrect-aria-attribute-type', schema, name);
warn(attribute, 'a11y-incorrect-aria-attribute-type', schema, name);
}
}
@ -758,7 +749,7 @@ function check_element(node, state) {
!is_interactive_element(node.name, attribute_map) &&
!attribute_map.has('tabindex')
) {
push_warning(attribute, 'a11y-aria-activedescendant-has-tabindex');
warn(attribute, 'a11y-aria-activedescendant-has-tabindex');
}
}
@ -766,7 +757,7 @@ function check_element(node, state) {
if (name === 'role') {
if (invisible_elements.includes(node.name)) {
// aria-unsupported-elements
push_warning(attribute, 'a11y-misplaced-role', node.name);
warn(attribute, 'a11y-misplaced-role', node.name);
}
const value = get_static_value(attribute);
@ -776,10 +767,10 @@ function check_element(node, state) {
/** @type {import('aria-query').ARIARoleDefinitionKey} current_role */ (c_r);
if (current_role && is_abstract_role(current_role)) {
push_warning(attribute, 'a11y-no-abstract-role', current_role);
warn(attribute, 'a11y-no-abstract-role', current_role);
} else if (current_role && !aria_roles.includes(current_role)) {
const match = fuzzymatch(current_role, aria_roles);
push_warning(attribute, 'a11y-unknown-role', current_role, match);
warn(attribute, 'a11y-unknown-role', current_role, match);
}
// no-redundant-roles
@ -788,7 +779,7 @@ function check_element(node, state) {
// <ul role="list"> is ok because CSS list-style:none removes the semantics and this is a way to bring them back
!['ul', 'ol', 'li'].includes(node.name)
) {
push_warning(attribute, 'a11y-no-redundant-roles', current_role);
warn(attribute, 'a11y-no-redundant-roles', current_role);
}
// Footers and headers are special cases, and should not have redundant roles unless they are the children of sections or articles.
@ -797,7 +788,7 @@ function check_element(node, state) {
const has_nested_redundant_role =
current_role === a11y_nested_implicit_semantics.get(node.name);
if (has_nested_redundant_role) {
push_warning(attribute, 'a11y-no-redundant-roles', current_role);
warn(attribute, 'a11y-no-redundant-roles', current_role);
}
}
@ -813,7 +804,7 @@ function check_element(node, state) {
(prop) => !attributes.find((a) => a.name === prop)
);
if (has_missing_props) {
push_warning(
warn(
attribute,
'a11y-role-has-required-aria-props',
current_role,
@ -836,7 +827,7 @@ function check_element(node, state) {
a11y_interactive_handlers.includes(handler)
);
if (has_interactive_handlers) {
push_warning(node, 'a11y-interactive-supports-focus', current_role);
warn(node, 'a11y-interactive-supports-focus', current_role);
}
}
@ -845,7 +836,7 @@ function check_element(node, state) {
is_interactive_element(node.name, attribute_map) &&
(is_non_interactive_roles(current_role) || is_presentation_role(current_role))
) {
push_warning(
warn(
node,
'a11y-no-interactive-element-to-noninteractive-role',
current_role,
@ -861,7 +852,7 @@ function check_element(node, state) {
current_role
)
) {
push_warning(
warn(
node,
'a11y-no-noninteractive-element-to-interactive-role',
current_role,
@ -874,17 +865,17 @@ function check_element(node, state) {
// no-access-key
if (name === 'accesskey') {
push_warning(attribute, 'a11y-accesskey');
warn(attribute, 'a11y-accesskey');
}
// no-autofocus
if (name === 'autofocus') {
push_warning(attribute, 'a11y-autofocus');
warn(attribute, 'a11y-autofocus');
}
// scope
if (name === 'scope' && !is_dynamic_element && node.name !== 'th') {
push_warning(attribute, 'a11y-misplaced-scope');
warn(attribute, 'a11y-misplaced-scope');
}
// tabindex-no-positive
@ -892,7 +883,7 @@ function check_element(node, state) {
const value = get_static_value(attribute);
// @ts-ignore todo is tabindex=true correct case?
if (!isNaN(value) && +value > 0) {
push_warning(attribute, 'a11y-positive-tabindex');
warn(attribute, 'a11y-positive-tabindex');
}
}
}
@ -916,7 +907,7 @@ function check_element(node, state) {
const has_key_event =
handlers.has('keydown') || handlers.has('keyup') || handlers.has('keypress');
if (!has_key_event) {
push_warning(node, 'a11y-click-events-have-key-events');
warn(node, 'a11y-click-events-have-key-events');
}
}
}
@ -934,7 +925,7 @@ function check_element(node, state) {
const tab_index = attribute_map.get('tabindex');
const tab_index_value = get_static_text_value(tab_index);
if (tab_index && (tab_index_value === null || Number(tab_index_value) >= 0)) {
push_warning(node, 'a11y-no-noninteractive-tabindex');
warn(node, 'a11y-no-noninteractive-tabindex');
}
}
@ -949,14 +940,7 @@ function check_element(node, state) {
if (
invalid_aria_props.includes(/** @type {import('aria-query').ARIAProperty} */ (attr.name))
) {
push_warning(
attr,
'a11y-role-supports-aria-props',
attr.name,
role_value,
is_implicit,
node.name
);
warn(attr, 'a11y-role-supports-aria-props', attr.name, role_value, is_implicit, node.name);
}
}
}
@ -974,7 +958,7 @@ function check_element(node, state) {
a11y_recommended_interactive_handlers.includes(handler)
);
if (has_interactive_handlers) {
push_warning(node, 'a11y-no-noninteractive-element-interactions', node.name);
warn(node, 'a11y-no-noninteractive-element-interactions', node.name);
}
}
@ -993,16 +977,16 @@ function check_element(node, state) {
a11y_interactive_handlers.includes(handler)
);
if (interactive_handlers.length > 0) {
push_warning(node, 'a11y-no-static-element-interactions', node.name, interactive_handlers);
warn(node, 'a11y-no-static-element-interactions', node.name, interactive_handlers);
}
}
if (handlers.has('mouseover') && !handlers.has('focus')) {
push_warning(node, 'a11y-mouse-events-have-key-events', 'mouseover', 'focus');
warn(node, 'a11y-mouse-events-have-key-events', 'mouseover', 'focus');
}
if (handlers.has('mouseout') && !handlers.has('blur')) {
push_warning(node, 'a11y-mouse-events-have-key-events', 'mouseout', 'blur');
warn(node, 'a11y-mouse-events-have-key-events', 'mouseout', 'blur');
}
// element-specific checks
@ -1021,14 +1005,14 @@ function check_element(node, state) {
const href_value = get_static_text_value(href);
if (href_value !== null) {
if (href_value === '' || href_value === '#' || /^\W*javascript:/i.test(href_value)) {
push_warning(href, 'a11y-invalid-attribute', href.name, href_value);
warn(href, 'a11y-invalid-attribute', href.name, href_value);
}
}
} else if (!has_spread) {
const id_attribute = get_static_value(attribute_map.get('id'));
const name_attribute = get_static_value(attribute_map.get('name'));
if (!id_attribute && !name_attribute) {
push_warning(...warn_missing_attribute(node, ['href']));
warn(...warn_missing_attribute(node, ['href']));
}
}
} else if (!has_spread) {
@ -1036,7 +1020,7 @@ function check_element(node, state) {
if (required_attributes) {
const has_attribute = required_attributes.some((name) => attribute_map.has(name));
if (!has_attribute) {
push_warning(...warn_missing_attribute(node, required_attributes));
warn(...warn_missing_attribute(node, required_attributes));
}
}
}
@ -1048,7 +1032,7 @@ function check_element(node, state) {
const required_attributes = ['alt', 'aria-label', 'aria-labelledby'];
const has_attribute = required_attributes.some((name) => attribute_map.has(name));
if (!has_attribute) {
push_warning(...warn_missing_attribute(node, required_attributes, 'input type="image"'));
warn(...warn_missing_attribute(node, required_attributes, 'input type="image"'));
}
}
// autocomplete-valid
@ -1056,7 +1040,7 @@ function check_element(node, state) {
if (type && autocomplete) {
const autocomplete_value = get_static_value(autocomplete);
if (!is_valid_autocomplete(autocomplete_value)) {
push_warning(autocomplete, 'a11y-autocomplete-valid', type_value, autocomplete_value);
warn(autocomplete, 'a11y-autocomplete-valid', type_value, autocomplete_value);
}
}
}
@ -1066,7 +1050,7 @@ function check_element(node, state) {
const aria_hidden = get_static_value(attribute_map.get('aria-hidden'));
if (alt_attribute && !aria_hidden) {
if (/\b(image|picture|photo)\b/i.test(alt_attribute)) {
push_warning(node, 'a11y-img-redundant-alt');
warn(node, 'a11y-img-redundant-alt');
}
}
}
@ -1096,7 +1080,7 @@ function check_element(node, state) {
return has;
};
if (!attribute_map.has('for') && !has_input_child(node)) {
push_warning(node, 'a11y-label-has-associated-control');
warn(node, 'a11y-label-has-associated-control');
}
}
@ -1118,13 +1102,13 @@ function check_element(node, state) {
);
}
if (!has_caption) {
push_warning(node, 'a11y-media-has-caption');
warn(node, 'a11y-media-has-caption');
}
}
if (node.name === 'figcaption') {
if (!is_parent(node.parent, ['figure'])) {
push_warning(node, 'a11y-structure', true);
warn(node, 'a11y-structure', true);
}
}
@ -1138,13 +1122,13 @@ function check_element(node, state) {
(child) => child.type === 'RegularElement' && child.name === 'figcaption'
);
if (index !== -1 && index !== 0 && index !== children.length - 1) {
push_warning(children[index], 'a11y-structure', false);
warn(children[index], 'a11y-structure', false);
}
}
if (a11y_distracting_elements.includes(node.name)) {
// no-distracting-elements
push_warning(node, 'a11y-distracting-elements', node.name);
warn(node, 'a11y-distracting-elements', node.name);
}
// Check content
@ -1154,7 +1138,7 @@ function check_element(node, state) {
a11y_required_content.includes(node.name) &&
node.fragment.nodes.length === 0
) {
push_warning(node, 'a11y-missing-content', node.name);
warn(node, 'a11y-missing-content', node.name);
}
}

@ -4,13 +4,12 @@ import { is_keyframes_node } from '../../css.js';
/**
* @param {import('#compiler').Css.StyleSheet} stylesheet
* @param {import('../../types.js').RawWarning[]} warnings
*/
export function warn_unused(stylesheet, warnings) {
walk(stylesheet, { warnings, stylesheet }, visitors);
export function warn_unused(stylesheet) {
walk(stylesheet, { stylesheet }, visitors);
}
/** @type {import('zimmerframe').Visitors<import('#compiler').Css.Node, { warnings: import('../../types.js').RawWarning[], stylesheet: import('#compiler').Css.StyleSheet }>} */
/** @type {import('zimmerframe').Visitors<import('#compiler').Css.Node, { stylesheet: import('#compiler').Css.StyleSheet }>} */
const visitors = {
Atrule(node, context) {
if (!is_keyframes_node(node)) {
@ -26,7 +25,7 @@ const visitors = {
if (!node.metadata.used) {
const content = context.state.stylesheet.content;
const text = content.styles.substring(node.start - content.start, node.end - content.start);
warn(context.state.warnings, node, 'css-unused-selector', text);
warn(node, 'css-unused-selector', text);
}
context.next();

@ -25,6 +25,7 @@ import { prune } from './css/css-prune.js';
import { hash } from './utils.js';
import { warn_unused } from './css/css-warn.js';
import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore.js';
import { reset_warnings } from '../../warnings.js';
/**
* @param {import('#compiler').Script | null} script
@ -234,16 +235,9 @@ export function analyze_module(ast, options) {
}
}
/** @type {import('../types').RawWarning[]} */
const warnings = [];
const analysis = {
warnings
};
walk(
/** @type {import('estree').Node} */ (ast),
{ scope, analysis },
{ scope },
// @ts-expect-error TODO clean this mess up
merge(set_scope(scopes), validation_runes_js, runes_scope_js_tweaker)
);
@ -251,7 +245,6 @@ export function analyze_module(ast, options) {
return {
module: { ast, scope, scopes },
name: options.filename || 'module',
warnings,
accessors: false,
runes: true,
immutable: true
@ -275,9 +268,6 @@ export function analyze_component(root, source, options) {
/** @type {import('../types.js').Template} */
const template = { ast: root.fragment, scope, scopes };
/** @type {import('../types').RawWarning[]} */
const warnings = [];
// create synthetic bindings for store subscriptions
for (const [name, references] of module.scope.references) {
if (name[0] !== '$' || ReservedKeywords.includes(name)) continue;
@ -331,7 +321,7 @@ export function analyze_component(root, source, options) {
} else if (declaration !== null && Runes.includes(/** @type {any} */ (name))) {
for (const { node, path } of references) {
if (path.at(-1)?.type === 'CallExpression') {
warn(warnings, node, 'store-with-rune-name', store_name);
warn(node, 'store-with-rune-name', store_name);
}
}
}
@ -391,7 +381,6 @@ export function analyze_component(root, source, options) {
reactive_statements: new Map(),
binding_groups: new Map(),
slot_names: new Map(),
warnings,
css: {
ast: root.css,
hash: root.css
@ -408,7 +397,7 @@ export function analyze_component(root, source, options) {
};
if (!options.customElement && root.options?.customElement) {
warn(analysis.warnings, root.options, 'missing-custom-element-compile-option');
warn(root.options, 'missing-custom-element-compile-option');
}
if (analysis.runes) {
@ -498,7 +487,7 @@ export function analyze_component(root, source, options) {
(r) => r.node !== binding.node && r.path.at(-1)?.type !== 'ExportSpecifier'
);
if (!references.length && !instance.scope.declarations.has(`$${name}`)) {
warn(warnings, binding.node, 'unused-export-let', name);
warn(binding.node, 'unused-export-let', name);
}
}
}
@ -538,7 +527,7 @@ export function analyze_component(root, source, options) {
type === 'AwaitBlock' ||
type === 'KeyBlock'
) {
warn(warnings, binding.node, 'non-state-reference', name);
warn(binding.node, 'non-state-reference', name);
continue outer;
}
}
@ -546,7 +535,7 @@ export function analyze_component(root, source, options) {
}
}
warn(warnings, binding.node, 'non-state-reference', name);
warn(binding.node, 'non-state-reference', name);
continue outer;
}
}
@ -565,7 +554,7 @@ export function analyze_component(root, source, options) {
!analysis.css.ast.content.comment ||
!extract_svelte_ignore(analysis.css.ast.content.comment.data).includes('css-unused-selector')
) {
warn_unused(analysis.css.ast, analysis.warnings);
warn_unused(analysis.css.ast);
}
outer: for (const element of analysis.elements) {
@ -688,7 +677,7 @@ const legacy_scope_tweaker = {
(d) => d.scope === state.analysis.module.scope && d.declaration_kind !== 'const'
)
) {
warn(state.analysis.warnings, node, 'module-script-reactive-declaration');
warn(node, 'module-script-reactive-declaration');
}
if (
@ -874,7 +863,7 @@ const legacy_scope_tweaker = {
}
};
/** @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, { scope: Scope, analysis: { warnings: import('../types').RawWarning[] } }>} */
/** @type {import('zimmerframe').Visitors<import('#compiler').SvelteNode, { scope: Scope }>} */
const runes_scope_js_tweaker = {
VariableDeclarator(node, { state }) {
if (node.init?.type !== 'CallExpression') return;
@ -1210,7 +1199,7 @@ const common_visitors = {
binding.kind === 'derived') &&
context.state.function_depth === binding.scope.function_depth
) {
warn(context.state.analysis.warnings, node, 'static-state-reference');
warn(node, 'static-state-reference');
}
}
},

@ -131,12 +131,7 @@ function validate_element(node, context) {
value.name === attribute.name &&
!context.state.scope.get(value.name)
) {
warn(
context.state.analysis.warnings,
attribute,
'global-event-reference',
attribute.name
);
warn(attribute, 'global-event-reference', attribute.name);
}
}
@ -146,18 +141,12 @@ function validate_element(node, context) {
}
if (attribute.name === 'is' && context.state.options.namespace !== 'foreign') {
warn(context.state.analysis.warnings, attribute, 'avoid-is');
warn(attribute, 'avoid-is');
}
const correct_name = react_attributes.get(attribute.name);
if (correct_name) {
warn(
context.state.analysis.warnings,
attribute,
'invalid-html-attribute',
attribute.name,
correct_name
);
warn(attribute, 'invalid-html-attribute', attribute.name, correct_name);
}
validate_attribute_name(attribute, context);
@ -233,7 +222,7 @@ function validate_attribute_name(attribute, context) {
!attribute.name.startsWith('xlink:') &&
!attribute.name.startsWith('xml:')
) {
warn(context.state.analysis.warnings, attribute, 'illegal-attribute-character');
warn(attribute, 'illegal-attribute-character');
}
}
@ -311,7 +300,7 @@ function validate_block_not_empty(node, context) {
// Assumption: If the block has zero elements, someone's in the middle of typing it out,
// so don't warn in that case because it would be distracting.
if (node.nodes.length === 1 && node.nodes[0].type === 'Text' && !node.nodes[0].raw.trim()) {
warn(context.state.analysis.warnings, node.nodes[0], 'empty-block');
warn(node.nodes[0], 'empty-block');
}
}
@ -372,12 +361,7 @@ const validation = {
}
if (binding?.kind === 'each' && binding.metadata?.inside_rest) {
warn(
context.state.analysis.warnings,
binding.node,
'invalid-rest-eachblock-binding',
binding.node.name
);
warn(binding.node, 'invalid-rest-eachblock-binding', binding.node.name);
}
const parent = context.path.at(-1);
@ -531,7 +515,7 @@ const validation = {
binding.declaration_kind === 'import' &&
binding.references.length === 0
) {
warn(context.state.analysis.warnings, node, 'component-name-lowercase', node.name);
warn(node, 'component-name-lowercase', node.name);
}
validate_element(node, context);
@ -570,7 +554,7 @@ const validation = {
!VoidElements.includes(node.name) &&
!SVGElements.includes(node.name)
) {
warn(context.state.analysis.warnings, node, 'invalid-self-closing-tag', node.name);
warn(node, 'invalid-self-closing-tag', node.name);
}
context.next({
@ -766,7 +750,7 @@ export const validation_legacy = merge(validation, a11y_validators, {
(state.ast_type !== 'instance' ||
/** @type {import('#compiler').SvelteNode} */ (path.at(-1)).type !== 'Program')
) {
warn(state.analysis.warnings, node, 'no-reactive-declaration');
warn(node, 'no-reactive-declaration');
}
},
UpdateExpression(node, { state }) {
@ -969,12 +953,12 @@ export const validation_runes_js = {
const allowed_depth = context.state.ast_type === 'module' ? 0 : 1;
if (context.state.scope.function_depth > allowed_depth) {
warn(context.state.analysis.warnings, node, 'avoid-nested-class');
warn(node, 'avoid-nested-class');
}
},
NewExpression(node, context) {
if (node.callee.type === 'ClassExpression' && context.state.scope.function_depth > 0) {
warn(context.state.analysis.warnings, node, 'avoid-inline-class');
warn(node, 'avoid-inline-class');
}
}
};
@ -1127,7 +1111,7 @@ export const validation_runes = merge(validation, a11y_validators, {
if (rune === null) {
if (init?.type === 'Identifier' && init.name === '$props' && !state.scope.get('props')) {
warn(state.analysis.warnings, node, 'invalid-props-declaration');
warn(node, 'invalid-props-declaration');
}
return;
}
@ -1180,7 +1164,7 @@ export const validation_runes = merge(validation, a11y_validators, {
arg.type === 'CallExpression' &&
(arg.callee.type === 'ArrowFunctionExpression' || arg.callee.type === 'FunctionExpression')
) {
warn(state.analysis.warnings, node, 'derived-iife');
warn(node, 'derived-iife');
}
}
},
@ -1190,19 +1174,19 @@ export const validation_runes = merge(validation, a11y_validators, {
node.right.name === '$bindable' &&
!state.scope.get('bindable')
) {
warn(state.analysis.warnings, node, 'invalid-bindable-declaration');
warn(node, 'invalid-bindable-declaration');
}
},
SlotElement(node, { state }) {
if (!state.analysis.custom_element) {
warn(state.analysis.warnings, node, 'deprecated-slot-element');
warn(node, 'deprecated-slot-element');
}
},
OnDirective(node, { state, path }) {
OnDirective(node, { path }) {
const parent_type = path.at(-1)?.type;
// Don't warn on component events; these might not be under the author's control so the warning would be unactionable
if (parent_type === 'RegularElement' || parent_type === 'SvelteElement') {
warn(state.analysis.warnings, node, 'deprecated-event-handler', node.name);
warn(node, 'deprecated-event-handler', node.name);
}
},
// TODO this is a code smell. need to refactor this stuff

@ -17,7 +17,7 @@ export function transform_component(analysis, source, options) {
return {
js: /** @type {any} */ (null),
css: null,
warnings: transform_warnings(source, options.filename, analysis.warnings),
warnings: /** @type {any} */ (null), // set afterwards
metadata: {
runes: analysis.runes
},
@ -60,7 +60,7 @@ export function transform_component(analysis, source, options) {
return {
js,
css,
warnings: transform_warnings(source, options.filename, analysis.warnings), // TODO apply preprocessor sourcemap
warnings: /** @type {any} */ (null), // set afterwards. TODO apply preprocessor sourcemap
metadata: {
runes: analysis.runes
},
@ -79,7 +79,7 @@ export function transform_module(analysis, source, options) {
return {
js: /** @type {any} */ (null),
css: null,
warnings: transform_warnings(source, analysis.name, analysis.warnings),
warnings: /** @type {any} */ (null), // set afterwards
metadata: {
runes: true
},
@ -105,45 +105,10 @@ export function transform_module(analysis, source, options) {
return {
js: print(program, {}),
css: null,
warnings: transform_warnings(source, analysis.name, analysis.warnings),
metadata: {
runes: true
},
warnings: /** @type {any} */ (null), // set afterwards
ast: /** @type {any} */ (null) // set afterwards
};
}
/**
* @param {string} source
* @param {string | undefined} name
* @param {import('../types').RawWarning[]} warnings
* @returns {import('#compiler').Warning[]}
*/
function transform_warnings(source, name, warnings) {
if (warnings.length === 0) return [];
const locate = getLocator(source, { offsetLine: 1 });
/** @type {import('#compiler').Warning[]} */
const result = [];
for (const warning of warnings) {
const start =
warning.position &&
/** @type {import('locate-character').Location} */ (locate(warning.position[0]));
const end =
warning.position &&
/** @type {import('locate-character').Location} */ (locate(warning.position[1]));
result.push({
start,
end,
filename: name,
message: warning.message,
code: warning.code
});
}
return result;
}

@ -6,7 +6,8 @@ import type {
SlotElement,
SvelteElement,
SvelteNode,
SvelteOptions
SvelteOptions,
Warning
} from '#compiler';
import type { Identifier, LabeledStatement, Program } from 'estree';
import type { Scope, ScopeRoot } from './scope.js';
@ -28,19 +29,12 @@ export interface ReactiveStatement {
dependencies: Binding[];
}
export interface RawWarning {
code: string;
message: string;
position: [number, number] | undefined;
}
/**
* Analysis common to modules and components
*/
export interface Analysis {
module: Js;
name: string; // TODO should this be filename? it's used in `compileModule` as well as `compile`
warnings: RawWarning[];
runes: boolean;
immutable: boolean;

@ -1,3 +1,5 @@
import { warnings as array, filename, locator } from './warnings.js';
/** @typedef {Record<string, (...args: any[]) => string>} Warnings */
/** @satisfies {Warnings} */
@ -270,25 +272,23 @@ const warnings = {
/**
* @template {keyof AllWarnings} T
* @param {import('./phases/types').RawWarning[]} array the array to push the warning to, if not ignored
* @param {{ start?: number, end?: number, type?: string, parent?: import('#compiler').SvelteNode | null, leadingComments?: import('estree').Comment[] } | null} node the node related to the warning
* @param {T} code the warning code
* @param {Parameters<AllWarnings[T]>} args the arguments to pass to the warning function
* @returns {void}
*/
export function warn(array, node, code, ...args) {
export function warn(node, code, ...args) {
// @ts-expect-error
if (node.ignores?.has(code)) return;
const fn = warnings[code];
const start = node?.start;
const end = node?.end;
array.push({
code,
// @ts-expect-error
message: fn(...args),
position: start !== undefined && end !== undefined ? [start, end] : undefined
filename,
start: node?.start !== undefined ? locator(node.start) : undefined,
end: node?.end !== undefined ? locator(node.end) : undefined
});
}

Loading…
Cancel
Save