UNPKG

@qwik.dev/core

Version:

An open source framework for building instant loading web apps at any scale, without the extra effort.

1,389 lines (1,358 loc) 629 kB
/** * @license * @qwik.dev/core 2.0.0-beta.36-dev+3268fab * Copyright QwikDev. All Rights Reserved. * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/QwikDev/qwik/blob/main/LICENSE */ import { isDev, isServer, isBrowser as isBrowser$1 } from '@qwik.dev/core/build'; export { isBrowser, isDev, isServer } from '@qwik.dev/core/build'; import { p } from '@qwik.dev/core/preloader'; // Direct `globalThis.X` accesses (no alias) so Terser's `global_defs` engages // and folds these to literal booleans in production builds, allowing // `qTest ? testBranch : prodBranch` shims to tree-shake. const qDev = globalThis.qDev !== false; const qInspector = globalThis.qInspector === true; const qDynamicPlatform = globalThis.qDynamicPlatform !== false; const qTest = globalThis.qTest === true; const qRuntimeQrl = globalThis.qRuntimeQrl === true; const seal = (obj) => { if (qDev) { Object.seal(obj); } }; const STYLE = qDev ? `background: #564CE0; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;` : ''; const logError = (message, ...optionalParams) => { return createAndLogError(false, message, ...optionalParams); }; const throwErrorAndStop = (message, ...optionalParams) => { const error = createAndLogError(false, message, ...optionalParams); // eslint-disable-next-line no-debugger debugger; throw error; }; const logErrorAndStop = (message, ...optionalParams) => { const err = createAndLogError(qDev, message, ...optionalParams); // eslint-disable-next-line no-debugger debugger; return err; }; const _printed = /*#__PURE__*/ new Set(); const logOnceWarn = (message, ...optionalParams) => { if (qDev) { const key = 'warn' + String(message); if (!_printed.has(key)) { _printed.add(key); logWarn(message, ...optionalParams); } } }; const logWarn = (message, ...optionalParams) => { if (qDev) { console.warn('%cQWIK WARN', STYLE, message, ...optionalParams); } }; const createAndLogError = (asyncThrow, message, ...optionalParams) => { const err = message instanceof Error ? message : new Error(message); // display the error message first, then the optional params, and finally the stack trace // the stack needs to be displayed last because the given params will be lost among large stack traces so it will // provide a bad developer experience !qTest && console.error('%cQWIK ERROR', STYLE, err.message, ...optionalParams, err.stack); asyncThrow && !qTest && setTimeout(() => { // throwing error asynchronously to avoid breaking the current call stack. // We throw so that the error is delivered to the global error handler for // reporting it to a third-party tools such as Qwik Insights, Sentry or New Relic. throw err; }, 0); return err; }; const ASSERT_DISCLAIMER = 'Internal assert, this is likely caused by a bug in Qwik: '; /*@__INLINE__*/ function assertDefined(value, text, ...parts) { if (isDev) { if (value != null) { return; } throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } /*@__INLINE__*/ function assertEqual(value1, value2, text, ...parts) { if (isDev) { if (value1 === value2) { return; } throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } /*@__INLINE__*/ function assertTrue(value1, text, ...parts) { if (isDev) { if (value1 === true) { return; } throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } /*@__INLINE__*/ function assertFalse(value1, text, ...parts) { if (isDev) { if (value1 === false) { return; } throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } /*@__INLINE__*/ function assertNumber(value1, text, ...parts) { if (isDev) { if (typeof value1 === 'number') { return; } throwErrorAndStop(ASSERT_DISCLAIMER + text, ...parts); } } /** @internal */ const mapApp_findIndx = (array, key, start) => { isDev && assertTrue(start % 2 === 0, 'Expecting even number.'); let bottom = start >> 1; let top = (array.length - 2) >> 1; while (bottom <= top) { const mid = bottom + ((top - bottom) >> 1); const midKey = array[mid << 1]; if (midKey === key) { return mid << 1; } if (midKey < key) { bottom = mid + 1; } else { top = mid - 1; } } return (bottom << 1) ^ -1; }; /** @internal */ const mapArray_set = (array, key, value, start, allowNullValue = false) => { const indx = mapApp_findIndx(array, key, start); if (indx >= 0) { if (value == null && !allowNullValue) { array.splice(indx, 2); } else { array[indx + 1] = value; } } else if (value != null || allowNullValue) { array.splice(indx ^ -1, 0, key, value); } }; /** @internal */ const mapArray_get = (array, key, start) => { const indx = mapApp_findIndx(array, key, start); if (indx >= 0) { return array[indx + 1]; } else { return null; } }; const mapArray_has = (array, key, start) => { return mapApp_findIndx(array, key, start) >= 0; }; /** @private */ const isSerializableObject = (v) => { const proto = Object.getPrototypeOf(v); return proto === Object.prototype || proto === Array.prototype || proto === null; }; const isObject = (v) => { return typeof v === 'object' && v !== null; }; const isArray = (v) => { return Array.isArray(v); }; const isString = (v) => { return typeof v === 'string'; }; const isFunction = (v) => { return typeof v === 'function'; }; const isPrimitiveOrNullUndefined = (v) => { return (typeof v !== 'object' && typeof v !== 'function') || v === null || v === undefined; }; const baseUrl = 'https://qwikdev-build-v2.qwik-8nx.pages.dev/docs/errors/#q'; const codeToText = (code, ...parts) => { if (isDev) { // Keep one error, one line to make it easier to search for the error message. // Keep in sync with packages/docs/src/routes/docs/errors/index.mdx const MAP = [ 'Error while serializing class or style attributes', // 0 'Scheduler not found', // 1 'track() received object, without prop to track', // 2 'Only primitive and object literals can be serialized. {{0}}', // 3 'You can render over a existing q:container. Skipping render().', // 4 'QRL is not a function', // 5 'Dynamic import {{0}} not found', // 6 'Unknown type argument', // 7 `Actual value for useContext({{0}}) can not be found, make sure some ancestor component has set a value using useContextProvider(). In the browser make sure that the context was used during SSR so its state was serialized.`, // 8 "Invoking 'use*()' method outside of invocation context.", // 9 `Calling a 'use*()' method outside 'component$(() => { HERE })' is not allowed. 'use*()' methods provide hooks to the 'component$' state and lifecycle, ie 'use' hooks can only be called synchronously within the 'component$' function or another 'use' method.\nSee https://qwik.dev/docs/core/tasks/#use-method-rules`, // 10 'The provided Context reference "{{0}}" is not a valid context created by createContextId()', // 11 'SsrError(tag): {{0}}', // 12 'QRLs can not be resolved because it does not have an attached container. This means that the QRL does not know where it belongs inside the DOM, so it cant dynamically import() from a relative path.', // 13 'QRLs can not be dynamically resolved, because it does not have a chunk path', // 14 '{{0}}\nThe JSX ref attribute must be a Signal', // 15 'Serialization Error: Deserialization of data type {{0}} is not implemented', // 16 'Serialization Error: Expected vnode for ref prop, but got {{0}}', // 17 'Serialization Error: Cannot allocate data type {{0}}', // 18 'Serialization Error: Missing root id for {{0}}', // 19 'Serialization Error: Serialization of data type {{0}} is not implemented', // 20 'Serialization Error: Unvisited {{0}}', // 21 'Serialization Error: Missing QRL chunk for {{0}}', // 22 '{{0}}\nThe value of the textarea must be a string found {{1}}', // 23 'Unable to find q:container', // 24 "Element must have 'q:container' attribute.", // 25 'Unknown vnode type {{0}}.', // 26 'Materialize error: missing element: {{0}} {{1}} {{2}}', // 27 'Cannot coerce a Signal, use `.value` instead', // 28 'useComputed$ QRL {{0}} {{1}} cannot return a Promise', // 29 '===\nQwik version {{0}} already imported while importing {{1}}.\nThis can lead to issues due to duplicated shared structures.\nVerify that the Qwik libraries you\'re using are in "resolve.noExternal[]" and in "optimizeDeps.exclude".\n===\n', // 30 'WrappedSignal is read-only', // 31 'Attribute value is unsafe for SSR {{0}}', // 32 'SerializerSymbol function returned rejected promise', // 33 'Serialization Error: Cannot serialize function: {{0}}', // 34 'Cannot read .value of a clientOnly async signal during SSR. Use .loading to check state, or provide an initial value.', // 35 ]; let text = MAP[code] ?? ''; if (parts.length) { text = text.replaceAll(/{{(\d+)}}/g, (_, index) => { let v = parts[index]; if (v && isObject(v) && v.constructor === Object) { v = JSON.stringify(v).slice(0, 50); } return v; }); } return `Code(Q${code}): ${text}`; } else { return `Code(Q${code}) ${baseUrl}${code}`; } }; const qError = (code, errorMessageArgs = []) => { const text = codeToText(code, ...errorMessageArgs); return logErrorAndStop(text, ...errorMessageArgs); }; /** QRL related utilities that you can import without importing all of Qwik. */ const SYNC_QRL = '<sync>'; /** Sync QRL is a function which is serialized into `<script q:func="qwik/json">` tag. */ const isSyncQrl = (value) => { return isQrl(value) && value.$symbol$ == SYNC_QRL; }; const isQrl = (value) => { return typeof value === 'function' && typeof value.getSymbol === 'function'; }; function assertQrl(qrl) { if (isDev) { if (!isQrl(qrl)) { throw new Error('Not a QRL'); } } } const getSymbolHash = (symbolName) => { const index = symbolName.lastIndexOf('_') + 1; return symbolName.slice(index); }; /** * A friendly name tag for a VirtualVNode. * * Theses are used to give a name to a VirtualVNode. This is useful for debugging and testing. * * The name is only added in development mode and is not included in production builds. */ const DEBUG_TYPE = 'q:type'; const VirtualTypeName = { ["V" /* VirtualType.Virtual */]: /* ********* */ 'Virtual', // ["F" /* VirtualType.Fragment */]: /* ******** */ 'Fragment', // ["S" /* VirtualType.WrappedSignal */]: /* *** */ 'Signal', // ["A" /* VirtualType.Awaited */]: /* ********* */ 'Awaited', // ["C" /* VirtualType.Component */]: /* ******* */ 'Component', // ["I" /* VirtualType.InlineComponent */]: /* * */ 'InlineComponent', // ["P" /* VirtualType.Projection */]: /* ****** */ 'Projection', // }; /** * @file * * VNodeData is additional information which allows the `vnode` to recover virtual VNode information * from the HTML. */ /** * VNodeDataSeparator contains information about splitting up the VNodeData and attaching it to the * HTML. */ const VNodeDataSeparator = { REFERENCE: /* ******** */ 126, // `~` is a reference to the node. Save it. ADVANCE_1: /* ********* */ 33, // `!` is vNodeData separator skipping 0. (ie next vNode) ADVANCE_8192: /* ****** */ 46, // `.` is vNodeData separator skipping 4096. }; /** * VNodeDataChar contains information about the VNodeData used for encoding props. * * Available character ranges: 59 - 64, 91 - 94, 96, 123 - 126 */ const VNodeDataChar = { OPEN: /* ************** */ 123, // `{` is the start of the VNodeData for a virtual element. CLOSE: /* ************* */ 125, // `}` is the end of the VNodeData for a virtual element. SCOPED_STYLE: /* ******* */ 59, // `;` - `q:sstyle` - Style attribute. RENDER_FN: /* ********** */ 60, // `<` - `q:renderFn' - Component QRL render function (body) ID: /* ***************** */ 61, // `=` - `q:id` - ID of the element. PROPS: /* ************** */ 62, // `>` - `q:props' - Component Props SLOT_PARENT: /* ******** */ 63, // `?` - `q:sparent` - Slot parent. KEY: /* **************** */ 64, // `@` - `q:key` - Element key. SEQ: /* **************** */ 91, // `[` - `q:seq' - Seq value from `useSequentialScope()` CONTEXT: /* ************ */ 93, // `]` - `q:ctx' - Component context/props SEQ_IDX: /* ************ */ 94, // `^` - `q:seqIdx' - Sequential scope id BACK_REFS: /* ********** */ 96, // '`' - `q:brefs' - Effect dependencies/subscriptions SEPARATOR: /* ********* */ 124, // `|` - Separator char to encode any key/value pairs. SLOT: /* ************** */ 126}; /** * Convert a segment-local vnode index into a key for the root container's shared qVNodeRefs map. * * Out-of-order Suspense segments each start counting vnode refs from 0, but the client merges all * segment refs into one root-level map. This uses a Cantor-style pairing function for the * zero-based pair `(segmentIndex, localIndex)`, then makes it negative so segment refs cannot * collide with root refs, which are non-negative. * * Examples: * * - Segment 1, local 0 -> -1 * - Segment 2, local 0 -> -2 * - Segment 1, local 1 -> -3 */ const getSegmentVNodeRefId = (segmentId, localIndex) => { const segmentIndex = parseInt(segmentId, 10) - 1; const diagonal = segmentIndex + localIndex; return -((diagonal * (diagonal + 1)) / 2 + localIndex + 1); }; function escapeHTML(html) { let escapedHTML = ''; const length = html.length; let idx = 0; let lastIdx = idx; for (; idx < length; idx++) { // We get the charCode NOT string. String would allocate memory. const ch = html.charCodeAt(idx); // Every time we concat a string we allocate memory. We want to minimize that. if (ch === 60 /* < */) { escapedHTML += html.substring(lastIdx, idx) + '&lt;'; } else if (ch === 62 /* > */) { escapedHTML += html.substring(lastIdx, idx) + '&gt;'; } else if (ch === 38 /* & */) { escapedHTML += html.substring(lastIdx, idx) + '&amp;'; } else if (ch === 34 /* " */) { escapedHTML += html.substring(lastIdx, idx) + '&quot;'; } else if (ch === 39 /* ' */) { escapedHTML += html.substring(lastIdx, idx) + '&#39;'; } else { continue; } lastIdx = idx + 1; } if (lastIdx === 0) { // This is most common case, just return previous string no memory allocation. return html; } else { // Add the tail of replacement. return escapedHTML + html.substring(lastIdx); } } function decodeVNodeDataString(str) { let result = ''; for (let i = 0; i < str.length; i++) { if (str.charAt(i) === '\\' && i + 1 < str.length) { result += str.charAt(i + 1); i++; } else { result += str.charAt(i); } } return result; } /** State factory of the component. */ const OnRenderProp = 'q:renderFn'; /** Target DOM element for external projection rendering. */ const QTargetElement = 'q:targetEl'; /** Component style content prefix */ const ComponentStylesPrefixContent = '⚡️'; /** `<some-element q:slot="...">` */ const QSlot = 'q:slot'; const QSlotParent = 'q:sparent'; const QSlotS = 'q:s'; const QStatePatchAttrSelector = '[q\\:patch]'; const QSuspenseResolved = 'q:r'; const QSuspenseResultParent = 'q:rp'; const QStyle = 'q:style'; const QStyleSelector = 'style[q\\:style]'; const QStyleSSelector = 'style[q\\:sstyle]'; const QStylesAllSelector = QStyleSelector + ',' + QStyleSSelector; const QScopedStyle = 'q:sstyle'; const QCtxAttr = 'q:ctx'; const QBackRefs = 'q:brefs'; const QFuncsPrefix = 'qFuncs_'; const getQFuncs = (document, hash) => { return document[QFuncsPrefix + hash] || []; }; const QBaseAttr = 'q:base'; const QLocaleAttr = 'q:locale'; const QManifestHashAttr = 'q:manifest-hash'; const QInstanceAttr = 'q:instance'; const QContainerIsland = 'q:container-island'; const QContainerIslandEnd = '/' + QContainerIsland; const QIgnore = 'q:ignore'; const QIgnoreEnd = '/' + QIgnore; const QContainerAttr = 'q:container'; const QContainerAttrEnd = '/' + QContainerAttr; const QCursorBoundary = 'q:cursorBoundary'; const QTemplate = 'q:template'; // the same selector should be inside the qwik loader // and the same selector should be inside the qwik router spa-shim and spa-init const QContainerSelector = '[q\\:container]:not([q\\:container=' + "html" /* QContainerValue.HTML */ + ']):not([q\\:container=' + "text" /* QContainerValue.TEXT */ + '])'; // Node namespaces const HTML_NS = 'http://www.w3.org/1999/xhtml'; const SVG_NS = 'http://www.w3.org/2000/svg'; const MATH_NS = 'http://www.w3.org/1998/Math/MathML'; // Attributes namespaces const XLINK_NS = 'http://www.w3.org/1999/xlink'; const XML_NS = 'http://www.w3.org/XML/1998/namespace'; const RenderEvent = 'qRender'; const TaskEvent = 'qTask'; /** `<q:slot name="...">` */ const QDefaultSlot = ''; /** * Attribute to mark that this VNode has a pointer to itself from the `qwik/json` state. * * As the VNode get materialized the vnode now becomes eligible for mutation. Once the vnode mutates * the `VNode` references from the `qwik/json` may become invalid. For this reason, these references * need to be eagerly resolved. `VNODE_REF` stores a pointer to "this" vnode. This allows the system * to eagerly resolve these pointes as the vnodes are materialized. */ const ELEMENT_ID = 'q:id'; const ELEMENT_KEY = 'q:key'; const ELEMENT_PROPS = 'q:props'; const ELEMENT_SEQ = 'q:seq'; const ELEMENT_SEQ_IDX = 'q:seqIdx'; const ITERATION_ITEM_SINGLE = 'q:p'; // Single iteration parameter (not an array) const ITERATION_ITEM_MULTI = 'q:ps'; // Multiple iteration parameters (array) /** Non serializable markers - always begins with `:` character */ const NON_SERIALIZABLE_MARKER_PREFIX = ':'; const USE_ON_LOCAL = NON_SERIALIZABLE_MARKER_PREFIX + 'on'; const USE_ON_LOCAL_SEQ_IDX = NON_SERIALIZABLE_MARKER_PREFIX + 'onIdx'; const USE_ON_LOCAL_FLAGS = NON_SERIALIZABLE_MARKER_PREFIX + 'onFlags'; const NEAREST_CURSOR_BOUNDARY = NON_SERIALIZABLE_MARKER_PREFIX + 'nearestCursorBoundary'; const Q_PROPS_SEPARATOR = ':'; const dangerouslySetInnerHTML = 'dangerouslySetInnerHTML'; const qwikInspectorAttr = 'data-qwik-inspector'; const debugStyleScopeIdPrefixAttr = '__scopedStyleIdPrefix__'; const MAX_RETRY_ON_PROMISE_COUNT = 100; const isPromise = (value) => { // not using "value instanceof Promise" to have zone.js support return !!value && typeof value == 'object' && typeof value.then === 'function'; }; const safeCall = (call, thenFn, rejectFn) => { try { const result = call(); if (isPromise(result)) { return result.then(thenFn, rejectFn); } else { return thenFn(result); } } catch (e) { return rejectFn(e); } }; const maybeThen = (valueOrPromise, thenFn) => { return isPromise(valueOrPromise) ? valueOrPromise.then(thenFn) : thenFn(valueOrPromise); }; const checkError = (e) => { if (isServer && e instanceof ReferenceError && e.message.includes('window')) { e.message = 'It seems like you forgot to add "if (isBrowser) {...}" here:' + e.message; } }; const justThrow = (e) => { throw e; }; /** * Retries a function that throws a promise. If you pass `onError`, you're responsible for handling * errors. */ function retryOnPromise(fn, onError = justThrow) { let ok = false; let result; try { result = fn(); ok = true; } catch (e) { result = e; } if (!isPromise(result)) { // Synchronous function or error, no need to retry if (ok) { return result; } isDev && checkError(result); return onError(result); } let retryCount = MAX_RETRY_ON_PROMISE_COUNT; const retry = async (p) => { while (isPromise(p)) { try { await p; // We waited for the thrown promise, now try again return await fn(); } catch (err) { if (isPromise(err)) { if (!--retryCount) { p = new Error('Exceeded max retry count in retryOnPromise'); break; } else { p = err; } } else { p = err; break; } } } isDev && checkError(p); return onError(p); }; return ok ? result.catch(retry) : retry(result); } const styleContent = (styleId) => { return ComponentStylesPrefixContent + styleId; }; function isClassAttr(key) { return key === 'class'; } function convertScopedStyleIdsToArray(scopedStyleIds) { return scopedStyleIds?.split(' ') ?? null; } function convertStyleIdsToString(scopedStyleIds) { return Array.from(scopedStyleIds).join(' '); } const addComponentStylePrefix = (styleId) => { if (styleId) { let idx = 0; do { styleId = styleId.substring(0, idx) + styleContent(styleId.substring(idx)); } while ((idx = styleId.indexOf(' ', idx) + 1) !== 0); } return styleId || null; }; /** * Think of `-` as an escape character which makes the next character uppercase. `--` is just `-`. * * Rules for JSX property event names starting with `on`: * * - Are case insensitive: `onClick$` is same `onclick$` * - A `--` is `-`: `dbl--click` => `dbl-click` * - Become case sensitive if prefixed by `-`: `-Click` is `Click` * - A `-` (not at the beginning) makes next character uppercase: `dbl-click` => `dblClick` */ const EVENT_SUFFIX = '$'; const DOM_CONTENT_LOADED_EVENT = 'DOMContentLoaded'; const isHtmlAttributeAnEventName = (name) => { return (name.charCodeAt(0) === 113 /* q */ && name.charCodeAt(1) === 45 /* - */ && (name.charCodeAt(3) === 58 /* : */ || (name.charCodeAt(3) === 112 /* p */ && name.charCodeAt(4) === 58)) /* : */); }; function jsxEventToHtmlAttribute(jsxEvent, isPassive = false) { if (jsxEvent.endsWith(EVENT_SUFFIX)) { const [prefix, idx] = getEventScopeDataFromJsxEvent(jsxEvent, isPassive); if (idx !== -1) { return prefix + normalizeJsxEventName(jsxEvent.slice(idx, -1)); } } return null; // Return null if not matching expected format } function createEventName(event, prefix = '') { const eventName = fromCamelToKebabCase(event); return prefix + eventName; } function getEventScopeDataFromJsxEvent(eventName, isPassive = false) { let prefix; let idx = -1; // set prefix and idx based on the scope if (eventName.startsWith("on" /* EventNameJSXScope.on */)) { prefix = isPassive ? "q-ep:" /* EventNameHtmlScope.onPassive */ : "q-e:" /* EventNameHtmlScope.on */; idx = 2; } else if (eventName.startsWith("window:on" /* EventNameJSXScope.window */)) { prefix = isPassive ? "q-wp:" /* EventNameHtmlScope.windowPassive */ : "q-w:" /* EventNameHtmlScope.window */; idx = 9; } else if (eventName.startsWith("document:on" /* EventNameJSXScope.document */)) { prefix = isPassive ? "q-dp:" /* EventNameHtmlScope.documentPassive */ : "q-d:" /* EventNameHtmlScope.document */; idx = 11; } return [prefix, idx]; } const normalizeJsxEventName = (name) => { return name === DOM_CONTENT_LOADED_EVENT ? '-d-o-m-content-loaded' : createEventName(name.charAt(0) === '-' ? // marker for case sensitive event name name.slice(1) : name.toLowerCase()); }; function isPreventDefault(key) { return key.startsWith('preventdefault:'); } /** Converts a camelCase string to kebab-case. This is used for event names. */ const fromCamelToKebabCase = (text) => { return text.replace(/([A-Z-])/g, (a) => '-' + a.toLowerCase()); }; /** E.g. `"q-e:click"` => `['e', 'click']`, `"q-ep:click"` => `['ep', 'click']` */ const getEventDataFromHtmlAttribute = (htmlKey) => { const separatorIndex = htmlKey.indexOf(':'); return [htmlKey.slice(2, separatorIndex), htmlKey.slice(separatorIndex + 1)]; }; /** E.g. `"e:click"`, `"w:load"` */ const getScopedEventName = (scope, eventName) => scope + ':' + eventName; /** CSS properties which accept numbers but are not in units of "px". */ const unitlessNumbers = new Set([ 'animationIterationCount', 'aspectRatio', 'borderImageOutset', 'borderImageSlice', 'borderImageWidth', 'boxFlex', 'boxFlexGroup', 'boxOrdinalGroup', 'columnCount', 'columns', 'flex', 'flexGrow', 'flexShrink', 'gridArea', 'gridRow', 'gridRowEnd', 'gridRowStart', 'gridColumn', 'gridColumnEnd', 'gridColumnStart', 'fontWeight', 'lineClamp', 'lineHeight', 'opacity', 'order', 'orphans', 'scale', 'tabSize', 'widows', 'zIndex', 'zoom', 'MozAnimationIterationCount', // Known Prefixed Properties 'MozBoxFlex', // TODO: Remove these since they shouldn't be used in modern code 'msFlex', 'msFlexPositive', 'WebkitAnimationIterationCount', 'WebkitBoxFlex', 'WebkitBoxOrdinalGroup', 'WebkitColumnCount', 'WebkitColumns', 'WebkitFlex', 'WebkitFlexGrow', 'WebkitFlexShrink', 'WebkitLineClamp', ]); const isUnitlessNumber = (name) => { return unitlessNumbers.has(name); }; const hashCode = (text, hash = 0) => { for (let i = 0; i < text.length; i++) { const chr = text.charCodeAt(i); hash = (hash << 5) - hash + chr; hash |= 0; // Convert to 32bit integer } return Number(Math.abs(hash)).toString(36); }; const serializeClass = (obj) => { if (!obj) { return ''; } if (isString(obj)) { return obj.trim(); } const classes = []; if (isArray(obj)) { for (let i = 0; i < obj.length; i++) { const o = obj[i]; const classList = serializeClass(o); if (classList) { classes.push(classList); } } } else { for (const [key, value] of Object.entries(obj)) { if (value) { classes.push(key.trim()); } } } return classes.join(' '); }; // Unlike fromCamelToKebabCase, this leaves `-` so that `background-color` stays `background-color` const fromCamelToKebabCaseWithDash = (text) => { return text.replace(/([A-Z])/g, '-$1').toLowerCase(); }; const stringifyStyle = (obj) => { if (obj == null) { return ''; } if (typeof obj == 'object') { if (isArray(obj)) { throw qError(0 /* QError.stringifyClassOrStyle */, [obj, 'style']); } else { const chunks = []; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = obj[key]; if (value != null && typeof value !== 'function') { if (key.startsWith('--')) { chunks.push(key + ':' + value); } else { chunks.push(fromCamelToKebabCaseWithDash(key) + ':' + setValueForStyle(key, value)); } } } } return chunks.join(';'); } } return String(obj); }; const serializeBooleanOrNumberAttribute = (value) => { return value != null ? String(value) : null; }; function serializeAttribute(key, value, styleScopedId) { if (isClassAttr(key)) { const serializedClass = serializeClass(value); value = styleScopedId ? styleScopedId + (serializedClass.length ? ' ' + serializedClass : serializedClass) : serializedClass; } else if (key === 'style') { value = stringifyStyle(value); } else if (isEnumeratedBooleanAttribute(key) || typeof value === 'number') { // aria attrs, tabindex etc. value = serializeBooleanOrNumberAttribute(value); } else if (value === false || value == null) { value = null; } else if (value === true && isPreventDefault(key)) { value = ''; } return value; } function isEnumeratedBooleanAttribute(key) { return isAriaAttribute(key) || ['spellcheck', 'draggable', 'contenteditable'].includes(key); } const setValueForStyle = (styleName, value) => { if (typeof value === 'number' && value !== 0 && !isUnitlessNumber(styleName)) { return value + 'px'; } return value; }; function isAriaAttribute(prop) { return prop.startsWith('aria-'); } const styleKey = (qStyles, index) => { assertQrl(qStyles); return `${hashCode(qStyles.$hash$)}-${index}`; }; /** * Creates a function that schedules `fn` to run as a microtask. Microtasks run before browser * paint, preventing flickering. */ const createMicroTask = (fn) => { return () => queueMicrotask(fn); }; /** * Creates a function that schedules `fn` to run as a macrotask. Macrotasks yield to the browser, * allowing paint and user input. Used for time-slicing to avoid blocking the main thread. */ const createMacroTask = (fn) => { let macroTask; if (typeof MessageChannel !== 'undefined') { const channel = new MessageChannel(); channel.port1.onmessage = () => fn(); macroTask = () => channel.port2.postMessage(null); } else { macroTask = () => setTimeout(fn); } return macroTask; }; // keep this import from core/build so the cjs build works const createPlatform = () => { return { isServer, importSymbol(containerEl, url, symbolName) { if (isServer) { const hash = getSymbolHash(symbolName); const regSym = globalThis.__qwik_reg_symbols?.get(hash); if (regSym) { return regSym; } // we never lazy import on the server throw qError(6 /* QError.dynamicImportFailed */, [symbolName]); } if (!url) { throw qError(14 /* QError.qrlMissingChunk */, [symbolName]); } if (!containerEl) { throw qError(13 /* QError.qrlMissingContainer */, [url, symbolName]); } const urlDoc = toUrl(containerEl.ownerDocument, containerEl, url).toString(); const urlCopy = new URL(urlDoc); urlCopy.hash = ''; const importURL = urlCopy.href; return import(/* @vite-ignore */ importURL).then((mod) => { return mod[symbolName]; }); }, raf: (fn) => { return new Promise((resolve) => { requestAnimationFrame(() => { resolve(fn()); }); }); }, chunkForSymbol(symbolName, chunk) { return [symbolName, chunk ?? '_']; }, }; }; /** * Convert relative base URI and relative URL into a fully qualified URL. * * @param base -`QRL`s are relative, and therefore they need a base for resolution. * * - `Element` use `base.ownerDocument.baseURI` * - `Document` use `base.baseURI` * - `string` use `base` as is * - `QConfig` use `base.baseURI` * * @param url - Relative URL * @returns Fully qualified URL. */ const toUrl = (doc, containerEl, url) => { const baseURI = doc.baseURI; const base = new URL(containerEl.getAttribute(QBaseAttr) ?? baseURI, baseURI); return new URL(url, base); }; let _platform = /*#__PURE__ */ createPlatform(); // <docs markdown="./readme.md#setPlatform"> // !!DO NOT EDIT THIS COMMENT DIRECTLY!!! // (edit ./readme.md#setPlatform instead and run `pnpm docs.sync`) /** * Sets the `CorePlatform`. * * This is useful to override the platform in tests to change the behavior of, * `requestAnimationFrame`, and import resolution. * * @param doc - The document of the application for which the platform is needed. * @param platform - The platform to use. * @public */ // </docs> const setPlatform = (plt) => (_platform = plt); // <docs markdown="./readme.md#getPlatform"> // !!DO NOT EDIT THIS COMMENT DIRECTLY!!! // (edit ./readme.md#getPlatform instead and run `pnpm docs.sync`) /** * Retrieve the `CorePlatform`. * * The `CorePlatform` is also responsible for retrieving the Manifest, that contains mappings from * symbols to javascript import chunks. For this reason, `CorePlatform` can't be global, but is * specific to the application currently running. On server it is possible that many different * applications are running in a single server instance, and for this reason the `CorePlatform` is * associated with the application document. * * @param docOrNode - The document (or node) of the application for which the platform is needed. * @public */ // </docs> const getPlatform = () => { return _platform; }; const isServerPlatform = () => { if (qDynamicPlatform) { return _platform.isServer; } return false; }; const hasDocument = typeof document !== 'undefined'; const isBrowser = (qTest ? !isServerPlatform() : !isServer) && hasDocument; // Browser-specific setup const doc = isBrowser ? document : undefined; const config = { $maxIdlePreloads$: 25, }; // Determine which rel attribute to use based on browser support const rel = isBrowser && doc.createElement('link').relList?.supports?.('modulepreload') ? 'modulePreload' : 'preload'; const isJSRegex = /\.[mc]?js$/; const yieldInterval = 1000 / 60; const BundleImportState_None = 0; const BundleImportState_Preload = 2; const BundleImportState_Alias = 3; const BundleImportState_Loaded = 4; let base; const makeBundle = (name, deps) => { return { $name$: name, $state$: isJSRegex.test(name) ? BundleImportState_None : BundleImportState_Alias, $deps$: deps, $inverseProbability$: 1, $createdTs$: performance.now(), $waitedMs$: 0, $loadedMs$: 0, }; }; const getBundle = (name) => { let bundle = bundles.get(name); if (!bundle) { let deps; bundle = makeBundle(name, deps); bundles.set(name, bundle); } return bundle; }; const bundles = new Map(); let preloadCount = 0; const queue = []; const nextTriggerMacroTask = createMacroTask(trigger); const nextAdjustmentMacroTask = createMacroTask(processPendingAdjustments); let isTriggerScheduled = false; let isAdjustmentScheduled = false; let isProcessingAdjustments = false; const adjustmentStack = []; /** * This is called when a bundle is queued, or finished loading. * * Because Chrome doesn't treat new modulepreloads as higher priority, we only make * maxSimultaneousPreloads links available at a time, so that when a new high priority bundle comes * in, it is soon preloaded. * * We make sure to first preload the high priority items. */ function trigger() { isTriggerScheduled = false; if (!queue.length) { return; } const deadline = performance.now() + yieldInterval; let shouldYield = false; while (queue.length) { const bundle = queue[0]; const inverseProbability = bundle.$inverseProbability$; const probability = 1 - inverseProbability; // We want to preload all the transitive static (1) and dynamic (0.99) dependencies, throttled by the user defined maxIdlePreloads. if (probability >= 0.99 || preloadCount < config.$maxIdlePreloads$) { queue.shift(); preloadOne(bundle); if (performance.now() >= deadline) { shouldYield = true; break; } } else { break; } } if (shouldYield && queue.length && !isTriggerScheduled) { isTriggerScheduled = true; nextTriggerMacroTask(); } } const enqueueAdjustment = (bundle, inverseProbability, seen) => { // Keep existing work on the stack hot and append new roots behind it. adjustmentStack.unshift({ $bundle$: bundle, $inverseProbability$: inverseProbability, $seen$: seen, }); }; const processAdjustmentFrame = () => { const frame = adjustmentStack[adjustmentStack.length - 1]; const bundle = frame.$bundle$; if (frame.$deps$) { const index = frame.$index$; if (index >= frame.$deps$.length) { adjustmentStack.pop(); return false; } const dep = frame.$deps$[index]; frame.$index$ = index + 1; const depBundle = getBundle(dep.$name$); if (depBundle.$inverseProbability$ === 0) { return true; } const probability = 1 - bundle.$inverseProbability$; let newInverseProbability; if (probability === 1 || probability >= 0.99) { // bundle is requested at max probability, so elevate all its transitive static and dynamic deps to 99% sure newInverseProbability = Math.min(0.01, 1 - dep.$importProbability$); } else { const newInverseImportProbability = 1 - dep.$importProbability$ * probability; /** We need to undo the previous adjustment */ const prevAdjust = dep.$factor$; const factor = newInverseImportProbability / prevAdjust; // limit organic probability to 98% newInverseProbability = Math.max(0.02, depBundle.$inverseProbability$ * factor); dep.$factor$ = factor; } adjustmentStack.push({ $bundle$: depBundle, $inverseProbability$: newInverseProbability, $seen$: frame.$seen$, }); return true; } if (frame.$seen$?.has(bundle)) { adjustmentStack.pop(); return false; } const previousInverseProbability = bundle.$inverseProbability$; bundle.$inverseProbability$ = frame.$inverseProbability$; // Don't propagate tiny changes if (previousInverseProbability - bundle.$inverseProbability$ < 0.01) { adjustmentStack.pop(); return false; } if (bundle.$deps$?.length) { const seen = frame.$seen$ || new Set(); seen.add(bundle); frame.$seen$ = seen; frame.$deps$ = bundle.$deps$; frame.$index$ = 0; return false; } adjustmentStack.pop(); return false; }; function processPendingAdjustments() { if (isProcessingAdjustments || !adjustmentStack.length) { return; } isAdjustmentScheduled = false; isProcessingAdjustments = true; const deadline = isBrowser ? performance.now() + yieldInterval : 0; let processed = false; while (adjustmentStack.length) { processed = true; const checkDeadline = processAdjustmentFrame(); if (isBrowser && checkDeadline && performance.now() >= deadline) { if (!isAdjustmentScheduled) { isAdjustmentScheduled = true; nextAdjustmentMacroTask(); } break; } } isProcessingAdjustments = false; if (processed && isBrowser) { nextTriggerMacroTask(); } } const preloadOne = (bundle) => { if (bundle.$state$ >= BundleImportState_Preload) { return; } preloadCount++; const start = performance.now(); bundle.$waitedMs$ = start - bundle.$createdTs$; bundle.$state$ = BundleImportState_Preload; const link = doc.createElement('link'); // Only bundles with state none are js bundles link.href = new URL(`${base}${bundle.$name$}`, doc.baseURI).toString(); link.rel = rel; // Needed when rel is 'preload' link.as = 'script'; // Handle completion of the preload link.onload = link.onerror = () => { preloadCount--; const end = performance.now(); bundle.$loadedMs$ = end - start; bundle.$state$ = BundleImportState_Loaded; // Keep the <head> clean link.remove(); // More bundles may be ready to preload nextTriggerMacroTask(); }; doc.head.appendChild(link); }; const handleBundle = (name, inverseProbability) => { const bundle = getBundle(name); if (bundle) { enqueueAdjustment(bundle, inverseProbability); } }; const preload = (item, probability) => { if (!item?.length) { return; } const inverseProbability = 1 - probability ; if (Array.isArray(item)) { // We must process in reverse order to ensure first bundles are handled first for (let i = item.length - 1; i >= 0; i--) { const bundle = item[i]; handleBundle(bundle, inverseProbability); } } else { handleBundle(item, inverseProbability); } if (isBrowser) { nextAdjustmentMacroTask(); } else { processPendingAdjustments(); } }; if (isBrowser) { // Get early hints from qwikloader document.addEventListener('qsymbol', (ev) => { const { symbol, href } = ev.detail; // the qrl class doesn't emit href, we don't need to preload if (href) { const hash = symbol.slice(symbol.lastIndexOf('_') + 1); preload(hash, 1); } }); } const isObjectEmpty = (obj) => { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { return false; } } return true; }; /** @internal */ const stringifyRootRefPath = (path) => { let text = String(path[0]); for (let i = 1; i < path.length; i++) { text += ' ' + path[i]; } return text; }; /** @internal */ const writeStringRootRef = (writer, id) => writer.write(String(id)); /** @internal */ const writeStringRootRefPath = (writer, path) => writer.write(stringifyRootRefPath(path)); /** @internal */ const createStringStreamWriter = (write) => ({ write, writeRootRef(id) { return writeStringRootRef(this, id); }, writeRootRefPath(path) { return writeStringRootRefPath(this, path); }, }); // Pre-allocated common strings to reduce allocation overhead const CLOSE_TAG = '</'; const ESCAPED_CLOSE_TAG = '<\\/'; const QUOTE = '"'; const BRACKET_OPEN = '['; const BRACKET_CLOSE = ']'; const COMMA = ','; /** * QWIK_VERSION * * @public */ const version = "2.0.0-beta.36-dev+3268fab"; const isNode = (value) => { return value && typeof value.nodeType === 'number'; }; const isDocument = (value) => { return value.nodeType === 9; }; const isElement$1 = (value) => { return value.nodeType === 1; }; /** @internal */ const getRootContainer = (container) => { const rootContainer = container.$rootContainer$; return rootContainer || container; }; /** @internal */ const isOutOfOrderSegmentContainer = (container) => { return container.$isOutOfOrderSegment$; }; /** @internal */ const isSameContainer = (left, right) => { return getRootContainer(left) === (right ? getRootContainer(right) : null); }; /** @internal */ const _EFFECT_BACK_REF = Symbol('backRef'); /** Class for back reference to the EffectSubscription */ class BackRef { [_EFFECT_BACK_REF] = undefined; } const isForeignObjectElement = (elementName) => { return isDev ? elementName.toLowerCase() === 'foreignobject' : elementName === 'foreignObject'; }; const isSvgElement = (elementName) => elementName === 'svg' || isForeignObjectElement(elementName); const isMathElement = (elementName) => elementName === 'math'; const vnode_isDefaultNamespace = (vnode) => { const flags = vnode.flags; return (flags & 1536 /* VNodeFlags.NAMESPACE_MASK */) === 0; }; const vnode_getElementNamespaceFlags = (element) => { const namespace = fastNamespaceURI(element); switch (namespace) { case SVG_NS: return 512 /* VNodeFlags.NS_svg */; case MATH_NS: return 1024 /* VNodeFlags.NS_math */; default: return 0 /* VNodeFlags.NS_html */; } }; /** This function clones an element with a different namespace, including the children */ function cloneDomTreeWithNamespace(element, elementName, namespace, deep = false) { const newElement = element.ownerDocument.createElementNS(namespace, elementName); // Copy all attributes for (let i = 0; i < element.attributes.length; i++) { const attr = element.attributes[i]; if (attr.name !== Q_PROPS_SEPARATOR) { newElement.setAttribute(attr.name, attr.value); } } if (deep) { // Recursively clone all child nodes for (let i = 0; i < element.childNodes.length; i++) { const child = element.childNodes[i]; const nodeType = child.nodeType; if (nodeType === 3 /* Node.TEXT_NODE */) { newElement.appendChild(child.cloneNode()); } else if (nodeType === 1 /* Node.ELEMENT_NODE */) { newElement.appendChild(cloneDomTreeWithNamespace(child, child.localName, namespace, deep)); } } } return newElement; } /** * This function clones an ElementVNode with a different namespace, including the children. This * traverse the tree using depth-first search and clones the elements using * `cloneElementWithNamespace`. */ function vnode_cloneElementWithNamespace(elementVNode, parentVNode, namespace, namespaceFlag) { ensureElementVNode(elementVNode); let vCursor = elementVNode; let vParent = null; let rootElement = null; let parentElement = null; while (vCursor) { let childElement = null; let newChildElement = null; if (vnode_isElementVNode(vCursor)) { // Clone the element childElement = vCursor.node; const childElementTag = vnode_getElementName(vCursor); // We need to check if the parent is a foreignObject element // and get a new namespace data. const vCursorParent = vCursor.parent; // For the first vNode parentNode is not parent from vNode tree, but parent from DOM tree // this is because vNode is not moved yet. // rootElement is null only for the first vNode const vCursorDomParent = rootElement == null ? parentVNode : vCursorParent && vnode_getDomParentVNode(vCursorParent, true); if (vCursorDomParent) { const namespaceData = getNewElementNamespaceData(vCursorDomParent, vnode_getElementName(vCursor)); namespace = namespaceData.elementNamespace; namespaceFlag = namespaceData.elementNamespaceFlag; } const vFirstChild = vnode_getFirstChild(vCursor); newChildElement = cloneDomTreeWithNamespace(childElement, childElementTag, namespace, // deep if there is no vnode children, children are probably inserted via innerHTML !vFirstChild); childElement.remove(); if (rootElement == null) { rootElement = newChildElement; } if (parentElement) { parentElement.appendChild(newChildElement); } // Descend into children // We need first get the first child, if any // Then we can overwrite the cursor with newly created element. // This is because we need to materialize the children before we assign new element vCursor.node = newChildElement; // Set correct namespace flag vCursor.flags &= -1537 /* VNodeFlags.NEGATED_NAMESPACE_MASK */; vCursor.flags |= namespaceFlag; if (vFirstChild) { vCursor = vFirstChild; parentElement = newChildElement; continue; } else if (shouldIgnoreChildren(childElement)) { // If we should ignore children of the element this means that the element is a container // We need to get the first child of the container const container = getDomContainerFromQContainerElement(childElement); if (container) { const innerContainerFirstVNode = vnode_getFirstChild(container.rootVNode); if (innerContainerFirstVNode) { vCursor = innerContainerFirstVNode; parentElement = newChildElement; continue; } } } } if (vCursor === elementVNode) { // we are where we started, this means that vNode has no children, so we are done. return rootElement; } // Out of children, go to next sibling c