bunch uf utils

pull/8569/head
Simon Holthausen 3 years ago
parent 49880c0930
commit dfee586567

@ -1,14 +1,14 @@
import * as assert from 'assert'; import * as assert from 'assert';
import get_name_from_filename from './get_name_from_filename'; import get_name_from_filename from './get_name_from_filename.js';
import { import {
is_contenteditable, is_contenteditable,
has_contenteditable_attr, has_contenteditable_attr,
is_name_contenteditable, is_name_contenteditable,
get_contenteditable_attr, get_contenteditable_attr,
CONTENTEDITABLE_BINDINGS CONTENTEDITABLE_BINDINGS
} from './contenteditable'; } from './contenteditable.js';
import Element from '../nodes/Element'; import Element from '../nodes/Element.js';
import Attribute from '../nodes/Attribute'; import Attribute from '../nodes/Attribute.js';
describe('get_name_from_filename', () => { describe('get_name_from_filename', () => {
it('uses the basename', () => { it('uses the basename', () => {
@ -33,34 +33,34 @@ describe('get_name_from_filename', () => {
describe('contenteditable', () => { describe('contenteditable', () => {
describe('is_contenteditable', () => { describe('is_contenteditable', () => {
it('returns false if node is input', () => { it('returns false if node is input', () => {
const node = { name: 'input' } as Element; const node = /** @type {Element} */ ({ name: 'input' });
assert.equal(is_contenteditable(node), false); assert.equal(is_contenteditable(node), false);
}); });
it('returns false if node is textarea', () => { it('returns false if node is textarea', () => {
const node = { name: 'textarea' } as Element; const node = /** @type {Element} */ ({ name: 'textarea' });
assert.equal(is_contenteditable(node), false); assert.equal(is_contenteditable(node), false);
}); });
it('returns false if node is not input or textarea AND it is not contenteditable', () => { it('returns false if node is not input or textarea AND it is not contenteditable', () => {
const attr = { name: 'href' } as Attribute; const attr = /** @type {Attribute} */ ({ name: 'href' });
const node = { name: 'a', attributes: [attr] } as Element; const node = /** @type {Element} */ ({ name: 'a', attributes: [attr] });
assert.equal(is_contenteditable(node), false); assert.equal(is_contenteditable(node), false);
}); });
it('returns true if node is not input or textarea AND it is contenteditable', () => { it('returns true if node is not input or textarea AND it is contenteditable', () => {
const attr = { name: 'contenteditable' } as Attribute; const attr = /** @type {Attribute} */ ({ name: 'contenteditable' });
const node = { name: 'a', attributes: [attr] } as Element; const node = /** @type {Element} */ ({ name: 'a', attributes: [attr] });
assert.equal(is_contenteditable(node), true); assert.equal(is_contenteditable(node), true);
}); });
}); });
describe('has_contenteditable_attr', () => { describe('has_contenteditable_attr', () => {
it('returns true if attribute is contenteditable', () => { it('returns true if attribute is contenteditable', () => {
const attr = { name: 'contenteditable' } as Attribute; const attr = /** @type {Attribute} */ ({ name: 'contenteditable' });
const node = { attributes: [attr] } as Element; const node = /** @type {Element} */ ({ attributes: [attr] });
assert.equal(has_contenteditable_attr(node), true); assert.equal(has_contenteditable_attr(node), true);
}); });
it('returns false if attribute is not contenteditable', () => { it('returns false if attribute is not contenteditable', () => {
const attr = { name: 'href' } as Attribute; const attr = /** @type {Attribute} */ ({ name: 'href' });
const node = { attributes: [attr] } as Element; const node = /** @type {Element} */ ({ attributes: [attr] });
assert.equal(has_contenteditable_attr(node), false); assert.equal(has_contenteditable_attr(node), false);
}); });
}); });
@ -76,12 +76,12 @@ describe('contenteditable', () => {
describe('get_contenteditable_attr', () => { describe('get_contenteditable_attr', () => {
it('returns the contenteditable Attribute if it exists', () => { it('returns the contenteditable Attribute if it exists', () => {
const attr = { name: 'contenteditable' } as Attribute; const attr = /** @type {Attribute} */ ({ name: 'contenteditable' });
const node = { name: 'div', attributes: [attr] } as Element; const node = /** @type {Element} */ ({ name: 'div', attributes: [attr] });
assert.equal(get_contenteditable_attr(node), attr); assert.equal(get_contenteditable_attr(node), attr);
}); });
it('returns undefined if contenteditable attribute cannot be found', () => { it('returns undefined if contenteditable attribute cannot be found', () => {
const node = { name: 'div', attributes: [] } as Element; const node = /** @type {Element} */ ({ name: 'div', attributes: [] });
assert.equal(get_contenteditable_attr(node), undefined); assert.equal(get_contenteditable_attr(node), undefined);
}); });
}); });

@ -0,0 +1,11 @@
/**
* @template T
* @param {Set<T>} a
* @param {Set<T> | T[]} b
*/
export default function add_to_set(a, b) {
// @ts-ignore
b.forEach((item) => {
a.add(item);
});
}

@ -1,6 +0,0 @@
export default function add_to_set<T>(a: Set<T>, b: Set<T> | T[]) {
// @ts-ignore
b.forEach((item) => {
a.add(item);
});
}

@ -0,0 +1,7 @@
/**
* @param {import("../../interfaces.js").EnableSourcemap} enable_sourcemap
* @param {keyof Extract<import("../../interfaces.js").EnableSourcemap, object>} namespace
*/
export default function check_enable_sourcemap(enable_sourcemap, namespace) {
return typeof enable_sourcemap === 'boolean' ? enable_sourcemap : enable_sourcemap[namespace];
}

@ -1,8 +0,0 @@
import { EnableSourcemap } from '../../interfaces';
export default function check_enable_sourcemap(
enable_sourcemap: EnableSourcemap,
namespace: keyof Extract<EnableSourcemap, object>
) {
return typeof enable_sourcemap === 'boolean' ? enable_sourcemap : enable_sourcemap[namespace];
}

@ -1,5 +1,10 @@
export default function check_graph_for_cycles(edges: Array<[any, any]>): any[] { /**
const graph: Map<any, any[]> = edges.reduce((g, edge) => { * @param {Array<[any, any]>} edges
* @returns {any[]}
*/
export default function check_graph_for_cycles(edges) {
/** @type {Map<any, any[]>} */
const graph = edges.reduce((g, edge) => {
const [u, v] = edge; const [u, v] = edge;
if (!g.has(u)) g.set(u, []); if (!g.has(u)) g.set(u, []);
if (!g.has(v)) g.set(v, []); if (!g.has(v)) g.set(v, []);

@ -1,10 +1,10 @@
import { TemplateLiteral } from 'estree'; import { escape_template } from './stringify.js';
import { escape_template } from './stringify';
/** /**
* Collapse string literals together * Collapse string literals together
* @param {import('estree').TemplateLiteral} literal
*/ */
export function collapse_template_literal(literal: TemplateLiteral) { export function collapse_template_literal(literal) {
if (!literal.quasis.length) return; if (!literal.quasis.length) return;
const collapsed_quasis = []; const collapsed_quasis = [];

@ -0,0 +1,21 @@
/**
* @param {import('estree').Node | void} a
* @param {import('estree').Node | void} b
*/
export function compare_node(a, b) {
if (a === b) return true;
if (!a || !b) return false;
if (a.type !== b.type) return false;
switch (a.type) {
case 'Identifier':
return a.name === /** @type {import('estree').Identifier} */ (b).name;
case 'MemberExpression':
return (
compare_node(a.object, /** @type {import('estree').MemberExpression} */ (b).object) &&
compare_node(a.property, /** @type {import('estree').MemberExpression} */ (b).property) &&
a.computed === /** @type {import('estree').MemberExpression} */ (b).computed
);
case 'Literal':
return a.value === /** @type {import('estree').Literal} */ (b).value;
}
}

@ -1,19 +0,0 @@
import { Node, Literal, Identifier, MemberExpression } from 'estree';
export function compare_node(a: Node | void, b: Node | void) {
if (a === b) return true;
if (!a || !b) return false;
if (a.type !== b.type) return false;
switch (a.type) {
case 'Identifier':
return a.name === (b as Identifier).name;
case 'MemberExpression':
return (
compare_node(a.object, (b as MemberExpression).object) &&
compare_node(a.property, (b as MemberExpression).property) &&
a.computed === (b as MemberExpression).computed
);
case 'Literal':
return a.value === (b as Literal).value;
}
}

@ -1,6 +1,6 @@
// Utilities for managing contenteditable nodes // Utilities for managing contenteditable nodes
import Attribute from '../nodes/Attribute'; import Attribute from '../nodes/Attribute.js';
import Element from '../nodes/Element'; import Element from '../nodes/Element.js';
export const CONTENTEDITABLE_BINDINGS = ['textContent', 'innerHTML', 'innerText']; export const CONTENTEDITABLE_BINDINGS = ['textContent', 'innerHTML', 'innerText'];
@ -8,7 +8,7 @@ export const CONTENTEDITABLE_BINDINGS = ['textContent', 'innerHTML', 'innerText'
* Returns true if node is an 'input' or 'textarea'. * Returns true if node is an 'input' or 'textarea'.
* @param {Element} node The element to be checked * @param {Element} node The element to be checked
*/ */
function is_input_or_textarea(node: Element): boolean { function is_input_or_textarea(node) {
return node.name === 'textarea' || node.name === 'input'; return node.name === 'textarea' || node.name === 'input';
} }
@ -16,7 +16,7 @@ function is_input_or_textarea(node: Element): boolean {
* Check if a given attribute is 'contenteditable'. * Check if a given attribute is 'contenteditable'.
* @param {Attribute} attribute A node.attribute * @param {Attribute} attribute A node.attribute
*/ */
function is_attr_contenteditable(attribute: Attribute): boolean { function is_attr_contenteditable(attribute) {
return attribute.name === 'contenteditable'; return attribute.name === 'contenteditable';
} }
@ -24,7 +24,7 @@ function is_attr_contenteditable(attribute: Attribute): boolean {
* Check if any of a node's attributes are 'contentenditable'. * Check if any of a node's attributes are 'contentenditable'.
* @param {Element} node The element to be checked * @param {Element} node The element to be checked
*/ */
export function has_contenteditable_attr(node: Element): boolean { export function has_contenteditable_attr(node) {
return node.attributes.some(is_attr_contenteditable); return node.attributes.some(is_attr_contenteditable);
} }
@ -32,7 +32,7 @@ export function has_contenteditable_attr(node: Element): boolean {
* Returns true if node is not textarea or input, but has 'contenteditable' attribute. * Returns true if node is not textarea or input, but has 'contenteditable' attribute.
* @param {Element} node The element to be tested * @param {Element} node The element to be tested
*/ */
export function is_contenteditable(node: Element): boolean { export function is_contenteditable(node) {
return !is_input_or_textarea(node) && has_contenteditable_attr(node); return !is_input_or_textarea(node) && has_contenteditable_attr(node);
} }
@ -40,7 +40,7 @@ export function is_contenteditable(node: Element): boolean {
* Returns true if a given binding/node is contenteditable. * Returns true if a given binding/node is contenteditable.
* @param {string} name A binding or node name to be checked * @param {string} name A binding or node name to be checked
*/ */
export function is_name_contenteditable(name: string): boolean { export function is_name_contenteditable(name) {
return CONTENTEDITABLE_BINDINGS.includes(name); return CONTENTEDITABLE_BINDINGS.includes(name);
} }
@ -48,6 +48,6 @@ export function is_name_contenteditable(name: string): boolean {
* Returns the contenteditable attribute from the node (if it exists). * Returns the contenteditable attribute from the node (if it exists).
* @param {Element} node The element to get the attribute from * @param {Element} node The element to get the attribute from
*/ */
export function get_contenteditable_attr(node: Element): Attribute | undefined { export function get_contenteditable_attr(node) {
return node.attributes.find(is_attr_contenteditable); return node.attributes.find(is_attr_contenteditable);
} }

@ -1,6 +1,7 @@
import { Node, Identifier } from 'estree'; /**
* @param {import('estree').Node} node
export default function flatten_reference(node: Node) { */
export default function flatten_reference(node) {
const nodes = []; const nodes = [];
const parts = []; const parts = [];
@ -8,7 +9,7 @@ export default function flatten_reference(node: Node) {
nodes.unshift(node.property); nodes.unshift(node.property);
if (!node.computed) { if (!node.computed) {
parts.unshift((node.property as Identifier).name); parts.unshift(/** @type {import('estree').Identifier} */ (node.property).name);
} else { } else {
const computed_property = to_string(node.property); const computed_property = to_string(node.property);
if (computed_property) { if (computed_property) {
@ -28,7 +29,10 @@ export default function flatten_reference(node: Node) {
return { name, nodes, parts }; return { name, nodes, parts };
} }
function to_string(node: Node) { /**
* @param {import('estree').Node} node
*/
function to_string(node) {
switch (node.type) { switch (node.type) {
case 'Literal': case 'Literal':
return String(node.value); return String(node.value);

@ -4,7 +4,10 @@ const regex_repeated_invalid_variable_identifier_characters = /[^a-zA-Z_$0-9]+/g
const regex_starts_with_digit = /^(\d)/; const regex_starts_with_digit = /^(\d)/;
const regex_may_starts_or_ends_with_underscore = /^_?(.+?)_?$/; const regex_may_starts_or_ends_with_underscore = /^_?(.+?)_?$/;
export default function get_name_from_filename(filename: string) { /**
* @param {string} filename
*/
export default function get_name_from_filename(filename) {
if (!filename) return null; if (!filename) return null;
const parts = filename.split(/[/\\]/).map(encodeURI); const parts = filename.split(/[/\\]/).map(encodeURI);

@ -0,0 +1,7 @@
/**
* @param {import('estree').Node} node
*/
export default function get_object(node) {
while (node.type === 'MemberExpression') node = node.object;
return /** @type {import('estree').Identifier} */ (node);
}

@ -1,6 +0,0 @@
import { Node, Identifier } from 'estree';
export default function get_object(node: Node): Identifier {
while (node.type === 'MemberExpression') node = node.object;
return node as Identifier;
}

@ -2,7 +2,11 @@
const regex_return_characters = /\r/g; const regex_return_characters = /\r/g;
export default function hash(str: string): string { /**
* @param {string} str
* @returns {string}
*/
export default function hash(str) {
str = str.replace(regex_return_characters, ''); str = str.replace(regex_return_characters, '');
let hash = 5381; let hash = 5381;
let i = str.length; let i = str.length;

@ -1,8 +1,18 @@
import { Node } from 'estree'; import is_reference from 'is-reference';
import is_reference, { NodeWithPropertyDefinition } from 'is-reference';
export default function is_used_as_reference(node: Node, parent: Node): boolean { /**
if (!is_reference(node as NodeWithPropertyDefinition, parent as NodeWithPropertyDefinition)) { *
* @param {import('estree').Node} node
* @param {import('estree').Node} parent
* @returns {boolean}
*/
export default function is_used_as_reference(node, parent) {
if (
!is_reference(
/** @type {import('is-reference').NodeWithPropertyDefinition} */ (node),
/** @type {import('is-reference').NodeWithPropertyDefinition} */ (parent)
)
) {
return false; return false;
} }
if (!parent) { if (!parent) {

@ -0,0 +1,16 @@
/**
* @param {import('estree').Node} node
* @param {import('estree').Node} replacement
*/
export default function replace_object(node, replacement) {
if (node.type === 'Identifier') return replacement;
const ancestor = node;
let parent;
while (node.type === 'MemberExpression') {
parent = node;
node = node.object;
}
parent.object = /** @type {any} */ (replacement);
return ancestor;
}

@ -1,14 +0,0 @@
import { Node } from 'estree';
export default function replace_object(node: Node, replacement: Node) {
if (node.type === 'Identifier') return replacement;
const ancestor = node;
let parent;
while (node.type === 'MemberExpression') {
parent = node;
node = node.object;
}
parent.object = replacement;
return ancestor;
}

@ -1,5 +1,6 @@
export const reserved_keywords = new Set(['$$props', '$$restProps', '$$slots']); export const reserved_keywords = new Set(['$$props', '$$restProps', '$$slots']);
/** @param {string} name */
export function is_reserved_keyword(name) { export function is_reserved_keyword(name) {
return reserved_keywords.has(name); return reserved_keywords.has(name);
} }

@ -1,7 +1,9 @@
import { Node } from 'estree';
import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic'; import { analyze, Scope, extract_names, extract_identifiers } from 'periscopic';
export function create_scopes(expression: Node) { /**
* @param {import('estree').Node} expression
*/
export function create_scopes(expression) {
return analyze(expression); return analyze(expression);
} }

@ -0,0 +1,19 @@
/**
* @param {string} name
*/
export function string_to_member_expression(name) {
const parts = name.split('.');
/** @type {import('estree').MemberExpression | import('estree').Identifier} */
let node = {
type: 'Identifier',
name: parts[0]
};
for (let i = 1; i < parts.length; i++) {
node = /** @type {import('estree').MemberExpression} */ ({
type: 'MemberExpression',
object: node,
property: { type: 'Identifier', name: parts[i] }
});
}
return node;
}

@ -1,17 +0,0 @@
import { MemberExpression, Identifier } from 'estree';
export function string_to_member_expression(name: string) {
const parts = name.split('.');
let node: MemberExpression | Identifier = {
type: 'Identifier',
name: parts[0]
};
for (let i = 1; i < parts.length; i++) {
node = {
type: 'MemberExpression',
object: node,
property: { type: 'Identifier', name: parts[i] }
} as MemberExpression;
}
return node;
}

@ -1,13 +1,19 @@
import { Literal } from 'estree'; /**
export function string_literal(data: string): Literal { * @param {string} data
* @returns {import('estree').Literal}
*/
export function string_literal(data) {
return { return {
type: 'Literal', type: 'Literal',
value: data value: data
}; };
} }
/**
export function escape(data: string, { only_escape_at_symbol = false } = {}) { * @param {string} data
return data.replace(only_escape_at_symbol ? /@+/g : /(@+|#+)/g, (match: string) => { * @param {{ only_escape_at_symbol?: boolean }} [options]
*/
export function escape(data, { only_escape_at_symbol = false } = {}) {
return data.replace(only_escape_at_symbol ? /@+/g : /(@+|#+)/g, (match) => {
return match + match[0]; return match + match[0];
}); });
} }
Loading…
Cancel
Save