diff --git a/.eslintrc.json b/.eslintrc.json index 0673b0a943..df79b01a09 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,12 +2,12 @@ "root": true, "rules": { "indent": "off", + "no-unused-vars": "off", "semi": [2, "always"], "keyword-spacing": [2, { "before": true, "after": true }], "space-before-blocks": [2, "always"], "no-mixed-spaces-and-tabs": [2, "smart-tabs"], "no-cond-assign": 0, - "no-unused-vars": 2, "object-shorthand": [2, "always"], "no-const-assign": 2, "no-class-assign": 2, @@ -22,10 +22,17 @@ "arrow-spacing": 2, "no-inner-declarations": 0, "@typescript-eslint/indent": [2, "tab", { "SwitchCase": 1 }], - "@typescript-eslint/explicit-function-return-type": ["error", { - "allowExpressions": true + "@typescript-eslint/camelcase": "off", + "@typescript-eslint/array-type": ["error", "array-simple"], + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/no-unused-vars": ["error", { + "argsIgnorePattern": "^_" }], - "@typescript-eslint/camelcase": "off" + "@typescript-eslint/no-object-literal-type-assertion": ["error", { + "allowAsParameter": true + }] }, "env": { "es6": true, @@ -45,6 +52,20 @@ "sourceType": "module" }, "settings": { - "import/core-modules": ["svelte"] - } + "import/core-modules": [ + "svelte", + "svelte/internal", + "svelte/store", + "svelte/easing", + "estree" + ] + }, + "overrides": [ + { + "files": ["*.js"], + "rules": { + "@typescript-eslint/no-var-requires": "off" + } + } + ] } diff --git a/src/compiler/Stats.ts b/src/compiler/Stats.ts index c54f1e8ea9..200fa448e9 100644 --- a/src/compiler/Stats.ts +++ b/src/compiler/Stats.ts @@ -5,7 +5,7 @@ const now = (typeof process !== 'undefined' && process.hrtime) } : () => self.performance.now(); -type Timing = { +interface Timing { label: string; start: number; end: number; diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index e5f3501716..29b51297d3 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -24,13 +24,13 @@ import unwrap_parens from './utils/unwrap_parens'; import Slot from './nodes/Slot'; import { Node as ESTreeNode } from 'estree'; -type ComponentOptions = { +interface ComponentOptions { namespace?: string; tag?: string; immutable?: boolean; accessors?: boolean; preserveWhitespace?: boolean; -}; +} // We need to tell estree-walker that it should always // look for an `else` block, otherwise it might get @@ -67,6 +67,120 @@ function remove_node(code: MagicString, start: number, end: number, body: Node, return; } +function process_component_options(component: Component, nodes) { + const component_options: ComponentOptions = { + immutable: component.compile_options.immutable || false, + accessors: 'accessors' in component.compile_options + ? component.compile_options.accessors + : !!component.compile_options.customElement, + preserveWhitespace: !!component.compile_options.preserveWhitespace + }; + + const node = nodes.find(node => node.name === 'svelte:options'); + + function get_value(attribute, code, message) { + const { value } = attribute; + const chunk = value[0]; + + if (!chunk) return true; + + if (value.length > 1) { + component.error(attribute, { code, message }); + } + + if (chunk.type === 'Text') return chunk.data; + + if (chunk.expression.type !== 'Literal') { + component.error(attribute, { code, message }); + } + + return chunk.expression.value; + } + + if (node) { + node.attributes.forEach(attribute => { + if (attribute.type === 'Attribute') { + const { name } = attribute; + + switch (name) { + case 'tag': { + const code = 'invalid-tag-attribute'; + const message = `'tag' must be a string literal`; + const tag = get_value(attribute, code, message); + + if (typeof tag !== 'string' && tag !== null) component.error(attribute, { code, message }); + + if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) { + component.error(attribute, { + code: `invalid-tag-property`, + message: `tag name must be two or more words joined by the '-' character` + }); + } + + component_options.tag = tag; + break; + } + + case 'namespace': { + const code = 'invalid-namespace-attribute'; + const message = `The 'namespace' attribute must be a string literal representing a valid namespace`; + const ns = get_value(attribute, code, message); + + if (typeof ns !== 'string') component.error(attribute, { code, message }); + + if (valid_namespaces.indexOf(ns) === -1) { + const match = fuzzymatch(ns, valid_namespaces); + if (match) { + component.error(attribute, { + code: `invalid-namespace-property`, + message: `Invalid namespace '${ns}' (did you mean '${match}'?)` + }); + } else { + component.error(attribute, { + code: `invalid-namespace-property`, + message: `Invalid namespace '${ns}'` + }); + } + } + + component_options.namespace = ns; + break; + } + + case 'accessors': + case 'immutable': + case 'preserveWhitespace': + { + const code = `invalid-${name}-value`; + const message = `${name} attribute must be true or false`; + const value = get_value(attribute, code, message); + + if (typeof value !== 'boolean') component.error(attribute, { code, message }); + + component_options[name] = value; + break; + } + + default: + component.error(attribute, { + code: `invalid-options-attribute`, + message: ` unknown attribute` + }); + } + } + + else { + component.error(attribute, { + code: `invalid-options-attribute`, + message: ` can only have static 'tag', 'namespace', 'accessors', 'immutable' and 'preserveWhitespace' attributes` + }); + } + }); + } + + return component_options; +} + export default class Component { stats: Stats; warnings: Warning[]; @@ -97,7 +211,7 @@ export default class Component { node_for_declaration: Map = new Map(); partly_hoisted: string[] = []; fully_hoisted: string[] = []; - reactive_declarations: Array<{ assignees: Set, dependencies: Set, node: Node, declaration: Node }> = []; + reactive_declarations: Array<{ assignees: Set; dependencies: Set; node: Node; declaration: Node }> = []; reactive_declaration_nodes: Set = new Set(); has_reactive_assignments = false; injected_reactive_declaration_vars: Set = new Set(); @@ -106,12 +220,12 @@ export default class Component { indirect_dependencies: Map> = new Map(); file: string; - locate: (c: number) => { line: number, column: number }; + locate: (c: number) => { line: number; column: number }; // TODO this does the same as component.locate! remove one or the other locator: (search: number, startIndex?: number) => { - line: number, - column: number + line: number; + column: number; }; stylesheet: Stylesheet; @@ -140,6 +254,7 @@ export default class Component { this.compile_options = compile_options; this.file = compile_options.filename && ( + // eslint-disable-next-line no-useless-escape typeof process !== 'undefined' ? compile_options.filename.replace(process.cwd(), '').replace(/^[\/\\]/, '') : compile_options.filename ); this.locate = getLocator(this.source); @@ -283,7 +398,7 @@ export default class Component { this.source ); - const parts = module.split('✂]'); + const parts = module.split('✂]'); const final_chunk = parts.pop(); const compiled = new Bundle({ separator: '' }); @@ -296,7 +411,7 @@ export default class Component { const { filename } = compile_options; - // special case — the source file doesn't actually get used anywhere. we need + // special case — the source file doesn't actually get used anywhere. we need // to add an empty file to populate map.sources and map.sourcesContent if (!parts.length) { compiled.addSource({ @@ -305,7 +420,7 @@ export default class Component { }); } - const pattern = /\[✂(\d+)-(\d+)$/; + const pattern = /\[✂(\d+)-(\d+)$/; parts.forEach((str: string) => { const chunk = str.replace(pattern, ''); @@ -398,12 +513,12 @@ export default class Component { error( pos: { - start: number, - end: number + start: number; + end: number; }, - e : { - code: string, - message: string + e: { + code: string; + message: string; } ) { error(e.message, { @@ -418,12 +533,12 @@ export default class Component { warn( pos: { - start: number, - end: number + start: number; + end: number; }, warning: { - code: string, - message: string + code: string; + message: string; } ) { if (!this.locator) { @@ -527,9 +642,9 @@ export default class Component { let result = ''; - script.content.body.forEach((node, i) => { + script.content.body.forEach((node) => { if (this.hoistable_nodes.has(node) || this.reactive_declaration_nodes.has(node)) { - if (a !== b) result += `[✂${a}-${b}✂]`; + if (a !== b) result += `[✂${a}-${b}✂]`; a = node.end; } @@ -541,7 +656,7 @@ export default class Component { b = script.content.end; while (/\s/.test(this.source[b - 1])) b -= 1; - if (a < b) result += `[✂${a}-${b}✂]`; + if (a < b) result += `[✂${a}-${b}✂]`; return result || null; } @@ -564,7 +679,7 @@ export default class Component { this.add_sourcemap_locations(script.content); - let { scope, globals } = create_scopes(script.content); + const { scope, globals } = create_scopes(script.content); this.module_scope = scope; scope.declarations.forEach((node, name) => { @@ -588,7 +703,7 @@ export default class Component { this.error(node, { code: 'illegal-subscription', message: `Cannot reference store value inside ', true); - element.end = parser.index; - } else if (name === 'style') { - // special case - const start = parser.index; - const data = parser.read_until(/<\/style>/); - const end = parser.index; - element.children.push({ start, end, type: 'Text', data }); - parser.eat('', true); - } else { - parser.stack.push(element); - } -} - function read_tag_name(parser: Parser) { const start = parser.index; @@ -313,6 +132,90 @@ function read_tag_name(parser: Parser) { return name; } +function get_directive_type(name: string): DirectiveType { + if (name === 'use') return 'Action'; + if (name === 'animate') return 'Animation'; + if (name === 'bind') return 'Binding'; + if (name === 'class') return 'Class'; + if (name === 'on') return 'EventHandler'; + if (name === 'let') return 'Let'; + if (name === 'ref') return 'Ref'; + if (name === 'in' || name === 'out' || name === 'transition') return 'Transition'; +} + +function read_sequence(parser: Parser, done: () => boolean): Node[] { + let current_chunk: Text = { + start: parser.index, + end: null, + type: 'Text', + raw: '', + data: null + }; + + const chunks: Node[] = []; + + function flush() { + if (current_chunk.raw) { + current_chunk.data = decode_character_references(current_chunk.raw); + current_chunk.end = parser.index; + chunks.push(current_chunk); + } + } + + while (parser.index < parser.template.length) { + const index = parser.index; + + if (done()) { + flush(); + return chunks; + } else if (parser.eat('{')) { + flush(); + + parser.allow_whitespace(); + const expression = read_expression(parser); + parser.allow_whitespace(); + parser.eat('}', true); + + chunks.push({ + start: index, + end: parser.index, + type: 'MustacheTag', + expression, + }); + + current_chunk = { + start: parser.index, + end: null, + type: 'Text', + raw: '', + data: null + }; + } else { + current_chunk.raw += parser.template[parser.index++]; + } + } + + parser.error({ + code: `unexpected-eof`, + message: `Unexpected end of input` + }); +} + +function read_attribute_value(parser: Parser) { + const quote_mark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null; + + const regex = ( + quote_mark === `'` ? /'/ : + quote_mark === `"` ? /"/ : + /(\/>|[\s"'=<>`])/ + ); + + const value = read_sequence(parser, () => !!parser.match_regex(regex)); + + if (quote_mark) parser.index += 1; + return value; +} + function read_attribute(parser: Parser, unique_names: Set) { const start = parser.index; @@ -358,7 +261,8 @@ function read_attribute(parser: Parser, unique_names: Set) { } } - let name = parser.read_until(/[\s=\/>"']/); + // eslint-disable-next-line no-useless-escape + const name = parser.read_until(/[\s=\/>"']/); if (!name) return null; let end = parser.index; @@ -396,12 +300,12 @@ function read_attribute(parser: Parser, unique_names: Set) { if (type === 'Ref') { parser.error({ code: `invalid-ref-directive`, - message: `The ref directive is no longer supported — use \`bind:this={${directive_name}}\` instead` + message: `The ref directive is no longer supported — use \`bind:this={${directive_name}}\` instead` }, start); } if (value[0]) { - if ((value as Array).length > 1 || value[0].type === 'Text') { + if ((value as any[]).length > 1 || value[0].type === 'Text') { parser.error({ code: `invalid-directive-value`, message: `Directive value must be a JavaScript expression enclosed in curly braces` @@ -445,86 +349,186 @@ function read_attribute(parser: Parser, unique_names: Set) { }; } -function get_directive_type(name: string):DirectiveType { - if (name === 'use') return 'Action'; - if (name === 'animate') return 'Animation'; - if (name === 'bind') return 'Binding'; - if (name === 'class') return 'Class'; - if (name === 'on') return 'EventHandler'; - if (name === 'let') return 'Let'; - if (name === 'ref') return 'Ref'; - if (name === 'in' || name === 'out' || name === 'transition') return 'Transition'; -} +export default function tag(parser: Parser) { + const start = parser.index++; -function read_attribute_value(parser: Parser) { - const quote_mark = parser.eat(`'`) ? `'` : parser.eat(`"`) ? `"` : null; + let parent = parser.current(); - const regex = ( - quote_mark === `'` ? /'/ : - quote_mark === `"` ? /"/ : - /(\/>|[\s"'=<>`])/ - ); + if (parser.eat('!--')) { + const data = parser.read_until(/-->/); + parser.eat('-->', true, 'comment was left open, expected -->'); - const value = read_sequence(parser, () => !!parser.match_regex(regex)); + parser.current().children.push({ + start, + end: parser.index, + type: 'Comment', + data, + }); - if (quote_mark) parser.index += 1; - return value; -} + return; + } -function read_sequence(parser: Parser, done: () => boolean): Node[] { - let current_chunk: Text = { - start: parser.index, - end: null, - type: 'Text', - raw: '', - data: null - }; + const is_closing_tag = parser.eat('/'); - function flush() { - if (current_chunk.raw) { - current_chunk.data = decode_character_references(current_chunk.raw); - current_chunk.end = parser.index; - chunks.push(current_chunk); + const name = read_tag_name(parser); + + if (meta_tags.has(name)) { + const slug = meta_tags.get(name).toLowerCase(); + if (is_closing_tag) { + if ( + (name === 'svelte:window' || name === 'svelte:body') && + parser.current().children.length + ) { + parser.error({ + code: `invalid-${name.slice(7)}-content`, + message: `<${name}> cannot have children` + }, parser.current().children[0].start); + } + } else { + if (name in parser.meta_tags) { + parser.error({ + code: `duplicate-${slug}`, + message: `A component can only have one <${name}> tag` + }, start); + } + + if (parser.stack.length > 1) { + parser.error({ + code: `invalid-${slug}-placement`, + message: `<${name}> tags cannot be inside elements or blocks` + }, start); + } + + parser.meta_tags[name] = true; } } - const chunks: Node[] = []; + const type = meta_tags.has(name) + ? meta_tags.get(name) + : (/[A-Z]/.test(name[0]) || name === 'svelte:self' || name === 'svelte:component') ? 'InlineComponent' + : name === 'title' && parent_is_head(parser.stack) ? 'Title' + : name === 'slot' && !parser.customElement ? 'Slot' : 'Element'; - while (parser.index < parser.template.length) { - const index = parser.index; + const element: Node = { + start, + end: null, // filled in later + type, + name, + attributes: [], + children: [], + }; - if (done()) { - flush(); - return chunks; - } else if (parser.eat('{')) { - flush(); + parser.allow_whitespace(); - parser.allow_whitespace(); - const expression = read_expression(parser); - parser.allow_whitespace(); - parser.eat('}', true); + if (is_closing_tag) { + if (is_void(name)) { + parser.error({ + code: `invalid-void-content`, + message: `<${name}> is a void element and cannot have children, or a closing tag` + }, start); + } - chunks.push({ - start: index, - end: parser.index, - type: 'MustacheTag', - expression, - }); + parser.eat('>', true); - current_chunk = { - start: parser.index, - end: null, - type: 'Text', - raw: '', - data: null - }; - } else { - current_chunk.raw += parser.template[parser.index++]; + // close any elements that don't have their own closing tags, e.g.

+ while (parent.name !== name) { + if (parent.type !== 'Element') + parser.error({ + code: `invalid-closing-tag`, + message: ` attempted to close an element that was not open` + }, start); + + parent.end = start; + parser.stack.pop(); + + parent = parser.current(); + } + + parent.end = parser.index; + parser.stack.pop(); + + return; + } else if (disallowed_contents.has(parent.name)) { + // can this be a child of the parent element, or does it implicitly + // close it, like `
  • one
  • two`? + if (disallowed_contents.get(parent.name).has(name)) { + parent.end = start; + parser.stack.pop(); } } - parser.error({ - code: `unexpected-eof`, - message: `Unexpected end of input` - }); + const unique_names = new Set(); + + let attribute; + while ((attribute = read_attribute(parser, unique_names))) { + element.attributes.push(attribute); + parser.allow_whitespace(); + } + + if (name === 'svelte:component') { + const index = element.attributes.findIndex(attr => attr.type === 'Attribute' && attr.name === 'this'); + if (!~index) { + parser.error({ + code: `missing-component-definition`, + message: ` must have a 'this' attribute` + }, start); + } + + const definition = element.attributes.splice(index, 1)[0]; + if (definition.value === true || definition.value.length !== 1 || definition.value[0].type === 'Text') { + parser.error({ + code: `invalid-component-definition`, + message: `invalid component definition` + }, definition.start); + } + + element.expression = definition.value[0].expression; + } + + // special cases – top-level ', true); + element.end = parser.index; + } else if (name === 'style') { + // special case + const start = parser.index; + const data = parser.read_until(/<\/style>/); + const end = parser.index; + element.children.push({ start, end, type: 'Text', data }); + parser.eat('', true); + } else { + parser.stack.push(element); + } } diff --git a/src/compiler/parse/state/text.ts b/src/compiler/parse/state/text.ts index 1d9099055e..56be592ca9 100644 --- a/src/compiler/parse/state/text.ts +++ b/src/compiler/parse/state/text.ts @@ -14,7 +14,7 @@ export default function text(parser: Parser) { data += parser.template[parser.index++]; } - let node = { + const node = { start, end: parser.index, type: 'Text', diff --git a/src/compiler/parse/utils/html.ts b/src/compiler/parse/utils/html.ts index b49989eacd..e7285329e8 100644 --- a/src/compiler/parse/utils/html.ts +++ b/src/compiler/parse/utils/html.ts @@ -1,5 +1,6 @@ import entities from './entities'; +const NUL = 0; const windows_1252 = [ 8364, 129, @@ -40,29 +41,6 @@ const entity_pattern = new RegExp( 'g' ); -export function decode_character_references(html: string) { - return html.replace(entity_pattern, (match, entity) => { - let code; - - // Handle named entities - if (entity[0] !== '#') { - code = entities[entity]; - } else if (entity[1] === 'x') { - code = parseInt(entity.substring(2), 16); - } else { - code = parseInt(entity.substring(1), 10); - } - - if (!code) { - return match; - } - - return String.fromCodePoint(validate_code(code)); - }); -} - -const NUL = 0; - // some code points are verboten. If we were inserting HTML, the browser would replace the illegal // code points with alternatives in some cases - since we're bypassing that mechanism, we need // to replace them ourselves @@ -80,7 +58,7 @@ function validate_code(code: number) { } // code points 128-159 are dealt with leniently by browsers, but they're incorrect. We need - // to correct the mistake or we'll end up with missing € signs and so on + // to correct the mistake or we'll end up with missing € signs and so on if (code <= 159) { return windows_1252[code - 128]; } @@ -112,3 +90,24 @@ function validate_code(code: number) { return NUL; } + +export function decode_character_references(html: string) { + return html.replace(entity_pattern, (match, entity) => { + let code; + + // Handle named entities + if (entity[0] !== '#') { + code = entities[entity]; + } else if (entity[1] === 'x') { + code = parseInt(entity.substring(2), 16); + } else { + code = parseInt(entity.substring(1), 10); + } + + if (!code) { + return match; + } + + return String.fromCodePoint(validate_code(code)); + }); +} diff --git a/src/compiler/preprocess/index.ts b/src/compiler/preprocess/index.ts index a9dad9f05b..8543a56d18 100644 --- a/src/compiler/preprocess/index.ts +++ b/src/compiler/preprocess/index.ts @@ -2,18 +2,18 @@ import { SourceMap } from 'magic-string'; export interface PreprocessorGroup { markup?: (options: { - content: string, - filename: string - }) => { code: string, map?: SourceMap | string, dependencies?: string[] }; + content: string; + filename: string; + }) => { code: string; map?: SourceMap | string; dependencies?: string[] }; style?: Preprocessor; script?: Preprocessor; } export type Preprocessor = (options: { - content: string, - attributes: Record, - filename?: string -}) => { code: string, map?: SourceMap | string, dependencies?: string[] }; + content: string; + attributes: Record; + filename?: string; +}) => { code: string; map?: SourceMap | string; dependencies?: string[] }; interface Processed { code: string; @@ -43,16 +43,16 @@ interface Replacement { } async function replace_async(str: string, re: RegExp, func: (...any) => Promise) { - const replacements: Promise[] = []; + const replacements: Array> = []; str.replace(re, (...args) => { replacements.push( func(...args).then( - res => - ({ + res => // eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion + ({ offset: args[args.length - 2], length: args[0].length, replacement: res, - }) + }) as Replacement ) ); return ''; diff --git a/src/compiler/utils/error.ts b/src/compiler/utils/error.ts index a34d3fe112..d13222578a 100644 --- a/src/compiler/utils/error.ts +++ b/src/compiler/utils/error.ts @@ -3,8 +3,8 @@ import get_code_frame from './get_code_frame'; class CompileError extends Error { code: string; - start: { line: number, column: number }; - end: { line: number, column: number }; + start: { line: number; column: number }; + end: { line: number; column: number }; pos: number; filename: string; frame: string; @@ -15,12 +15,12 @@ class CompileError extends Error { } export default function error(message: string, props: { - name: string, - code: string, - source: string, - filename: string, - start: number, - end?: number + name: string; + code: string; + source: string; + filename: string; + start: number; + end?: number; }) { const error = new CompileError(message); error.name = props.name; diff --git a/src/compiler/utils/full_char_code_at.ts b/src/compiler/utils/full_char_code_at.ts index e0c15588c7..b5187693eb 100644 --- a/src/compiler/utils/full_char_code_at.ts +++ b/src/compiler/utils/full_char_code_at.ts @@ -2,9 +2,9 @@ // Reproduced under MIT License https://github.com/acornjs/acorn/blob/master/LICENSE export default function full_char_code_at(str: string, i: number): number { - let code = str.charCodeAt(i) + const code = str.charCodeAt(i); if (code <= 0xd7ff || code >= 0xe000) return code; - let next = str.charCodeAt(i + 1); + const next = str.charCodeAt(i + 1); return (code << 10) + next - 0x35fdc00; } \ No newline at end of file diff --git a/src/compiler/utils/fuzzymatch.ts b/src/compiler/utils/fuzzymatch.ts index 89d98fe1c1..6988785b11 100644 --- a/src/compiler/utils/fuzzymatch.ts +++ b/src/compiler/utils/fuzzymatch.ts @@ -1,32 +1,9 @@ -export default function fuzzymatch(name: string, names: string[]) { - const set = new FuzzySet(names); - const matches = set.get(name); - - return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null; -} - // adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js // BSD Licensed const GRAM_SIZE_LOWER = 2; const GRAM_SIZE_UPPER = 3; -// return an edit distance from 0 to 1 -function _distance(str1: string, str2: string) { - if (str1 === null && str2 === null) - throw 'Trying to compare two null values'; - if (str1 === null || str2 === null) return 0; - str1 = String(str1); - str2 = String(str2); - - const distance = levenshtein(str1, str2); - if (str1.length > str2.length) { - return 1 - distance / str1.length; - } else { - return 1 - distance / str2.length; - } -} - // helper functions function levenshtein(str1: string, str2: string) { const current: number[] = []; @@ -53,6 +30,22 @@ function levenshtein(str1: string, str2: string) { return current.pop(); } +// return an edit distance from 0 to 1 +function _distance(str1: string, str2: string) { + if (str1 === null && str2 === null) + throw 'Trying to compare two null values'; + if (str1 === null || str2 === null) return 0; + str1 = String(str1); + str2 = String(str2); + + const distance = levenshtein(str1, str2); + if (str1.length > str2.length) { + return 1 - distance / str1.length; + } else { + return 1 - distance / str2.length; + } +} + const non_word_regex = /[^\w, ]+/; function iterate_grams(value: string, gram_size = 2) { @@ -144,7 +137,7 @@ class FuzzySet { items[index] = [vector_normal, normalized_value]; this.items[gram_size] = items; this.exact_set[normalized_value] = value; - }; + } get(value: string) { const normalized_value = value.toLowerCase(); @@ -232,5 +225,13 @@ class FuzzySet { } return new_results; - }; -} \ No newline at end of file + } +} + +export default function fuzzymatch(name: string, names: string[]) { + const set = new FuzzySet(names); + const matches = set.get(name); + + return matches && matches[0] && matches[0][0] > 0.7 ? matches[0][1] : null; +} + diff --git a/src/runtime/animate/index.ts b/src/runtime/animate/index.ts index f5500020cc..0518417ab8 100644 --- a/src/runtime/animate/index.ts +++ b/src/runtime/animate/index.ts @@ -3,20 +3,20 @@ import { is_function } from 'svelte/internal'; // todo: same as Transition, should it be shared? export interface AnimationConfig { - delay?: number, - duration?: number, - easing?: (t: number) => number, - css?: (t: number, u: number) => string, - tick?: (t: number, u: number) => void + delay?: number; + duration?: number; + easing?: (t: number) => number; + css?: (t: number, u: number) => string; + tick?: (t: number, u: number) => void; } interface FlipParams { delay: number; duration: number | ((len: number) => number); - easing: (t: number) => number, + easing: (t: number) => number; } -export function flip(node: Element, animation: { from: DOMRect, to: DOMRect }, params: FlipParams): AnimationConfig { +export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams): AnimationConfig { const style = getComputedStyle(node); const transform = style.transform === 'none' ? '' : style.transform; diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 714d15df34..eb2b6208cf 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -3,6 +3,7 @@ import { current_component, set_current_component } from './lifecycle'; import { blank_object, is_function, run, run_all, noop } from './utils'; import { children } from './dom'; +// eslint-disable-next-line @typescript-eslint/class-name-casing interface T$$ { dirty: null; ctx: null|any; @@ -16,7 +17,7 @@ interface T$$ { before_render: any[]; context: Map; on_mount: any[]; - on_destroy: any[] + on_destroy: any[]; } export function bind(component, name, callback) { @@ -115,8 +116,10 @@ export function init(component, options, instance, create_fragment, not_equal, p if (options.target) { if (options.hydrate) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion $$.fragment!.l(children(options.target)); } else { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion $$.fragment!.c(); } diff --git a/src/runtime/internal/animations.ts b/src/runtime/internal/animations.ts index 5a055ba6d2..0314833b9b 100644 --- a/src/runtime/internal/animations.ts +++ b/src/runtime/internal/animations.ts @@ -7,7 +7,7 @@ import { AnimationConfig } from '../animate'; //todo: documentation says it is DOMRect, but in IE it would be ClientRect type PositionRect = DOMRect|ClientRect; -type AnimationFn = (node: Element, { from, to }: { from: PositionRect, to: PositionRect }, params: any) => AnimationConfig; +type AnimationFn = (node: Element, { from, to }: { from: PositionRect; to: PositionRect }, params: any) => AnimationConfig; export function create_animation(node: Element & ElementCSSInlineStyle, from: PositionRect, fn: AnimationFn, params) { if (!from) return noop; diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index edf394e4da..2cd7090b23 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -1,8 +1,8 @@ -export function append(target:Node, node:Node) { +export function append(target: Node, node: Node) { target.appendChild(node); } -export function insert(target: Node, node: Node, anchor?:Node) { +export function insert(target: Node, node: Node, anchor?: Node) { target.insertBefore(node, anchor || null); } @@ -16,13 +16,13 @@ export function detach_between(before: Node, after: Node) { } } -export function detach_before(after:Node) { +export function detach_before(after: Node) { while (after.previousSibling) { after.parentNode.removeChild(after.previousSibling); } } -export function detach_after(before:Node) { +export function detach_after(before: Node) { while (before.nextSibling) { before.parentNode.removeChild(before.nextSibling); } @@ -38,8 +38,8 @@ export function element(name: K) { return document.createElement(name); } -export function object_without_properties(obj:T, exclude: K[]) { - const target = {} as Pick>; +export function object_without_properties(obj: T, exclude: K[]) { + const target: Pick> = {}; for (const k in obj) { if ( Object.prototype.hasOwnProperty.call(obj, k) @@ -53,11 +53,11 @@ export function object_without_properties(obj:T, exclude: K return target; } -export function svg_element(name:K):SVGElement { +export function svg_element(name: K): SVGElement { return document.createElementNS('http://www.w3.org/2000/svg', name); } -export function text(data:string) { +export function text(data: string) { return document.createTextNode(data); } @@ -95,7 +95,7 @@ export function attr(node: Element, attribute: string, value?: string) { else node.setAttribute(attribute, value); } -export function set_attributes(node: Element & ElementCSSInlineStyle, attributes: { [x: string]: string; }) { +export function set_attributes(node: Element & ElementCSSInlineStyle, attributes: { [x: string]: string }) { for (const key in attributes) { if (key === 'style') { node.style.cssText = attributes[key]; diff --git a/src/runtime/internal/loop.ts b/src/runtime/internal/loop.ts index c41d72ed74..cc6161105d 100644 --- a/src/runtime/internal/loop.ts +++ b/src/runtime/internal/loop.ts @@ -23,7 +23,7 @@ export function clear_loops() { running = false; } -export function loop(fn: (number)=>void): Task { +export function loop(fn: (number) => void): Task { let task; if (!running) { diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index a26a4f8c33..aefbaf57ae 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -10,28 +10,19 @@ const binding_callbacks = []; const render_callbacks = []; const flush_callbacks = []; -export function schedule_update() { - if (!update_scheduled) { - update_scheduled = true; - resolved_promise.then(flush); - } -} - -export function tick() { - schedule_update(); - return resolved_promise; -} - -export function add_binding_callback(fn) { - binding_callbacks.push(fn); -} - export function add_render_callback(fn) { render_callbacks.push(fn); } -export function add_flush_callback(fn) { - flush_callbacks.push(fn); +function update($$) { + if ($$.fragment) { + $$.update($$.dirty); + run_all($$.before_render); + $$.fragment.p($$.dirty, $$.ctx); + $$.dirty = null; + + $$.after_render.forEach(add_render_callback); + } } export function flush() { @@ -69,13 +60,22 @@ export function flush() { update_scheduled = false; } -function update($$) { - if ($$.fragment) { - $$.update($$.dirty); - run_all($$.before_render); - $$.fragment.p($$.dirty, $$.ctx); - $$.dirty = null; - - $$.after_render.forEach(add_render_callback); +export function schedule_update() { + if (!update_scheduled) { + update_scheduled = true; + resolved_promise.then(flush); } } + +export function tick() { + schedule_update(); + return resolved_promise; +} + +export function add_binding_callback(fn) { + binding_callbacks.push(fn); +} + +export function add_flush_callback(fn) { + flush_callbacks.push(fn); +} diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index 2721200627..c6e1c17d8b 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -44,6 +44,15 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: return name; } +export function clear_rules() { + raf(() => { + if (active) return; + let i = stylesheet.cssRules.length; + while (i--) stylesheet.deleteRule(i); + current_rules = {}; + }); +} + export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) { node.style.animation = (node.style.animation || '') .split(', ') @@ -55,12 +64,3 @@ export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string if (name && !--active) clear_rules(); } - -export function clear_rules() { - raf(() => { - if (active) return; - let i = stylesheet.cssRules.length; - while (i--) stylesheet.deleteRule(i); - current_rules = {}; - }); -} diff --git a/src/runtime/internal/transitions.ts b/src/runtime/internal/transitions.ts index 4c6191964c..5591ca1d51 100644 --- a/src/runtime/internal/transitions.ts +++ b/src/runtime/internal/transitions.ts @@ -71,14 +71,14 @@ export function create_in_transition(node: Element & ElementCSSInlineStyle, fn: if (task) task.abort(); running = true; - add_render_callback(() => dispatch(node, true, 'start')); + add_render_callback(() => dispatch(node, true, 'start')); task = loop(now => { if (running) { if (now >= end_time) { tick(1, 0); - dispatch(node, true, 'end'); + dispatch(node, true, 'end'); cleanup(); return running = false; @@ -146,14 +146,14 @@ export function create_out_transition(node: Element & ElementCSSInlineStyle, fn: const start_time = now() + delay; const end_time = start_time + duration; - add_render_callback(() => dispatch(node, false, 'start')); + add_render_callback(() => dispatch(node, false, 'start')); loop(now => { if (running) { if (now >= end_time) { tick(0, 1); - dispatch(node, false, 'end'); + dispatch(node, false, 'end'); if (!--group.remaining) { // this will result in `end()` being called, diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index 7997a25b1d..aa614ce644 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -2,7 +2,7 @@ export function noop() {} export const identity = x => x; -export function assign(tar:T, src:S): T & S { +export function assign(tar: T, src: S): T & S { // @ts-ignore for (const k in src) tar[k] = src[k]; return tar as T & S; @@ -56,6 +56,12 @@ export function subscribe(component, store, callback) { : unsub); } +export function get_slot_context(definition, ctx, fn) { + return definition[1] + ? assign({}, assign(ctx.$$scope.ctx, definition[1](fn ? fn(ctx) : {}))) + : ctx.$$scope.ctx; +} + export function create_slot(definition, ctx, fn) { if (definition) { const slot_ctx = get_slot_context(definition, ctx, fn); @@ -63,12 +69,6 @@ export function create_slot(definition, ctx, fn) { } } -export function get_slot_context(definition, ctx, fn) { - return definition[1] - ? assign({}, assign(ctx.$$scope.ctx, definition[1](fn ? fn(ctx) : {}))) - : ctx.$$scope.ctx; -} - export function get_slot_changes(definition, ctx, changed, fn) { return definition[1] ? assign({}, assign(ctx.$$scope.changed || {}, definition[1](fn ? fn(changed) : {}))) diff --git a/src/runtime/motion/spring.ts b/src/runtime/motion/spring.ts index f99e8db41f..7742dd4106 100644 --- a/src/runtime/motion/spring.ts +++ b/src/runtime/motion/spring.ts @@ -6,10 +6,10 @@ interface TickContext { inv_mass: number; dt: number; opts: Spring; - settled: boolean + settled: boolean; } -function tick_spring(ctx: TickContext, last_value: T, current_value: T, target_value: T):T { +function tick_spring(ctx: TickContext, last_value: T, current_value: T, target_value: T): T { if (typeof current_value === 'number' || is_date(current_value)) { // @ts-ignore const delta = target_value - current_value; @@ -45,9 +45,9 @@ function tick_spring(ctx: TickContext, last_value: T, current_value: T, ta } interface SpringOpts { - stiffness?: number, - damping?: number, - precision?: number, + stiffness?: number; + damping?: number; + precision?: number; } interface SpringUpdateOpts { @@ -62,7 +62,7 @@ interface Spring extends Readable{ update: (fn: Updater, opts?: SpringUpdateOpts) => Promise; precision: number; damping: number; - stiffness: number + stiffness: number; } export function spring(value: T, opts: SpringOpts = {}): Spring { @@ -72,13 +72,14 @@ export function spring(value: T, opts: SpringOpts = {}): Spring { let last_time: number; let task: Task; let current_token: object; - let last_value:T = value; - let target_value:T = value; + let last_value: T = value; + let target_value: T = value; let inv_mass = 1; let inv_mass_recovery_rate = 0; let cancel_task = false; + /* eslint-disable @typescript-eslint/no-use-before-define */ function set(new_value: T, opts: SpringUpdateOpts={}): Promise { target_value = new_value; const token = current_token = {}; @@ -133,15 +134,16 @@ export function spring(value: T, opts: SpringOpts = {}): Spring { }); }); } + /* eslint-enable @typescript-eslint/no-use-before-define */ - const spring = { + const spring: Spring = { set, - update: (fn, opts:SpringUpdateOpts) => set(fn(target_value, value), opts), + update: (fn, opts: SpringUpdateOpts) => set(fn(target_value, value), opts), subscribe: store.subscribe, stiffness, damping, precision - } as Spring; + }; return spring; } diff --git a/src/runtime/motion/tweened.ts b/src/runtime/motion/tweened.ts index ea3f2cdd86..7159c17eb5 100644 --- a/src/runtime/motion/tweened.ts +++ b/src/runtime/motion/tweened.ts @@ -56,9 +56,9 @@ function get_interpolator(a, b) { interface Options { delay?: number; - duration?: number | ((from: T, to: T) => number) + duration?: number | ((from: T, to: T) => number); easing?: (t: number) => number; - interpolate?: (a: T, b: T) => (t: number) => T + interpolate?: (a: T, b: T) => (t: number) => T; } type Updater = (target_value: T, value: T) => T; @@ -69,7 +69,7 @@ interface Tweened extends Readable { update(updater: Updater, opts: Options): Promise; } -export function tweened(value: T, defaults: Options = {}):Tweened { +export function tweened(value: T, defaults: Options = {}): Tweened { const store = writable(value); let task: Task; @@ -122,7 +122,7 @@ export function tweened(value: T, defaults: Options = {}):Tweened { return { set, - update: (fn, opts:Options) => set(fn(target_value, value), opts), + update: (fn, opts: Options) => set(fn(target_value, value), opts), subscribe: store.subscribe }; } diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index e7db228401..21ff6ebe2c 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -43,17 +43,6 @@ export interface Writable extends Readable { /** Pair of subscriber and invalidator. */ type SubscribeInvalidateTuple = [Subscriber, Invalidater]; -/** - * Creates a `Readable` store that allows reading by subscription. - * @param value initial value - * @param {StartStopNotifier}start start and stop notifications for subscriptions - */ -export function readable(value: T, start: StartStopNotifier): Readable { - return { - subscribe: writable(value, start).subscribe, - }; -} - /** * Create a `Writable` store that allows both updating and reading by subscription. * @param {*=}value initial value @@ -100,6 +89,17 @@ export function writable(value: T, start: StartStopNotifier = noop): Writa return { set, update, subscribe }; } +/** + * Creates a `Readable` store that allows reading by subscription. + * @param value initial value + * @param {StartStopNotifier}start start and stop notifications for subscriptions + */ +export function readable(value: T, start: StartStopNotifier): Readable { + return { + subscribe: writable(value, start).subscribe, + }; +} + /** One or more `Readable`s. */ type Stores = Readable | [Readable, ...Array>]; diff --git a/src/runtime/transition/index.ts b/src/runtime/transition/index.ts index b3f4f21288..7fe5153e44 100644 --- a/src/runtime/transition/index.ts +++ b/src/runtime/transition/index.ts @@ -2,11 +2,11 @@ import { cubicOut, cubicInOut } from 'svelte/easing'; import { assign, is_function } from 'svelte/internal'; export interface TransitionConfig { - delay?: number, - duration?: number, - easing?: (t: number) => number, - css?: (t: number, u: number) => string, - tick?: (t: number, u: number) => void + delay?: number; + duration?: number; + easing?: (t: number) => number; + css?: (t: number, u: number) => string; + tick?: (t: number, u: number) => void; } interface FadeParams { @@ -30,7 +30,7 @@ export function fade(node: Element, { interface FlyParams { delay: number; duration: number; - easing: (t: number)=>number, + easing: (t: number) => number; x: number; y: number; opacity: number; @@ -63,7 +63,7 @@ export function fly(node: Element, { interface SlideParams { delay: number; duration: number; - easing: (t: number)=>number, + easing: (t: number) => number; } export function slide(node: Element, { @@ -101,7 +101,7 @@ export function slide(node: Element, { interface ScaleParams { delay: number; duration: number; - easing: (t: number)=>number, + easing: (t: number) => number; start: number; opacity: number; } @@ -135,7 +135,7 @@ interface DrawParams { delay: number; speed: number; duration: number | ((len: number) => number); - easing: (t: number) => number, + easing: (t: number) => number; } export function draw(node: SVGElement & { getTotalLength(): number }, { @@ -167,18 +167,18 @@ export function draw(node: SVGElement & { getTotalLength(): number }, { interface CrossfadeParams { delay: number; duration: number | ((len: number) => number); - easing: (t: number) => number, + easing: (t: number) => number; } type ClientRectMap = Map; export function crossfade({ fallback, ...defaults }: CrossfadeParams & { - fallback: (node: Element, params: CrossfadeParams, intro: boolean)=> TransitionConfig + fallback: (node: Element, params: CrossfadeParams, intro: boolean) => TransitionConfig; }) { const to_receive: ClientRectMap = new Map(); const to_send: ClientRectMap = new Map(); - function crossfade(from: ClientRect, node: Element, params: CrossfadeParams):TransitionConfig { + function crossfade(from: ClientRect, node: Element, params: CrossfadeParams): TransitionConfig { const { delay = 0, duration = d => Math.sqrt(d) * 30, diff --git a/test/.eslintrc.json b/test/.eslintrc.json new file mode 100644 index 0000000000..4a5acc2244 --- /dev/null +++ b/test/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "rules": { + "@typescript-eslint/no-unused-vars": "off", + "no-console": "off" + } +} diff --git a/test/css/index.js b/test/css/index.js index be2a10bef1..4e4a95a63b 100644 --- a/test/css/index.js +++ b/test/css/index.js @@ -36,6 +36,14 @@ function create(code) { return module.exports.default; } +function read(file) { + try { + return fs.readFileSync(file, 'utf-8'); + } catch (err) { + return null; + } +} + describe('css', () => { fs.readdirSync('test/css/samples').forEach(dir => { if (dir[0] === '.') return; @@ -128,11 +136,3 @@ describe('css', () => { }); }); }); - -function read(file) { - try { - return fs.readFileSync(file, 'utf-8'); - } catch (err) { - return null; - } -} \ No newline at end of file diff --git a/test/helpers.js b/test/helpers.js index e07d7c9b06..b0e8c74af3 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -187,6 +187,12 @@ export function showOutput(cwd, options = {}, compile = svelte.compile) { }); } +function getTrailingIndentation(str) { + let i = str.length; + while (str[i - 1] === ' ' || str[i - 1] === '\t') i -= 1; + return str.slice(i, str.length); +} + const start = /\n(\t+)/; export function deindent(strings, ...values) { const indentation = start.exec(strings[0])[1]; @@ -222,12 +228,6 @@ export function deindent(strings, ...values) { return result.trim().replace(/\t+$/gm, ''); } -function getTrailingIndentation(str) { - let i = str.length; - while (str[i - 1] === ' ' || str[i - 1] === '\t') i -= 1; - return str.slice(i, str.length); -} - export function spaces(i) { let result = ''; while (i--) result += ' '; diff --git a/test/runtime/samples/animation-css/_config.js b/test/runtime/samples/animation-css/_config.js index 544c6378ff..201a282e58 100644 --- a/test/runtime/samples/animation-css/_config.js +++ b/test/runtime/samples/animation-css/_config.js @@ -29,9 +29,9 @@ export default { right: 100, top, bottom: top + 20 - } + }; }; - }) + }); component.things = [ { id: 5, name: 'e' }, diff --git a/test/runtime/samples/animation-js-delay/_config.js b/test/runtime/samples/animation-js-delay/_config.js index faed25ecaa..6bd02d17e6 100644 --- a/test/runtime/samples/animation-js-delay/_config.js +++ b/test/runtime/samples/animation-js-delay/_config.js @@ -29,9 +29,9 @@ export default { right: 100, top, bottom: top + 20 - } + }; }; - }) + }); component.things = [ { id: 5, name: 'e' }, diff --git a/test/runtime/samples/animation-js-easing/_config.js b/test/runtime/samples/animation-js-easing/_config.js index a31825c3f9..415af042a9 100644 --- a/test/runtime/samples/animation-js-easing/_config.js +++ b/test/runtime/samples/animation-js-easing/_config.js @@ -29,9 +29,9 @@ export default { right: 100, top, bottom: top + 20 - } + }; }; - }) + }); component.things = [ { id: 5, name: 'e' }, diff --git a/test/runtime/samples/animation-js/_config.js b/test/runtime/samples/animation-js/_config.js index d5991a915a..521753633d 100644 --- a/test/runtime/samples/animation-js/_config.js +++ b/test/runtime/samples/animation-js/_config.js @@ -29,7 +29,7 @@ export default { right: 100, top, bottom: top + 20 - } + }; }; }); diff --git a/test/runtime/samples/await-containing-if/_config.js b/test/runtime/samples/await-containing-if/_config.js index 9e24e4f614..cd83585cc1 100644 --- a/test/runtime/samples/await-containing-if/_config.js +++ b/test/runtime/samples/await-containing-if/_config.js @@ -1,6 +1,6 @@ let fulfil; -let thePromise = new Promise(f => { +const thePromise = new Promise(f => { fulfil = f; }); diff --git a/test/runtime/samples/await-in-each/_config.js b/test/runtime/samples/await-in-each/_config.js index b1a232e4bd..6c7da69ee3 100644 --- a/test/runtime/samples/await-in-each/_config.js +++ b/test/runtime/samples/await-in-each/_config.js @@ -1,6 +1,6 @@ let fulfil; -let thePromise = new Promise(f => { +const thePromise = new Promise(f => { fulfil = f; }); diff --git a/test/runtime/samples/await-then-catch-order/_config.js b/test/runtime/samples/await-then-catch-order/_config.js index 5c4eb530e9..f972520904 100644 --- a/test/runtime/samples/await-then-catch-order/_config.js +++ b/test/runtime/samples/await-then-catch-order/_config.js @@ -1,6 +1,6 @@ let fulfil; -let thePromise = new Promise(f => { +const thePromise = new Promise(f => { fulfil = f; }); diff --git a/test/runtime/samples/await-with-components/_config.js b/test/runtime/samples/await-with-components/_config.js index ffef7441ff..d0e5a2bf18 100644 --- a/test/runtime/samples/await-with-components/_config.js +++ b/test/runtime/samples/await-with-components/_config.js @@ -1,6 +1,6 @@ export default { async test({ assert, component, target }) { - let resolve, reject; + let resolve; let reject; let promise = new Promise(ok => resolve = ok); component.promise = promise; diff --git a/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js b/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js index 212c57308a..a07a1482bc 100644 --- a/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js +++ b/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js @@ -22,4 +22,4 @@ export default { `); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/test/runtime/samples/element-invalid-name/_config.js b/test/runtime/samples/element-invalid-name/_config.js index af6e4933ce..d70c920a19 100644 --- a/test/runtime/samples/element-invalid-name/_config.js +++ b/test/runtime/samples/element-invalid-name/_config.js @@ -2,4 +2,4 @@ export default { html: ` Hello ` -} +}; diff --git a/test/runtime/samples/export-function-hoisting/_config.js b/test/runtime/samples/export-function-hoisting/_config.js index c56851d065..f01c8b4841 100644 --- a/test/runtime/samples/export-function-hoisting/_config.js +++ b/test/runtime/samples/export-function-hoisting/_config.js @@ -1,3 +1,3 @@ export default { - html: 'Compile plz' -} + html: 'Compile plz' +}; diff --git a/test/runtime/samples/function-hoisting/_config.js b/test/runtime/samples/function-hoisting/_config.js index 5a22ffaf0b..91492d8c38 100644 --- a/test/runtime/samples/function-hoisting/_config.js +++ b/test/runtime/samples/function-hoisting/_config.js @@ -1,7 +1,7 @@ export default { - props: { - greeting: 'Good day' - }, + props: { + greeting: 'Good day' + }, - html: '

    Good day, world

    ' -} + html: '

    Good day, world

    ' +}; diff --git a/test/runtime/samples/get-after-destroy/_config.js b/test/runtime/samples/get-after-destroy/_config.js index bf4d8e90ba..5d10bbe72a 100644 --- a/test/runtime/samples/get-after-destroy/_config.js +++ b/test/runtime/samples/get-after-destroy/_config.js @@ -10,4 +10,4 @@ export default { const { foo } = component; assert.equal(foo, undefined); } -} \ No newline at end of file +}; diff --git a/test/runtime/samples/immutable-nested/_config.js b/test/runtime/samples/immutable-nested/_config.js index da90b43727..8b1dd7e68a 100644 --- a/test/runtime/samples/immutable-nested/_config.js +++ b/test/runtime/samples/immutable-nested/_config.js @@ -15,7 +15,7 @@ export default { `, test({ assert, component, target }) { - var nested = component.nested; + const nested = component.nested; assert.htmlEqual(target.innerHTML, `
    @@ -24,6 +24,7 @@ export default {
    `); + // eslint-disable-next-line no-self-assign nested.foo = nested.foo; assert.htmlEqual(target.innerHTML, `
    diff --git a/test/runtime/samples/immutable-option/_config.js b/test/runtime/samples/immutable-option/_config.js index 0aaa742fbe..1224d0213a 100644 --- a/test/runtime/samples/immutable-option/_config.js +++ b/test/runtime/samples/immutable-option/_config.js @@ -4,6 +4,7 @@ export default { html: `

    Called 1 times.

    `, test({ assert, component, target }) { + // eslint-disable-next-line no-self-assign component.foo = component.foo; assert.htmlEqual(target.innerHTML, `

    Called 1 times.

    `); } diff --git a/test/runtime/samples/immutable-svelte-meta-false/_config.js b/test/runtime/samples/immutable-svelte-meta-false/_config.js index 933c151d6b..664f99f087 100644 --- a/test/runtime/samples/immutable-svelte-meta-false/_config.js +++ b/test/runtime/samples/immutable-svelte-meta-false/_config.js @@ -4,6 +4,7 @@ export default { html: `

    Called 1 times.

    `, test({ assert, component, target }) { + // eslint-disable-next-line no-self-assign component.foo = component.foo; assert.htmlEqual(target.innerHTML, `

    Called 2 times.

    `); } diff --git a/test/runtime/samples/immutable-svelte-meta/_config.js b/test/runtime/samples/immutable-svelte-meta/_config.js index 9bd32dbd11..4e39f36224 100644 --- a/test/runtime/samples/immutable-svelte-meta/_config.js +++ b/test/runtime/samples/immutable-svelte-meta/_config.js @@ -2,6 +2,7 @@ export default { html: `

    Called 1 times.

    `, test({ assert, component, target }) { + // eslint-disable-next-line no-self-assign component.foo = component.foo; assert.htmlEqual(target.innerHTML, `

    Called 1 times.

    `); } diff --git a/test/runtime/samples/internal-state/_config.js b/test/runtime/samples/internal-state/_config.js index 09ea61a8eb..6b8440aaf3 100644 --- a/test/runtime/samples/internal-state/_config.js +++ b/test/runtime/samples/internal-state/_config.js @@ -1,18 +1,18 @@ export default { - html: ` -

    internal: 1

    - - `, + html: ` +

    internal: 1

    + + `, - async test({ assert, target, window }) { - const button = target.querySelector('button'); - const click = new window.MouseEvent('click'); + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); - await button.dispatchEvent(click); + await button.dispatchEvent(click); - assert.htmlEqual(target.innerHTML, ` -

    internal: 1

    - - `); - } -}; \ No newline at end of file + assert.htmlEqual(target.innerHTML, ` +

    internal: 1

    + + `); + } +}; diff --git a/test/runtime/samples/mixed-let-export/_config.js b/test/runtime/samples/mixed-let-export/_config.js index 5ac8585742..f3da4215d9 100644 --- a/test/runtime/samples/mixed-let-export/_config.js +++ b/test/runtime/samples/mixed-let-export/_config.js @@ -1,9 +1,9 @@ export default { - props: { - a: 42 - }, + props: { + a: 42 + }, - html: ` - 42 - ` -} + html: ` + 42 + ` +}; diff --git a/test/runtime/samples/prop-exports/_config.js b/test/runtime/samples/prop-exports/_config.js index 631c9eb0ad..e1620c015f 100644 --- a/test/runtime/samples/prop-exports/_config.js +++ b/test/runtime/samples/prop-exports/_config.js @@ -2,29 +2,29 @@ import { writable } from '../../../../store'; export default { props: { - s1: writable(42), - s2: writable(43), - p1: 2, - p3: 3, - a1: writable(1), - a2: 4, - a6: writable(29), - for: 'loop', - continue: '...', + s1: writable(42), + s2: writable(43), + p1: 2, + p3: 3, + a1: writable(1), + a2: 4, + a6: writable(29), + for: 'loop', + continue: '...', }, html: ` - $s1=42 - $s2=43 - p1=2 - p3=3 - $v1=1 - v2=4 - vi1=4 - $vs1=1 - vl0=hello - vl1=test - $s3=29 - loop... - ` -} + $s1=42 + $s2=43 + p1=2 + p3=3 + $v1=1 + v2=4 + vi1=4 + $vs1=1 + vl0=hello + vl1=test + $s3=29 + loop... + ` +}; diff --git a/test/runtime/samples/spring/_config.js b/test/runtime/samples/spring/_config.js index 79399b777a..49367ce08b 100644 --- a/test/runtime/samples/spring/_config.js +++ b/test/runtime/samples/spring/_config.js @@ -1,3 +1,3 @@ export default { html: `

    0

    ` -} \ No newline at end of file +}; diff --git a/test/runtime/samples/transition-js-await-block/_config.js b/test/runtime/samples/transition-js-await-block/_config.js index 2e01dd7f76..80546ae6b8 100644 --- a/test/runtime/samples/transition-js-await-block/_config.js +++ b/test/runtime/samples/transition-js-await-block/_config.js @@ -1,7 +1,7 @@ let fulfil; let reject; -let promise = new Promise((f, r) => { +const promise = new Promise((f, r) => { fulfil = f; reject = r; }); @@ -14,7 +14,7 @@ export default { intro: true, test({ assert, target, raf }) { - let p = target.querySelector('p'); + const p = target.querySelector('p'); assert.equal(p.className, 'pending'); assert.equal(p.foo, 0); @@ -26,7 +26,7 @@ export default { return promise.then(() => { raf.tick(80); - let ps = document.querySelectorAll('p'); + const ps = document.querySelectorAll('p'); assert.equal(ps[1].className, 'pending'); assert.equal(ps[0].className, 'then'); assert.equal(ps[1].foo, 0.2); diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js index 4b648c0591..cf6e5ad964 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.js @@ -1,7 +1,6 @@ import * as assert from "assert"; import * as fs from "fs"; import * as path from "path"; -import * as glob from 'tiny-glob/sync.js'; import { showOutput, diff --git a/test/setup.js b/test/setup.js index 8bb73d81d0..7406a07dd9 100644 --- a/test/setup.js +++ b/test/setup.js @@ -7,7 +7,7 @@ process.env.TEST = true; require.extensions['.js'] = function(module, filename) { const exports = []; - var code = fs.readFileSync(filename, 'utf-8') + let code = fs.readFileSync(filename, 'utf-8') .replace(/^import \* as (\w+) from ['"]([^'"]+)['"];?/gm, 'var $1 = require("$2");') .replace(/^import (\w+) from ['"]([^'"]+)['"];?/gm, 'var {default: $1} = require("$2");') .replace(/^import {([^}]+)} from ['"](.+)['"];?/gm, 'var {$1} = require("$2");') @@ -35,4 +35,4 @@ require.extensions['.js'] = function(module, filename) { console.log(code); // eslint-disable-line no-console throw err; } -}; \ No newline at end of file +}; diff --git a/test/sourcemaps/index.js b/test/sourcemaps/index.js index 79028719b9..ee169ebe1b 100644 --- a/test/sourcemaps/index.js +++ b/test/sourcemaps/index.js @@ -1,7 +1,7 @@ import * as fs from "fs"; import * as path from "path"; import * as assert from "assert"; -import { loadConfig, svelte } from "../helpers.js"; +import { svelte } from "../helpers.js"; import { SourceMapConsumer } from "source-map"; import { getLocator } from "locate-character"; @@ -18,8 +18,6 @@ describe("sourcemaps", () => { } (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = loadConfig(`./sourcemaps/samples/${dir}/_config.js`); - const filename = path.resolve( `test/sourcemaps/samples/${dir}/input.svelte` ); diff --git a/test/store/index.ts b/test/store/index.ts index 13b8e1f869..d4934f756a 100644 --- a/test/store/index.ts +++ b/test/store/index.ts @@ -49,7 +49,7 @@ describe('store', () => { const store = writable(obj); - store.subscribe(value => { + store.subscribe(() => { called += 1; }); diff --git a/test/test.js b/test/test.js index 380bbee304..7759941dbb 100644 --- a/test/test.js +++ b/test/test.js @@ -2,6 +2,6 @@ const glob = require("tiny-glob/sync.js"); require("./setup"); -glob("*/index.{js,ts}", { cwd: "test" }).forEach(function(file) { +glob("*/index.{js,ts}", { cwd: "test" }).forEach((file) => { require("./" + file); -}); \ No newline at end of file +}); diff --git a/test/validator/index.js b/test/validator/index.js index b26b087bb3..1e54cc20db 100644 --- a/test/validator/index.js +++ b/test/validator/index.js @@ -24,7 +24,7 @@ describe("validate", () => { let error; try { - let { warnings } = svelte.compile(input, { + const { warnings } = svelte.compile(input, { dev: config.dev, legacy: config.legacy, generate: false @@ -59,7 +59,7 @@ describe("validate", () => { assert.deepEqual(error.end, expected.end); assert.equal(error.pos, expected.pos); } catch (e) { - console.error(error) + console.error(error); // eslint-disable-line no-console throw e; } }