Convert src/compiler/compile/nodes/shared/Context.ts to JavaScript

pull/8569/head
Simon Holthausen 3 years ago
parent c6fd759ddf
commit b3aa0fe9af

@ -1,5 +1,6 @@
import Node from './Node.js'; import Node from './Node.js';
import compiler_warnings from '../../compiler_warnings.js'; import compiler_warnings from '../../compiler_warnings.js';
const regex_non_whitespace_characters = /[^ \r\n\f\v\t]/; const regex_non_whitespace_characters = /[^ \r\n\f\v\t]/;
/** @extends Node */ /** @extends Node */

@ -1,100 +1,71 @@
import { x } from 'code-red'; import { x } from 'code-red';
import { Node, Identifier, Expression, PrivateIdentifier, Pattern } from 'estree';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import is_reference, { NodeWithPropertyDefinition } from 'is-reference'; import is_reference from 'is-reference';
import { clone } from '../../../utils/clone'; import { clone } from '../../../utils/clone.js';
import Component from '../../Component'; import flatten_reference from '../../utils/flatten_reference.js';
import flatten_reference from '../../utils/flatten_reference'; /**
import TemplateScope from './TemplateScope'; * @param {{
* contexts: Context[];
export type Context = DestructuredVariable | ComputedProperty; * node: import('estree').Pattern;
* modifier?: DestructuredVariable['modifier'];
interface ComputedProperty { * default_modifier?: DestructuredVariable['default_modifier'];
type: 'ComputedProperty'; * scope: import('./TemplateScope.js').default;
property_name: Identifier; * component: import('../../Component.js').default;
key: Expression | PrivateIdentifier; * context_rest_properties: Map<string, import('estree').Node>;
} * in_rest_element?: boolean;
* }}
interface DestructuredVariable { */
type: 'DestructuredVariable'; export function unpack_destructuring({ contexts, node, modifier = (node) => node, default_modifier = (node) => node, scope, component, context_rest_properties, in_rest_element = false }) {
key: Identifier; if (!node)
name?: string; return;
modifier: (node: Node) => Node;
default_modifier: (node: Node, to_ctx: (name: string) => Node) => Node;
}
export function unpack_destructuring({
contexts,
node,
modifier = (node) => node,
default_modifier = (node) => node,
scope,
component,
context_rest_properties,
in_rest_element = false
}: {
contexts: Context[];
node: Pattern;
modifier?: DestructuredVariable['modifier'];
default_modifier?: DestructuredVariable['default_modifier'];
scope: TemplateScope;
component: Component;
context_rest_properties: Map<string, Node>;
in_rest_element?: boolean;
}) {
if (!node) return;
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
contexts.push({ contexts.push({
type: 'DestructuredVariable', type: 'DestructuredVariable',
key: node as Identifier, key: /** @type {import('estree').Identifier} */ (node),
modifier, modifier,
default_modifier default_modifier
}); });
if (in_rest_element) { if (in_rest_element) {
context_rest_properties.set(node.name, node); context_rest_properties.set(node.name, node);
} }
} else if (node.type === 'ArrayPattern') { }
node.elements.forEach((element: Pattern | null, i: number) => { else if (node.type === 'ArrayPattern') {
node.elements.forEach((element, i) => {
if (!element) { if (!element) {
return; return;
} else if (element.type === 'RestElement') { }
else if (element.type === 'RestElement') {
unpack_destructuring({ unpack_destructuring({
contexts, contexts,
node: element.argument, node: element.argument,
modifier: (node) => x`${modifier(node)}.slice(${i})` as Node, modifier: (node) => /** @type {import('estree').Node} */ (x `${modifier(node)}.slice(${i})`),
default_modifier, default_modifier,
scope, scope,
component, component,
context_rest_properties, context_rest_properties,
in_rest_element: true in_rest_element: true
}); });
} else if (element.type === 'AssignmentPattern') { }
else if (element.type === 'AssignmentPattern') {
const n = contexts.length; const n = contexts.length;
mark_referenced(element.right, scope, component); mark_referenced(element.right, scope, component);
unpack_destructuring({ unpack_destructuring({
contexts, contexts,
node: element.left, node: element.left,
modifier: (node) => x `${modifier(node)}[${i}]`, modifier: (node) => x `${modifier(node)}[${i}]`,
default_modifier: (node, to_ctx) => default_modifier: (node, to_ctx) =>
x`${node} !== undefined ? ${node} : ${update_reference( /** @type {import('estree').Node} */ (x `${node} !== undefined ? ${node} : ${update_reference(contexts, n, element.right, to_ctx)}`),
contexts,
n,
element.right,
to_ctx
)}` as Node,
scope, scope,
component, component,
context_rest_properties, context_rest_properties,
in_rest_element in_rest_element
}); });
} else { }
else {
unpack_destructuring({ unpack_destructuring({
contexts, contexts,
node: element, node: element,
modifier: (node) => x`${modifier(node)}[${i}]` as Node, modifier: (node) => /** @type {import('estree').Node} */ (x `${modifier(node)}[${i}]`),
default_modifier, default_modifier,
scope, scope,
component, component,
@ -103,75 +74,69 @@ export function unpack_destructuring({
}); });
} }
}); });
} else if (node.type === 'ObjectPattern') { }
else if (node.type === 'ObjectPattern') {
const used_properties = []; const used_properties = [];
node.properties.forEach((property) => { node.properties.forEach((property) => {
if (property.type === 'RestElement') { if (property.type === 'RestElement') {
unpack_destructuring({ unpack_destructuring({
contexts, contexts,
node: property.argument, node: property.argument,
modifier: (node) => modifier: (node) =>
x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node, /** @type {import('estree').Node} */ (x `@object_without_properties(${modifier(node)}, [${used_properties}])`),
default_modifier, default_modifier,
scope, scope,
component, component,
context_rest_properties, context_rest_properties,
in_rest_element: true in_rest_element: true
}); });
} else if (property.type === 'Property') { }
else if (property.type === 'Property') {
const key = property.key; const key = property.key;
const value = property.value; const value = property.value;
let new_modifier: (node: Node) => Node; /** @type {(node: import('estree').Node) => import('estree').Node} */
let new_modifier;
if (property.computed) { if (property.computed) {
// e.g { [computedProperty]: ... } // e.g { [computedProperty]: ... }
const property_name = component.get_unique_name('computed_property'); const property_name = component.get_unique_name('computed_property');
contexts.push({ contexts.push({
type: 'ComputedProperty', type: 'ComputedProperty',
property_name, property_name,
key key
}); });
new_modifier = (node) => x `${modifier(node)}[${property_name}]`; new_modifier = (node) => x `${modifier(node)}[${property_name}]`;
used_properties.push(x `${property_name}`); used_properties.push(x `${property_name}`);
} else if (key.type === 'Identifier') { }
else if (key.type === 'Identifier') {
// e.g. { someProperty: ... } // e.g. { someProperty: ... }
const property_name = key.name; const property_name = key.name;
new_modifier = (node) => x `${modifier(node)}.${property_name}`; new_modifier = (node) => x `${modifier(node)}.${property_name}`;
used_properties.push(x `"${property_name}"`); used_properties.push(x `"${property_name}"`);
} else if (key.type === 'Literal') { }
else if (key.type === 'Literal') {
// e.g. { "property-in-quotes": ... } or { 14: ... } // e.g. { "property-in-quotes": ... } or { 14: ... }
const property_name = key.value; const property_name = key.value;
new_modifier = (node) => x `${modifier(node)}["${property_name}"]`; new_modifier = (node) => x `${modifier(node)}["${property_name}"]`;
used_properties.push(x `"${property_name}"`); used_properties.push(x `"${property_name}"`);
} }
if (value.type === 'AssignmentPattern') { if (value.type === 'AssignmentPattern') {
// e.g. { property = default } or { property: newName = default } // e.g. { property = default } or { property: newName = default }
const n = contexts.length; const n = contexts.length;
mark_referenced(value.right, scope, component); mark_referenced(value.right, scope, component);
unpack_destructuring({ unpack_destructuring({
contexts, contexts,
node: value.left, node: value.left,
modifier: new_modifier, modifier: new_modifier,
default_modifier: (node, to_ctx) => default_modifier: (node, to_ctx) =>
x`${node} !== undefined ? ${node} : ${update_reference( /** @type {import('estree').Node} */ (x `${node} !== undefined ? ${node} : ${update_reference(contexts, n, value.right, to_ctx)}`),
contexts,
n,
value.right,
to_ctx
)}` as Node,
scope, scope,
component, component,
context_rest_properties, context_rest_properties,
in_rest_element in_rest_element
}); });
} else { }
else {
// e.g. { property } or { property: newName } // e.g. { property } or { property: newName }
unpack_destructuring({ unpack_destructuring({
contexts, contexts,
@ -189,16 +154,21 @@ export function unpack_destructuring({
} }
} }
function update_reference( /**
contexts: Context[], * @param {Context[]} contexts
n: number, * @param {number} n
expression: Expression, * @param {import('estree').Expression} expression
to_ctx: (name: string) => Node * @param {(name: string) => import('estree').Node} to_ctx
): Node { * @returns {import("C:/repos/svelte/svelte/node_modules/.pnpm/@types+estree@1.0.0/node_modules/@types/estree/index").import('estree').Node}
const find_from_context = (node: Identifier) => { */
function update_reference(contexts, n, expression, to_ctx) {
/** @param {import('estree').Identifier} node */
const find_from_context = (node) => {
for (let i = n; i < contexts.length; i++) { for (let i = n; i < contexts.length; i++) {
const cur_context = contexts[i]; const cur_context = contexts[i];
if (cur_context.type !== 'DestructuredVariable') continue; if (cur_context.type !== 'DestructuredVariable')
continue;
const { key } = cur_context; const { key } = cur_context;
if (node.name === key.name) { if (node.name === key.name) {
throw new Error(`Cannot access '${node.name}' before initialization`); throw new Error(`Cannot access '${node.name}' before initialization`);
@ -206,28 +176,30 @@ function update_reference(
} }
return to_ctx(node.name); return to_ctx(node.name);
}; };
if (expression.type === 'Identifier') { if (expression.type === 'Identifier') {
return find_from_context(expression); return find_from_context(expression);
} }
// NOTE: avoid unnecessary deep clone? // NOTE: avoid unnecessary deep clone?
expression = clone(expression) as Expression; expression = /** @type {import('estree').Expression} */ (clone(expression));
walk(expression, { walk(expression, {
enter(node, parent: Node) { enter(node, parent) {
if (is_reference(node as NodeWithPropertyDefinition, parent as NodeWithPropertyDefinition)) { if (is_reference(/** @type {import('is-reference').NodeWithPropertyDefinition} */ (node), /** @type {import('is-reference').NodeWithPropertyDefinition} */ (parent))) {
this.replace(find_from_context(node as Identifier)); this.replace(find_from_context(/** @type {import('estree').Identifier} */ (node)));
this.skip(); this.skip();
} }
} }
}); });
return expression; return expression;
} }
function mark_referenced(node: Node, scope: TemplateScope, component: Component) { /**
* @param {import('estree').Node} node
* @param {import('./TemplateScope.js').default} scope
* @param {import('../../Component.js').default} component
*/
function mark_referenced(node, scope, component) {
walk(node, { walk(node, {
enter(node: any, parent: any) { enter(node, parent) {
if (is_reference(node, parent)) { if (is_reference(node, parent)) {
const { name } = flatten_reference(node); const { name } = flatten_reference(node);
if (!scope.is_let(name) && !scope.names.has(name)) { if (!scope.is_let(name) && !scope.names.has(name)) {
@ -237,3 +209,19 @@ function mark_referenced(node: Node, scope: TemplateScope, component: Component)
} }
}); });
} }
/** @typedef {DestructuredVariable | ComputedProperty} Context */
/** @typedef {Object} ComputedProperty
* @property {'ComputedProperty'} type
* @property {Identifier} property_name
* @property {Expression|PrivateIdentifier} key
*/
/** @typedef {Object} DestructuredVariable
* @property {'DestructuredVariable'} type
* @property {Identifier} key
* @property {string} [name]
* @property {(node:Node)=>Node} modifier
* @property {(node:Node,to_ctx:(name:string)=>Node)=>Node} default_modifier
*/
Loading…
Cancel
Save