UNPKG

@glimmer/util

Version:
262 lines (236 loc) 6.98 kB
'use strict'; const EMPTY_ARRAY = Object.freeze([]); function emptyArray() { return EMPTY_ARRAY; } const EMPTY_STRING_ARRAY = emptyArray(); const EMPTY_NUMBER_ARRAY = emptyArray(); /** * This function returns `true` if the input array is the special empty array sentinel, * which is sometimes used for optimizations. */ function isEmptyArray(input) { return input === EMPTY_ARRAY; } function* reverse(input) { for (let i = input.length - 1; i >= 0; i--) { yield input[i]; } } function* enumerate(input) { let i = 0; for (const item of input) { yield [i++, item]; } } /** * Zip two tuples with the same type and number of elements. */ function* zipTuples(left, right) { for (let i = 0; i < left.length; i++) { yield [i, left[i], right[i]]; } } function* zipArrays(left, right) { for (let i = 0; i < left.length; i++) { const perform = i < right.length ? 'retain' : 'pop'; yield [perform, i, left[i], right[i]]; } for (let i = left.length; i < right.length; i++) { yield ['push', i, undefined, right[i]]; } } function unwrap(val) { return val; } function isPresentArray(list) { return list.length > 0; } function getLast(list) { return list.length === 0 ? undefined : list[list.length - 1]; } function getFirst(list) { return list.length === 0 ? undefined : list[0]; } function dict() { return Object.create(null); } function isDict(u) { return u !== null && u !== undefined; } function isIndexable(u) { return typeof u === 'function' || typeof u === 'object' && u !== null; } class StackImpl { stack; current = null; constructor(values = []) { this.stack = values; } get size() { return this.stack.length; } push(item) { this.current = item; this.stack.push(item); } pop() { let item = this.stack.pop(); this.current = getLast(this.stack) ?? null; return item === undefined ? null : item; } nth(from) { let len = this.stack.length; return len < from ? null : unwrap(this.stack[len - from]); } isEmpty() { return this.stack.length === 0; } snapshot() { return [...this.stack]; } toArray() { return this.stack; } } /// <reference types="qunit" /> let beginTestSteps; let endTestSteps; let verifySteps; let logStep; function clearElement(parent) { let current = parent.firstChild; while (current) { let next = current.nextSibling; parent.removeChild(current); current = next; } } /** Strongly hint runtimes to intern the provided string. When do I need to use this function? For the most part, never. Pre-mature optimization is bad, and often the runtime does exactly what you need it to, and more often the trade-off isn't worth it. Why? Runtimes store strings in at least 2 different representations: Ropes and Symbols (interned strings). The Rope provides a memory efficient data-structure for strings created from concatenation or some other string manipulation like splitting. Unfortunately checking equality of different ropes can be quite costly as runtimes must resort to clever string comparison algorithms. These algorithms typically cost in proportion to the length of the string. Luckily, this is where the Symbols (interned strings) shine. As Symbols are unique by their string content, equality checks can be done by pointer comparison. How do I know if my string is a rope or symbol? Typically (warning general sweeping statement, but truthy in runtimes at present) static strings created as part of the JS source are interned. Strings often used for comparisons can be interned at runtime if some criteria are met. One of these criteria can be the size of the entire rope. For example, in chrome 38 a rope longer then 12 characters will not intern, nor will segments of that rope. Some numbers: http://jsperf.com/eval-vs-keys/8 Known Trick™ @private @return {String} interned version of the provided string */ function intern(str) { let obj = {}; obj[str] = 1; for (let key in obj) { if (key === str) { return key; } } return str; } const SERIALIZATION_FIRST_NODE_STRING = '%+b:0%'; function isSerializationFirstNode(node) { return node.nodeValue === SERIALIZATION_FIRST_NODE_STRING; } let assign = Object.assign; function values(obj) { return Object.values(obj); } function entries(dict) { return Object.entries(dict); } function keys(obj) { return Object.keys(obj); } function strip(strings, ...args) { let out = ''; for (const [i, string] of enumerate(strings)) { let dynamic = args[i] !== undefined ? String(args[i]) : ''; out += `${string}${dynamic}`; } let lines = out.split('\n'); while (isPresentArray(lines) && /^\s*$/u.test(getFirst(lines))) { lines.shift(); } while (isPresentArray(lines) && /^\s*$/u.test(getLast(lines))) { lines.pop(); } let min = Infinity; for (let line of lines) { let leading = /^\s*/u.exec(line)[0].length; min = Math.min(min, leading); } let stripped = []; for (let line of lines) { stripped.push(line.slice(min)); } return stripped.join('\n'); } /** * This constant exists to make it easier to differentiate normal logs from * errant console.logs. LOCAL_LOGGER should only be used inside a * LOCAL_TRACE_LOGGING check. * * It does not alleviate the need to check LOCAL_TRACE_LOGGING, which is used * for stripping. */ const LOCAL_LOGGER = console; /** * This constant exists to make it easier to differentiate normal logs from * errant console.logs. LOGGER can be used outside of LOCAL_TRACE_LOGGING checks, * and is meant to be used in the rare situation where a console.* call is * actually appropriate. */ const LOGGER = console; function assertNever(value, desc = 'unexpected unreachable branch') { LOGGER.log('unreachable', value); LOGGER.log(`${desc} :: ${JSON.stringify(value)} (${value})`); throw new Error(`code reached unreachable`); } exports.EMPTY_ARRAY = EMPTY_ARRAY; exports.EMPTY_NUMBER_ARRAY = EMPTY_NUMBER_ARRAY; exports.EMPTY_STRING_ARRAY = EMPTY_STRING_ARRAY; exports.LOCAL_LOGGER = LOCAL_LOGGER; exports.LOGGER = LOGGER; exports.SERIALIZATION_FIRST_NODE_STRING = SERIALIZATION_FIRST_NODE_STRING; exports.Stack = StackImpl; exports.assertNever = assertNever; exports.assign = assign; exports.beginTestSteps = beginTestSteps; exports.clearElement = clearElement; exports.dict = dict; exports.emptyArray = emptyArray; exports.endTestSteps = endTestSteps; exports.entries = entries; exports.enumerate = enumerate; exports.intern = intern; exports.isDict = isDict; exports.isEmptyArray = isEmptyArray; exports.isIndexable = isIndexable; exports.isSerializationFirstNode = isSerializationFirstNode; exports.keys = keys; exports.logStep = logStep; exports.reverse = reverse; exports.strip = strip; exports.values = values; exports.verifySteps = verifySteps; exports.zipArrays = zipArrays; exports.zipTuples = zipTuples; //# sourceMappingURL=index.cjs.map