From 0d9d3c53d1acb2c5352a4506b41ee63e94189eb7 Mon Sep 17 00:00:00 2001 From: pushkine Date: Wed, 6 May 2020 09:02:42 +0200 Subject: [PATCH] 06/05 --- .gitignore | 1 + package.json | 54 +-- rollup.config.js | 150 ++++---- src/ambient.ts | 55 +++ src/compiler/compile/Component.ts | 322 ++++++++---------- src/compiler/compile/create_module.ts | 150 ++++---- src/compiler/compile/render_dom/Block.ts | 6 +- src/compiler/compile/render_dom/index.ts | 2 +- .../render_dom/wrappers/Element/index.ts | 4 - src/compiler/index.ts | 4 +- src/compiler/interfaces.ts | 38 +-- src/runtime/easing/index.ts | 11 +- src/runtime/index.ts | 3 +- src/runtime/internal/Component.ts | 7 +- src/runtime/internal/dev.legacy.ts | 90 +++++ src/runtime/internal/dev.ts | 97 ++++-- src/runtime/internal/dom.ts | 182 +++++----- src/runtime/internal/environment.ts | 2 +- src/runtime/internal/index.ts | 1 + src/runtime/internal/lifecycle.ts | 30 +- src/runtime/internal/loop.ts | 172 +++++----- src/runtime/internal/scheduler.ts | 26 +- src/runtime/internal/stores.ts | 65 ++++ src/runtime/internal/style_manager.ts | 4 +- src/runtime/internal/transitions.ts | 4 +- src/runtime/internal/utils.ts | 11 +- src/runtime/motion/_tweened.ts | 133 ++++++++ src/runtime/motion/index.ts | 2 +- src/runtime/motion/spring.ts | 214 ++++-------- src/runtime/motion/tweened.ts | 133 -------- src/runtime/store/index.ts | 243 +++---------- src/runtime/tsconfig.json | 2 +- 32 files changed, 1148 insertions(+), 1070 deletions(-) create mode 100644 src/ambient.ts create mode 100644 src/runtime/internal/dev.legacy.ts create mode 100644 src/runtime/internal/stores.ts create mode 100644 src/runtime/motion/_tweened.ts delete mode 100644 src/runtime/motion/tweened.ts diff --git a/.gitignore b/.gitignore index f7fac04eba..516738b185 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ node_modules /compiler.*js /index.*js /internal +/dev /store /easing /motion diff --git a/package.json b/package.json index 215a6099e9..734affc654 100644 --- a/package.json +++ b/package.json @@ -56,41 +56,41 @@ }, "homepage": "https://github.com/sveltejs/svelte#README", "devDependencies": { - "@rollup/plugin-commonjs": "^11.0.0", - "@rollup/plugin-json": "^4.0.1", - "@rollup/plugin-node-resolve": "^6.0.0", - "@rollup/plugin-replace": "^2.3.0", + "@rollup/plugin-commonjs": "^11.1.0", + "@rollup/plugin-json": "^4.0.3", + "@rollup/plugin-node-resolve": "^7.1.3", + "@rollup/plugin-replace": "^2.3.2", "@rollup/plugin-sucrase": "^3.0.0", - "@rollup/plugin-typescript": "^2.0.1", - "@rollup/plugin-virtual": "^2.0.0", - "@types/mocha": "^5.2.7", - "@types/node": "^8.10.53", - "@typescript-eslint/eslint-plugin": "^1.13.0", - "@typescript-eslint/parser": "^2.1.0", - "acorn": "^7.1.0", - "agadoo": "^1.1.0", - "c8": "^5.0.1", + "@rollup/plugin-typescript": "^4.1.1", + "@rollup/plugin-virtual": "^2.0.1", + "@types/mocha": "^7.0.2", + "@types/node": "^13.13.4", + "@typescript-eslint/eslint-plugin": "^2.30.0", + "@typescript-eslint/parser": "^2.30.0", + "acorn": "^7.1.1", + "agadoo": "^2.0.0", + "c8": "^7.1.1", "code-red": "0.1.1", - "codecov": "^3.5.0", - "css-tree": "1.0.0-alpha22", - "eslint": "^6.3.0", - "eslint-plugin-import": "^2.18.2", + "codecov": "^3.6.5", + "css-tree": "1.0.0-alpha39", + "eslint": "^6.8.0", + "eslint-plugin-import": "^2.20.2", "eslint-plugin-svelte3": "^2.7.3", - "estree-walker": "^1.0.0", + "estree-walker": "^2.0.1", "is-reference": "^1.1.4", - "jsdom": "^15.1.1", + "jsdom": "^16.2.2", "kleur": "^3.0.3", "locate-character": "^2.0.5", - "magic-string": "^0.25.3", - "mocha": "^6.2.0", - "periscopic": "^2.0.1", - "puppeteer": "^1.19.0", - "rollup": "^1.27.14", + "magic-string": "^0.25.7", + "mocha": "^7.1.2", + "periscopic": "^2.0.2", + "puppeteer": "^3.0.2", + "rollup": "^2.7.6", "source-map": "^0.7.3", - "source-map-support": "^0.5.13", + "source-map-support": "^0.5.19", "tiny-glob": "^0.2.6", - "tslib": "^1.10.0", - "typescript": "^3.5.3" + "tslib": "^1.11.1", + "typescript": "^3.8.3" }, "nyc": { "include": [ diff --git a/rollup.config.js b/rollup.config.js index de1159f442..43d4331dd0 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,89 +7,121 @@ import sucrase from '@rollup/plugin-sucrase'; import typescript from '@rollup/plugin-typescript'; import pkg from './package.json'; -const esm = { format: 'esm' }; -const cjs = { format: 'cjs' }; - const is_publish = !!process.env.PUBLISH; - +const external = (id) => id.startsWith('svelte/'); const ts_plugin = is_publish ? typescript({ include: 'src/**', typescript: require('typescript') }) : sucrase({ transforms: ['typescript'] }); -const external = (id) => id.startsWith('svelte/'); -const version = replace({ __VERSION__: pkg.version }); - -fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, VERSION } from './types/compiler/index';`); - -const output = (obj) => - Object.keys(obj).flatMap((name) => - [esm, cjs].map(({ format }) => ({ - format, - file: `${name !== 'default' ? name + '/' : ''}${obj[name].file}.${format === 'esm' ? 'mjs' : 'js'}`, - paths: (id) => external(id) && `${id.replace('svelte', obj[name].path)}`, - plugins: (defaults.plugins || []).concat([replace({ __DEV__: name === 'dev' })]), +// documented in src/ambient.ts +const globals = (name) => + replace({ + 'var __DEV__: boolean': 'var rollup_removes_this', + '__DEV__': name === 'dev', + 'var __TEST__: boolean': 'var rollup_removes_this', + '__TEST__': name === 'test', + 'var __VERSION__: string': 'var rollup_removes_this', + '__VERSION__': `"${pkg.version}"`, + }); +/** + * + */ +function map_outputs({ output, plugins, ...rest }) { + return Object.keys(output).flatMap((name) => + ['esm', 'cjs'].map((format) => ({ + external, + ...rest, + output: { + format, + file: `${name !== 'default' ? name + '/' : ''}${output[name].file}.${format === 'esm' ? 'mjs' : 'js'}`, + paths: (id) => external(id) && `${id.replace('svelte', output[name].path)}`, + }, + plugins: [globals(name), ...(plugins || [])], })) ); +} function writeFileSync(...arr) { arr.filter(Boolean).forEach(({ dir, content }) => fs.writeFileSync(dir, content)); } + +fs.writeFileSync(`./compiler.d.ts`, `export { compile, parse, preprocess, version } from './types/compiler/index';`); export default [ - /* runtime main */ - { + /* main exports */ + ...map_outputs({ input: `src/runtime/index.ts`, - output: output({ + output: { + /** + * Production main runtime + * -> 'svelte/index.js' + */ default: { file: 'index', path: '.' }, + /** + * Development main runtime + * -> 'svelte/dev/index.js' + * redirected to by the compiler + */ dev: { file: 'index', path: '.' }, - }), - external, + }, plugins: [ts_plugin], - }, - /* svelte/[library] */ + }), + /* 'svelte/[library]' exports */ ...fs .readdirSync('src/runtime') .filter((dir) => fs.statSync(`src/runtime/${dir}`).isDirectory()) - .map((dir) => ({ - input: `src/runtime/${dir}/index.ts`, - output: output({ - [dir]: { file: 'index', path: '..' }, - dev: { file: dir, path: '.' }, - }), - external, - plugins: [ - version, - ts_plugin, - { - writeBundle(bundle) { - writeFileSync( - dir === 'internal' && - bundle['index.mjs'] && { - dir: `src/compiler/compile/internal_exports.ts`, - content: ` - // This file is automatically generated - export default new Set(${JSON.stringify(bundle['index.mjs'].exports)});`, + .flatMap((library) => + map_outputs({ + input: `src/runtime/${library}/index.ts`, + output: { + /** + * Production runtime + * -> 'svelte/[library]/index.js' + */ + [library]: { file: 'index', path: '..' }, + /** + * Development runtime + * -> 'svelte/dev/[library].js' + * Must be redirected to by user's bundler + */ + dev: { file: library, path: '.' }, + }, + plugins: [ + ts_plugin, + { + writeBundle(bundle) { + writeFileSync( + /* gives internal function names to the compiler */ + library === 'internal' && + bundle['index.mjs'] && { + dir: `src/compiler/compile/internal_exports.ts`, + content: ` + // This file is automatically generated + export default new Set(${JSON.stringify(bundle['index.mjs'].exports)});`, + }, + /* adds package.json to every svelte/[library] */ + { + dir: `${library}/package.json`, + content: `{ + "main": "./index", + "module": "./index.mjs", + "types": "./index.d.ts" + }`, }, - { - dir: `${dir}/package.json`, - content: `{ - "main": "./index", - "module": "./index.mjs", - "types": "./index.d.ts" - }`, - }, - { - dir: `${dir}/index.d.ts`, - content: `export * from '../types/runtime/${dir}/index';`, - } - ); + /* exports types */ + { + dir: `${library}/index.d.ts`, + content: `export * from '../types/runtime/${library}/index';`, + } + ); + }, }, - }, - ], - })), + ], + }) + ), /* compiler.js */ { input: 'src/compiler/index.ts', - plugins: [version, resolve(), commonjs({ include: ['node_modules/**'] }), json(), ts_plugin], + plugins: [globals('compiler'), resolve(), commonjs({ include: ['node_modules/**'] }), json(), ts_plugin], output: { file: 'compiler.js', format: is_publish ? 'umd' : 'cjs', diff --git a/src/ambient.ts b/src/ambient.ts new file mode 100644 index 0000000000..12defbaa3c --- /dev/null +++ b/src/ambient.ts @@ -0,0 +1,55 @@ +/** + * __DEV__ + * + * Used in src/runtime + * + * Bundles dev runtime to svelte/dev/[library].[m]js + * the compiler rewrites its own 'svelte' imports to 'svelte/dev' automatically + * + */ +declare var __DEV__: boolean; +/** + * __VERSION__ + * + * Svelte's version in package.json + * Used in src/compiler and src/runtime + * + */ +declare var __VERSION__: string; +/** + * __COMPILER_API_VERSION__ + * + * Unique ID passed to the compiler + * Used to mitigate breaking changes with bundler plugins + * + * VERSIONS ( default is "3.0.0" ) + * + * >3.22.0 : { + * + * The change : + * A different runtime is now used in dev mode, + * every import has to go through 'svelte/dev' instead of 'svelte' + * + * Requirement : + * In dev mode, bundler plugins must make sure that every 'svelte' import is replaced by 'svelte/dev' + * + * } + * + */ +//declare var __COMPILER_API_VERSION__: boolean; +/** + * Unique ID devtools must pass to the compiler + * Used to + * + * VERSIONS ( default is "^3.21.0" ) + * - 0 (default) : compiler imports from prod runtime + * - 1 : in dev mode, compiler imports from dev runtime + */ +//declare var __DEVTOOLS_API_VERSION__: boolean; + +/** + * TODO + * Bundle different runtime for tests + * instead of relying on hacks + */ +declare var __TEST__: boolean; \ No newline at end of file diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index c83f8153a0..04b3bfc212 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -4,12 +4,7 @@ import Stats from '../Stats'; import { globals, reserved, is_valid } from '../utils/names'; import { namespaces, valid_namespaces } from '../utils/namespaces'; import create_module from './create_module'; -import { - create_scopes, - extract_names, - Scope, - extract_identifiers, -} from './utils/scope'; +import { create_scopes, extract_names, Scope, extract_identifiers } from './utils/scope'; import Stylesheet from './css/Stylesheet'; import { test } from '../config'; import Fragment from './nodes/Fragment'; @@ -24,7 +19,15 @@ import TemplateScope from './nodes/shared/TemplateScope'; import fuzzymatch from '../utils/fuzzymatch'; import get_object from './utils/get_object'; import Slot from './nodes/Slot'; -import { Node, ImportDeclaration, Identifier, Program, ExpressionStatement, AssignmentExpression, Literal } from 'estree'; +import { + Node, + ImportDeclaration, + Identifier, + Program, + ExpressionStatement, + AssignmentExpression, + Literal, +} from 'estree'; import add_to_set from './utils/add_to_set'; import check_graph_for_cycles from './utils/check_graph_for_cycles'; import { print, x, b } from 'code-red'; @@ -66,8 +69,8 @@ export default class Component { hoistable_nodes: Set = new Set(); node_for_declaration: Map = new Map(); - partly_hoisted: Array<(Node | Node[])> = []; - fully_hoisted: Array<(Node | Node[])> = []; + partly_hoisted: Array = []; + fully_hoisted: Array = []; reactive_declarations: Array<{ assignees: Set; dependencies: Set; @@ -116,43 +119,29 @@ export default class Component { html: ast.html, css: ast.css, instance: ast.instance && JSON.parse(JSON.stringify(ast.instance)), - module: ast.module + module: ast.module, }; this.file = compile_options.filename && (typeof process !== 'undefined' - ? compile_options.filename - .replace(process.cwd(), '') - .replace(/^[/\\]/, '') + ? compile_options.filename.replace(process.cwd(), '').replace(/^[/\\]/, '') : compile_options.filename); this.locate = getLocator(this.source, { offsetLine: 1 }); // styles - this.stylesheet = new Stylesheet( - source, - ast, - compile_options.filename, - compile_options.dev - ); + this.stylesheet = new Stylesheet(source, ast, compile_options.filename, compile_options.dev); this.stylesheet.validate(this); - this.component_options = process_component_options( - this, - this.ast.html.children - ); - this.namespace = - namespaces[this.component_options.namespace] || - this.component_options.namespace; + this.component_options = process_component_options(this, this.ast.html.children); + this.namespace = namespaces[this.component_options.namespace] || this.component_options.namespace; if (compile_options.customElement) { - if ( - this.component_options.tag === undefined && - compile_options.tag === undefined - ) { - const svelteOptions = ast.html.children.find( - child => child.name === 'svelte:options' - ) || { start: 0, end: 0 }; + if (this.component_options.tag === undefined && compile_options.tag === undefined) { + const svelteOptions = ast.html.children.find((child) => child.name === 'svelte:options') || { + start: 0, + end: 0, + }; this.warn(svelteOptions, { code: 'custom-element-no-tag', message: `No custom element 'tag' option was specified. To automatically register a custom element, specify a name with a hyphen in it, e.g. . To hide this warning, use `, @@ -205,7 +194,7 @@ export default class Component { const variable = this.var_lookup.get(subscribable_name); if (variable) { - variable.referenced = true; + variable.referenced = true; variable.subscribable = true; } } else { @@ -235,7 +224,10 @@ export default class Component { const { compile_options, name } = this; const { format = 'esm' } = compile_options; - const banner = `${this.file ? `${this.file} ` : ``}generated by Svelte v${'__VERSION__'}`; + const banner = `${this.file ? `${this.file} ` : ``}generated by Svelte v${__VERSION__}`; + + const legacy_dev_runtime = compile_options.dev && compile_options.version < 3.22; + const dev_runtime = compile_options.dev && compile_options.version >= 3.22; const program: any = { type: 'Program', body: result.js }; @@ -249,11 +241,11 @@ export default class Component { } else { let name = node.name.slice(1); - if (compile_options.dev) { - if (internal_exports.has(`${name}_dev`)) { - name += '_dev'; - } else if (internal_exports.has(`${name}Dev`)) { - name += 'Dev'; + if (legacy_dev_runtime) { + if (internal_exports.has(`${name}_dev$legacy`)) { + name += '_dev$legacy'; + } else if (internal_exports.has(`${name}Dev$legacy`)) { + name += 'Dev$legacy'; } } @@ -261,23 +253,19 @@ export default class Component { this.helpers.set(name, alias); node.name = alias.name; } - } - - else if (node.name[0] !== '#' && !is_valid(node.name)) { + } else if (node.name[0] !== '#' && !is_valid(node.name)) { // this hack allows x`foo.${bar}` where bar could be invalid const literal: Literal = { type: 'Literal', value: node.name }; if (parent.type === 'Property' && key === 'key') { parent.key = literal; - } - - else if (parent.type === 'MemberExpression' && key === 'property') { + } else if (parent.type === 'MemberExpression' && key === 'property') { parent.property = literal; parent.computed = true; } } } - } + }, }); const referenced_globals = Array.from( @@ -297,33 +285,31 @@ export default class Component { format, name, banner, - compile_options.sveltePath, + compile_options.sveltePath + (dev_runtime ? '/dev' : ''), imported_helpers, referenced_globals, this.imports, this.vars - .filter(variable => variable.module && variable.export_name) - .map(variable => ({ + .filter((variable) => variable.module && variable.export_name) + .map((variable) => ({ name: variable.name, as: variable.export_name, })) ); - css = compile_options.customElement - ? { code: null, map: null } - : result.css; + css = compile_options.customElement ? { code: null, map: null } : result.css; js = print(program, { - sourceMapSource: compile_options.filename + sourceMapSource: compile_options.filename, }); js.map.sources = [ - compile_options.filename ? get_relative_path(compile_options.outputFilename || '', compile_options.filename) : null + compile_options.filename + ? get_relative_path(compile_options.outputFilename || '', compile_options.filename) + : null, ]; - js.map.sourcesContent = [ - this.source - ]; + js.map.sourcesContent = [this.source]; } return { @@ -332,8 +318,8 @@ export default class Component { ast: this.original_ast, warnings: this.warnings, vars: this.vars - .filter(v => !v.global && !v.internal) - .map(v => ({ + .filter((v) => !v.global && !v.internal) + .map((v) => ({ name: v.name, export_name: v.export_name || null, injected: v.injected || false, @@ -378,17 +364,13 @@ export default class Component { return (name: string): Identifier => { if (test) name = `${name}$`; let alias = name; - for ( - let i = 1; - this.used_names.has(alias) || local_used_names.has(alias); - alias = `${name}_${i++}` - ); + for (let i = 1; this.used_names.has(alias) || local_used_names.has(alias); alias = `${name}_${i++}`); local_used_names.add(alias); this.globally_used_names.add(alias); return { type: 'Identifier', - name: alias + name: alias, }; }; } @@ -440,8 +422,7 @@ export default class Component { end, pos: pos.start, filename: this.compile_options.filename, - toString: () => - `${warning.message} (${start.line}:${start.column})\n${frame}`, + toString: () => `${warning.message} (${start.line}:${start.column})\n${frame}`, }); } @@ -466,14 +447,17 @@ export default class Component { } if (node.declaration) { if (node.declaration.type === 'VariableDeclaration') { - node.declaration.declarations.forEach(declarator => { - extract_names(declarator.id).forEach(name => { + node.declaration.declarations.forEach((declarator) => { + extract_names(declarator.id).forEach((name) => { const variable = this.var_lookup.get(name); variable.export_name = name; - if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { + if ( + variable.writable && + !(variable.referenced || variable.referenced_from_script || variable.subscribable) + ) { this.warn(declarator, { code: `unused-export-let`, - message: `${this.name.name} has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\`` + message: `${this.name.name} has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\``, }); } }); @@ -487,16 +471,19 @@ export default class Component { return node.declaration; } else { - node.specifiers.forEach(specifier => { + node.specifiers.forEach((specifier) => { const variable = this.var_lookup.get(specifier.local.name); if (variable) { variable.export_name = specifier.exported.name; - if (variable.writable && !(variable.referenced || variable.referenced_from_script || variable.subscribable)) { + if ( + variable.writable && + !(variable.referenced || variable.referenced_from_script || variable.subscribable) + ) { this.warn(specifier, { code: `unused-export-let`, - message: `${this.name.name} has unused export property '${specifier.exported.name}'. If it is for external reference only, please consider using \`export const ${specifier.exported.name}\`` + message: `${this.name.name} has unused export property '${specifier.exported.name}'. If it is for external reference only, please consider using \`export const ${specifier.exported.name}\``, }); } } @@ -510,13 +497,12 @@ export default class Component { extract_javascript(script) { if (!script) return null; - return script.content.body.filter(node => { + return script.content.body.filter((node) => { if (!node) return false; if (this.hoistable_nodes.has(node)) return false; if (this.reactive_declaration_nodes.has(node)) return false; if (node.type === 'ImportDeclaration') return false; - if (node.type === 'ExportDeclaration' && node.specifiers.length > 0) - return false; + if (node.type === 'ExportDeclaration' && node.specifiers.length > 0) return false; return true; }); } @@ -554,7 +540,7 @@ export default class Component { name, module: true, hoistable: true, - writable + writable, }); }); @@ -568,7 +554,7 @@ export default class Component { this.add_var({ name, global: true, - hoistable: true + hoistable: true, }); } }); @@ -598,7 +584,7 @@ export default class Component { if (!script) return; // inject vars for reactive declarations - script.content.body.forEach(node => { + script.content.body.forEach((node) => { if (node.type !== 'LabeledStatement') return; if (node.body.type !== 'ExpressionStatement') return; @@ -606,16 +592,14 @@ export default class Component { if (expression.type !== 'AssignmentExpression') return; if (expression.left.type === 'MemberExpression') return; - extract_names(expression.left).forEach(name => { + extract_names(expression.left).forEach((name) => { if (!this.var_lookup.has(name) && name[0] !== '$') { this.injected_reactive_declaration_vars.add(name); } }); }); - const { scope: instance_scope, map, globals } = create_scopes( - script.content - ); + const { scope: instance_scope, map, globals } = create_scopes(script.content); this.instance_scope = instance_scope; this.instance_scope_map = map; @@ -632,7 +616,7 @@ export default class Component { this.add_var({ name, initialised: instance_scope.initialised_declarations.has(name), - writable + writable, }); this.node_for_declaration.set(name, node); @@ -658,7 +642,7 @@ export default class Component { if (name === '$' || name[1] === '$') { this.error(node as any, { code: 'illegal-global', - message: `${name} is an illegal variable name` + message: `${name} is an illegal variable name`, }); } @@ -680,7 +664,7 @@ export default class Component { this.add_var({ name, global: true, - hoistable: true + hoistable: true, }); } }); @@ -744,7 +728,11 @@ export default class Component { leave(node: Node) { // do it on leave, to prevent infinite loop if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) { - const to_replace_for_loop_protect = component.loop_protect(node, scope, component.compile_options.loopGuardTimeout); + const to_replace_for_loop_protect = component.loop_protect( + node, + scope, + component.compile_options.loopGuardTimeout + ); if (to_replace_for_loop_protect) { this.replace(to_replace_for_loop_protect); scope_updated = true; @@ -796,13 +784,9 @@ export default class Component { const deep = assignee.type === 'MemberExpression'; - names.forEach(name => { + names.forEach((name) => { const scope_owner = scope.find_owner(name); - if ( - scope_owner !== null - ? scope_owner === instance_scope - : module_scope && module_scope.has(name) - ) { + if (scope_owner !== null ? scope_owner === instance_scope : module_scope && module_scope.has(name)) { const variable = component.var_lookup.get(name); variable[deep ? 'mutated' : 'reassigned'] = true; } @@ -827,11 +811,7 @@ export default class Component { } warn_on_undefined_store_value_references(node, parent, scope) { - if ( - node.type === 'LabeledStatement' && - node.label.name === '$' && - parent.type !== 'Program' - ) { + if (node.type === 'LabeledStatement' && node.label.name === '$' && parent.type !== 'Program') { this.warn(node as any, { code: 'non-top-level-reactive-declaration', message: '$: has no effect outside of the top-level', @@ -849,9 +829,7 @@ export default class Component { } loop_protect(node, scope: Scope, timeout: number): Node | null { - if (node.type === 'WhileStatement' || - node.type === 'ForStatement' || - node.type === 'DoWhileStatement') { + if (node.type === 'WhileStatement' || node.type === 'ForStatement' || node.type === 'DoWhileStatement') { const guard = this.get_unique_name('guard', scope); this.used_names.add(guard.name); @@ -869,10 +847,7 @@ export default class Component { return { type: 'BlockStatement', - body: [ - before[0], - node, - ], + body: [before[0], node], }; } return null; @@ -897,11 +872,11 @@ export default class Component { if (node.type === 'VariableDeclaration') { if (node.kind === 'var' || scope === instance_scope) { - node.declarations.forEach(declarator => { + node.declarations.forEach((declarator) => { if (declarator.id.type !== 'Identifier') { const inserts = []; - extract_names(declarator.id).forEach(name => { + extract_names(declarator.id).forEach((name) => { const variable = component.var_lookup.get(name); if (variable.export_name) { @@ -928,29 +903,29 @@ export default class Component { const variable = component.var_lookup.get(name); if (variable.export_name && variable.writable) { - const insert = variable.subscribable - ? get_insert(variable) - : null; + const insert = variable.subscribable ? get_insert(variable) : null; parent[key].splice(index + 1, 0, insert); declarator.id = { type: 'ObjectPattern', - properties: [{ - type: 'Property', - method: false, - shorthand: false, - computed: false, - kind: 'init', - key: { type: 'Identifier', name: variable.export_name }, - value: declarator.init - ? { - type: 'AssignmentPattern', - left: declarator.id, - right: declarator.init - } - : declarator.id - }] + properties: [ + { + type: 'Property', + method: false, + shorthand: false, + computed: false, + kind: 'init', + key: { type: 'Identifier', name: variable.export_name }, + value: declarator.init + ? { + type: 'AssignmentPattern', + left: declarator.id, + right: declarator.init, + } + : declarator.id, + }, + ], }; declarator.init = x`$$props`; @@ -981,12 +956,7 @@ export default class Component { // reference instance variables other than other // hoistable functions. TODO others? - const { - hoistable_nodes, - var_lookup, - injected_reactive_declaration_vars, - imports, - } = this; + const { hoistable_nodes, var_lookup, injected_reactive_declaration_vars, imports } = this; const top_level_function_declarations = new Map(); @@ -996,7 +966,7 @@ export default class Component { const node = body[i]; if (node.type === 'VariableDeclaration') { - const all_hoistable = node.declarations.every(d => { + const all_hoistable = node.declarations.every((d) => { if (!d.init) return false; if (d.init.type !== 'Literal') return false; @@ -1011,18 +981,13 @@ export default class Component { if (v.export_name) return false; if (this.var_lookup.get(name).reassigned) return false; - if ( - this.vars.find( - variable => variable.name === name && variable.module - ) - ) - return false; + if (this.vars.find((variable) => variable.name === name && variable.module)) return false; return true; }); if (all_hoistable) { - node.declarations.forEach(d => { + node.declarations.forEach((d) => { const variable = this.var_lookup.get((d.id as Identifier).name); variable.hoistable = true; }); @@ -1050,7 +1015,7 @@ export default class Component { const checked = new Set(); const walking = new Set(); - const is_hoistable = fn_declaration => { + const is_hoistable = (fn_declaration) => { if (fn_declaration.type === 'ExportNamedDeclaration') { fn_declaration = fn_declaration.declaration; } @@ -1090,9 +1055,7 @@ export default class Component { if (variable.hoistable) return; if (top_level_function_declarations.has(name)) { - const other_declaration = top_level_function_declarations.get( - name - ); + const other_declaration = top_level_function_declarations.get(name); if (walking.has(other_declaration)) { hoistable = false; @@ -1152,7 +1115,7 @@ export default class Component { const unsorted_reactive_declarations = []; - this.ast.instance.content.body.forEach(node => { + this.ast.instance.content.body.forEach((node) => { if (node.type === 'LabeledStatement' && node.label.name === '$') { this.reactive_declaration_nodes.add(node); @@ -1172,7 +1135,7 @@ export default class Component { if (node.type === 'AssignmentExpression') { const left = get_object(node.left); - extract_identifiers(left).forEach(node => { + extract_identifiers(left).forEach((node) => { assignee_nodes.add(node); assignees.add(node.name); }); @@ -1190,12 +1153,8 @@ export default class Component { const owner = scope.find_owner(name); const variable = component.var_lookup.get(name); if (variable) variable.is_reactive_dependency = true; - const is_writable_or_mutated = - variable && (variable.writable || variable.mutated); - if ( - (!owner || owner === component.instance_scope) && - (name[0] === '$' || is_writable_or_mutated) - ) { + const is_writable_or_mutated = variable && (variable.writable || variable.mutated); + if ((!owner || owner === component.instance_scope) && (name[0] === '$' || is_writable_or_mutated)) { dependencies.add(name); } } @@ -1225,8 +1184,8 @@ export default class Component { const lookup = new Map(); - unsorted_reactive_declarations.forEach(declaration => { - declaration.assignees.forEach(name => { + unsorted_reactive_declarations.forEach((declaration) => { + declaration.assignees.forEach((name) => { if (!lookup.has(name)) { lookup.set(name, []); } @@ -1237,34 +1196,35 @@ export default class Component { }); }); - const cycle = check_graph_for_cycles(unsorted_reactive_declarations.reduce((acc, declaration) => { - declaration.assignees.forEach(v => { - declaration.dependencies.forEach(w => { - if (!declaration.assignees.has(w)) { - acc.push([v, w]); - } + const cycle = check_graph_for_cycles( + unsorted_reactive_declarations.reduce((acc, declaration) => { + declaration.assignees.forEach((v) => { + declaration.dependencies.forEach((w) => { + if (!declaration.assignees.has(w)) { + acc.push([v, w]); + } + }); }); - }); - return acc; - }, [])); + return acc; + }, []) + ); if (cycle && cycle.length) { const declarationList = lookup.get(cycle[0]); const declaration = declarationList[0]; this.error(declaration.node, { code: 'cyclical-reactive-declaration', - message: `Cyclical dependency detected: ${cycle.join(' → ')}` + message: `Cyclical dependency detected: ${cycle.join(' → ')}`, }); } - const add_declaration = declaration => { + const add_declaration = (declaration) => { if (this.reactive_declarations.includes(declaration)) return; - - declaration.dependencies.forEach(name => { + + declaration.dependencies.forEach((name) => { if (declaration.assignees.has(name)) return; const earlier_declarations = lookup.get(name); - if (earlier_declarations) - earlier_declarations.forEach(add_declaration); + if (earlier_declarations) earlier_declarations.forEach(add_declaration); }); this.reactive_declarations.push(declaration); @@ -1275,10 +1235,10 @@ export default class Component { warn_if_undefined(name: string, node, template_scope: TemplateScope) { if (name[0] === '$') { - if (name === '$' || name[1] === '$' && !is_reserved_keyword(name)) { + if (name === '$' || (name[1] === '$' && !is_reserved_keyword(name))) { this.error(node, { code: 'illegal-global', - message: `${name} is an illegal variable name` + message: `${name} is an illegal variable name`, }); } @@ -1294,8 +1254,7 @@ export default class Component { if (globals.has(name) && node.type !== 'InlineComponent') return; let message = `'${name}' is not defined`; - if (!this.ast.instance) - message += `. Consider adding a