UNPKG

18.1 kBJavaScriptView Raw
1import { DEBUG } from '@glimmer/env';
2
3const EMPTY_ARRAY = Object.freeze([]);
4function emptyArray() {
5 return EMPTY_ARRAY;
6}
7const EMPTY_STRING_ARRAY = emptyArray();
8const EMPTY_NUMBER_ARRAY = emptyArray();
9
10/**
11 * This function returns `true` if the input array is the special empty array sentinel,
12 * which is sometimes used for optimizations.
13 */
14function isEmptyArray(input) {
15 return input === EMPTY_ARRAY;
16}
17function* reverse(input) {
18 for (let i = input.length - 1; i >= 0; i--) {
19 yield input[i];
20 }
21}
22function* enumerate(input) {
23 let i = 0;
24 for (const item of input) {
25 yield [i++, item];
26 }
27}
28
29// import Logger from './logger';
30
31
32// let alreadyWarned = false;
33
34function debugAssert(test, msg) {
35 // if (!alreadyWarned) {
36 // alreadyWarned = true;
37 // Logger.warn("Don't leave debug assertions on in public builds");
38 // }
39
40 if (!test) {
41 throw new Error(msg || 'assertion failure');
42 }
43}
44function deprecate(desc) {
45 LOCAL_LOGGER.warn(`DEPRECATION: ${desc}`);
46}
47
48function keys(obj) {
49 return Object.keys(obj);
50}
51function unwrap(val) {
52 if (val === null || val === undefined) throw new Error(`Expected value to be present`);
53 return val;
54}
55function expect(val, message) {
56 if (val === null || val === undefined) throw new Error(message);
57 return val;
58}
59function unreachable(message = 'unreachable') {
60 return new Error(message);
61}
62function exhausted(value) {
63 throw new Error(`Exhausted ${String(value)}`);
64}
65const tuple = (...args) => args;
66
67function isPresent(value) {
68 return value !== null && value !== undefined;
69}
70function assertPresent(value, message) {
71 if (!isPresent(value)) {
72 throw new Error(`Expected present, got ${typeof value === 'string' ? value : message}`);
73 }
74}
75function isPresentArray(list) {
76 return list.length > 0;
77}
78function ifPresent(list, ifPresent, otherwise) {
79 if (isPresentArray(list)) {
80 return ifPresent(list);
81 } else {
82 return otherwise();
83 }
84}
85function arrayToOption(list) {
86 if (isPresentArray(list)) {
87 return list;
88 } else {
89 return null;
90 }
91}
92function assertPresentArray(list, message = `unexpected empty list`) {
93 if (!isPresentArray(list)) {
94 throw new Error(message);
95 }
96}
97function asPresentArray(list, message = `unexpected empty list`) {
98 assertPresentArray(list, message);
99 return list;
100}
101function getLast(list) {
102 return list.length === 0 ? undefined : list[list.length - 1];
103}
104function getFirst(list) {
105 return list.length === 0 ? undefined : list[0];
106}
107function mapPresentArray(list, mapper) {
108 if (list === null) {
109 return null;
110 }
111 let out = [];
112 for (let item of list) {
113 out.push(mapper(item));
114 }
115 return out;
116}
117
118function dict() {
119 return Object.create(null);
120}
121function isDict(u) {
122 return u !== null && u !== undefined;
123}
124function isObject(u) {
125 return typeof u === 'function' || typeof u === 'object' && u !== null;
126}
127class StackImpl {
128 stack;
129 current = null;
130 constructor(values = []) {
131 this.stack = values;
132 }
133 get size() {
134 return this.stack.length;
135 }
136 push(item) {
137 this.current = item;
138 this.stack.push(item);
139 }
140 pop() {
141 let item = this.stack.pop();
142 this.current = getLast(this.stack) ?? null;
143 return item === undefined ? null : item;
144 }
145 nth(from) {
146 let len = this.stack.length;
147 return len < from ? null : unwrap(this.stack[len - from]);
148 }
149 isEmpty() {
150 return this.stack.length === 0;
151 }
152 toArray() {
153 return this.stack;
154 }
155}
156
157/// <reference types="qunit" />
158
159let beginTestSteps;
160let endTestSteps;
161let verifySteps;
162let logStep;
163
164let debugToString;
165if (DEBUG) {
166 let getFunctionName = fn => {
167 let functionName = fn.name;
168 if (functionName === undefined) {
169 let match = /function (\w+)\s*\(/u.exec(String(fn));
170 functionName = match && match[1] || '';
171 }
172 return functionName.replace(/^bound /u, '');
173 };
174 let getObjectName = obj => {
175 let name;
176 let className;
177 if (obj.constructor && typeof obj.constructor === 'function') {
178 className = getFunctionName(obj.constructor);
179 }
180 if ('toString' in obj && obj.toString !== Object.prototype.toString && obj.toString !== Function.prototype.toString) {
181 name = obj.toString();
182 }
183
184 // If the class has a decent looking name, and the `toString` is one of the
185 // default Ember toStrings, replace the constructor portion of the toString
186 // with the class name. We check the length of the class name to prevent doing
187 // this when the value is minified.
188 if (name && /<.*:ember\d+>/u.test(name) && className && className[0] !== '_' && className.length > 2 && className !== 'Class') {
189 return name.replace(/<.*:/u, `<${className}:`);
190 }
191 return name || className;
192 };
193 let getPrimitiveName = value => {
194 return String(value);
195 };
196 debugToString = value => {
197 if (typeof value === 'function') {
198 return getFunctionName(value) || `(unknown function)`;
199 } else if (typeof value === 'object' && value !== null) {
200 return getObjectName(value) || `(unknown object)`;
201 } else {
202 return getPrimitiveName(value);
203 }
204 };
205}
206var debugToString$1 = debugToString;
207
208function clearElement(parent) {
209 let current = parent.firstChild;
210 while (current) {
211 let next = current.nextSibling;
212 parent.removeChild(current);
213 current = next;
214 }
215}
216
217const RAW_NODE = -1;
218const ELEMENT_NODE = 1;
219const TEXT_NODE = 3;
220const COMMENT_NODE = 8;
221const DOCUMENT_NODE = 9;
222const DOCUMENT_TYPE_NODE = 10;
223const DOCUMENT_FRAGMENT_NODE = 11;
224const NS_HTML = 'http://www.w3.org/1999/xhtml';
225const NS_MATHML = 'http://www.w3.org/1998/Math/MathML';
226const NS_SVG = 'http://www.w3.org/2000/svg';
227const NS_XLINK = 'http://www.w3.org/1999/xlink';
228const NS_XML = 'http://www.w3.org/XML/1998/namespace';
229const NS_XMLNS = 'http://www.w3.org/2000/xmlns/';
230const INSERT_BEFORE_BEGIN = 'beforebegin';
231const INSERT_AFTER_BEGIN = 'afterbegin';
232const INSERT_BEFORE_END = 'beforeend';
233const INSERT_AFTER_END = 'afterend';
234
235/*
236 Encoding notes
237
238 We use 30 bit integers for encoding, so that we don't ever encode a non-SMI
239 integer to push on the stack.
240
241 Handles are >= 0
242 Immediates are < 0
243
244 True, False, Undefined and Null are pushed as handles into the symbol table,
245 with well known handles (0, 1, 2, 3)
246
247 The negative space is divided into positives and negatives. Positives are
248 higher numbers (-1, -2, -3, etc), negatives are lower.
249
250 We only encode immediates for two reasons:
251
252 1. To transfer over the wire, so they're smaller in general
253 2. When pushing values onto the stack from the low level/inner VM, which may
254 be converted into WASM one day.
255
256 This allows the low-level VM to always use SMIs, and to minimize using JS
257 values via handles for things like the stack pointer and frame pointer.
258 Externally, most code pushes values as JS values, except when being pulled
259 from the append byte code where it was already encoded.
260
261 Logically, this is because the low level VM doesn't really care about these
262 higher level values. For instance, the result of a userland helper may be a
263 number, or a boolean, or undefined/null, but it's extra work to figure that
264 out and push it correctly, vs. just pushing the value as a JS value with a
265 handle.
266
267 Note: The details could change here in the future, this is just the current
268 strategy.
269*/
270
271let ImmediateConstants = /*#__PURE__*/function (ImmediateConstants) {
272 ImmediateConstants[ImmediateConstants["MAX_SMI"] = 1073741823] = "MAX_SMI";
273 ImmediateConstants[ImmediateConstants["MIN_SMI"] = -1073741824] = "MIN_SMI";
274 ImmediateConstants[ImmediateConstants["SIGN_BIT"] = -536870913] = "SIGN_BIT";
275 ImmediateConstants[ImmediateConstants["MAX_INT"] = 536870911] = "MAX_INT";
276 ImmediateConstants[ImmediateConstants["MIN_INT"] = -536870912] = "MIN_INT";
277 ImmediateConstants[ImmediateConstants["FALSE_HANDLE"] = 0] = "FALSE_HANDLE";
278 ImmediateConstants[ImmediateConstants["TRUE_HANDLE"] = 1] = "TRUE_HANDLE";
279 ImmediateConstants[ImmediateConstants["NULL_HANDLE"] = 2] = "NULL_HANDLE";
280 ImmediateConstants[ImmediateConstants["UNDEFINED_HANDLE"] = 3] = "UNDEFINED_HANDLE";
281 ImmediateConstants[ImmediateConstants["ENCODED_FALSE_HANDLE"] = 0] = "ENCODED_FALSE_HANDLE";
282 ImmediateConstants[ImmediateConstants["ENCODED_TRUE_HANDLE"] = 1] = "ENCODED_TRUE_HANDLE";
283 ImmediateConstants[ImmediateConstants["ENCODED_NULL_HANDLE"] = 2] = "ENCODED_NULL_HANDLE";
284 ImmediateConstants[ImmediateConstants["ENCODED_UNDEFINED_HANDLE"] = 3] = "ENCODED_UNDEFINED_HANDLE";
285 return ImmediateConstants;
286}({});
287function isHandle(value) {
288 return value >= 0;
289}
290function isNonPrimitiveHandle(value) {
291 return value > ImmediateConstants.ENCODED_UNDEFINED_HANDLE;
292}
293function constants(...values) {
294 return [false, true, null, undefined, ...values];
295}
296function isSmallInt(value) {
297 return value % 1 === 0 && value <= ImmediateConstants.MAX_INT && value >= ImmediateConstants.MIN_INT;
298}
299function encodeNegative(num) {
300 return num & ImmediateConstants.SIGN_BIT;
301}
302function decodeNegative(num) {
303 return num | ~ImmediateConstants.SIGN_BIT;
304}
305function encodePositive(num) {
306 return ~num;
307}
308function decodePositive(num) {
309 return ~num;
310}
311function encodeHandle(num) {
312 return num;
313}
314function decodeHandle(num) {
315 return num;
316}
317function encodeImmediate(num) {
318 num |= 0;
319 return num < 0 ? encodeNegative(num) : encodePositive(num);
320}
321function decodeImmediate(num) {
322 num |= 0;
323 return num > ImmediateConstants.SIGN_BIT ? decodePositive(num) : decodeNegative(num);
324}
325[1, -1].forEach(x => decodeImmediate(encodeImmediate(x)));
326
327/**
328 Strongly hint runtimes to intern the provided string.
329
330 When do I need to use this function?
331
332 For the most part, never. Pre-mature optimization is bad, and often the
333 runtime does exactly what you need it to, and more often the trade-off isn't
334 worth it.
335
336 Why?
337
338 Runtimes store strings in at least 2 different representations:
339 Ropes and Symbols (interned strings). The Rope provides a memory efficient
340 data-structure for strings created from concatenation or some other string
341 manipulation like splitting.
342
343 Unfortunately checking equality of different ropes can be quite costly as
344 runtimes must resort to clever string comparison algorithms. These
345 algorithms typically cost in proportion to the length of the string.
346 Luckily, this is where the Symbols (interned strings) shine. As Symbols are
347 unique by their string content, equality checks can be done by pointer
348 comparison.
349
350 How do I know if my string is a rope or symbol?
351
352 Typically (warning general sweeping statement, but truthy in runtimes at
353 present) static strings created as part of the JS source are interned.
354 Strings often used for comparisons can be interned at runtime if some
355 criteria are met. One of these criteria can be the size of the entire rope.
356 For example, in chrome 38 a rope longer then 12 characters will not
357 intern, nor will segments of that rope.
358
359 Some numbers: http://jsperf.com/eval-vs-keys/8
360
361 Known Trick™
362
363 @private
364 @return {String} interned version of the provided string
365*/
366function intern(str) {
367 let obj = {};
368 obj[str] = 1;
369 for (let key in obj) {
370 if (key === str) {
371 return key;
372 }
373 }
374 return str;
375}
376
377const SERIALIZATION_FIRST_NODE_STRING = '%+b:0%';
378function isSerializationFirstNode(node) {
379 return node.nodeValue === SERIALIZATION_FIRST_NODE_STRING;
380}
381
382let assign = Object.assign;
383function values(obj) {
384 return Object.values(obj);
385}
386function entries(dict) {
387 return Object.entries(dict);
388}
389
390function castToSimple(node) {
391 if (isDocument(node)) {
392 return node;
393 } else if (isSimpleElement(node)) {
394 return node;
395 } else {
396 return node;
397 }
398}
399
400// If passed a document, verify we're in the browser and return it as a Document
401
402// If we don't know what this is, but the check requires it to be an element,
403// the cast will mandate that it's a browser element
404
405// Finally, if it's a more generic check, the cast will mandate that it's a
406// browser node and return a BrowserNodeUtils corresponding to the check
407
408function castToBrowser(node, sugaryCheck) {
409 if (node === null || node === undefined) {
410 return null;
411 }
412 if (typeof document === undefined) {
413 throw new Error('Attempted to cast to a browser node in a non-browser context');
414 }
415 if (isDocument(node)) {
416 return node;
417 }
418 if (node.ownerDocument !== document) {
419 throw new Error('Attempted to cast to a browser node with a node that was not created from this document');
420 }
421 return checkBrowserNode(node, sugaryCheck);
422}
423function checkError(from, check) {
424 return new Error(`cannot cast a ${from} into ${String(check)}`);
425}
426function isDocument(node) {
427 return node.nodeType === DOCUMENT_NODE;
428}
429function isSimpleElement(node) {
430 return node?.nodeType === ELEMENT_NODE;
431}
432function isElement(node) {
433 return node?.nodeType === ELEMENT_NODE && node instanceof Element;
434}
435function checkBrowserNode(node, check) {
436 let isMatch = false;
437 if (node !== null) {
438 if (typeof check === 'string') {
439 isMatch = stringCheckNode(node, check);
440 } else if (Array.isArray(check)) {
441 isMatch = check.some(c => stringCheckNode(node, c));
442 } else {
443 throw unreachable();
444 }
445 }
446 if (isMatch && node instanceof Node) {
447 return node;
448 } else {
449 throw checkError(`SimpleElement(${node?.constructor?.name ?? 'null'})`, check);
450 }
451}
452function stringCheckNode(node, check) {
453 switch (check) {
454 case 'NODE':
455 return true;
456 case 'HTML':
457 return node instanceof HTMLElement;
458 case 'SVG':
459 return node instanceof SVGElement;
460 case 'ELEMENT':
461 return node instanceof Element;
462 default:
463 if (check.toUpperCase() === check) {
464 throw new Error(`BUG: this code is missing handling for a generic node type`);
465 }
466 return node instanceof Element && node.tagName.toLowerCase() === check;
467 }
468}
469
470function strip(strings, ...args) {
471 let out = '';
472 for (const [i, string] of enumerate(strings)) {
473 let dynamic = args[i] !== undefined ? String(args[i]) : '';
474 out += `${string}${dynamic}`;
475 }
476 let lines = out.split('\n');
477 while (isPresentArray(lines) && /^\s*$/u.test(getFirst(lines))) {
478 lines.shift();
479 }
480 while (isPresentArray(lines) && /^\s*$/u.test(getLast(lines))) {
481 lines.pop();
482 }
483 let min = Infinity;
484 for (let line of lines) {
485 let leading = /^\s*/u.exec(line)[0].length;
486 min = Math.min(min, leading);
487 }
488 let stripped = [];
489 for (let line of lines) {
490 stripped.push(line.slice(min));
491 }
492 return stripped.join('\n');
493}
494
495function unwrapHandle(handle) {
496 if (typeof handle === 'number') {
497 return handle;
498 } else {
499 let error = handle.errors[0];
500 throw new Error(`Compile Error: ${error.problem} @ ${error.span.start}..${error.span.end}`);
501 }
502}
503function unwrapTemplate(template) {
504 if (template.result === 'error') {
505 throw new Error(`Compile Error: ${template.problem} @ ${template.span.start}..${template.span.end}`);
506 }
507 return template;
508}
509function extractHandle(handle) {
510 if (typeof handle === 'number') {
511 return handle;
512 } else {
513 return handle.handle;
514 }
515}
516function isOkHandle(handle) {
517 return typeof handle === 'number';
518}
519function isErrHandle(handle) {
520 return typeof handle === 'number';
521}
522
523function buildUntouchableThis(source) {
524 let context = null;
525 if (DEBUG) {
526 let assertOnProperty = property => {
527 let access = typeof property === 'symbol' || typeof property === 'number' ? `[${String(property)}]` : `.${property}`;
528 throw new Error(`You accessed \`this${access}\` from a function passed to the ${source}, but the function itself was not bound to a valid \`this\` context. Consider updating to use a bound function (for instance, use an arrow function, \`() => {}\`).`);
529 };
530 context = new Proxy({}, {
531 get(_target, property) {
532 assertOnProperty(property);
533 },
534 set(_target, property) {
535 assertOnProperty(property);
536 return false;
537 },
538 has(_target, property) {
539 assertOnProperty(property);
540 return false;
541 }
542 });
543 }
544 return context;
545}
546
547/**
548 * This constant exists to make it easier to differentiate normal logs from
549 * errant console.logs. LOCAL_LOGGER should only be used inside a
550 * LOCAL_SHOULD_LOG check.
551 *
552 * It does not alleviate the need to check LOCAL_SHOULD_LOG, which is used
553 * for stripping.
554 */
555const LOCAL_LOGGER = console;
556
557/**
558 * This constant exists to make it easier to differentiate normal logs from
559 * errant console.logs. LOGGER can be used outside of LOCAL_SHOULD_LOG checks,
560 * and is meant to be used in the rare situation where a console.* call is
561 * actually appropriate.
562 */
563const LOGGER = console;
564function assertNever(value, desc = 'unexpected unreachable branch') {
565 LOGGER.log('unreachable', value);
566 LOGGER.log(`${desc} :: ${JSON.stringify(value)} (${value})`);
567 throw new Error(`code reached unreachable`);
568}
569
570export { COMMENT_NODE, DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, DOCUMENT_TYPE_NODE, ELEMENT_NODE, EMPTY_ARRAY, EMPTY_NUMBER_ARRAY, EMPTY_STRING_ARRAY, INSERT_AFTER_BEGIN, INSERT_AFTER_END, INSERT_BEFORE_BEGIN, INSERT_BEFORE_END, ImmediateConstants, LOCAL_LOGGER, LOGGER, NS_HTML, NS_MATHML, NS_SVG, NS_XLINK, NS_XML, NS_XMLNS, RAW_NODE, SERIALIZATION_FIRST_NODE_STRING, StackImpl as Stack, TEXT_NODE, arrayToOption, asPresentArray, debugAssert as assert, assertNever, assertPresent, assertPresentArray, assign, beginTestSteps, buildUntouchableThis, castToBrowser, castToSimple, checkBrowserNode as checkNode, clearElement, constants, debugToString$1 as debugToString, decodeHandle, decodeImmediate, decodeNegative, decodePositive, deprecate, dict, emptyArray, encodeHandle, encodeImmediate, encodeNegative, encodePositive, endTestSteps, entries, enumerate, exhausted, expect, extractHandle, getFirst, getLast, ifPresent, intern, isDict, isElement, isEmptyArray, isErrHandle, isHandle, isNonPrimitiveHandle, isObject, isOkHandle, isPresent, isPresentArray, isSerializationFirstNode, isSimpleElement, isSmallInt, keys, logStep, mapPresentArray, reverse, strip, tuple, unreachable, unwrap, unwrapHandle, unwrapTemplate, values, verifySteps };
571//# sourceMappingURL=index.js.map