build: ship pre-built API bundle; images copy output instead of installing
- API: `pnpm build` bundles dist/ into apps/api/.output (esbuild), with Linux musl native packages for sharp and argon2 kept alongside - docker/api.Dockerfile copies that output; no pnpm install on the server - add dist/migrate.js (prisma migrate deploy equivalent, same _prisma_migrations table and lock) so the Prisma CLI is not needed at runtime - pnpm-workspace: supportedArchitectures for linux/x64/musl - admin: apps/admin/.env.production sets VITE_SITE_URL for the production build - drop NPM_REGISTRY / SITE_URL build args from compose and .env.example - gitignore: anchor node_modules rules so pre-built node_modules are committed - README: describe the pre-built deploy flow Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
ee8258556d
commit
10e453e4be
+14616
File diff suppressed because it is too large
Load Diff
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@babel/parser",
|
||||
"version": "7.29.9",
|
||||
"description": "A JavaScript parser",
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-parser",
|
||||
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"babel",
|
||||
"javascript",
|
||||
"parser",
|
||||
"tc39",
|
||||
"ecmascript",
|
||||
"@babel/parser"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-parser"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"types": "./typings/babel-parser.d.ts",
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"typings/babel-parser.d.ts",
|
||||
"index.cjs"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"# dependencies": "This package doesn't actually have runtime dependencies. @babel/types is only needed for type definitions.",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.29.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/helper-check-duplicate-nodes": "^7.29.7",
|
||||
"@babel/helper-fixtures": "^7.29.7",
|
||||
"@babel/helper-string-parser": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"charcodes": "^0.2.0"
|
||||
},
|
||||
"bin": "./bin/babel-parser.js",
|
||||
"type": "commonjs"
|
||||
}
|
||||
Generated
Vendored
+6802
File diff suppressed because it is too large
Load Diff
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "@vue/compiler-core",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/compiler-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-core.esm-bundler.js",
|
||||
"types": "dist/compiler-core.d.ts",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/compiler-core.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/compiler-core.cjs.prod.js",
|
||||
"development": "./dist/compiler-core.cjs.js",
|
||||
"default": "./dist/compiler-core.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/compiler-core.esm-bundler.js",
|
||||
"import": "./dist/compiler-core.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"buildOptions": {
|
||||
"name": "VueCompilerCore",
|
||||
"compat": true,
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"cjs"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/compiler-core"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-core#readme",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.9",
|
||||
"@vue/shared": "3.5.44",
|
||||
"entities": "^7.0.1",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.29.8"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+729
@@ -0,0 +1,729 @@
|
||||
/**
|
||||
* @vue/compiler-dom v3.5.44
|
||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||
* @license MIT
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var compilerCore = require('@vue/compiler-core');
|
||||
var shared = require('@vue/shared');
|
||||
|
||||
const V_MODEL_RADIO = /* @__PURE__ */ Symbol(``);
|
||||
const V_MODEL_CHECKBOX = /* @__PURE__ */ Symbol(
|
||||
``
|
||||
);
|
||||
const V_MODEL_TEXT = /* @__PURE__ */ Symbol(``);
|
||||
const V_MODEL_SELECT = /* @__PURE__ */ Symbol(
|
||||
``
|
||||
);
|
||||
const V_MODEL_DYNAMIC = /* @__PURE__ */ Symbol(
|
||||
``
|
||||
);
|
||||
const V_ON_WITH_MODIFIERS = /* @__PURE__ */ Symbol(
|
||||
``
|
||||
);
|
||||
const V_ON_WITH_KEYS = /* @__PURE__ */ Symbol(
|
||||
``
|
||||
);
|
||||
const V_SHOW = /* @__PURE__ */ Symbol(``);
|
||||
const TRANSITION = /* @__PURE__ */ Symbol(``);
|
||||
const TRANSITION_GROUP = /* @__PURE__ */ Symbol(
|
||||
``
|
||||
);
|
||||
compilerCore.registerRuntimeHelpers({
|
||||
[V_MODEL_RADIO]: `vModelRadio`,
|
||||
[V_MODEL_CHECKBOX]: `vModelCheckbox`,
|
||||
[V_MODEL_TEXT]: `vModelText`,
|
||||
[V_MODEL_SELECT]: `vModelSelect`,
|
||||
[V_MODEL_DYNAMIC]: `vModelDynamic`,
|
||||
[V_ON_WITH_MODIFIERS]: `withModifiers`,
|
||||
[V_ON_WITH_KEYS]: `withKeys`,
|
||||
[V_SHOW]: `vShow`,
|
||||
[TRANSITION]: `Transition`,
|
||||
[TRANSITION_GROUP]: `TransitionGroup`
|
||||
});
|
||||
|
||||
const parserOptions = {
|
||||
parseMode: "html",
|
||||
isVoidTag: shared.isVoidTag,
|
||||
isNativeTag: (tag) => shared.isHTMLTag(tag) || shared.isSVGTag(tag) || shared.isMathMLTag(tag),
|
||||
isPreTag: (tag) => tag === "pre",
|
||||
isIgnoreNewlineTag: (tag) => tag === "pre" || tag === "textarea",
|
||||
decodeEntities: void 0,
|
||||
isBuiltInComponent: (tag) => {
|
||||
if (tag === "Transition" || tag === "transition") {
|
||||
return TRANSITION;
|
||||
} else if (tag === "TransitionGroup" || tag === "transition-group") {
|
||||
return TRANSITION_GROUP;
|
||||
}
|
||||
},
|
||||
// https://html.spec.whatwg.org/multipage/parsing.html#tree-construction-dispatcher
|
||||
getNamespace(tag, parent, rootNamespace) {
|
||||
let ns = parent ? parent.ns : rootNamespace;
|
||||
if (parent && ns === 2) {
|
||||
if (parent.tag === "annotation-xml") {
|
||||
if (tag === "svg") {
|
||||
return 1;
|
||||
}
|
||||
if (parent.props.some(
|
||||
(a) => a.type === 6 && a.name === "encoding" && a.value != null && (a.value.content === "text/html" || a.value.content === "application/xhtml+xml")
|
||||
)) {
|
||||
ns = 0;
|
||||
}
|
||||
} else if (/^m(?:[ions]|text)$/.test(parent.tag) && tag !== "mglyph" && tag !== "malignmark") {
|
||||
ns = 0;
|
||||
}
|
||||
} else if (parent && ns === 1) {
|
||||
if (parent.tag === "foreignObject" || parent.tag === "desc" || parent.tag === "title") {
|
||||
ns = 0;
|
||||
}
|
||||
}
|
||||
if (ns === 0) {
|
||||
if (tag === "svg") {
|
||||
return 1;
|
||||
}
|
||||
if (tag === "math") {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return ns;
|
||||
}
|
||||
};
|
||||
|
||||
const transformStyle = (node) => {
|
||||
if (node.type === 1) {
|
||||
node.props.forEach((p, i) => {
|
||||
if (p.type === 6 && p.name === "style" && p.value) {
|
||||
node.props[i] = {
|
||||
type: 7,
|
||||
name: `bind`,
|
||||
arg: compilerCore.createSimpleExpression(`style`, true, p.loc),
|
||||
exp: parseInlineCSS(p.value.content, p.loc),
|
||||
modifiers: [],
|
||||
loc: p.loc
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
const parseInlineCSS = (cssText, loc) => {
|
||||
const normalized = shared.parseStringStyle(cssText);
|
||||
return compilerCore.createSimpleExpression(
|
||||
JSON.stringify(normalized),
|
||||
false,
|
||||
loc,
|
||||
3
|
||||
);
|
||||
};
|
||||
|
||||
function createDOMCompilerError(code, loc) {
|
||||
return compilerCore.createCompilerError(
|
||||
code,
|
||||
loc,
|
||||
DOMErrorMessages
|
||||
);
|
||||
}
|
||||
const DOMErrorCodes = {
|
||||
"X_V_HTML_NO_EXPRESSION": 54,
|
||||
"54": "X_V_HTML_NO_EXPRESSION",
|
||||
"X_V_HTML_WITH_CHILDREN": 55,
|
||||
"55": "X_V_HTML_WITH_CHILDREN",
|
||||
"X_V_TEXT_NO_EXPRESSION": 56,
|
||||
"56": "X_V_TEXT_NO_EXPRESSION",
|
||||
"X_V_TEXT_WITH_CHILDREN": 57,
|
||||
"57": "X_V_TEXT_WITH_CHILDREN",
|
||||
"X_V_MODEL_ON_INVALID_ELEMENT": 58,
|
||||
"58": "X_V_MODEL_ON_INVALID_ELEMENT",
|
||||
"X_V_MODEL_ARG_ON_ELEMENT": 59,
|
||||
"59": "X_V_MODEL_ARG_ON_ELEMENT",
|
||||
"X_V_MODEL_ON_FILE_INPUT_ELEMENT": 60,
|
||||
"60": "X_V_MODEL_ON_FILE_INPUT_ELEMENT",
|
||||
"X_V_MODEL_UNNECESSARY_VALUE": 61,
|
||||
"61": "X_V_MODEL_UNNECESSARY_VALUE",
|
||||
"X_V_SHOW_NO_EXPRESSION": 62,
|
||||
"62": "X_V_SHOW_NO_EXPRESSION",
|
||||
"X_TRANSITION_INVALID_CHILDREN": 63,
|
||||
"63": "X_TRANSITION_INVALID_CHILDREN",
|
||||
"X_IGNORED_SIDE_EFFECT_TAG": 64,
|
||||
"64": "X_IGNORED_SIDE_EFFECT_TAG",
|
||||
"__EXTEND_POINT__": 65,
|
||||
"65": "__EXTEND_POINT__"
|
||||
};
|
||||
const DOMErrorMessages = {
|
||||
[54]: `v-html is missing expression.`,
|
||||
[55]: `v-html will override element children.`,
|
||||
[56]: `v-text is missing expression.`,
|
||||
[57]: `v-text will override element children.`,
|
||||
[58]: `v-model can only be used on <input>, <textarea> and <select> elements.`,
|
||||
[59]: `v-model argument is not supported on plain elements.`,
|
||||
[60]: `v-model cannot be used on file inputs since they are read-only. Use a v-on:change listener instead.`,
|
||||
[61]: `Unnecessary value binding used alongside v-model. It will interfere with v-model's behavior.`,
|
||||
[62]: `v-show is missing expression.`,
|
||||
[63]: `<Transition> expects exactly one child element or component.`,
|
||||
[64]: `Tags with side effect (<script> and <style>) are ignored in client component templates.`
|
||||
};
|
||||
|
||||
const transformVHtml = (dir, node, context) => {
|
||||
const { exp, loc } = dir;
|
||||
if (!exp) {
|
||||
context.onError(
|
||||
createDOMCompilerError(54, loc)
|
||||
);
|
||||
}
|
||||
if (node.children.length) {
|
||||
context.onError(
|
||||
createDOMCompilerError(55, loc)
|
||||
);
|
||||
node.children.length = 0;
|
||||
}
|
||||
return {
|
||||
props: [
|
||||
compilerCore.createObjectProperty(
|
||||
compilerCore.createSimpleExpression(`innerHTML`, true, loc),
|
||||
exp || compilerCore.createSimpleExpression("", true)
|
||||
)
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
const transformVText = (dir, node, context) => {
|
||||
const { exp, loc } = dir;
|
||||
if (!exp) {
|
||||
context.onError(
|
||||
createDOMCompilerError(56, loc)
|
||||
);
|
||||
}
|
||||
if (node.children.length) {
|
||||
context.onError(
|
||||
createDOMCompilerError(57, loc)
|
||||
);
|
||||
node.children.length = 0;
|
||||
}
|
||||
return {
|
||||
props: [
|
||||
compilerCore.createObjectProperty(
|
||||
compilerCore.createSimpleExpression(`textContent`, true),
|
||||
exp ? compilerCore.getConstantType(exp, context) > 0 ? exp : compilerCore.createCallExpression(
|
||||
context.helperString(compilerCore.TO_DISPLAY_STRING),
|
||||
[exp],
|
||||
loc
|
||||
) : compilerCore.createSimpleExpression("", true)
|
||||
)
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
const transformModel = (dir, node, context) => {
|
||||
const baseResult = compilerCore.transformModel(dir, node, context);
|
||||
if (!baseResult.props.length || node.tagType === 1) {
|
||||
return baseResult;
|
||||
}
|
||||
if (dir.arg) {
|
||||
context.onError(
|
||||
createDOMCompilerError(
|
||||
59,
|
||||
dir.arg.loc
|
||||
)
|
||||
);
|
||||
}
|
||||
const { tag } = node;
|
||||
const isCustomElement = context.isCustomElement(tag);
|
||||
if (tag === "input" || tag === "textarea" || tag === "select" || isCustomElement) {
|
||||
let directiveToUse = V_MODEL_TEXT;
|
||||
let isInvalidType = false;
|
||||
if (tag === "input" || isCustomElement) {
|
||||
const type = compilerCore.findProp(node, `type`);
|
||||
if (type) {
|
||||
if (type.type === 7) {
|
||||
directiveToUse = V_MODEL_DYNAMIC;
|
||||
} else if (type.value) {
|
||||
switch (type.value.content) {
|
||||
case "radio":
|
||||
directiveToUse = V_MODEL_RADIO;
|
||||
break;
|
||||
case "checkbox":
|
||||
directiveToUse = V_MODEL_CHECKBOX;
|
||||
break;
|
||||
case "file":
|
||||
isInvalidType = true;
|
||||
context.onError(
|
||||
createDOMCompilerError(
|
||||
60,
|
||||
dir.loc
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (compilerCore.hasDynamicKeyVBind(node)) {
|
||||
directiveToUse = V_MODEL_DYNAMIC;
|
||||
} else ;
|
||||
} else if (tag === "select") {
|
||||
directiveToUse = V_MODEL_SELECT;
|
||||
} else ;
|
||||
if (!isInvalidType) {
|
||||
baseResult.needRuntime = context.helper(directiveToUse);
|
||||
}
|
||||
} else {
|
||||
context.onError(
|
||||
createDOMCompilerError(
|
||||
58,
|
||||
dir.loc
|
||||
)
|
||||
);
|
||||
}
|
||||
baseResult.props = baseResult.props.filter(
|
||||
(p) => !(p.key.type === 4 && p.key.content === "modelValue")
|
||||
);
|
||||
return baseResult;
|
||||
};
|
||||
|
||||
const isEventOptionModifier = /* @__PURE__ */ shared.makeMap(`passive,once,capture`);
|
||||
const isNonKeyModifier = /* @__PURE__ */ shared.makeMap(
|
||||
// event propagation management
|
||||
`stop,prevent,self,ctrl,shift,alt,meta,exact,middle`
|
||||
);
|
||||
const maybeKeyModifier = /* @__PURE__ */ shared.makeMap("left,right");
|
||||
const isKeyboardEvent = /* @__PURE__ */ shared.makeMap(`onkeyup,onkeydown,onkeypress`);
|
||||
const resolveModifiers = (key, modifiers, context, loc) => {
|
||||
const keyModifiers = [];
|
||||
const nonKeyModifiers = [];
|
||||
const eventOptionModifiers = [];
|
||||
for (let i = 0; i < modifiers.length; i++) {
|
||||
const modifier = modifiers[i].content;
|
||||
if (modifier === "native" && compilerCore.checkCompatEnabled(
|
||||
"COMPILER_V_ON_NATIVE",
|
||||
context,
|
||||
loc
|
||||
)) {
|
||||
eventOptionModifiers.push(modifier);
|
||||
} else if (isEventOptionModifier(modifier)) {
|
||||
eventOptionModifiers.push(modifier);
|
||||
} else {
|
||||
if (maybeKeyModifier(modifier)) {
|
||||
if (compilerCore.isStaticExp(key)) {
|
||||
if (isKeyboardEvent(key.content.toLowerCase())) {
|
||||
keyModifiers.push(modifier);
|
||||
} else {
|
||||
nonKeyModifiers.push(modifier);
|
||||
}
|
||||
} else {
|
||||
keyModifiers.push(modifier);
|
||||
nonKeyModifiers.push(modifier);
|
||||
}
|
||||
} else {
|
||||
if (isNonKeyModifier(modifier)) {
|
||||
nonKeyModifiers.push(modifier);
|
||||
} else {
|
||||
keyModifiers.push(modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
keyModifiers,
|
||||
nonKeyModifiers,
|
||||
eventOptionModifiers
|
||||
};
|
||||
};
|
||||
const transformClick = (key, event) => {
|
||||
const isStaticClick = compilerCore.isStaticExp(key) && key.content.toLowerCase() === "onclick";
|
||||
return isStaticClick ? compilerCore.createSimpleExpression(event, true) : key.type !== 4 ? compilerCore.createCompoundExpression([
|
||||
`(`,
|
||||
key,
|
||||
`) === "onClick" ? "${event}" : (`,
|
||||
key,
|
||||
`)`
|
||||
]) : key;
|
||||
};
|
||||
const transformOn = (dir, node, context) => {
|
||||
return compilerCore.transformOn(dir, node, context, (baseResult) => {
|
||||
const { modifiers } = dir;
|
||||
if (!modifiers.length) return baseResult;
|
||||
let { key, value: handlerExp } = baseResult.props[0];
|
||||
const { keyModifiers, nonKeyModifiers, eventOptionModifiers } = resolveModifiers(key, modifiers, context, dir.loc);
|
||||
if (nonKeyModifiers.includes("right")) {
|
||||
key = transformClick(key, `onContextmenu`);
|
||||
}
|
||||
if (nonKeyModifiers.includes("middle")) {
|
||||
key = transformClick(key, `onMouseup`);
|
||||
}
|
||||
if (nonKeyModifiers.length) {
|
||||
handlerExp = compilerCore.createCallExpression(context.helper(V_ON_WITH_MODIFIERS), [
|
||||
handlerExp,
|
||||
JSON.stringify(nonKeyModifiers)
|
||||
]);
|
||||
}
|
||||
if (keyModifiers.length && // if event name is dynamic, always wrap with keys guard
|
||||
(!compilerCore.isStaticExp(key) || isKeyboardEvent(key.content.toLowerCase()))) {
|
||||
handlerExp = compilerCore.createCallExpression(context.helper(V_ON_WITH_KEYS), [
|
||||
handlerExp,
|
||||
JSON.stringify(keyModifiers)
|
||||
]);
|
||||
}
|
||||
if (eventOptionModifiers.length) {
|
||||
const modifierPostfix = eventOptionModifiers.map(shared.capitalize).join("");
|
||||
key = compilerCore.isStaticExp(key) ? compilerCore.createSimpleExpression(`${key.content}${modifierPostfix}`, true) : compilerCore.createCompoundExpression([`(`, key, `) + "${modifierPostfix}"`]);
|
||||
}
|
||||
return {
|
||||
props: [compilerCore.createObjectProperty(key, handlerExp)]
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const transformShow = (dir, node, context) => {
|
||||
const { exp, loc } = dir;
|
||||
if (!exp) {
|
||||
context.onError(
|
||||
createDOMCompilerError(62, loc)
|
||||
);
|
||||
}
|
||||
return {
|
||||
props: [],
|
||||
needRuntime: context.helper(V_SHOW)
|
||||
};
|
||||
};
|
||||
|
||||
const transformTransition = (node, context) => {
|
||||
if (node.type === 1 && node.tagType === 1) {
|
||||
const component = context.isBuiltInComponent(node.tag);
|
||||
if (component === TRANSITION) {
|
||||
return () => {
|
||||
if (!node.children.length) {
|
||||
return;
|
||||
}
|
||||
hasMultipleChildren(node);
|
||||
const child = node.children[0];
|
||||
if (child.type === 1) {
|
||||
for (const p of child.props) {
|
||||
if (p.type === 7 && p.name === "show") {
|
||||
node.props.push({
|
||||
type: 6,
|
||||
name: "persisted",
|
||||
nameLoc: node.loc,
|
||||
value: void 0,
|
||||
loc: node.loc
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
function hasMultipleChildren(node) {
|
||||
const children = node.children = node.children.filter(
|
||||
(c) => !compilerCore.isCommentOrWhitespace(c)
|
||||
);
|
||||
const child = children[0];
|
||||
return children.length !== 1 || child.type === 11 || child.type === 9 && child.branches.some(hasMultipleChildren);
|
||||
}
|
||||
|
||||
const expReplaceRE = /__VUE_EXP_START__(.*?)__VUE_EXP_END__/g;
|
||||
const stringifyStatic = (children, context, parent) => {
|
||||
if (context.scopes.vSlot > 0) {
|
||||
return;
|
||||
}
|
||||
const isParentCached = parent.type === 1 && parent.codegenNode && parent.codegenNode.type === 13 && parent.codegenNode.children && !shared.isArray(parent.codegenNode.children) && parent.codegenNode.children.type === 20;
|
||||
let nc = 0;
|
||||
let ec = 0;
|
||||
const currentChunk = [];
|
||||
const stringifyCurrentChunk = (currentIndex) => {
|
||||
if (nc >= 20 || ec >= 5) {
|
||||
const staticCall = compilerCore.createCallExpression(context.helper(compilerCore.CREATE_STATIC), [
|
||||
JSON.stringify(
|
||||
currentChunk.map((node) => stringifyNode(node, context)).join("")
|
||||
).replace(expReplaceRE, `" + $1 + "`),
|
||||
// the 2nd argument indicates the number of DOM nodes this static vnode
|
||||
// will insert / hydrate
|
||||
String(currentChunk.length)
|
||||
]);
|
||||
const deleteCount = currentChunk.length - 1;
|
||||
if (isParentCached) {
|
||||
children.splice(
|
||||
currentIndex - currentChunk.length,
|
||||
currentChunk.length,
|
||||
// @ts-expect-error
|
||||
staticCall
|
||||
);
|
||||
} else {
|
||||
currentChunk[0].codegenNode.value = staticCall;
|
||||
if (currentChunk.length > 1) {
|
||||
children.splice(currentIndex - currentChunk.length + 1, deleteCount);
|
||||
const cacheIndex = context.cached.indexOf(
|
||||
currentChunk[currentChunk.length - 1].codegenNode
|
||||
);
|
||||
if (cacheIndex > -1) {
|
||||
for (let i2 = cacheIndex; i2 < context.cached.length; i2++) {
|
||||
const c = context.cached[i2];
|
||||
if (c) c.index -= deleteCount;
|
||||
}
|
||||
context.cached.splice(cacheIndex - deleteCount + 1, deleteCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deleteCount;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
let i = 0;
|
||||
for (; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
const isCached = isParentCached || getCachedNode(child);
|
||||
if (isCached) {
|
||||
const result = analyzeNode(child);
|
||||
if (result) {
|
||||
nc += result[0];
|
||||
ec += result[1];
|
||||
currentChunk.push(child);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
i -= stringifyCurrentChunk(i);
|
||||
nc = 0;
|
||||
ec = 0;
|
||||
currentChunk.length = 0;
|
||||
}
|
||||
stringifyCurrentChunk(i);
|
||||
};
|
||||
const getCachedNode = (node) => {
|
||||
if ((node.type === 1 && node.tagType === 0 || node.type === 12) && node.codegenNode && node.codegenNode.type === 20) {
|
||||
return node.codegenNode;
|
||||
}
|
||||
};
|
||||
const dataAriaRE = /^(?:data|aria)-/;
|
||||
const isStringifiableAttr = (name, ns) => {
|
||||
return (ns === 0 ? shared.isKnownHtmlAttr(name) : ns === 1 ? shared.isKnownSvgAttr(name) : ns === 2 ? shared.isKnownMathMLAttr(name) : false) || dataAriaRE.test(name);
|
||||
};
|
||||
const isNonStringifiable = /* @__PURE__ */ shared.makeMap(
|
||||
`caption,thead,tr,th,tbody,td,tfoot,colgroup,col`
|
||||
);
|
||||
function analyzeNode(node) {
|
||||
if (node.type === 1 && isNonStringifiable(node.tag)) {
|
||||
return false;
|
||||
}
|
||||
if (node.type === 1 && compilerCore.findDir(node, "once", true)) {
|
||||
return false;
|
||||
}
|
||||
if (node.type === 12) {
|
||||
return [1, 0];
|
||||
}
|
||||
let nc = 1;
|
||||
let ec = node.props.length > 0 ? 1 : 0;
|
||||
let bailed = false;
|
||||
const bail = () => {
|
||||
bailed = true;
|
||||
return false;
|
||||
};
|
||||
function walk(node2) {
|
||||
const isOptionTag = node2.tag === "option" && node2.ns === 0;
|
||||
for (let i = 0; i < node2.props.length; i++) {
|
||||
const p = node2.props[i];
|
||||
if (p.type === 6 && !isStringifiableAttr(p.name, node2.ns)) {
|
||||
return bail();
|
||||
}
|
||||
if (p.type === 7 && p.name === "bind") {
|
||||
if (p.arg && (p.arg.type === 8 || p.arg.isStatic && !isStringifiableAttr(p.arg.content, node2.ns))) {
|
||||
return bail();
|
||||
}
|
||||
if (p.exp && (p.exp.type === 8 || p.exp.constType < 3)) {
|
||||
return bail();
|
||||
}
|
||||
if (isOptionTag && compilerCore.isStaticArgOf(p.arg, "value") && p.exp && !p.exp.isStatic) {
|
||||
return bail();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < node2.children.length; i++) {
|
||||
nc++;
|
||||
const child = node2.children[i];
|
||||
if (child.type === 1) {
|
||||
if (child.props.length > 0) {
|
||||
ec++;
|
||||
}
|
||||
walk(child);
|
||||
if (bailed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return walk(node) ? [nc, ec] : false;
|
||||
}
|
||||
function stringifyNode(node, context) {
|
||||
if (shared.isString(node)) {
|
||||
return node;
|
||||
}
|
||||
if (shared.isSymbol(node)) {
|
||||
return ``;
|
||||
}
|
||||
switch (node.type) {
|
||||
case 1:
|
||||
return stringifyElement(node, context);
|
||||
case 2:
|
||||
return shared.escapeHtml(node.content);
|
||||
case 3:
|
||||
return `<!--${shared.escapeHtml(node.content)}-->`;
|
||||
case 5:
|
||||
return shared.escapeHtml(shared.toDisplayString(evaluateConstant(node.content)));
|
||||
case 8:
|
||||
return shared.escapeHtml(evaluateConstant(node));
|
||||
case 12:
|
||||
return stringifyNode(node.content, context);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
function stringifyElement(node, context) {
|
||||
var _a;
|
||||
let res = `<${node.tag}`;
|
||||
let innerHTML = void 0;
|
||||
for (let i = 0; i < node.props.length; i++) {
|
||||
const p = node.props[i];
|
||||
if (p.type === 6) {
|
||||
res += ` ${p.name}`;
|
||||
if (p.value) {
|
||||
res += `="${shared.escapeHtml(p.value.content)}"`;
|
||||
}
|
||||
} else if (p.type === 7) {
|
||||
if (p.name === "bind") {
|
||||
const exp = p.exp;
|
||||
if (exp.content[0] === "_") {
|
||||
res += ` ${p.arg.content}="__VUE_EXP_START__${exp.content}__VUE_EXP_END__"`;
|
||||
continue;
|
||||
}
|
||||
if ((shared.isBooleanAttr(p.arg.content) || p.arg.content === "hidden") && exp.content === "false") {
|
||||
continue;
|
||||
}
|
||||
let evaluated = evaluateConstant(exp);
|
||||
if (evaluated != null) {
|
||||
const arg = p.arg && p.arg.content;
|
||||
if (arg === "hidden" && typeof evaluated === "number" && !shared.includeBooleanAttr(evaluated)) {
|
||||
continue;
|
||||
}
|
||||
if (arg === "class") {
|
||||
evaluated = shared.normalizeClass(evaluated);
|
||||
} else if (arg === "style") {
|
||||
evaluated = shared.stringifyStyle(shared.normalizeStyle(evaluated));
|
||||
}
|
||||
res += ` ${p.arg.content}="${shared.escapeHtml(
|
||||
evaluated
|
||||
)}"`;
|
||||
}
|
||||
} else if (p.name === "html") {
|
||||
innerHTML = (_a = evaluateConstant(p.exp)) != null ? _a : "";
|
||||
} else if (p.name === "text") {
|
||||
innerHTML = shared.escapeHtml(
|
||||
shared.toDisplayString(evaluateConstant(p.exp))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context.scopeId) {
|
||||
res += ` ${context.scopeId}`;
|
||||
}
|
||||
res += `>`;
|
||||
if (innerHTML !== void 0) {
|
||||
res += innerHTML;
|
||||
} else {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
res += stringifyNode(node.children[i], context);
|
||||
}
|
||||
}
|
||||
if (!shared.isVoidTag(node.tag)) {
|
||||
res += `</${node.tag}>`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function evaluateConstant(exp) {
|
||||
if (exp.type === 4) {
|
||||
return new Function(`return (${exp.content})`)();
|
||||
} else {
|
||||
let res = ``;
|
||||
exp.children.forEach((c) => {
|
||||
if (shared.isString(c) || shared.isSymbol(c)) {
|
||||
return;
|
||||
}
|
||||
if (c.type === 2) {
|
||||
res += c.content;
|
||||
} else if (c.type === 5) {
|
||||
res += shared.toDisplayString(evaluateConstant(c.content));
|
||||
} else {
|
||||
res += evaluateConstant(c);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
const ignoreSideEffectTags = (node, context) => {
|
||||
if (node.type === 1 && node.tagType === 0 && (node.tag === "script" || node.tag === "style")) {
|
||||
context.removeNode();
|
||||
}
|
||||
};
|
||||
|
||||
const DOMNodeTransforms = [
|
||||
transformStyle,
|
||||
transformTransition,
|
||||
...[]
|
||||
];
|
||||
const DOMDirectiveTransforms = {
|
||||
cloak: compilerCore.noopDirectiveTransform,
|
||||
html: transformVHtml,
|
||||
text: transformVText,
|
||||
model: transformModel,
|
||||
// override compiler-core
|
||||
on: transformOn,
|
||||
// override compiler-core
|
||||
show: transformShow
|
||||
};
|
||||
function compile(src, options = {}) {
|
||||
return compilerCore.baseCompile(
|
||||
src,
|
||||
shared.extend({}, parserOptions, options, {
|
||||
nodeTransforms: [
|
||||
// ignore <script> and <tag>
|
||||
// this is not put inside DOMNodeTransforms because that list is used
|
||||
// by compiler-ssr to generate vnode fallback branches
|
||||
ignoreSideEffectTags,
|
||||
...DOMNodeTransforms,
|
||||
...options.nodeTransforms || []
|
||||
],
|
||||
directiveTransforms: shared.extend(
|
||||
{},
|
||||
DOMDirectiveTransforms,
|
||||
options.directiveTransforms || {}
|
||||
),
|
||||
transformHoist: stringifyStatic
|
||||
})
|
||||
);
|
||||
}
|
||||
function parse(template, options = {}) {
|
||||
return compilerCore.baseParse(template, shared.extend({}, parserOptions, options));
|
||||
}
|
||||
|
||||
exports.DOMDirectiveTransforms = DOMDirectiveTransforms;
|
||||
exports.DOMErrorCodes = DOMErrorCodes;
|
||||
exports.DOMErrorMessages = DOMErrorMessages;
|
||||
exports.DOMNodeTransforms = DOMNodeTransforms;
|
||||
exports.TRANSITION = TRANSITION;
|
||||
exports.TRANSITION_GROUP = TRANSITION_GROUP;
|
||||
exports.V_MODEL_CHECKBOX = V_MODEL_CHECKBOX;
|
||||
exports.V_MODEL_DYNAMIC = V_MODEL_DYNAMIC;
|
||||
exports.V_MODEL_RADIO = V_MODEL_RADIO;
|
||||
exports.V_MODEL_SELECT = V_MODEL_SELECT;
|
||||
exports.V_MODEL_TEXT = V_MODEL_TEXT;
|
||||
exports.V_ON_WITH_KEYS = V_ON_WITH_KEYS;
|
||||
exports.V_ON_WITH_MODIFIERS = V_ON_WITH_MODIFIERS;
|
||||
exports.V_SHOW = V_SHOW;
|
||||
exports.compile = compile;
|
||||
exports.createDOMCompilerError = createDOMCompilerError;
|
||||
exports.parse = parse;
|
||||
exports.parserOptions = parserOptions;
|
||||
exports.transformStyle = transformStyle;
|
||||
Object.keys(compilerCore).forEach(function (k) {
|
||||
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = compilerCore[k];
|
||||
});
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "@vue/compiler-dom",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/compiler-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-dom.esm-bundler.js",
|
||||
"types": "dist/compiler-dom.d.ts",
|
||||
"unpkg": "dist/compiler-dom.global.js",
|
||||
"jsdelivr": "dist/compiler-dom.global.js",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/compiler-dom.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/compiler-dom.cjs.prod.js",
|
||||
"development": "./dist/compiler-dom.cjs.js",
|
||||
"default": "./dist/compiler-dom.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/compiler-dom.esm-bundler.js",
|
||||
"import": "./dist/compiler-dom.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"buildOptions": {
|
||||
"name": "VueCompilerDOM",
|
||||
"compat": true,
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"esm-browser",
|
||||
"cjs",
|
||||
"global"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/compiler-dom"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-dom#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.44",
|
||||
"@vue/compiler-core": "3.5.44"
|
||||
}
|
||||
}
|
||||
+1420
File diff suppressed because it is too large
Load Diff
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@vue/compiler-ssr",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/compiler-ssr",
|
||||
"main": "dist/compiler-ssr.cjs.js",
|
||||
"types": "dist/compiler-ssr.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"buildOptions": {
|
||||
"prod": false,
|
||||
"formats": [
|
||||
"cjs"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/compiler-ssr"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/compiler-ssr#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.44",
|
||||
"@vue/compiler-dom": "3.5.44"
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
//#region src/index-node.ts
|
||||
function addCustomCommand() {}
|
||||
function addCustomTab() {}
|
||||
function onDevToolsClientConnected(_fn) {
|
||||
return new Promise(() => {});
|
||||
}
|
||||
function onDevToolsConnected(_fn) {
|
||||
return new Promise(() => {});
|
||||
}
|
||||
function removeCustomCommand() {}
|
||||
function setupDevToolsPlugin(_pluginDescriptor, _setupFn) {}
|
||||
//#endregion
|
||||
export { addCustomCommand, addCustomTab, onDevToolsClientConnected, onDevToolsConnected, removeCustomCommand, setupDevToolsPlugin, setupDevToolsPlugin as setupDevtoolsPlugin };
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@vue/devtools-api",
|
||||
"type": "module",
|
||||
"version": "8.2.1",
|
||||
"author": "webfansplz",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"directory": "packages/devtools-api",
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/devtools.git"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"node": {
|
||||
"import": "./dist/index-node.js",
|
||||
"require": "./dist/index-node.cjs"
|
||||
},
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"unpkg": "dist/vue-devtools-api.global.js",
|
||||
"jsdelivr": "dist/vue-devtools-api.global.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@vue/devtools-kit": "^8.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown --clean",
|
||||
"prepare:type": "tsdown --dts-only",
|
||||
"stub": "tsdown --watch --onSuccess 'tsdown --dts-only'"
|
||||
}
|
||||
}
|
||||
+1933
File diff suppressed because it is too large
Load Diff
+55
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@vue/reactivity",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/reactivity",
|
||||
"main": "index.js",
|
||||
"module": "dist/reactivity.esm-bundler.js",
|
||||
"types": "dist/reactivity.d.ts",
|
||||
"unpkg": "dist/reactivity.global.js",
|
||||
"jsdelivr": "dist/reactivity.global.js",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/reactivity.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/reactivity.cjs.prod.js",
|
||||
"development": "./dist/reactivity.cjs.js",
|
||||
"default": "./dist/reactivity.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/reactivity.esm-bundler.js",
|
||||
"import": "./dist/reactivity.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/reactivity"
|
||||
},
|
||||
"buildOptions": {
|
||||
"name": "VueReactivity",
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"esm-browser",
|
||||
"cjs",
|
||||
"global"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/reactivity#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.44"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+7085
File diff suppressed because it is too large
Load Diff
+52
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "@vue/runtime-core",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/runtime-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-core.esm-bundler.js",
|
||||
"types": "dist/runtime-core.d.ts",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/runtime-core.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/runtime-core.cjs.prod.js",
|
||||
"development": "./dist/runtime-core.cjs.js",
|
||||
"default": "./dist/runtime-core.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/runtime-core.esm-bundler.js",
|
||||
"import": "./dist/runtime-core.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"buildOptions": {
|
||||
"name": "VueRuntimeCore",
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"cjs"
|
||||
]
|
||||
},
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/runtime-core"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-core#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.44",
|
||||
"@vue/reactivity": "3.5.44"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+1917
File diff suppressed because it is too large
Load Diff
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "@vue/runtime-dom",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/runtime-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-dom.esm-bundler.js",
|
||||
"types": "dist/runtime-dom.d.ts",
|
||||
"unpkg": "dist/runtime-dom.global.js",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/runtime-dom.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/runtime-dom.cjs.prod.js",
|
||||
"development": "./dist/runtime-dom.cjs.js",
|
||||
"default": "./dist/runtime-dom.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/runtime-dom.esm-bundler.js",
|
||||
"import": "./dist/runtime-dom.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"buildOptions": {
|
||||
"name": "VueRuntimeDOM",
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"esm-browser",
|
||||
"cjs",
|
||||
"global"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/runtime-dom"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/runtime-dom#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.44",
|
||||
"@vue/runtime-core": "3.5.44",
|
||||
"@vue/reactivity": "3.5.44",
|
||||
"csstype": "^3.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+951
@@ -0,0 +1,951 @@
|
||||
/**
|
||||
* @vue/server-renderer v3.5.44
|
||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||
* @license MIT
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
var Vue = require('@vue/runtime-dom');
|
||||
var shared = require('@vue/shared');
|
||||
var compilerSsr = require('@vue/compiler-ssr');
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
var n = Object.create(null);
|
||||
if (e) {
|
||||
for (var k in e) {
|
||||
n[k] = e[k];
|
||||
}
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
var Vue__namespace = /*#__PURE__*/_interopNamespaceDefault(Vue);
|
||||
|
||||
const shouldIgnoreProp = /* @__PURE__ */ shared.makeMap(
|
||||
`,key,ref,innerHTML,textContent,ref_key,ref_for`
|
||||
);
|
||||
function ssrRenderAttrs(props, tag) {
|
||||
let ret = "";
|
||||
for (let key in props) {
|
||||
if (shouldIgnoreProp(key) || shared.isOn(key) || tag === "textarea" && key === "value" || // force as property (not rendered in SSR)
|
||||
key.startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
const value = props[key];
|
||||
if (key.startsWith("^")) key = key.slice(1);
|
||||
if (key === "class") {
|
||||
ret += ` class="${ssrRenderClass(value)}"`;
|
||||
} else if (key === "style") {
|
||||
ret += ` style="${ssrRenderStyle(value)}"`;
|
||||
} else if (key === "className") {
|
||||
if (value != null) {
|
||||
ret += ` class="${shared.escapeHtml(String(value))}"`;
|
||||
}
|
||||
} else {
|
||||
ret += ssrRenderDynamicAttr(key, value, tag);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function ssrRenderDynamicAttr(key, value, tag) {
|
||||
if (!shared.isRenderableAttrValue(value)) {
|
||||
return ``;
|
||||
}
|
||||
const attrKey = tag && (tag.indexOf("-") > 0 || shared.isSVGTag(tag)) ? key : shared.propsToAttrMap[key] || key.toLowerCase();
|
||||
if (shared.isBooleanAttr(attrKey) || attrKey === "hidden" && (typeof value === "boolean" || typeof value === "number")) {
|
||||
return shared.includeBooleanAttr(value) ? ` ${attrKey}` : ``;
|
||||
} else if (shared.isSSRSafeAttrName(attrKey)) {
|
||||
return value === "" ? ` ${attrKey}` : ` ${attrKey}="${shared.escapeHtml(value)}"`;
|
||||
} else {
|
||||
console.warn(
|
||||
`[@vue/server-renderer] Skipped rendering unsafe attribute name: ${attrKey}`
|
||||
);
|
||||
return ``;
|
||||
}
|
||||
}
|
||||
function ssrRenderAttr(key, value) {
|
||||
if (!shared.isRenderableAttrValue(value)) {
|
||||
return ``;
|
||||
}
|
||||
return ` ${key}="${shared.escapeHtml(value)}"`;
|
||||
}
|
||||
function ssrRenderClass(raw) {
|
||||
return shared.escapeHtml(shared.normalizeClass(raw));
|
||||
}
|
||||
function ssrRenderStyle(raw) {
|
||||
if (!raw) {
|
||||
return "";
|
||||
}
|
||||
if (shared.isString(raw)) {
|
||||
return shared.escapeHtml(raw);
|
||||
}
|
||||
const styles = shared.normalizeStyle(ssrResetCssVars(raw));
|
||||
return shared.escapeHtml(shared.stringifyStyle(styles));
|
||||
}
|
||||
function ssrResetCssVars(raw) {
|
||||
if (!shared.isArray(raw) && shared.isObject(raw)) {
|
||||
const res = {};
|
||||
for (const key in raw) {
|
||||
if (key.startsWith(":--")) {
|
||||
res[key.slice(1)] = shared.normalizeCssVarValue(raw[key]);
|
||||
} else {
|
||||
res[key] = raw[key];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function ssrRenderComponent(comp, props = null, children = null, parentComponent = null, slotScopeId) {
|
||||
return renderComponentVNode(
|
||||
Vue.createVNode(comp, props, children),
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
);
|
||||
}
|
||||
|
||||
const { ensureValidVNode } = Vue.ssrUtils;
|
||||
function ssrRenderSlot(slots, slotName, slotProps, fallbackRenderFn, push, parentComponent, slotScopeId) {
|
||||
push(`<!--[-->`);
|
||||
ssrRenderSlotInner(
|
||||
slots,
|
||||
slotName,
|
||||
slotProps,
|
||||
fallbackRenderFn,
|
||||
push,
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
);
|
||||
push(`<!--]-->`);
|
||||
}
|
||||
function ssrRenderSlotInner(slots, slotName, slotProps, fallbackRenderFn, push, parentComponent, slotScopeId, transition) {
|
||||
const slotFn = slots[slotName];
|
||||
if (slotFn) {
|
||||
const slotBuffer = [];
|
||||
const bufferedPush = (item) => {
|
||||
slotBuffer.push(item);
|
||||
};
|
||||
const ret = slotFn(
|
||||
// keep the slot function's contract in sync with `renderSlot`, which also
|
||||
// normalizes nullish props before invoking the slot
|
||||
slotProps || {},
|
||||
bufferedPush,
|
||||
parentComponent,
|
||||
slotScopeId ? " " + slotScopeId : ""
|
||||
);
|
||||
if (shared.isArray(ret)) {
|
||||
const validSlotContent = ensureValidVNode(ret);
|
||||
if (validSlotContent) {
|
||||
renderVNodeChildren(
|
||||
push,
|
||||
validSlotContent,
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
);
|
||||
} else if (fallbackRenderFn) {
|
||||
fallbackRenderFn();
|
||||
} else if (transition) {
|
||||
push(`<!---->`);
|
||||
}
|
||||
} else {
|
||||
let isEmptySlot = true;
|
||||
if (transition) {
|
||||
isEmptySlot = false;
|
||||
} else {
|
||||
for (let i = 0; i < slotBuffer.length; i++) {
|
||||
if (!isComment(slotBuffer[i])) {
|
||||
isEmptySlot = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isEmptySlot) {
|
||||
if (fallbackRenderFn) {
|
||||
fallbackRenderFn();
|
||||
}
|
||||
} else {
|
||||
let start = 0;
|
||||
let end = slotBuffer.length;
|
||||
if (transition && slotBuffer[0] === "<!--[-->" && slotBuffer[end - 1] === "<!--]-->") {
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
if (start < end) {
|
||||
for (let i = start; i < end; i++) {
|
||||
push(slotBuffer[i]);
|
||||
}
|
||||
} else if (transition) {
|
||||
push(`<!---->`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fallbackRenderFn) {
|
||||
fallbackRenderFn();
|
||||
} else if (transition) {
|
||||
push(`<!---->`);
|
||||
}
|
||||
}
|
||||
const commentTestRE = /^<!--[\s\S]*-->$/;
|
||||
const commentRE = /<!--[^]*?-->/gm;
|
||||
function isComment(item) {
|
||||
if (typeof item !== "string" || !commentTestRE.test(item)) return false;
|
||||
if (item.length <= 8) return true;
|
||||
return !item.replace(commentRE, "").trim();
|
||||
}
|
||||
|
||||
function ssrRenderTeleport(parentPush, contentRenderFn, target, disabled, parentComponent) {
|
||||
parentPush("<!--teleport start-->");
|
||||
const context = parentComponent.appContext.provides[Vue.ssrContextKey];
|
||||
const teleportBuffers = context.__teleportBuffers || (context.__teleportBuffers = {});
|
||||
const targetBuffer = teleportBuffers[target] || (teleportBuffers[target] = []);
|
||||
const bufferIndex = targetBuffer.length;
|
||||
let teleportContent;
|
||||
if (disabled) {
|
||||
contentRenderFn(parentPush);
|
||||
teleportContent = `<!--teleport start anchor--><!--teleport anchor-->`;
|
||||
} else {
|
||||
const { getBuffer, push } = createBuffer();
|
||||
push(`<!--teleport start anchor-->`);
|
||||
contentRenderFn(push);
|
||||
push(`<!--teleport anchor-->`);
|
||||
teleportContent = getBuffer();
|
||||
}
|
||||
targetBuffer.splice(bufferIndex, 0, teleportContent);
|
||||
if (shared.isPromise(teleportContent) || shared.isArray(teleportContent) && teleportContent.hasAsync) {
|
||||
targetBuffer.hasAsync = true;
|
||||
}
|
||||
parentPush("<!--teleport end-->");
|
||||
}
|
||||
|
||||
function ssrInterpolate(value) {
|
||||
return shared.escapeHtml(shared.toDisplayString(value));
|
||||
}
|
||||
|
||||
function ssrRenderList(source, renderItem) {
|
||||
if (shared.isArray(source) || shared.isString(source)) {
|
||||
for (let i = 0, l = source.length; i < l; i++) {
|
||||
renderItem(source[i], i);
|
||||
}
|
||||
} else if (typeof source === "number") {
|
||||
if (!Number.isInteger(source) || source < 0) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < source; i++) {
|
||||
renderItem(i + 1, i);
|
||||
}
|
||||
} else if (shared.isObject(source)) {
|
||||
if (source[Symbol.iterator]) {
|
||||
let i = 0;
|
||||
for (const item of source) {
|
||||
renderItem(item, i++);
|
||||
}
|
||||
} else {
|
||||
const keys = Object.keys(source);
|
||||
for (let i = 0, l = keys.length; i < l; i++) {
|
||||
const key = keys[i];
|
||||
renderItem(source[key], key, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ssrRenderSuspense(push, { default: renderContent }) {
|
||||
if (renderContent) {
|
||||
renderContent();
|
||||
} else {
|
||||
push(`<!---->`);
|
||||
}
|
||||
}
|
||||
|
||||
function ssrGetDirectiveProps(instance, dir, value, arg, modifiers = {}) {
|
||||
if (typeof dir !== "function" && dir.getSSRProps) {
|
||||
return dir.getSSRProps(
|
||||
{
|
||||
dir,
|
||||
instance: Vue.ssrUtils.getComponentPublicInstance(instance.$),
|
||||
value,
|
||||
oldValue: void 0,
|
||||
arg,
|
||||
modifiers
|
||||
},
|
||||
null
|
||||
) || {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const ssrLooseEqual = shared.looseEqual;
|
||||
function ssrLooseContain(arr, value) {
|
||||
return shared.looseIndexOf(arr, value) > -1;
|
||||
}
|
||||
function ssrRenderDynamicModel(type, model, value) {
|
||||
switch (type) {
|
||||
case "radio":
|
||||
return shared.looseEqual(model, value) ? " checked" : "";
|
||||
case "checkbox":
|
||||
return (shared.isArray(model) ? ssrLooseContain(model, value) : model) ? " checked" : "";
|
||||
default:
|
||||
return ssrRenderAttr("value", model);
|
||||
}
|
||||
}
|
||||
function ssrGetDynamicModelProps(existingProps = {}, model) {
|
||||
const { type, value } = existingProps;
|
||||
switch (type) {
|
||||
case "radio":
|
||||
return shared.looseEqual(model, value) ? { checked: true } : null;
|
||||
case "checkbox":
|
||||
return (shared.isArray(model) ? ssrLooseContain(model, value) : model) ? { checked: true } : null;
|
||||
default:
|
||||
return { value: model };
|
||||
}
|
||||
}
|
||||
|
||||
var helpers = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
ssrGetDirectiveProps: ssrGetDirectiveProps,
|
||||
ssrGetDynamicModelProps: ssrGetDynamicModelProps,
|
||||
ssrIncludeBooleanAttr: shared.includeBooleanAttr,
|
||||
ssrInterpolate: ssrInterpolate,
|
||||
ssrLooseContain: ssrLooseContain,
|
||||
ssrLooseEqual: ssrLooseEqual,
|
||||
ssrRenderAttr: ssrRenderAttr,
|
||||
ssrRenderAttrs: ssrRenderAttrs,
|
||||
ssrRenderClass: ssrRenderClass,
|
||||
ssrRenderComponent: ssrRenderComponent,
|
||||
ssrRenderDynamicAttr: ssrRenderDynamicAttr,
|
||||
ssrRenderDynamicModel: ssrRenderDynamicModel,
|
||||
ssrRenderList: ssrRenderList,
|
||||
ssrRenderSlot: ssrRenderSlot,
|
||||
ssrRenderSlotInner: ssrRenderSlotInner,
|
||||
ssrRenderStyle: ssrRenderStyle,
|
||||
ssrRenderSuspense: ssrRenderSuspense,
|
||||
ssrRenderTeleport: ssrRenderTeleport,
|
||||
ssrRenderVNode: renderVNode
|
||||
});
|
||||
|
||||
const compileCache = /* @__PURE__ */ Object.create(null);
|
||||
function ssrCompile(template, instance) {
|
||||
const Component = instance.type;
|
||||
const { isCustomElement, compilerOptions } = instance.appContext.config;
|
||||
const { delimiters, compilerOptions: componentCompilerOptions } = Component;
|
||||
const finalCompilerOptions = shared.extend(
|
||||
shared.extend(
|
||||
{
|
||||
isCustomElement,
|
||||
delimiters
|
||||
},
|
||||
compilerOptions
|
||||
),
|
||||
componentCompilerOptions
|
||||
);
|
||||
finalCompilerOptions.isCustomElement = finalCompilerOptions.isCustomElement || shared.NO;
|
||||
finalCompilerOptions.isNativeTag = finalCompilerOptions.isNativeTag || shared.NO;
|
||||
const cacheKey = JSON.stringify(
|
||||
{
|
||||
template,
|
||||
compilerOptions: finalCompilerOptions
|
||||
},
|
||||
(key, value) => {
|
||||
return shared.isFunction(value) ? value.toString() : value;
|
||||
}
|
||||
);
|
||||
const cached = compileCache[cacheKey];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
finalCompilerOptions.onError = (err) => {
|
||||
{
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
const { code } = compilerSsr.compile(template, finalCompilerOptions);
|
||||
const requireMap = {
|
||||
vue: Vue__namespace,
|
||||
"vue/server-renderer": helpers
|
||||
};
|
||||
const fakeRequire = (id) => requireMap[id];
|
||||
return compileCache[cacheKey] = Function("require", code)(fakeRequire);
|
||||
}
|
||||
|
||||
const {
|
||||
createComponentInstance,
|
||||
setCurrentRenderingInstance,
|
||||
setupComponent,
|
||||
renderComponentRoot,
|
||||
normalizeVNode,
|
||||
pushWarningContext,
|
||||
popWarningContext
|
||||
} = Vue.ssrUtils;
|
||||
function createBuffer() {
|
||||
let appendable = false;
|
||||
const buffer = [];
|
||||
return {
|
||||
getBuffer() {
|
||||
return buffer;
|
||||
},
|
||||
push(item) {
|
||||
const isStringItem = shared.isString(item);
|
||||
if (appendable && isStringItem) {
|
||||
buffer[buffer.length - 1] += item;
|
||||
return;
|
||||
}
|
||||
buffer.push(item);
|
||||
appendable = isStringItem;
|
||||
if (shared.isPromise(item) || shared.isArray(item) && item.hasAsync) {
|
||||
buffer.hasAsync = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function renderComponentVNode(vnode, parentComponent = null, slotScopeId) {
|
||||
const instance = vnode.component = createComponentInstance(
|
||||
vnode,
|
||||
parentComponent,
|
||||
null
|
||||
);
|
||||
const res = setupComponent(
|
||||
instance,
|
||||
true
|
||||
/* isSSR */
|
||||
);
|
||||
const hasAsyncSetup = shared.isPromise(res);
|
||||
let prefetches = instance.sp;
|
||||
let p;
|
||||
if (hasAsyncSetup || prefetches) {
|
||||
p = Promise.resolve(res).then(() => {
|
||||
if (hasAsyncSetup) prefetches = instance.sp;
|
||||
if (prefetches) {
|
||||
try {
|
||||
return Promise.all(
|
||||
prefetches.map((prefetch) => prefetch.call(instance.proxy))
|
||||
).catch(shared.NOOP);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}).then(() => renderComponentSubTree(instance, slotScopeId));
|
||||
} else {
|
||||
try {
|
||||
return renderComponentSubTree(instance, slotScopeId);
|
||||
} catch (err) {
|
||||
p = Promise.reject(err);
|
||||
}
|
||||
}
|
||||
p.catch(shared.NOOP);
|
||||
return p;
|
||||
}
|
||||
function renderComponentSubTree(instance, slotScopeId) {
|
||||
const comp = instance.type;
|
||||
const { getBuffer, push } = createBuffer();
|
||||
if (shared.isFunction(comp)) {
|
||||
let root = renderComponentRoot(instance);
|
||||
if (!comp.props) {
|
||||
for (const key in instance.attrs) {
|
||||
if (key.startsWith(`data-v-`)) {
|
||||
(root.props || (root.props = {}))[key] = ``;
|
||||
}
|
||||
}
|
||||
}
|
||||
renderVNode(push, instance.subTree = root, instance, slotScopeId);
|
||||
} else {
|
||||
if ((!instance.render || instance.render === shared.NOOP) && !instance.ssrRender && !comp.ssrRender && shared.isString(comp.template)) {
|
||||
comp.ssrRender = ssrCompile(comp.template, instance);
|
||||
}
|
||||
const ssrRender = instance.ssrRender || comp.ssrRender;
|
||||
if (ssrRender) {
|
||||
let attrs = instance.inheritAttrs !== false ? instance.attrs : void 0;
|
||||
let hasCloned = false;
|
||||
let cur = instance;
|
||||
while (true) {
|
||||
const scopeId = cur.vnode.scopeId;
|
||||
if (scopeId) {
|
||||
if (!hasCloned) {
|
||||
attrs = { ...attrs };
|
||||
hasCloned = true;
|
||||
}
|
||||
attrs[scopeId] = "";
|
||||
}
|
||||
const parent = cur.parent;
|
||||
if (parent && parent.subTree && parent.subTree === cur.vnode) {
|
||||
cur = parent;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slotScopeId) {
|
||||
if (!hasCloned) attrs = { ...attrs };
|
||||
const slotScopeIdList = slotScopeId.trim().split(" ");
|
||||
for (let i = 0; i < slotScopeIdList.length; i++) {
|
||||
attrs[slotScopeIdList[i]] = "";
|
||||
}
|
||||
}
|
||||
const prev = setCurrentRenderingInstance(instance);
|
||||
try {
|
||||
ssrRender(
|
||||
instance.proxy,
|
||||
push,
|
||||
instance,
|
||||
attrs,
|
||||
// compiler-optimized bindings
|
||||
instance.props,
|
||||
instance.setupState,
|
||||
instance.data,
|
||||
instance.ctx
|
||||
);
|
||||
} catch (err) {
|
||||
Vue.handleError(err, instance, 1);
|
||||
} finally {
|
||||
setCurrentRenderingInstance(prev);
|
||||
}
|
||||
} else if (instance.render && instance.render !== shared.NOOP) {
|
||||
renderVNode(
|
||||
push,
|
||||
instance.subTree = renderComponentRoot(instance),
|
||||
instance,
|
||||
slotScopeId
|
||||
);
|
||||
} else {
|
||||
const componentName = comp.name || comp.__file || `<Anonymous>`;
|
||||
Vue.warn(`Component ${componentName} is missing template or render function.`);
|
||||
push(`<!---->`);
|
||||
}
|
||||
}
|
||||
return getBuffer();
|
||||
}
|
||||
function renderVNode(push, vnode, parentComponent, slotScopeId) {
|
||||
const { type, shapeFlag, children, dirs, props } = vnode;
|
||||
if (dirs) {
|
||||
vnode.props = applySSRDirectives(vnode, props, dirs);
|
||||
}
|
||||
switch (type) {
|
||||
case Vue.Text:
|
||||
push(shared.escapeHtml(children));
|
||||
break;
|
||||
case Vue.Comment:
|
||||
push(
|
||||
children ? `<!--${shared.escapeHtmlComment(children)}-->` : `<!---->`
|
||||
);
|
||||
break;
|
||||
case Vue.Static:
|
||||
push(children);
|
||||
break;
|
||||
case Vue.Fragment:
|
||||
if (vnode.slotScopeIds) {
|
||||
slotScopeId = (slotScopeId ? slotScopeId + " " : "") + vnode.slotScopeIds.join(" ");
|
||||
}
|
||||
push(`<!--[-->`);
|
||||
renderVNodeChildren(
|
||||
push,
|
||||
children,
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
);
|
||||
push(`<!--]-->`);
|
||||
break;
|
||||
default:
|
||||
if (shapeFlag & 1) {
|
||||
renderElementVNode(push, vnode, parentComponent, slotScopeId);
|
||||
} else if (shapeFlag & 6) {
|
||||
push(renderComponentVNode(vnode, parentComponent, slotScopeId));
|
||||
} else if (shapeFlag & 64) {
|
||||
renderTeleportVNode(push, vnode, parentComponent, slotScopeId);
|
||||
} else if (shapeFlag & 128) {
|
||||
renderVNode(push, vnode.ssContent, parentComponent, slotScopeId);
|
||||
} else {
|
||||
Vue.warn(
|
||||
"[@vue/server-renderer] Invalid VNode type:",
|
||||
type,
|
||||
`(${typeof type})`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
function renderVNodeChildren(push, children, parentComponent, slotScopeId) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
renderVNode(push, normalizeVNode(children[i]), parentComponent, slotScopeId);
|
||||
}
|
||||
}
|
||||
function renderElementVNode(push, vnode, parentComponent, slotScopeId) {
|
||||
const tag = vnode.type;
|
||||
let { props, children, shapeFlag, scopeId } = vnode;
|
||||
let openTag = `<${tag}`;
|
||||
if (props) {
|
||||
openTag += ssrRenderAttrs(props, tag);
|
||||
}
|
||||
const renderedScopeIds = [];
|
||||
const appendScopeId = (id) => {
|
||||
if (id && (!props || !shared.hasOwn(props, id)) && !renderedScopeIds.includes(id)) {
|
||||
openTag += ` ${id}`;
|
||||
renderedScopeIds.push(id);
|
||||
}
|
||||
};
|
||||
if (scopeId) {
|
||||
appendScopeId(scopeId);
|
||||
}
|
||||
let curParent = parentComponent;
|
||||
let curVnode = vnode;
|
||||
while (curParent && curVnode === curParent.subTree) {
|
||||
curVnode = curParent.vnode;
|
||||
if (curVnode.scopeId) {
|
||||
appendScopeId(curVnode.scopeId);
|
||||
}
|
||||
curParent = curParent.parent;
|
||||
}
|
||||
if (slotScopeId) {
|
||||
const slotScopeIdList = slotScopeId.trim().split(" ");
|
||||
for (let i = 0; i < slotScopeIdList.length; i++) {
|
||||
appendScopeId(slotScopeIdList[i]);
|
||||
}
|
||||
}
|
||||
push(openTag + `>`);
|
||||
if (!shared.isVoidTag(tag)) {
|
||||
let hasChildrenOverride = false;
|
||||
if (props) {
|
||||
if (props.innerHTML != null) {
|
||||
hasChildrenOverride = true;
|
||||
push(String(props.innerHTML));
|
||||
} else if (props.textContent != null) {
|
||||
hasChildrenOverride = true;
|
||||
push(shared.escapeHtml(props.textContent));
|
||||
} else if (tag === "textarea" && props.value != null) {
|
||||
hasChildrenOverride = true;
|
||||
push(shared.escapeHtml(props.value));
|
||||
}
|
||||
}
|
||||
if (!hasChildrenOverride) {
|
||||
if (shapeFlag & 8) {
|
||||
push(shared.escapeHtml(children));
|
||||
} else if (shapeFlag & 16) {
|
||||
renderVNodeChildren(
|
||||
push,
|
||||
children,
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
);
|
||||
}
|
||||
}
|
||||
push(`</${tag}>`);
|
||||
}
|
||||
}
|
||||
function applySSRDirectives(vnode, rawProps, dirs) {
|
||||
const toMerge = [];
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
const binding = dirs[i];
|
||||
const {
|
||||
dir: { getSSRProps }
|
||||
} = binding;
|
||||
if (getSSRProps) {
|
||||
const props = getSSRProps(binding, vnode);
|
||||
if (props) toMerge.push(props);
|
||||
}
|
||||
}
|
||||
return Vue.mergeProps(rawProps || {}, ...toMerge);
|
||||
}
|
||||
function renderTeleportVNode(push, vnode, parentComponent, slotScopeId) {
|
||||
const target = vnode.props && vnode.props.to;
|
||||
const disabled = vnode.props && vnode.props.disabled;
|
||||
if (!target) {
|
||||
if (!disabled) {
|
||||
Vue.warn(`[@vue/server-renderer] Teleport is missing target prop.`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
if (!shared.isString(target)) {
|
||||
Vue.warn(
|
||||
`[@vue/server-renderer] Teleport target must be a query selector string.`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
ssrRenderTeleport(
|
||||
push,
|
||||
(push2) => {
|
||||
renderVNodeChildren(
|
||||
push2,
|
||||
vnode.children,
|
||||
parentComponent,
|
||||
slotScopeId
|
||||
);
|
||||
},
|
||||
target,
|
||||
disabled || disabled === "",
|
||||
parentComponent
|
||||
);
|
||||
}
|
||||
|
||||
const { isVNode: isVNode$1 } = Vue.ssrUtils;
|
||||
function nestedUnrollBuffer(buffer, parentRet, startIndex) {
|
||||
if (!buffer.hasAsync) {
|
||||
return parentRet + unrollBufferSync$1(buffer);
|
||||
}
|
||||
let ret = parentRet;
|
||||
for (let i = startIndex; i < buffer.length; i += 1) {
|
||||
const item = buffer[i];
|
||||
if (shared.isString(item)) {
|
||||
ret += item;
|
||||
continue;
|
||||
}
|
||||
if (shared.isPromise(item)) {
|
||||
return item.then((nestedItem) => {
|
||||
buffer[i] = nestedItem;
|
||||
return nestedUnrollBuffer(buffer, ret, i);
|
||||
});
|
||||
}
|
||||
const result = nestedUnrollBuffer(item, ret, 0);
|
||||
if (shared.isPromise(result)) {
|
||||
return result.then((nestedItem) => {
|
||||
buffer[i] = nestedItem;
|
||||
return nestedUnrollBuffer(buffer, "", i);
|
||||
});
|
||||
}
|
||||
ret = result;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function unrollBuffer$1(buffer) {
|
||||
return nestedUnrollBuffer(buffer, "", 0);
|
||||
}
|
||||
function unrollBufferSync$1(buffer) {
|
||||
let ret = "";
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
let item = buffer[i];
|
||||
if (shared.isString(item)) {
|
||||
ret += item;
|
||||
} else {
|
||||
ret += unrollBufferSync$1(item);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
async function renderToString(input, context = {}) {
|
||||
if (isVNode$1(input)) {
|
||||
return renderToString(Vue.createApp({ render: () => input }), context);
|
||||
}
|
||||
const vnode = Vue.createVNode(input._component, input._props);
|
||||
vnode.appContext = input._context;
|
||||
input.provide(Vue.ssrContextKey, context);
|
||||
let buffer;
|
||||
let result;
|
||||
try {
|
||||
buffer = await renderComponentVNode(vnode);
|
||||
result = await unrollBuffer$1(buffer);
|
||||
await resolveTeleports(context);
|
||||
} catch (err) {
|
||||
cleanupFailedRender(buffer, context);
|
||||
throw err;
|
||||
}
|
||||
stopWatchers(context);
|
||||
return result;
|
||||
}
|
||||
function stopWatchers(context, start = 0) {
|
||||
const handles = context.__watcherHandles;
|
||||
if (handles) {
|
||||
for (let i = start; i < handles.length; i++) {
|
||||
handles[i]();
|
||||
}
|
||||
}
|
||||
}
|
||||
function cleanupFailedRender(buffer, context) {
|
||||
const stopped = context.__watcherHandles ? context.__watcherHandles.length : 0;
|
||||
try {
|
||||
stopWatchers(context);
|
||||
} catch {
|
||||
}
|
||||
(async () => {
|
||||
if (buffer) await settleBuffer(buffer);
|
||||
for (const key in context.__teleportBuffers) {
|
||||
await settleBuffer(context.__teleportBuffers[key]);
|
||||
}
|
||||
stopWatchers(context, stopped);
|
||||
})().catch(shared.NOOP);
|
||||
}
|
||||
async function settleBuffer(buffer) {
|
||||
if (!buffer.hasAsync) return;
|
||||
for (let item of buffer) {
|
||||
try {
|
||||
if (shared.isPromise(item)) item = await item;
|
||||
if (!shared.isString(item)) await settleBuffer(item);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
async function resolveTeleports(context) {
|
||||
if (context.__teleportBuffers) {
|
||||
context.teleports = context.teleports || {};
|
||||
for (const key in context.__teleportBuffers) {
|
||||
context.teleports[key] = await unrollBuffer$1(
|
||||
context.__teleportBuffers[key]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { isVNode } = Vue.ssrUtils;
|
||||
async function unrollBuffer(buffer, stream) {
|
||||
if (buffer.hasAsync) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
let item = buffer[i];
|
||||
if (shared.isPromise(item)) {
|
||||
item = await item;
|
||||
}
|
||||
if (shared.isString(item)) {
|
||||
stream.push(item);
|
||||
} else {
|
||||
await unrollBuffer(item, stream);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unrollBufferSync(buffer, stream);
|
||||
}
|
||||
}
|
||||
function unrollBufferSync(buffer, stream) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
let item = buffer[i];
|
||||
if (shared.isString(item)) {
|
||||
stream.push(item);
|
||||
} else {
|
||||
unrollBufferSync(item, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
function renderToSimpleStream(input, context, stream) {
|
||||
if (isVNode(input)) {
|
||||
return renderToSimpleStream(
|
||||
Vue.createApp({ render: () => input }),
|
||||
context,
|
||||
stream
|
||||
);
|
||||
}
|
||||
const vnode = Vue.createVNode(input._component, input._props);
|
||||
vnode.appContext = input._context;
|
||||
input.provide(Vue.ssrContextKey, context);
|
||||
let buffer;
|
||||
Promise.resolve(renderComponentVNode(vnode)).then((b) => unrollBuffer(buffer = b, stream)).then(() => resolveTeleports(context)).then(
|
||||
() => {
|
||||
stopWatchers(context);
|
||||
stream.push(null);
|
||||
},
|
||||
(error) => {
|
||||
cleanupFailedRender(buffer, context);
|
||||
throw error;
|
||||
}
|
||||
).catch((error) => {
|
||||
stream.destroy(error);
|
||||
});
|
||||
return stream;
|
||||
}
|
||||
function renderToStream(input, context = {}) {
|
||||
console.warn(
|
||||
`[@vue/server-renderer] renderToStream is deprecated - use renderToNodeStream instead.`
|
||||
);
|
||||
return renderToNodeStream(input, context);
|
||||
}
|
||||
function renderToNodeStream(input, context = {}) {
|
||||
const stream = new (require("node:stream")).Readable({ read() {
|
||||
} }) ;
|
||||
if (!stream) {
|
||||
throw new Error(
|
||||
`ESM build of renderToStream() does not support renderToNodeStream(). Use pipeToNodeWritable() with an existing Node.js Writable stream instance instead.`
|
||||
);
|
||||
}
|
||||
return renderToSimpleStream(input, context, stream);
|
||||
}
|
||||
function pipeToNodeWritable(input, context = {}, writable) {
|
||||
renderToSimpleStream(input, context, {
|
||||
push(content) {
|
||||
if (content != null) {
|
||||
writable.write(content);
|
||||
} else {
|
||||
writable.end();
|
||||
}
|
||||
},
|
||||
destroy(err) {
|
||||
writable.destroy(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
function renderToWebStream(input, context = {}) {
|
||||
if (typeof ReadableStream !== "function") {
|
||||
throw new Error(
|
||||
`ReadableStream constructor is not available in the global scope. If the target environment does support web streams, consider using pipeToWebWritable() with an existing WritableStream instance instead.`
|
||||
);
|
||||
}
|
||||
const encoder = new TextEncoder();
|
||||
let cancelled = false;
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
renderToSimpleStream(input, context, {
|
||||
push(content) {
|
||||
if (cancelled) return;
|
||||
if (content != null) {
|
||||
controller.enqueue(encoder.encode(content));
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
},
|
||||
destroy(err) {
|
||||
controller.error(err);
|
||||
}
|
||||
});
|
||||
},
|
||||
cancel() {
|
||||
cancelled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function pipeToWebWritable(input, context = {}, writable) {
|
||||
const writer = writable.getWriter();
|
||||
const encoder = new TextEncoder();
|
||||
let hasReady = false;
|
||||
try {
|
||||
hasReady = shared.isPromise(writer.ready);
|
||||
} catch (e) {
|
||||
}
|
||||
renderToSimpleStream(input, context, {
|
||||
async push(content) {
|
||||
if (hasReady) {
|
||||
await writer.ready;
|
||||
}
|
||||
if (content != null) {
|
||||
return writer.write(encoder.encode(content));
|
||||
} else {
|
||||
return writer.close();
|
||||
}
|
||||
},
|
||||
destroy(err) {
|
||||
console.log(err);
|
||||
writer.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Vue.initDirectivesForSSR();
|
||||
|
||||
exports.ssrIncludeBooleanAttr = shared.includeBooleanAttr;
|
||||
exports.pipeToNodeWritable = pipeToNodeWritable;
|
||||
exports.pipeToWebWritable = pipeToWebWritable;
|
||||
exports.renderToNodeStream = renderToNodeStream;
|
||||
exports.renderToSimpleStream = renderToSimpleStream;
|
||||
exports.renderToStream = renderToStream;
|
||||
exports.renderToString = renderToString;
|
||||
exports.renderToWebStream = renderToWebStream;
|
||||
exports.ssrGetDirectiveProps = ssrGetDirectiveProps;
|
||||
exports.ssrGetDynamicModelProps = ssrGetDynamicModelProps;
|
||||
exports.ssrInterpolate = ssrInterpolate;
|
||||
exports.ssrLooseContain = ssrLooseContain;
|
||||
exports.ssrLooseEqual = ssrLooseEqual;
|
||||
exports.ssrRenderAttr = ssrRenderAttr;
|
||||
exports.ssrRenderAttrs = ssrRenderAttrs;
|
||||
exports.ssrRenderClass = ssrRenderClass;
|
||||
exports.ssrRenderComponent = ssrRenderComponent;
|
||||
exports.ssrRenderDynamicAttr = ssrRenderDynamicAttr;
|
||||
exports.ssrRenderDynamicModel = ssrRenderDynamicModel;
|
||||
exports.ssrRenderList = ssrRenderList;
|
||||
exports.ssrRenderSlot = ssrRenderSlot;
|
||||
exports.ssrRenderSlotInner = ssrRenderSlotInner;
|
||||
exports.ssrRenderStyle = ssrRenderStyle;
|
||||
exports.ssrRenderSuspense = ssrRenderSuspense;
|
||||
exports.ssrRenderTeleport = ssrRenderTeleport;
|
||||
exports.ssrRenderVNode = renderVNode;
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "@vue/server-renderer",
|
||||
"version": "3.5.44",
|
||||
"description": "@vue/server-renderer",
|
||||
"main": "index.js",
|
||||
"module": "dist/server-renderer.esm-bundler.js",
|
||||
"types": "dist/server-renderer.d.ts",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/server-renderer.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/server-renderer.cjs.prod.js",
|
||||
"development": "./dist/server-renderer.cjs.js",
|
||||
"default": "./dist/server-renderer.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/server-renderer.esm-bundler.js",
|
||||
"import": "./dist/server-renderer.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"buildOptions": {
|
||||
"name": "VueServerRenderer",
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"esm-browser",
|
||||
"cjs"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/server-renderer"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/server-renderer#readme",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.44",
|
||||
"@vue/compiler-ssr": "3.5.44",
|
||||
"@vue/runtime-dom": "3.5.44"
|
||||
}
|
||||
}
|
||||
+655
@@ -0,0 +1,655 @@
|
||||
/**
|
||||
* @vue/shared v3.5.44
|
||||
* (c) 2018-present Yuxi (Evan) You and Vue contributors
|
||||
* @license MIT
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
// @__NO_SIDE_EFFECTS__
|
||||
function makeMap(str) {
|
||||
const map = /* @__PURE__ */ Object.create(null);
|
||||
for (const key of str.split(",")) map[key] = 1;
|
||||
return (val) => val in map;
|
||||
}
|
||||
|
||||
const EMPTY_OBJ = {};
|
||||
const EMPTY_ARR = [];
|
||||
const NOOP = () => {
|
||||
};
|
||||
const NO = () => false;
|
||||
const isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter
|
||||
(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97);
|
||||
const isModelListener = (key) => key.startsWith("onUpdate:");
|
||||
const extend = Object.assign;
|
||||
const remove = (arr, el) => {
|
||||
const i = arr.indexOf(el);
|
||||
if (i > -1) {
|
||||
arr.splice(i, 1);
|
||||
}
|
||||
};
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
|
||||
const isArray = Array.isArray;
|
||||
const isMap = (val) => toTypeString(val) === "[object Map]";
|
||||
const isSet = (val) => toTypeString(val) === "[object Set]";
|
||||
const isDate = (val) => toTypeString(val) === "[object Date]";
|
||||
const isRegExp = (val) => toTypeString(val) === "[object RegExp]";
|
||||
const isFunction = (val) => typeof val === "function";
|
||||
const isString = (val) => typeof val === "string";
|
||||
const isSymbol = (val) => typeof val === "symbol";
|
||||
const isObject = (val) => val !== null && typeof val === "object";
|
||||
const isPromise = (val) => {
|
||||
return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch);
|
||||
};
|
||||
const objectToString = Object.prototype.toString;
|
||||
const toTypeString = (value) => objectToString.call(value);
|
||||
const toRawType = (value) => {
|
||||
return toTypeString(value).slice(8, -1);
|
||||
};
|
||||
const isPlainObject = (val) => toTypeString(val) === "[object Object]";
|
||||
const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key;
|
||||
const isReservedProp = /* @__PURE__ */ makeMap(
|
||||
// the leading comma is intentional so empty string "" is also included
|
||||
",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"
|
||||
);
|
||||
const isBuiltInDirective = /* @__PURE__ */ makeMap(
|
||||
"bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo"
|
||||
);
|
||||
const cacheStringFunction = (fn) => {
|
||||
const cache = /* @__PURE__ */ Object.create(null);
|
||||
return ((str) => {
|
||||
const hit = cache[str];
|
||||
return hit || (cache[str] = fn(str));
|
||||
});
|
||||
};
|
||||
const camelizeRE = /-\w/g;
|
||||
const camelize = cacheStringFunction(
|
||||
(str) => {
|
||||
return str.replace(camelizeRE, (c) => c.slice(1).toUpperCase());
|
||||
}
|
||||
);
|
||||
const hyphenateRE = /\B([A-Z])/g;
|
||||
const hyphenate = cacheStringFunction(
|
||||
(str) => str.replace(hyphenateRE, "-$1").toLowerCase()
|
||||
);
|
||||
const capitalize = cacheStringFunction((str) => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
});
|
||||
const toHandlerKey = cacheStringFunction(
|
||||
(str) => {
|
||||
const s = str ? `on${capitalize(str)}` : ``;
|
||||
return s;
|
||||
}
|
||||
);
|
||||
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
|
||||
const invokeArrayFns = (fns, ...arg) => {
|
||||
for (let i = 0; i < fns.length; i++) {
|
||||
fns[i](...arg);
|
||||
}
|
||||
};
|
||||
const def = (obj, key, value, writable = false) => {
|
||||
Object.defineProperty(obj, key, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable,
|
||||
value
|
||||
});
|
||||
};
|
||||
const looseToNumber = (val) => {
|
||||
const n = parseFloat(val);
|
||||
return isNaN(n) ? val : n;
|
||||
};
|
||||
const toNumber = (val) => {
|
||||
const n = isString(val) ? Number(val) : NaN;
|
||||
return isNaN(n) ? val : n;
|
||||
};
|
||||
let _globalThis;
|
||||
const getGlobalThis = () => {
|
||||
return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {});
|
||||
};
|
||||
const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/;
|
||||
function genPropsAccessExp(name) {
|
||||
return identRE.test(name) ? `__props.${name}` : `__props[${JSON.stringify(name)}]`;
|
||||
}
|
||||
function genCacheKey(source, options) {
|
||||
return source + JSON.stringify(
|
||||
options,
|
||||
(_, val) => typeof val === "function" ? val.toString() : val
|
||||
);
|
||||
}
|
||||
|
||||
const PatchFlags = {
|
||||
"TEXT": 1,
|
||||
"1": "TEXT",
|
||||
"CLASS": 2,
|
||||
"2": "CLASS",
|
||||
"STYLE": 4,
|
||||
"4": "STYLE",
|
||||
"PROPS": 8,
|
||||
"8": "PROPS",
|
||||
"FULL_PROPS": 16,
|
||||
"16": "FULL_PROPS",
|
||||
"NEED_HYDRATION": 32,
|
||||
"32": "NEED_HYDRATION",
|
||||
"STABLE_FRAGMENT": 64,
|
||||
"64": "STABLE_FRAGMENT",
|
||||
"KEYED_FRAGMENT": 128,
|
||||
"128": "KEYED_FRAGMENT",
|
||||
"UNKEYED_FRAGMENT": 256,
|
||||
"256": "UNKEYED_FRAGMENT",
|
||||
"NEED_PATCH": 512,
|
||||
"512": "NEED_PATCH",
|
||||
"DYNAMIC_SLOTS": 1024,
|
||||
"1024": "DYNAMIC_SLOTS",
|
||||
"DEV_ROOT_FRAGMENT": 2048,
|
||||
"2048": "DEV_ROOT_FRAGMENT",
|
||||
"CACHED": -1,
|
||||
"-1": "CACHED",
|
||||
"BAIL": -2,
|
||||
"-2": "BAIL"
|
||||
};
|
||||
const PatchFlagNames = {
|
||||
[1]: `TEXT`,
|
||||
[2]: `CLASS`,
|
||||
[4]: `STYLE`,
|
||||
[8]: `PROPS`,
|
||||
[16]: `FULL_PROPS`,
|
||||
[32]: `NEED_HYDRATION`,
|
||||
[64]: `STABLE_FRAGMENT`,
|
||||
[128]: `KEYED_FRAGMENT`,
|
||||
[256]: `UNKEYED_FRAGMENT`,
|
||||
[512]: `NEED_PATCH`,
|
||||
[1024]: `DYNAMIC_SLOTS`,
|
||||
[2048]: `DEV_ROOT_FRAGMENT`,
|
||||
[-1]: `CACHED`,
|
||||
[-2]: `BAIL`
|
||||
};
|
||||
|
||||
const ShapeFlags = {
|
||||
"ELEMENT": 1,
|
||||
"1": "ELEMENT",
|
||||
"FUNCTIONAL_COMPONENT": 2,
|
||||
"2": "FUNCTIONAL_COMPONENT",
|
||||
"STATEFUL_COMPONENT": 4,
|
||||
"4": "STATEFUL_COMPONENT",
|
||||
"TEXT_CHILDREN": 8,
|
||||
"8": "TEXT_CHILDREN",
|
||||
"ARRAY_CHILDREN": 16,
|
||||
"16": "ARRAY_CHILDREN",
|
||||
"SLOTS_CHILDREN": 32,
|
||||
"32": "SLOTS_CHILDREN",
|
||||
"TELEPORT": 64,
|
||||
"64": "TELEPORT",
|
||||
"SUSPENSE": 128,
|
||||
"128": "SUSPENSE",
|
||||
"COMPONENT_SHOULD_KEEP_ALIVE": 256,
|
||||
"256": "COMPONENT_SHOULD_KEEP_ALIVE",
|
||||
"COMPONENT_KEPT_ALIVE": 512,
|
||||
"512": "COMPONENT_KEPT_ALIVE",
|
||||
"COMPONENT": 6,
|
||||
"6": "COMPONENT"
|
||||
};
|
||||
|
||||
const SlotFlags = {
|
||||
"STABLE": 1,
|
||||
"1": "STABLE",
|
||||
"DYNAMIC": 2,
|
||||
"2": "DYNAMIC",
|
||||
"FORWARDED": 3,
|
||||
"3": "FORWARDED"
|
||||
};
|
||||
const slotFlagsText = {
|
||||
[1]: "STABLE",
|
||||
[2]: "DYNAMIC",
|
||||
[3]: "FORWARDED"
|
||||
};
|
||||
|
||||
const GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error,Symbol";
|
||||
const isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED);
|
||||
const isGloballyWhitelisted = isGloballyAllowed;
|
||||
|
||||
const range = 2;
|
||||
function generateCodeFrame(source, start = 0, end = source.length) {
|
||||
start = Math.max(0, Math.min(start, source.length));
|
||||
end = Math.max(0, Math.min(end, source.length));
|
||||
if (start > end) return "";
|
||||
let lines = source.split(/(\r?\n)/);
|
||||
const newlineSequences = lines.filter((_, idx) => idx % 2 === 1);
|
||||
lines = lines.filter((_, idx) => idx % 2 === 0);
|
||||
let count = 0;
|
||||
const res = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0);
|
||||
if (count >= start) {
|
||||
for (let j = i - range; j <= i + range || end > count; j++) {
|
||||
if (j < 0 || j >= lines.length) continue;
|
||||
const line = j + 1;
|
||||
res.push(
|
||||
`${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`
|
||||
);
|
||||
const lineLength = lines[j].length;
|
||||
const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0;
|
||||
if (j === i) {
|
||||
const pad = start - (count - (lineLength + newLineSeqLength));
|
||||
const length = Math.max(
|
||||
1,
|
||||
end > count ? lineLength - pad : end - start
|
||||
);
|
||||
res.push(` | ` + " ".repeat(pad) + "^".repeat(length));
|
||||
} else if (j > i) {
|
||||
if (end > count) {
|
||||
const length = Math.max(Math.min(end - count, lineLength), 1);
|
||||
res.push(` | ` + "^".repeat(length));
|
||||
}
|
||||
count += lineLength + newLineSeqLength;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
function normalizeStyle(value) {
|
||||
if (isArray(value)) {
|
||||
const res = {};
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const item = value[i];
|
||||
const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item);
|
||||
if (normalized) {
|
||||
for (const key in normalized) {
|
||||
res[key] = normalized[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
} else if (isString(value) || isObject(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
const listDelimiterRE = /;(?![^(]*\))/g;
|
||||
const propertyDelimiterRE = /:([^]+)/;
|
||||
const styleCommentRE = /"(?:[^"\\]|\\[^])*"|'(?:[^'\\]|\\[^])*'|\\[^]|\/\*[^]*?\*\//g;
|
||||
function parseStringStyle(cssText) {
|
||||
const ret = {};
|
||||
cssText.replace(styleCommentRE, (match) => match.startsWith("/*") ? "" : match).split(listDelimiterRE).forEach((item) => {
|
||||
if (item) {
|
||||
const tmp = item.split(propertyDelimiterRE);
|
||||
tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
function stringifyStyle(styles) {
|
||||
if (!styles) return "";
|
||||
if (isString(styles)) return styles;
|
||||
let ret = "";
|
||||
for (const key in styles) {
|
||||
const value = styles[key];
|
||||
if (isString(value) || typeof value === "number") {
|
||||
const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key);
|
||||
ret += `${normalizedKey}:${value};`;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function normalizeClass(value) {
|
||||
let res = "";
|
||||
if (isString(value)) {
|
||||
res = value;
|
||||
} else if (isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const normalized = normalizeClass(value[i]);
|
||||
if (normalized) {
|
||||
res += normalized + " ";
|
||||
}
|
||||
}
|
||||
} else if (isObject(value)) {
|
||||
for (const name in value) {
|
||||
if (value[name]) {
|
||||
res += name + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.trim();
|
||||
}
|
||||
function normalizeProps(props) {
|
||||
if (!props) return null;
|
||||
let { class: klass, style } = props;
|
||||
if (klass && !isString(klass)) {
|
||||
props.class = normalizeClass(klass);
|
||||
}
|
||||
if (style) {
|
||||
props.style = normalizeStyle(style);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
const HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot";
|
||||
const SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view";
|
||||
const MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics";
|
||||
const VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr";
|
||||
const isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS);
|
||||
const isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS);
|
||||
const isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS);
|
||||
const isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS);
|
||||
|
||||
const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,playsinline,readonly`;
|
||||
const isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs);
|
||||
const isBooleanAttr = /* @__PURE__ */ makeMap(
|
||||
specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`
|
||||
);
|
||||
function includeBooleanAttr(value) {
|
||||
return !!value || value === "";
|
||||
}
|
||||
const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u000d\u0020]/;
|
||||
const attrValidationCache = {};
|
||||
function isSSRSafeAttrName(name) {
|
||||
if (attrValidationCache.hasOwnProperty(name)) {
|
||||
return attrValidationCache[name];
|
||||
}
|
||||
const isUnsafe = unsafeAttrCharRE.test(name);
|
||||
if (isUnsafe) {
|
||||
console.error(`unsafe attribute name: ${name}`);
|
||||
}
|
||||
return attrValidationCache[name] = !isUnsafe;
|
||||
}
|
||||
const propsToAttrMap = {
|
||||
acceptCharset: "accept-charset",
|
||||
className: "class",
|
||||
htmlFor: "for",
|
||||
httpEquiv: "http-equiv"
|
||||
};
|
||||
const isKnownHtmlAttr = /* @__PURE__ */ makeMap(
|
||||
`accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`
|
||||
);
|
||||
const isKnownSvgAttr = /* @__PURE__ */ makeMap(
|
||||
`xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan`
|
||||
);
|
||||
const isKnownMathMLAttr = /* @__PURE__ */ makeMap(
|
||||
`accent,accentunder,actiontype,align,alignmentscope,altimg,altimg-height,altimg-valign,altimg-width,alttext,bevelled,close,columnsalign,columnlines,columnspan,denomalign,depth,dir,display,displaystyle,encoding,equalcolumns,equalrows,fence,fontstyle,fontweight,form,frame,framespacing,groupalign,height,href,id,indentalign,indentalignfirst,indentalignlast,indentshift,indentshiftfirst,indentshiftlast,indextype,justify,largetop,largeop,lquote,lspace,mathbackground,mathcolor,mathsize,mathvariant,maxsize,minlabelspacing,mode,other,overflow,position,rowalign,rowlines,rowspan,rquote,rspace,scriptlevel,scriptminsize,scriptsizemultiplier,selection,separator,separators,shift,side,src,stackalign,stretchy,subscriptshift,superscriptshift,symmetric,voffset,width,widths,xlink:href,xlink:show,xlink:type,xmlns`
|
||||
);
|
||||
function isRenderableAttrValue(value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
const type = typeof value;
|
||||
return type === "string" || type === "number" || type === "boolean";
|
||||
}
|
||||
|
||||
const escapeRE = /["'&<>]/;
|
||||
function escapeHtml(string) {
|
||||
const str = "" + string;
|
||||
const match = escapeRE.exec(str);
|
||||
if (!match) {
|
||||
return str;
|
||||
}
|
||||
let html = "";
|
||||
let escaped;
|
||||
let index;
|
||||
let lastIndex = 0;
|
||||
for (index = match.index; index < str.length; index++) {
|
||||
switch (str.charCodeAt(index)) {
|
||||
case 34:
|
||||
escaped = """;
|
||||
break;
|
||||
case 38:
|
||||
escaped = "&";
|
||||
break;
|
||||
case 39:
|
||||
escaped = "'";
|
||||
break;
|
||||
case 60:
|
||||
escaped = "<";
|
||||
break;
|
||||
case 62:
|
||||
escaped = ">";
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (lastIndex !== index) {
|
||||
html += str.slice(lastIndex, index);
|
||||
}
|
||||
lastIndex = index + 1;
|
||||
html += escaped;
|
||||
}
|
||||
return lastIndex !== index ? html + str.slice(lastIndex, index) : html;
|
||||
}
|
||||
const commentStripRE = /^(?:-?>)+|<!--|-->|--!>|<!-$/g;
|
||||
function escapeHtmlComment(src) {
|
||||
let prev;
|
||||
do {
|
||||
prev = src;
|
||||
src = src.replace(commentStripRE, "");
|
||||
} while (src !== prev);
|
||||
return src;
|
||||
}
|
||||
const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g;
|
||||
function getEscapedCssVarName(key, doubleEscape) {
|
||||
return key.replace(
|
||||
cssVarNameEscapeSymbolsRE,
|
||||
(s) => doubleEscape ? s === '"' ? '\\\\\\"' : `\\\\${s}` : `\\${s}`
|
||||
);
|
||||
}
|
||||
|
||||
function looseCompareArrays(a, b, seen) {
|
||||
if (a.length !== b.length) return false;
|
||||
let equal = true;
|
||||
for (let i = 0; equal && i < a.length; i++) {
|
||||
equal = looseEqual(a[i], b[i], seen);
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
function looseCompareCollections(a, b, seen) {
|
||||
if (a.size !== b.size) return false;
|
||||
const candidates = Array.from(b);
|
||||
const matched = new Uint8Array(candidates.length);
|
||||
for (const item of a) {
|
||||
let index = -1;
|
||||
for (let i = 0; i < candidates.length; i++) {
|
||||
if (!matched[i] && looseEqual(item, candidates[i], seen)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0) return false;
|
||||
matched[index] = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function looseCompareObjects(a, b, seen) {
|
||||
let aValidType = isMap(a);
|
||||
let bValidType = isMap(b);
|
||||
if (aValidType || bValidType) {
|
||||
return aValidType && bValidType ? looseCompareCollections(a, b, seen) : false;
|
||||
}
|
||||
aValidType = isSet(a);
|
||||
bValidType = isSet(b);
|
||||
if (aValidType || bValidType) {
|
||||
return aValidType && bValidType ? looseCompareCollections(a, b, seen) : false;
|
||||
}
|
||||
const aKeysCount = Object.keys(a).length;
|
||||
const bKeysCount = Object.keys(b).length;
|
||||
if (aKeysCount !== bKeysCount) {
|
||||
return false;
|
||||
}
|
||||
for (const key in a) {
|
||||
const aHasKey = a.hasOwnProperty(key);
|
||||
const bHasKey = b.hasOwnProperty(key);
|
||||
if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key], seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return String(a) === String(b);
|
||||
}
|
||||
function looseCompareNested(a, b, seen, compare) {
|
||||
if (!seen) {
|
||||
seen = [/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map()];
|
||||
}
|
||||
const [seenA, seenB] = seen;
|
||||
if (seenA.has(a) || seenB.has(b)) {
|
||||
return seenA.get(a) === b && seenB.get(b) === a;
|
||||
}
|
||||
seenA.set(a, b);
|
||||
seenB.set(b, a);
|
||||
const equal = compare(a, b, seen);
|
||||
seenA.delete(a);
|
||||
seenB.delete(b);
|
||||
return equal;
|
||||
}
|
||||
function looseEqual(a, b, seen) {
|
||||
if (a === b) return true;
|
||||
let aValidType = isDate(a);
|
||||
let bValidType = isDate(b);
|
||||
if (aValidType || bValidType) {
|
||||
return aValidType && bValidType ? a.getTime() === b.getTime() : false;
|
||||
}
|
||||
aValidType = isSymbol(a);
|
||||
bValidType = isSymbol(b);
|
||||
if (aValidType || bValidType) {
|
||||
return a === b;
|
||||
}
|
||||
aValidType = isArray(a);
|
||||
bValidType = isArray(b);
|
||||
if (aValidType || bValidType) {
|
||||
return aValidType && bValidType ? looseCompareNested(a, b, seen, looseCompareArrays) : false;
|
||||
}
|
||||
aValidType = isObject(a);
|
||||
bValidType = isObject(b);
|
||||
if (aValidType || bValidType) {
|
||||
if (!aValidType || !bValidType) {
|
||||
return false;
|
||||
}
|
||||
return looseCompareNested(a, b, seen, looseCompareObjects);
|
||||
}
|
||||
return String(a) === String(b);
|
||||
}
|
||||
function looseIndexOf(arr, val) {
|
||||
return arr.findIndex((item) => looseEqual(item, val));
|
||||
}
|
||||
|
||||
const isRef = (val) => {
|
||||
return !!(val && val["__v_isRef"] === true);
|
||||
};
|
||||
const toDisplayString = (val) => {
|
||||
return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? isRef(val) ? toDisplayString(val.value) : JSON.stringify(val, replacer, 2) : String(val);
|
||||
};
|
||||
const replacer = (_key, val) => {
|
||||
if (isRef(val)) {
|
||||
return replacer(_key, val.value);
|
||||
} else if (isMap(val)) {
|
||||
return {
|
||||
[`Map(${val.size})`]: [...val.entries()].reduce(
|
||||
(entries, [key, val2], i) => {
|
||||
entries[stringifySymbol(key, i) + " =>"] = val2;
|
||||
return entries;
|
||||
},
|
||||
{}
|
||||
)
|
||||
};
|
||||
} else if (isSet(val)) {
|
||||
return {
|
||||
[`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v))
|
||||
};
|
||||
} else if (isSymbol(val)) {
|
||||
return stringifySymbol(val);
|
||||
} else if (isObject(val) && !isArray(val) && !isPlainObject(val)) {
|
||||
return String(val);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
const stringifySymbol = (v, i = "") => {
|
||||
var _a;
|
||||
return (
|
||||
// Symbol.description in es2019+ so we need to cast here to pass
|
||||
// the lib: es2016 check
|
||||
isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v
|
||||
);
|
||||
};
|
||||
|
||||
function normalizeCssVarValue(value) {
|
||||
if (value == null) {
|
||||
return "initial";
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return value === "" ? " " : value;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
exports.EMPTY_ARR = EMPTY_ARR;
|
||||
exports.EMPTY_OBJ = EMPTY_OBJ;
|
||||
exports.NO = NO;
|
||||
exports.NOOP = NOOP;
|
||||
exports.PatchFlagNames = PatchFlagNames;
|
||||
exports.PatchFlags = PatchFlags;
|
||||
exports.ShapeFlags = ShapeFlags;
|
||||
exports.SlotFlags = SlotFlags;
|
||||
exports.camelize = camelize;
|
||||
exports.capitalize = capitalize;
|
||||
exports.cssVarNameEscapeSymbolsRE = cssVarNameEscapeSymbolsRE;
|
||||
exports.def = def;
|
||||
exports.escapeHtml = escapeHtml;
|
||||
exports.escapeHtmlComment = escapeHtmlComment;
|
||||
exports.extend = extend;
|
||||
exports.genCacheKey = genCacheKey;
|
||||
exports.genPropsAccessExp = genPropsAccessExp;
|
||||
exports.generateCodeFrame = generateCodeFrame;
|
||||
exports.getEscapedCssVarName = getEscapedCssVarName;
|
||||
exports.getGlobalThis = getGlobalThis;
|
||||
exports.hasChanged = hasChanged;
|
||||
exports.hasOwn = hasOwn;
|
||||
exports.hyphenate = hyphenate;
|
||||
exports.includeBooleanAttr = includeBooleanAttr;
|
||||
exports.invokeArrayFns = invokeArrayFns;
|
||||
exports.isArray = isArray;
|
||||
exports.isBooleanAttr = isBooleanAttr;
|
||||
exports.isBuiltInDirective = isBuiltInDirective;
|
||||
exports.isDate = isDate;
|
||||
exports.isFunction = isFunction;
|
||||
exports.isGloballyAllowed = isGloballyAllowed;
|
||||
exports.isGloballyWhitelisted = isGloballyWhitelisted;
|
||||
exports.isHTMLTag = isHTMLTag;
|
||||
exports.isIntegerKey = isIntegerKey;
|
||||
exports.isKnownHtmlAttr = isKnownHtmlAttr;
|
||||
exports.isKnownMathMLAttr = isKnownMathMLAttr;
|
||||
exports.isKnownSvgAttr = isKnownSvgAttr;
|
||||
exports.isMap = isMap;
|
||||
exports.isMathMLTag = isMathMLTag;
|
||||
exports.isModelListener = isModelListener;
|
||||
exports.isObject = isObject;
|
||||
exports.isOn = isOn;
|
||||
exports.isPlainObject = isPlainObject;
|
||||
exports.isPromise = isPromise;
|
||||
exports.isRegExp = isRegExp;
|
||||
exports.isRenderableAttrValue = isRenderableAttrValue;
|
||||
exports.isReservedProp = isReservedProp;
|
||||
exports.isSSRSafeAttrName = isSSRSafeAttrName;
|
||||
exports.isSVGTag = isSVGTag;
|
||||
exports.isSet = isSet;
|
||||
exports.isSpecialBooleanAttr = isSpecialBooleanAttr;
|
||||
exports.isString = isString;
|
||||
exports.isSymbol = isSymbol;
|
||||
exports.isVoidTag = isVoidTag;
|
||||
exports.looseEqual = looseEqual;
|
||||
exports.looseIndexOf = looseIndexOf;
|
||||
exports.looseToNumber = looseToNumber;
|
||||
exports.makeMap = makeMap;
|
||||
exports.normalizeClass = normalizeClass;
|
||||
exports.normalizeCssVarValue = normalizeCssVarValue;
|
||||
exports.normalizeProps = normalizeProps;
|
||||
exports.normalizeStyle = normalizeStyle;
|
||||
exports.objectToString = objectToString;
|
||||
exports.parseStringStyle = parseStringStyle;
|
||||
exports.propsToAttrMap = propsToAttrMap;
|
||||
exports.remove = remove;
|
||||
exports.slotFlagsText = slotFlagsText;
|
||||
exports.stringifyStyle = stringifyStyle;
|
||||
exports.toDisplayString = toDisplayString;
|
||||
exports.toHandlerKey = toHandlerKey;
|
||||
exports.toNumber = toNumber;
|
||||
exports.toRawType = toRawType;
|
||||
exports.toTypeString = toTypeString;
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@vue/shared",
|
||||
"version": "3.5.44",
|
||||
"description": "internal utils shared across @vue packages",
|
||||
"main": "index.js",
|
||||
"module": "dist/shared.esm-bundler.js",
|
||||
"types": "dist/shared.d.ts",
|
||||
"files": [
|
||||
"index.js",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/shared.d.ts",
|
||||
"node": {
|
||||
"production": "./dist/shared.cjs.prod.js",
|
||||
"development": "./dist/shared.cjs.js",
|
||||
"default": "./dist/shared.cjs.prod.js"
|
||||
},
|
||||
"module": "./dist/shared.esm-bundler.js",
|
||||
"import": "./dist/shared.esm-bundler.js",
|
||||
"require": "./index.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"buildOptions": {
|
||||
"formats": [
|
||||
"esm-bundler",
|
||||
"cjs"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vuejs/core.git",
|
||||
"directory": "packages/shared"
|
||||
},
|
||||
"keywords": [
|
||||
"vue"
|
||||
],
|
||||
"author": "Evan You",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vuejs/core/issues"
|
||||
},
|
||||
"homepage": "https://github.com/vuejs/core/tree/main/packages/shared#readme"
|
||||
}
|
||||
+8756
File diff suppressed because it is too large
Load Diff
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@vueuse/core",
|
||||
"type": "module",
|
||||
"version": "15.0.0",
|
||||
"description": "Collection of essential Vue Composition Utilities",
|
||||
"author": "Anthony Fu <https://github.com/antfu>",
|
||||
"license": "MIT",
|
||||
"funding": "https://github.com/sponsors/antfu",
|
||||
"homepage": "https://github.com/vueuse/vueuse#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vueuse/vueuse.git",
|
||||
"directory": "packages/core"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vueuse/vueuse/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"vue-use",
|
||||
"utils"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./*": "./dist/*",
|
||||
"./metadata": "./dist/metadata.mjs",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"unpkg": "./dist/index.iife.min.js",
|
||||
"jsdelivr": "./dist/index.iife.min.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "15.0.0",
|
||||
"@vueuse/shared": "15.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@js-temporal/polyfill": "^0.5.1",
|
||||
"temporal-polyfill": "^1.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown"
|
||||
}
|
||||
}
|
||||
+2021
File diff suppressed because it is too large
Load Diff
+45
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@vueuse/shared",
|
||||
"type": "module",
|
||||
"version": "15.0.0",
|
||||
"author": "Anthony Fu <https://github.com/antfu>",
|
||||
"license": "MIT",
|
||||
"funding": "https://github.com/sponsors/antfu",
|
||||
"homepage": "https://github.com/vueuse/vueuse/tree/main/packages/shared#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vueuse/vueuse.git",
|
||||
"directory": "packages/shared"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vueuse/vueuse/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"vue-use",
|
||||
"utils"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./*": "./dist/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"unpkg": "./dist/index.iife.min.js",
|
||||
"jsdelivr": "./dist/index.iife.min.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown"
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "devalue",
|
||||
"description": "Gets the job done when JSON.stringify can't",
|
||||
"version": "6.0.3",
|
||||
"repository": "sveltejs/devalue",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/types.d.ts",
|
||||
"import": "./src/index.js",
|
||||
"default": "./src/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"types"
|
||||
],
|
||||
"types": "./types/types.d.ts",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.29.6",
|
||||
"@js-temporal/polyfill": "^0.5.1",
|
||||
"@types/node": "^24.12.0",
|
||||
"prettier": "^3.2.4",
|
||||
"publint": "^0.3.12",
|
||||
"typescript": "^7.0.2",
|
||||
"vite": "8.3.0",
|
||||
"vitest": "5.0.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"changeset:version": "changeset version",
|
||||
"changeset:publish": "changeset publish",
|
||||
"build": "tsc",
|
||||
"test": "vitest run --project unit",
|
||||
"test:unit": "vitest run --project unit",
|
||||
"test:watch": "vitest --project unit",
|
||||
"test:types": "tsc -p . --noEmit",
|
||||
"check:format": "prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
"bench": "node --allow-natives-syntax ./benchmarking/run.js",
|
||||
"bench:compare": "node --allow-natives-syntax ./benchmarking/compare/index.js"
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/* Baseline 2025 runtimes */
|
||||
|
||||
/** @type {(array_buffer: ArrayBuffer) => string} */
|
||||
export function encode_native(array_buffer) {
|
||||
return new Uint8Array(array_buffer).toBase64();
|
||||
}
|
||||
|
||||
/** @type {(base64: string) => ArrayBuffer} */
|
||||
export function decode_native(base64) {
|
||||
return Uint8Array.fromBase64(base64).buffer;
|
||||
}
|
||||
|
||||
/* Node-compatible runtimes */
|
||||
|
||||
/** @type {(array_buffer: ArrayBuffer) => string} */
|
||||
export function encode_buffer(array_buffer) {
|
||||
return Buffer.from(array_buffer).toString('base64');
|
||||
}
|
||||
|
||||
/** @type {(base64: string) => ArrayBuffer} */
|
||||
export function decode_buffer(base64) {
|
||||
return Uint8Array.from(Buffer.from(base64, 'base64')).buffer;
|
||||
}
|
||||
|
||||
/* Legacy runtimes */
|
||||
|
||||
/** @type {(array_buffer: ArrayBuffer) => string} */
|
||||
export function encode_legacy(array_buffer) {
|
||||
const array = new Uint8Array(array_buffer);
|
||||
let binary = '';
|
||||
|
||||
// the maximum number of arguments to String.fromCharCode.apply
|
||||
// should be around 0xFFFF in modern engines
|
||||
const chunk_size = 0x8000;
|
||||
for (let i = 0; i < array.length; i += chunk_size) {
|
||||
const chunk = array.subarray(i, i + chunk_size);
|
||||
// `apply` accepts any array-like at runtime; lib types only admit `number[]`.
|
||||
binary += String.fromCharCode.apply(
|
||||
null,
|
||||
/** @type {number[]} */ (/** @type {unknown} */ (chunk))
|
||||
);
|
||||
}
|
||||
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
/** @type {(base64: string) => ArrayBuffer} */
|
||||
export function decode_legacy(base64) {
|
||||
const binary_string = atob(base64);
|
||||
const len = binary_string.length;
|
||||
const array = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
array[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
|
||||
return array.buffer;
|
||||
}
|
||||
|
||||
const native = typeof Uint8Array.fromBase64 === 'function';
|
||||
const buffer = typeof process === 'object' && process.versions?.node !== undefined;
|
||||
|
||||
export const encode64 = native ? encode_native : buffer ? encode_buffer : encode_legacy;
|
||||
export const decode64 = native ? decode_native : buffer ? decode_buffer : decode_legacy;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
export const UNDEFINED = -1;
|
||||
export const HOLE = -2;
|
||||
export const NAN = -3;
|
||||
export const POSITIVE_INFINITY = -4;
|
||||
export const NEGATIVE_INFINITY = -5;
|
||||
export const NEGATIVE_ZERO = -6;
|
||||
export const SPARSE = -7;
|
||||
|
||||
// The largest valid value for a JavaScript array's `length` property,
|
||||
// and the largest valid array index (one less than the max length).
|
||||
export const MAX_ARRAY_LEN = 2 ** 32 - 1;
|
||||
export const MAX_ARRAY_INDEX = MAX_ARRAY_LEN - 1;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
export { uneval } from './uneval.js';
|
||||
export { parse, unflatten } from './parse.js';
|
||||
export { stringify, stringifyAsync } from './stringify.js';
|
||||
export {
|
||||
default_stringify_operations as defaultStringifyOperations,
|
||||
default_parse_operations as defaultParseOperations
|
||||
} from './operations.js';
|
||||
export { DevalueError, filter_array_indices as filterArrayIndices } from './utils.js';
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
const SOURCE = Symbol('JavaScriptSource');
|
||||
|
||||
export class JavaScriptSource {
|
||||
/**
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {unknown[]} values
|
||||
*/
|
||||
constructor(strings, values) {
|
||||
this.strings = strings;
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: unknown) => string} fn
|
||||
*/
|
||||
render(fn) {
|
||||
const { strings, values } = this;
|
||||
let result = strings[0];
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const value = values[i];
|
||||
result += JavaScriptSource.is_fragment(value)
|
||||
? JavaScriptSource.from(value).render(fn)
|
||||
: fn(value);
|
||||
result += strings[i + 1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: unknown) => void} fn
|
||||
* @param {Set<string>} reserved
|
||||
* @param {Set<readonly string[]>} templates
|
||||
*/
|
||||
visit(fn, reserved, templates) {
|
||||
const { strings, values } = this;
|
||||
// Each template site reuses its string array, but its interpolations can change.
|
||||
if (!templates.has(strings)) {
|
||||
templates.add(strings);
|
||||
for (const string of strings) {
|
||||
// Generated names are ASCII, but literal identifiers can use Unicode escapes.
|
||||
const decoded = string.replace(
|
||||
/\\u(?:([\da-f]{4})|\{([\da-f]+)\})/gi,
|
||||
(escape, hex, code_point) => {
|
||||
const code = parseInt(hex || code_point, 16);
|
||||
return code < 128 ? String.fromCharCode(code) : escape;
|
||||
}
|
||||
);
|
||||
// Conservatively reserve words even in property names, strings and comments.
|
||||
for (const name of decoded.match(/[a-zA-Z_$][\w$]*/g) || []) reserved.add(name);
|
||||
}
|
||||
}
|
||||
for (const value of values) {
|
||||
if (JavaScriptSource.is_fragment(value)) {
|
||||
JavaScriptSource.from(value).visit(fn, reserved, templates);
|
||||
} else {
|
||||
fn(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} value
|
||||
* @returns {value is JavaScriptFragment}
|
||||
*/
|
||||
static is_fragment(value) {
|
||||
return typeof value === 'object' && value !== null && SOURCE in value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JavaScriptFragment} fragment
|
||||
* @returns {JavaScriptSource}
|
||||
*/
|
||||
static from(fragment) {
|
||||
if (!JavaScriptSource.is_fragment(fragment)) {
|
||||
throw new TypeError('Invalid JavaScript fragment');
|
||||
}
|
||||
return fragment[SOURCE];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TemplateStringsArray} strings
|
||||
* @param {...unknown} values
|
||||
* @returns {JavaScriptFragment}
|
||||
*/
|
||||
export function js(strings, ...values) {
|
||||
if (!Array.isArray(strings) || !Array.isArray(strings.raw)) {
|
||||
throw new TypeError(
|
||||
'`js` must be used as a tagged template, but was called as a regular function'
|
||||
);
|
||||
}
|
||||
return { [SOURCE]: new JavaScriptSource(strings, values) };
|
||||
}
|
||||
|
||||
/**
|
||||
* The opaque result of the `js` tag passed to an `uneval` replacer.
|
||||
* @typedef {{ readonly [SOURCE]: JavaScriptSource }} JavaScriptFragment
|
||||
*/
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
import { MAX_ARRAY_INDEX } from './constants.js';
|
||||
import {
|
||||
enumerable_symbols,
|
||||
get_type,
|
||||
is_buffer,
|
||||
is_plain_object,
|
||||
valid_array_indices
|
||||
} from './utils.js';
|
||||
|
||||
/**
|
||||
* Merges caller-provided operation overrides over the defaults. Iterating the
|
||||
* default keys (rather than the override's own keys) means nullish members
|
||||
* fall back to the default, and inherited members — e.g. from a class
|
||||
* instance — are picked up.
|
||||
*
|
||||
* @template {Record<string, any>} T
|
||||
* @param {T} defaults
|
||||
* @param {Partial<T> | undefined} overrides
|
||||
* @returns {T}
|
||||
*/
|
||||
export function merge_operations(defaults, overrides) {
|
||||
if (!overrides) return defaults;
|
||||
|
||||
const merged = /** @type {T} */ ({});
|
||||
|
||||
for (const key of /** @type {(keyof T)[]} */ (Object.keys(defaults))) {
|
||||
merged[key] = overrides[key] ?? defaults[key];
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
/** @type {{ kind: 'not-plain' }} */
|
||||
const NOT_PLAIN = /* @__PURE__ */ Object.freeze({ kind: 'not-plain' });
|
||||
|
||||
/** @type {{ kind: 'symbol-keys' }} */
|
||||
const SYMBOL_KEYS = /* @__PURE__ */ Object.freeze({ kind: 'symbol-keys' });
|
||||
|
||||
/**
|
||||
* The default implementations of every introspection/extraction operation
|
||||
* `stringify` performs on the value being serialized. Each one uses native
|
||||
* JavaScript semantics (property access, iteration, prototype methods, etc).
|
||||
*
|
||||
* Pass overrides via the `operations` option of `stringify`/`stringifyAsync`
|
||||
* to customize how values are inspected — e.g. to serialize values without
|
||||
* triggering getters, proxy traps, or patched prototype methods, or to
|
||||
* serialize values that live in a different JavaScript runtime (a `node:vm`
|
||||
* context, a WASM-hosted engine, a remote process) through handle objects.
|
||||
*
|
||||
* The object is frozen — it is shared by every `stringify` call that does
|
||||
* not override a given operation.
|
||||
*
|
||||
*/
|
||||
/** @type {import('./types.js').DefaultStringifyOperations} */
|
||||
const stringify_operations = {
|
||||
identify: (value) => value,
|
||||
|
||||
typeOf: (value) => (value === null ? 'null' : typeof value),
|
||||
|
||||
toPrimitive: (value) => value,
|
||||
|
||||
tagOf: (value) => get_type(value),
|
||||
|
||||
isThenable: (value) => typeof value.then === 'function',
|
||||
|
||||
toPromise: (thenable) => Promise.resolve(thenable),
|
||||
|
||||
unbox: (boxed) => boxed.valueOf(),
|
||||
|
||||
toISOString: (date) => (isNaN(date.getDate()) ? '' : date.toISOString()),
|
||||
|
||||
toStringValue: (value) => value.toString(),
|
||||
|
||||
regExpInfo: (regexp) => ({ source: regexp.source, flags: regexp.flags }),
|
||||
|
||||
valuesOf: (set) => set,
|
||||
|
||||
entriesOf: (map) => map,
|
||||
|
||||
viewInfo: (view) => {
|
||||
// Node Buffers may share a pool containing unrelated, sensitive data.
|
||||
// Copy only the visible bytes, without using Buffer's pooling or slice.
|
||||
if (is_buffer(view)) view = new Uint8Array(view);
|
||||
|
||||
return {
|
||||
buffer: view.buffer,
|
||||
byteOffset: view.byteOffset,
|
||||
byteLength: view.byteLength,
|
||||
length: view.length,
|
||||
bufferByteLength: view.buffer.byteLength
|
||||
};
|
||||
},
|
||||
|
||||
toArrayBuffer: (buffer) => buffer,
|
||||
|
||||
lengthOf: (array) => array.length,
|
||||
|
||||
hasOwn: (value, key) => Object.hasOwn(value, key),
|
||||
|
||||
indicesOf: (array) => valid_array_indices(array),
|
||||
|
||||
shapeOf: (value) => {
|
||||
if (!is_plain_object(value)) return NOT_PLAIN;
|
||||
if (enumerable_symbols(value).length > 0) return SYMBOL_KEYS;
|
||||
|
||||
return {
|
||||
kind: Object.getPrototypeOf(value) === null ? 'null-proto' : 'plain',
|
||||
keys: Object.keys(value)
|
||||
};
|
||||
},
|
||||
|
||||
get: (value, key) => value[key]
|
||||
};
|
||||
|
||||
export const default_stringify_operations = /* @__PURE__ */ Object.freeze(stringify_operations);
|
||||
|
||||
const array_buffer_byte_length = /** @type {() => number} */ (
|
||||
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength')?.get
|
||||
);
|
||||
|
||||
const shared_array_buffer_byte_length =
|
||||
typeof SharedArrayBuffer === 'undefined'
|
||||
? undefined
|
||||
: Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength')?.get;
|
||||
|
||||
/**
|
||||
* The default implementations of every construction operation `parse` and
|
||||
* `unflatten` perform while reviving a value. Each one uses native
|
||||
* JavaScript semantics (built-in constructors, property assignment, etc).
|
||||
*
|
||||
* Pass overrides via the `operations` option of `parse`/`unflatten` to
|
||||
* customize how values are built — e.g. to construct them from the
|
||||
* intrinsics of a different realm (a `node:vm` context), or to build up
|
||||
* values inside another JavaScript runtime (a WASM-hosted engine, a remote
|
||||
* process) through handle objects.
|
||||
*
|
||||
* The object is frozen — it is shared by every `parse` call that does not
|
||||
* override a given operation.
|
||||
*
|
||||
*/
|
||||
/** @type {import('./types.js').DefaultParseOperations} */
|
||||
const parse_operations = {
|
||||
fromPrimitive: (primitive) => primitive,
|
||||
|
||||
fromISOString: (iso) => new Date(iso),
|
||||
|
||||
fromStringValue: (tag, text) => {
|
||||
if (tag === 'URL') return new URL(text);
|
||||
if (tag === 'URLSearchParams') return new URLSearchParams(text);
|
||||
// 'Temporal.Instant', 'Temporal.PlainDate', ...
|
||||
// @ts-expect-error TS doesn't know about Temporal yet
|
||||
return Temporal[tag.slice(9)].from(text);
|
||||
},
|
||||
|
||||
fromArrayBuffer: (buffer) => buffer,
|
||||
|
||||
fromRegExpInfo: (source, flags) => new RegExp(source, flags),
|
||||
|
||||
fromViewInfo: (tag, buffer, byteOffset, length) => {
|
||||
// A reviver can replace an ArrayBuffer with a length or array-like value,
|
||||
// which a typed array constructor would use to allocate a new buffer.
|
||||
// The native getters check internal slots, so they work across realms
|
||||
// and cannot be fooled by a forged prototype or Symbol.toStringTag.
|
||||
try {
|
||||
array_buffer_byte_length.call(buffer);
|
||||
} catch (error) {
|
||||
if (!shared_array_buffer_byte_length) throw error;
|
||||
shared_array_buffer_byte_length.call(buffer);
|
||||
}
|
||||
|
||||
const Constructor = /** @type {any} */ (globalThis)[tag];
|
||||
return byteOffset !== undefined
|
||||
? new Constructor(buffer, byteOffset, length)
|
||||
: new Constructor(buffer);
|
||||
},
|
||||
|
||||
box: (value) => Object(value),
|
||||
|
||||
createArray: (length) => new Array(length),
|
||||
|
||||
createSparseArray: (length) => {
|
||||
/** @type {any[]} */
|
||||
const array = [];
|
||||
|
||||
// Setting `array.length = length` (or equivalently calling
|
||||
// `new Array(length)`) on an untrusted length is a DoS vector: V8
|
||||
// eagerly allocates a contiguous backing store for array lengths below
|
||||
// ~10^8, so a small payload with a huge declared length can force
|
||||
// arbitrary memory allocation. Touching the largest-possible index
|
||||
// first forces V8 into dictionary-elements mode, where `length` is
|
||||
// just a number and no contiguous allocation occurs.
|
||||
array[MAX_ARRAY_INDEX] = undefined;
|
||||
delete array[MAX_ARRAY_INDEX];
|
||||
array.length = length;
|
||||
|
||||
return array;
|
||||
},
|
||||
|
||||
createObject: () => ({}),
|
||||
|
||||
createNullPrototypeObject: () => Object.create(null),
|
||||
|
||||
createSet: () => new Set(),
|
||||
|
||||
createMap: () => new Map(),
|
||||
|
||||
set: (target, key, value) => {
|
||||
target[key] = value;
|
||||
},
|
||||
|
||||
addValue: (set, value) => {
|
||||
set.add(value);
|
||||
},
|
||||
|
||||
addEntry: (map, key, value) => {
|
||||
map.set(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
export const default_parse_operations = /* @__PURE__ */ Object.freeze(parse_operations);
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
import { decode64 } from './base64.js';
|
||||
import {
|
||||
HOLE,
|
||||
NAN,
|
||||
NEGATIVE_INFINITY,
|
||||
NEGATIVE_ZERO,
|
||||
POSITIVE_INFINITY,
|
||||
SPARSE,
|
||||
UNDEFINED
|
||||
} from './constants.js';
|
||||
import { default_parse_operations, merge_operations } from './operations.js';
|
||||
import { is_valid_array_index, is_valid_array_len } from './utils.js';
|
||||
|
||||
function invalid() {
|
||||
throw new Error('Invalid input');
|
||||
}
|
||||
|
||||
/**
|
||||
* Revive a value serialized with `devalue.stringify`
|
||||
* @param {string} serialized
|
||||
* @param {Record<string, (value: any) => any>} [revivers]
|
||||
* @param {import('./types.js').ParseOptions} [options]
|
||||
*/
|
||||
export function parse(serialized, revivers, options) {
|
||||
return unflatten(JSON.parse(serialized), revivers, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revive a value flattened with `devalue.stringify`
|
||||
* @param {number | any[]} parsed
|
||||
* @param {Record<string, (value: any) => any>} [revivers]
|
||||
* @param {import('./types.js').ParseOptions} [options]
|
||||
*/
|
||||
export function unflatten(parsed, revivers, options) {
|
||||
/** @type {import('./types.js').ParseOperations} */
|
||||
const ops = merge_operations(default_parse_operations, options?.operations);
|
||||
|
||||
if (typeof parsed === 'number') return hydrate(parsed, true);
|
||||
|
||||
if (!Array.isArray(parsed) || parsed.length === 0) {
|
||||
invalid();
|
||||
}
|
||||
|
||||
const values = /** @type {any[]} */ (parsed);
|
||||
|
||||
const hydrated = Array(values.length);
|
||||
|
||||
/**
|
||||
* A set of values currently being hydrated with custom revivers,
|
||||
* used to detect invalid cyclical dependencies
|
||||
* @type {Set<number> | null}
|
||||
*/
|
||||
let hydrating = null;
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
* @returns {any}
|
||||
*/
|
||||
function get_raw(index) {
|
||||
if (!is_valid_array_index(index) || index >= values.length) {
|
||||
invalid();
|
||||
}
|
||||
|
||||
return values[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
* @returns {any}
|
||||
*/
|
||||
function hydrate(index, standalone = false) {
|
||||
if (index === UNDEFINED) return ops.fromPrimitive(undefined);
|
||||
if (index === NAN) return ops.fromPrimitive(NaN);
|
||||
if (index === POSITIVE_INFINITY) return ops.fromPrimitive(Infinity);
|
||||
if (index === NEGATIVE_INFINITY) return ops.fromPrimitive(-Infinity);
|
||||
if (index === NEGATIVE_ZERO) return ops.fromPrimitive(-0);
|
||||
|
||||
if (standalone || typeof index !== 'number') {
|
||||
invalid();
|
||||
}
|
||||
|
||||
if (index in hydrated) return hydrated[index];
|
||||
|
||||
if (index >= values.length) {
|
||||
invalid();
|
||||
}
|
||||
|
||||
const value = values[index];
|
||||
|
||||
if (!value || typeof value !== 'object') {
|
||||
hydrated[index] = ops.fromPrimitive(value);
|
||||
} else if (Array.isArray(value)) {
|
||||
if (typeof value[0] === 'string') {
|
||||
const type = value[0];
|
||||
|
||||
const reviver = revivers && Object.hasOwn(revivers, type) ? revivers[type] : undefined;
|
||||
|
||||
if (reviver) {
|
||||
let i = value[1];
|
||||
if (typeof i !== 'number') {
|
||||
// if it's not a number, it was serialized by a builtin reviver
|
||||
// so we need to munge it into the format expected by a custom reviver
|
||||
i = values.push(value[1]) - 1;
|
||||
}
|
||||
|
||||
// If the payload is already hydrated, its recursion has already
|
||||
// terminated (e.g. a self-referential object cached itself before
|
||||
// following its own back-reference), so revive it directly. Falling
|
||||
// through to the `hydrating` guard here would wrongly reject a valid
|
||||
// cycle. An actually infinite payload (e.g. `[["Custom", 0]]`) is never
|
||||
// cached, so it still hits the guard below.
|
||||
if (Object.hasOwn(hydrated, i)) {
|
||||
return (hydrated[index] = reviver(hydrated[i]));
|
||||
}
|
||||
|
||||
hydrating ??= new Set();
|
||||
|
||||
if (hydrating.has(i)) {
|
||||
throw new Error('Invalid circular reference');
|
||||
}
|
||||
|
||||
hydrating.add(i);
|
||||
hydrated[index] = reviver(hydrate(i));
|
||||
hydrating.delete(i);
|
||||
|
||||
return hydrated[index];
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'Date':
|
||||
hydrated[index] = ops.fromISOString(value[1]);
|
||||
break;
|
||||
|
||||
case 'Set':
|
||||
const set = ops.createSet();
|
||||
hydrated[index] = set;
|
||||
for (let i = 1; i < value.length; i += 1) {
|
||||
ops.addValue(set, hydrate(value[i]));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
const map = ops.createMap();
|
||||
hydrated[index] = map;
|
||||
for (let i = 1; i < value.length; i += 2) {
|
||||
ops.addEntry(map, hydrate(value[i]), hydrate(value[i + 1]));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RegExp':
|
||||
hydrated[index] = ops.fromRegExpInfo(value[1], value[2]);
|
||||
break;
|
||||
|
||||
case 'Object': {
|
||||
const wrapped_index = value[1];
|
||||
|
||||
// Only a primitive can be boxed. The negative sentinels below are the
|
||||
// ones `stringify` emits for boxed numbers; every other sentinel
|
||||
// (`UNDEFINED`, `HOLE`, `SPARSE`) would box `undefined` into a plain
|
||||
// `{}`, which is not a value `stringify` can produce.
|
||||
const is_boxable_sentinel =
|
||||
wrapped_index === NAN ||
|
||||
wrapped_index === POSITIVE_INFINITY ||
|
||||
wrapped_index === NEGATIVE_INFINITY ||
|
||||
wrapped_index === NEGATIVE_ZERO;
|
||||
|
||||
if (!is_boxable_sentinel) {
|
||||
const wrapped = get_raw(wrapped_index);
|
||||
|
||||
const is_bigint = Array.isArray(wrapped) && wrapped[0] === 'BigInt';
|
||||
|
||||
if ((wrapped === undefined || typeof wrapped === 'object') && !is_bigint) {
|
||||
// avoid infinite recusion in case of malformed input
|
||||
invalid();
|
||||
}
|
||||
}
|
||||
|
||||
hydrated[index] = ops.box(hydrate(wrapped_index));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'BigInt':
|
||||
hydrated[index] = ops.fromPrimitive(BigInt(value[1]));
|
||||
break;
|
||||
|
||||
case 'null':
|
||||
const obj = ops.createNullPrototypeObject();
|
||||
hydrated[index] = obj;
|
||||
for (let i = 1; i < value.length; i += 2) {
|
||||
const key = value[i];
|
||||
if (typeof key !== 'string') {
|
||||
throw new Error('Cannot parse an object with a non-string key');
|
||||
}
|
||||
|
||||
if (key === '__proto__') {
|
||||
throw new Error('Cannot parse an object with a `__proto__` property');
|
||||
}
|
||||
|
||||
ops.set(obj, key, hydrate(value[i + 1]));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Int8Array':
|
||||
case 'Uint8Array':
|
||||
case 'Uint8ClampedArray':
|
||||
case 'Int16Array':
|
||||
case 'Uint16Array':
|
||||
case 'Float16Array':
|
||||
case 'Int32Array':
|
||||
case 'Uint32Array':
|
||||
case 'Float32Array':
|
||||
case 'Float64Array':
|
||||
case 'BigInt64Array':
|
||||
case 'BigUint64Array':
|
||||
case 'DataView': {
|
||||
const buffer_index = value[1];
|
||||
|
||||
const raw = get_raw(buffer_index);
|
||||
|
||||
if (!Array.isArray(raw) || raw[0] !== 'ArrayBuffer') {
|
||||
// without this, if we receive malformed input we could
|
||||
// end up trying to hydrate in a circle or allocate
|
||||
// huge amounts of memory when we call `new TypedArrayConstructor(buffer)`
|
||||
invalid();
|
||||
}
|
||||
|
||||
const buffer = hydrate(buffer_index);
|
||||
|
||||
hydrated[index] = ops.fromViewInfo(type, buffer, value[2], value[3]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ArrayBuffer': {
|
||||
const base64 = value[1];
|
||||
if (typeof base64 !== 'string') {
|
||||
throw new Error('Invalid ArrayBuffer encoding');
|
||||
}
|
||||
hydrated[index] = ops.fromArrayBuffer(decode64(base64));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'URL':
|
||||
case 'URLSearchParams':
|
||||
case 'Temporal.Duration':
|
||||
case 'Temporal.Instant':
|
||||
case 'Temporal.PlainDate':
|
||||
case 'Temporal.PlainTime':
|
||||
case 'Temporal.PlainDateTime':
|
||||
case 'Temporal.PlainMonthDay':
|
||||
case 'Temporal.PlainYearMonth':
|
||||
case 'Temporal.ZonedDateTime': {
|
||||
// the same tags `toStringValue` serializes on the stringify side
|
||||
hydrated[index] = ops.fromStringValue(type, value[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown type ${type}`);
|
||||
}
|
||||
} else if (value[0] === SPARSE) {
|
||||
// Sparse array encoding: [SPARSE, length, idx, val, idx, val, ...]
|
||||
const len = value[1];
|
||||
|
||||
if (!is_valid_array_len(len)) {
|
||||
invalid();
|
||||
}
|
||||
|
||||
// `len` comes from the input rather than being bounded by it, so
|
||||
// `createSparseArray` is responsible for not allocating storage
|
||||
// proportional to it.
|
||||
const array = ops.createSparseArray(len);
|
||||
hydrated[index] = array;
|
||||
|
||||
for (let i = 2; i < value.length; i += 2) {
|
||||
const idx = value[i];
|
||||
|
||||
if (!is_valid_array_index(idx) || idx >= len) {
|
||||
invalid();
|
||||
}
|
||||
|
||||
ops.set(array, idx, hydrate(value[i + 1]));
|
||||
}
|
||||
} else {
|
||||
const array = ops.createArray(value.length);
|
||||
hydrated[index] = array;
|
||||
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
const n = value[i];
|
||||
if (n === HOLE) continue;
|
||||
|
||||
ops.set(array, i, hydrate(n));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const object = ops.createObject();
|
||||
hydrated[index] = object;
|
||||
|
||||
for (const key of Object.keys(value)) {
|
||||
if (key === '__proto__') {
|
||||
throw new Error('Cannot parse an object with a `__proto__` property');
|
||||
}
|
||||
|
||||
ops.set(object, key, hydrate(value[key]));
|
||||
}
|
||||
}
|
||||
|
||||
return hydrated[index];
|
||||
}
|
||||
|
||||
return hydrate(0);
|
||||
}
|
||||
+438
@@ -0,0 +1,438 @@
|
||||
import { DevalueError, MAP_KEY, format_path, quote_key, stringify_string } from './utils.js';
|
||||
import {
|
||||
HOLE,
|
||||
NAN,
|
||||
NEGATIVE_INFINITY,
|
||||
NEGATIVE_ZERO,
|
||||
POSITIVE_INFINITY,
|
||||
SPARSE,
|
||||
UNDEFINED
|
||||
} from './constants.js';
|
||||
import { encode64 } from './base64.js';
|
||||
import { default_stringify_operations, merge_operations } from './operations.js';
|
||||
|
||||
/**
|
||||
* Turn a value into a JSON string that can be parsed with `devalue.parse`
|
||||
* @param {any} value
|
||||
* @param {Record<string, (value: any) => any>} [reducers]
|
||||
* @param {import('./types.js').StringifyOptions} [options]
|
||||
*/
|
||||
export function stringify(value, reducers, options) {
|
||||
const stringified = run(false, value, reducers, options);
|
||||
return typeof stringified === 'string' ? stringified : `[${stringified.join(',')}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a value into a JSON string that can be parsed with `devalue.parse`
|
||||
* @param {any} value
|
||||
* @param {Record<string, (value: any) => any>} [reducers]
|
||||
* @param {import('./types.js').StringifyOptions} [options]
|
||||
*/
|
||||
export async function stringifyAsync(value, reducers, options) {
|
||||
const stringified = run(true, value, reducers, options);
|
||||
|
||||
if (typeof stringified === 'string') {
|
||||
return stringified;
|
||||
}
|
||||
|
||||
let out = '[';
|
||||
|
||||
for (let i = 0; i < stringified.length; i += 1) {
|
||||
let value = stringified[i];
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
await value;
|
||||
value = stringified[i];
|
||||
|
||||
if (i === 0 && value < 0) {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
out += value;
|
||||
|
||||
if (i < stringified.length - 1) {
|
||||
out += ',';
|
||||
}
|
||||
}
|
||||
|
||||
out += ']';
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} async
|
||||
* @param {any} value
|
||||
* @param {Record<string, (value: any) => any>} [reducers]
|
||||
* @param {import('./types.js').StringifyOptions} [options]
|
||||
*/
|
||||
function run(async, value, reducers, options) {
|
||||
const ops = merge_operations(default_stringify_operations, options?.operations);
|
||||
|
||||
/** @type {any[]} */
|
||||
const stringified = [];
|
||||
|
||||
/** @type {Map<any, number>} */
|
||||
const indexes = new Map();
|
||||
|
||||
/** @type {Array<{ key: string, fn: (value: any) => any }>} */
|
||||
const custom = [];
|
||||
if (reducers) {
|
||||
for (const key of Object.getOwnPropertyNames(reducers)) {
|
||||
custom.push({ key, fn: reducers[key] });
|
||||
}
|
||||
}
|
||||
|
||||
// the path to the value being serialized, recorded as-is and only
|
||||
// formatted by `error` — see `format_path`
|
||||
/** @type {any[]} */
|
||||
let keys = [];
|
||||
|
||||
let p = 0;
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {any} thing
|
||||
* @returns {never}
|
||||
*/
|
||||
function error(message, thing) {
|
||||
const path = format_path(keys, (key) => {
|
||||
const key_type = ops.typeOf(key);
|
||||
const key_is_primitive =
|
||||
key_type !== 'object' && key_type !== 'function' && key_type !== 'symbol';
|
||||
return key_is_primitive ? stringify_primitive(ops.toPrimitive(key)) : '...';
|
||||
});
|
||||
|
||||
throw new DevalueError(message, path, thing, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} thing
|
||||
* @param {number} [index]
|
||||
*/
|
||||
function flatten(thing, index) {
|
||||
const type = ops.typeOf(thing);
|
||||
|
||||
if (type === 'undefined') return UNDEFINED;
|
||||
|
||||
/** @type {number | undefined} */
|
||||
let number;
|
||||
|
||||
// `ops.toPrimitive` is the boundary between the value being serialized and
|
||||
// plain host JavaScript: everything below operates on the extracted host
|
||||
// primitive, so native comparisons and arithmetic are correct there.
|
||||
if (type === 'number') {
|
||||
number = /** @type {number} */ (ops.toPrimitive(thing));
|
||||
if (Number.isNaN(number)) return NAN;
|
||||
if (number === Infinity) return POSITIVE_INFINITY;
|
||||
if (number === -Infinity) return NEGATIVE_INFINITY;
|
||||
if (number === 0 && 1 / number < 0) return NEGATIVE_ZERO;
|
||||
}
|
||||
|
||||
const id = ops.identify(thing);
|
||||
|
||||
if (indexes.has(id)) return /** @type {number} */ (indexes.get(id));
|
||||
|
||||
index ??= p++;
|
||||
indexes.set(id, index);
|
||||
|
||||
for (const { key, fn } of custom) {
|
||||
const value = fn(thing);
|
||||
if (value) {
|
||||
stringified[index] = `["${key}",${flatten(value)}]`;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'function') {
|
||||
error(`Cannot stringify a function`, thing);
|
||||
} else if (type === 'symbol') {
|
||||
error(`Cannot stringify a Symbol primitive`, thing);
|
||||
}
|
||||
|
||||
/** @type {string | Promise<any>} */
|
||||
let str = '';
|
||||
|
||||
if (type !== 'object') {
|
||||
str = stringify_primitive(type === 'number' ? number : ops.toPrimitive(thing));
|
||||
} else if (ops.isThenable(thing)) {
|
||||
if (!async) {
|
||||
error(`Cannot stringify a Promise or thenable — use stringifyAsync instead`, thing);
|
||||
}
|
||||
|
||||
// the callback runs after the synchronous walk has unwound `keys`,
|
||||
// so the path to the thenable has to be captured and reinstated
|
||||
const prev_keys = keys.slice();
|
||||
|
||||
str = ops.toPromise(thing).then((value) => {
|
||||
keys = prev_keys;
|
||||
|
||||
const i = flatten(value, index);
|
||||
if (i < 0) stringified[index] = i;
|
||||
});
|
||||
|
||||
// Promises are awaited sequentially, and traversal may throw before
|
||||
// reaching them. Handle rejections immediately, but keep the original
|
||||
// promise so awaiting it still propagates the error to the caller.
|
||||
str.catch(() => {});
|
||||
} else {
|
||||
const tag = ops.tagOf(thing);
|
||||
|
||||
switch (tag) {
|
||||
case 'Number':
|
||||
case 'String':
|
||||
case 'Boolean':
|
||||
case 'BigInt':
|
||||
str = `["Object",${flatten(ops.unbox(thing))}]`;
|
||||
break;
|
||||
|
||||
case 'Date':
|
||||
str = `["Date","${ops.toISOString(thing)}"]`;
|
||||
break;
|
||||
|
||||
case 'URL':
|
||||
str = `["URL",${stringify_string(ops.toStringValue(thing))}]`;
|
||||
break;
|
||||
|
||||
case 'URLSearchParams':
|
||||
str = `["URLSearchParams",${stringify_string(ops.toStringValue(thing))}]`;
|
||||
break;
|
||||
|
||||
case 'RegExp':
|
||||
const { source, flags } = ops.regExpInfo(thing);
|
||||
str = flags
|
||||
? `["RegExp",${stringify_string(source)},"${flags}"]`
|
||||
: `["RegExp",${stringify_string(source)}]`;
|
||||
break;
|
||||
|
||||
case 'Array': {
|
||||
// For dense arrays (no holes), we iterate normally.
|
||||
// When we encounter the first hole, we call Object.keys
|
||||
// to determine the sparseness, then decide between:
|
||||
// - HOLE encoding: [-2, val, -2, ...] (default)
|
||||
// - Sparse encoding: [-7, length, idx, val, ...] (for very sparse arrays)
|
||||
// Only the sparse path avoids iterating every slot, which
|
||||
// is what protects against the DoS of e.g. `arr[1000000] = 1`.
|
||||
let mostly_dense = false;
|
||||
|
||||
const length = ops.lengthOf(thing);
|
||||
|
||||
str = '[';
|
||||
|
||||
for (let i = 0; i < length; i += 1) {
|
||||
if (i > 0) str += ',';
|
||||
|
||||
if (ops.hasOwn(thing, i)) {
|
||||
keys.push(i);
|
||||
str += flatten(ops.get(thing, i));
|
||||
keys.pop();
|
||||
} else if (mostly_dense) {
|
||||
// Use dense encoding. The heuristic guarantees the
|
||||
// array is only mildly sparse, so iterating over every
|
||||
// slot is fine.
|
||||
str += HOLE;
|
||||
} else {
|
||||
// Decide between HOLE encoding and sparse encoding.
|
||||
//
|
||||
// HOLE encoding: each hole is serialized as the HOLE
|
||||
// sentinel (-2). For example, [, "a", ,] becomes
|
||||
// [-2, 0, -2]. Each hole costs 3 chars ("-2" + comma).
|
||||
//
|
||||
// Sparse encoding: lists only populated indices.
|
||||
// For example, [, "a", ,] becomes [-7, 3, 1, 0] — the
|
||||
// -7 sentinel, the array length (3), then index-value
|
||||
// pairs. This avoids paying per-hole, but each element
|
||||
// costs extra chars to write its index.
|
||||
//
|
||||
// The values are the same size either way, so the
|
||||
// choice comes down to structural overhead:
|
||||
//
|
||||
// HOLE overhead:
|
||||
// 3 chars per hole ("-2" + comma)
|
||||
// = (L - P) * 3
|
||||
//
|
||||
// Sparse overhead:
|
||||
// "-7," — 3 chars (sparse sentinel + comma)
|
||||
// + length + "," — (d + 1) chars (array length + comma)
|
||||
// + per element: index + "," — (d + 1) chars
|
||||
// = (4 + d) + P * (d + 1)
|
||||
//
|
||||
// where L is the array length, P is the number of
|
||||
// populated elements, and d is the number of digits
|
||||
// in L (an upper bound on the digits in any index).
|
||||
//
|
||||
// Sparse encoding is cheaper when:
|
||||
// (4 + d) + P * (d + 1) < (L - P) * 3
|
||||
const populated_keys = ops.indicesOf(thing);
|
||||
const population = populated_keys.length;
|
||||
const d = String(length).length;
|
||||
|
||||
const hole_cost = (length - population) * 3;
|
||||
const sparse_cost = 4 + d + population * (d + 1);
|
||||
|
||||
if (hole_cost > sparse_cost) {
|
||||
str = '[' + SPARSE + ',' + length;
|
||||
for (let j = 0; j < populated_keys.length; j++) {
|
||||
const key = populated_keys[j];
|
||||
keys.push(+key);
|
||||
str += ',' + key + ',' + flatten(ops.get(thing, key));
|
||||
keys.pop();
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
mostly_dense = true;
|
||||
str += HOLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str += ']';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Set':
|
||||
str = '["Set"';
|
||||
|
||||
for (const value of ops.valuesOf(thing)) {
|
||||
str += `,${flatten(value)}`;
|
||||
}
|
||||
|
||||
str += ']';
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
str = '["Map"';
|
||||
|
||||
for (const [key, value] of ops.entriesOf(thing)) {
|
||||
keys.push(MAP_KEY, key);
|
||||
str += `,${flatten(key)},${flatten(value)}`;
|
||||
keys.pop();
|
||||
keys.pop();
|
||||
}
|
||||
|
||||
str += ']';
|
||||
break;
|
||||
|
||||
case 'Int8Array':
|
||||
case 'Uint8Array':
|
||||
case 'Uint8ClampedArray':
|
||||
case 'Int16Array':
|
||||
case 'Uint16Array':
|
||||
case 'Float16Array':
|
||||
case 'Int32Array':
|
||||
case 'Uint32Array':
|
||||
case 'Float32Array':
|
||||
case 'Float64Array':
|
||||
case 'BigInt64Array':
|
||||
case 'BigUint64Array': {
|
||||
const info = ops.viewInfo(thing);
|
||||
str = '["' + tag + '",' + flatten(info.buffer);
|
||||
|
||||
// handle subarrays
|
||||
if (info.byteLength !== info.bufferByteLength) {
|
||||
str += `,${info.byteOffset},${info.length}`;
|
||||
}
|
||||
|
||||
str += ']';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'DataView': {
|
||||
const info = ops.viewInfo(thing);
|
||||
str = '["' + tag + '",' + flatten(info.buffer);
|
||||
|
||||
if (info.byteLength !== info.bufferByteLength) {
|
||||
str += `,${info.byteOffset},${info.byteLength}`;
|
||||
}
|
||||
|
||||
str += ']';
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ArrayBuffer': {
|
||||
const base64 = encode64(ops.toArrayBuffer(thing));
|
||||
|
||||
str = `["ArrayBuffer","${base64}"]`;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Temporal.Duration':
|
||||
case 'Temporal.Instant':
|
||||
case 'Temporal.PlainDate':
|
||||
case 'Temporal.PlainTime':
|
||||
case 'Temporal.PlainDateTime':
|
||||
case 'Temporal.PlainMonthDay':
|
||||
case 'Temporal.PlainYearMonth':
|
||||
case 'Temporal.ZonedDateTime':
|
||||
str = `["${tag}",${stringify_string(ops.toStringValue(thing))}]`;
|
||||
break;
|
||||
|
||||
default: {
|
||||
const shape = ops.shapeOf(thing);
|
||||
|
||||
if (shape.kind === 'not-plain') {
|
||||
error(`Cannot stringify arbitrary non-POJOs`, thing);
|
||||
}
|
||||
|
||||
if (shape.kind === 'symbol-keys') {
|
||||
error(`Cannot stringify POJOs with symbolic keys`, thing);
|
||||
}
|
||||
|
||||
if (shape.kind === 'null-proto') {
|
||||
str = '["null"';
|
||||
for (const key of shape.keys) {
|
||||
if (key === '__proto__') {
|
||||
error(`Cannot stringify objects with __proto__ keys`, thing);
|
||||
}
|
||||
|
||||
keys.push(key);
|
||||
str += `,${quote_key(key)},${flatten(ops.get(thing, key))}`;
|
||||
keys.pop();
|
||||
}
|
||||
str += ']';
|
||||
} else {
|
||||
str = '{';
|
||||
let started = false;
|
||||
for (const key of shape.keys) {
|
||||
if (key === '__proto__') {
|
||||
error(`Cannot stringify objects with __proto__ keys`, thing);
|
||||
}
|
||||
|
||||
if (started) str += ',';
|
||||
started = true;
|
||||
keys.push(key);
|
||||
str += `${quote_key(key)}:${flatten(ops.get(thing, key))}`;
|
||||
keys.pop();
|
||||
}
|
||||
str += '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stringified[index] = str;
|
||||
return index;
|
||||
}
|
||||
|
||||
const index = flatten(value);
|
||||
|
||||
// special case — value is represented as a negative index
|
||||
if (index < 0) return `${index}`;
|
||||
|
||||
return stringified;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} thing
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringify_primitive(thing) {
|
||||
const type = typeof thing;
|
||||
if (type === 'string') return stringify_string(thing);
|
||||
if (thing === void 0) return UNDEFINED.toString();
|
||||
if (thing === 0 && 1 / thing < 0) return NEGATIVE_ZERO.toString();
|
||||
if (type === 'bigint') return `["BigInt","${thing}"]`;
|
||||
return String(thing);
|
||||
}
|
||||
+622
@@ -0,0 +1,622 @@
|
||||
/**
|
||||
* @import { UnevalReplacer } from './types.js';
|
||||
*/
|
||||
import { js, JavaScriptSource } from './javascript-source.js';
|
||||
import {
|
||||
DevalueError,
|
||||
MAP_KEY,
|
||||
enumerable_symbols,
|
||||
escaped,
|
||||
format_path,
|
||||
get_name,
|
||||
get_type,
|
||||
is_buffer,
|
||||
is_plain_object,
|
||||
is_primitive,
|
||||
stringify_sparse_array,
|
||||
stringify_string,
|
||||
valid_array_indices
|
||||
} from './utils.js';
|
||||
|
||||
const unsafe_chars = /[<\b\f\n\r\t\0\u2028\u2029]/g;
|
||||
// Short strings have bounded escaping/output costs per reference.
|
||||
const MIN_STRING_LENGTH = 128;
|
||||
|
||||
/**
|
||||
* Turn a value into the JavaScript that creates an equivalent value
|
||||
* @param {any} value
|
||||
* @param {UnevalReplacer} [replacer]
|
||||
*/
|
||||
export function uneval(value, replacer) {
|
||||
/** @type {Map<any, string>} */
|
||||
const names = new Map();
|
||||
const reserved = new Set();
|
||||
const templates = new Set();
|
||||
const seen = new Set();
|
||||
|
||||
// the path to the value being walked, recorded as-is and only formatted
|
||||
// by `error` — see `format_path`
|
||||
/** @type {any[]} */
|
||||
const keys = [];
|
||||
|
||||
/** @type {Map<any, JavaScriptSource>} */
|
||||
const custom = new Map();
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {any} thing
|
||||
* @returns {never}
|
||||
*/
|
||||
function error(message, thing) {
|
||||
const path = format_path(keys, (key) => (is_primitive(key) ? stringify_primitive(key) : '...'));
|
||||
throw new DevalueError(message, path, thing, value);
|
||||
}
|
||||
|
||||
/** @type {Map<string | bigint, number> | undefined} */
|
||||
let primitive_counts;
|
||||
/** @type {Map<string | bigint, string> | undefined} */
|
||||
let primitives;
|
||||
|
||||
/** @param {any} thing */
|
||||
function stringify_cached_primitive(thing) {
|
||||
if (
|
||||
(typeof thing === 'string' && thing.length >= MIN_STRING_LENGTH) ||
|
||||
typeof thing === 'bigint'
|
||||
) {
|
||||
primitives ??= new Map();
|
||||
let literal = primitives.get(thing);
|
||||
if (literal === undefined) {
|
||||
literal = stringify_primitive(thing);
|
||||
primitives.set(thing, literal);
|
||||
}
|
||||
return literal;
|
||||
}
|
||||
return stringify_primitive(thing);
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
function walk(thing) {
|
||||
if (!is_primitive(thing)) {
|
||||
if (seen.has(thing)) {
|
||||
if (!names.has(thing)) names.set(thing, '');
|
||||
return;
|
||||
}
|
||||
|
||||
seen.add(thing);
|
||||
|
||||
if (replacer) {
|
||||
const fragment = replacer(thing, js);
|
||||
|
||||
if (fragment) {
|
||||
const source = JavaScriptSource.from(fragment);
|
||||
custom.set(thing, source);
|
||||
source.visit(walk, reserved, templates);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fragment !== undefined && fragment !== null && fragment !== false) {
|
||||
throw new TypeError('Invalid uneval replacer result');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof thing === 'function') {
|
||||
error(`Cannot stringify a function`, thing);
|
||||
}
|
||||
|
||||
const type = get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'BigInt':
|
||||
case 'String':
|
||||
walk(thing.valueOf());
|
||||
return;
|
||||
|
||||
case 'Number':
|
||||
case 'Boolean':
|
||||
case 'Date':
|
||||
case 'RegExp':
|
||||
case 'URL':
|
||||
case 'URLSearchParams':
|
||||
return;
|
||||
|
||||
case 'Array':
|
||||
// Never scan the logical length of a dictionary-backed sparse array.
|
||||
for (const i of valid_array_indices(thing)) {
|
||||
keys.push(+i);
|
||||
walk(thing[i]);
|
||||
keys.pop();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Set':
|
||||
for (const value of thing) walk(value);
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
for (const [key, value] of thing) {
|
||||
keys.push(MAP_KEY, key);
|
||||
walk(key);
|
||||
walk(value);
|
||||
keys.pop();
|
||||
keys.pop();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Int8Array':
|
||||
case 'Uint8Array':
|
||||
case 'Uint8ClampedArray':
|
||||
case 'Int16Array':
|
||||
case 'Uint16Array':
|
||||
case 'Float16Array':
|
||||
case 'Int32Array':
|
||||
case 'Uint32Array':
|
||||
case 'Float32Array':
|
||||
case 'Float64Array':
|
||||
case 'BigInt64Array':
|
||||
case 'BigUint64Array':
|
||||
case 'DataView':
|
||||
// Buffer pools can contain unrelated, sensitive bytes.
|
||||
if (!is_buffer(thing)) walk(thing.buffer);
|
||||
return;
|
||||
|
||||
case 'ArrayBuffer':
|
||||
return;
|
||||
|
||||
case 'Temporal.Duration':
|
||||
case 'Temporal.Instant':
|
||||
case 'Temporal.PlainDate':
|
||||
case 'Temporal.PlainTime':
|
||||
case 'Temporal.PlainDateTime':
|
||||
case 'Temporal.PlainMonthDay':
|
||||
case 'Temporal.PlainYearMonth':
|
||||
case 'Temporal.ZonedDateTime':
|
||||
return;
|
||||
|
||||
default:
|
||||
if (!is_plain_object(thing)) {
|
||||
error(`Cannot stringify arbitrary non-POJOs`, thing);
|
||||
}
|
||||
|
||||
if (enumerable_symbols(thing).length > 0) {
|
||||
error(`Cannot stringify POJOs with symbolic keys`, thing);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(thing)) {
|
||||
if (key === '__proto__') {
|
||||
error(`Cannot stringify objects with __proto__ keys`, thing);
|
||||
}
|
||||
|
||||
keys.push(key);
|
||||
walk(thing[key]);
|
||||
keys.pop();
|
||||
}
|
||||
}
|
||||
} else if (typeof thing === 'symbol') {
|
||||
error(`Cannot stringify a Symbol primitive`, thing);
|
||||
} else if (
|
||||
(typeof thing === 'string' && thing.length >= MIN_STRING_LENGTH) ||
|
||||
typeof thing === 'bigint'
|
||||
) {
|
||||
primitive_counts ??= new Map();
|
||||
primitive_counts.set(thing, (primitive_counts.get(thing) || 0) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
walk(value);
|
||||
|
||||
let name_index = 0;
|
||||
function next_name() {
|
||||
let name;
|
||||
do {
|
||||
name = get_name(name_index++);
|
||||
} while (reserved.has(name));
|
||||
return name;
|
||||
}
|
||||
|
||||
// Wait until every custom source has been visited before assigning names.
|
||||
for (const thing of names.keys()) names.set(thing, next_name());
|
||||
if (primitive_counts) {
|
||||
for (const [thing, count] of primitive_counts) {
|
||||
if (count < 2) continue;
|
||||
const name = next_name();
|
||||
const length = stringify_cached_primitive(thing).length;
|
||||
// Include the declaration and IIFE overhead even if one already exists.
|
||||
if (length * count > length + (count + 1) * name.length + 40) names.set(thing, name);
|
||||
}
|
||||
}
|
||||
|
||||
// Reuse the traversal set to track declarations during serialization.
|
||||
seen.clear();
|
||||
const inlining = custom.size > 0 ? new Set() : null;
|
||||
|
||||
/** @type {Map<any, () => void>} */
|
||||
const initializers = new Map();
|
||||
|
||||
/** @type {string[]} */
|
||||
const statements = [];
|
||||
|
||||
/**
|
||||
* @param {any} thing
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringify(thing) {
|
||||
const name = names.get(thing);
|
||||
|
||||
if (name) {
|
||||
if (!seen.has(thing)) {
|
||||
if (is_primitive(thing)) {
|
||||
seen.add(thing);
|
||||
statements.push(`let ${name}=${stringify_cached_primitive(thing)}`);
|
||||
return name;
|
||||
}
|
||||
const type = custom.has(thing) ? null : get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'Object':
|
||||
seen.add(thing);
|
||||
statements.push(
|
||||
`let ${name}=${Object.getPrototypeOf(thing) === null ? 'Object.create(null)' : '{}'}`
|
||||
);
|
||||
Object.keys(thing).forEach((key) => {
|
||||
statements.push(`${name}${safe_prop(key)}=${stringify(thing[key])}`);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'Array': {
|
||||
seen.add(thing);
|
||||
const indices = valid_array_indices(thing);
|
||||
const array =
|
||||
thing.length > 32 + 2 * indices.length
|
||||
? stringify_sparse_array(thing.length)
|
||||
: `Array(${thing.length})`;
|
||||
statements.push(`let ${name}=${array}`);
|
||||
for (const i of indices) {
|
||||
statements.push(`${name}[${i}]=${stringify(thing[i])}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Set':
|
||||
case 'Map': {
|
||||
seen.add(thing);
|
||||
/** @type {string[]} */
|
||||
const entries = [];
|
||||
let initialized = false;
|
||||
let statement_index = -1;
|
||||
|
||||
// Collect a ready prefix until a reference needs the collection.
|
||||
// Emit subsequent entries eagerly, preserving insertion order.
|
||||
const initialize = () => {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
initializers.delete(thing);
|
||||
statements.push(
|
||||
`let ${name}=new ${type}${entries.length ? `([${entries.join(',')}])` : ''}`
|
||||
);
|
||||
};
|
||||
|
||||
initializers.set(thing, initialize);
|
||||
|
||||
for (const entry of thing) {
|
||||
const args =
|
||||
type === 'Map' ? `${stringify(entry[0])},${stringify(entry[1])}` : stringify(entry);
|
||||
|
||||
if (!initialized) {
|
||||
entries.push(type === 'Map' ? `[${args}]` : args);
|
||||
} else {
|
||||
const call = `.${type === 'Map' ? 'set' : 'add'}(${args})`;
|
||||
if (statement_index === statements.length - 1) {
|
||||
statements[statement_index] += call;
|
||||
} else {
|
||||
statement_index = statements.push(name + call) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initialize();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
const str = actually_stringify(thing);
|
||||
if (!seen.has(thing)) statements.push(`let ${name}=${str}`);
|
||||
seen.add(thing);
|
||||
}
|
||||
} else {
|
||||
initializers.get(thing)?.();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
if (is_primitive(thing)) {
|
||||
return stringify_cached_primitive(thing);
|
||||
}
|
||||
|
||||
if (inlining === null) return actually_stringify(thing);
|
||||
|
||||
// A singly referenced value can still be part of a custom constructor's
|
||||
// cycle. If inlining it re-enters itself, hoist it to break the cycle.
|
||||
if (inlining.has(thing)) {
|
||||
names.set(thing, next_name());
|
||||
return stringify(thing);
|
||||
}
|
||||
|
||||
inlining.add(thing);
|
||||
const str = actually_stringify(thing);
|
||||
inlining.delete(thing);
|
||||
return names.get(thing) || str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} thing
|
||||
* @returns {string}
|
||||
*/
|
||||
function actually_stringify(thing) {
|
||||
const source = custom.get(thing);
|
||||
|
||||
if (source) {
|
||||
return source.render(stringify);
|
||||
}
|
||||
|
||||
const type = get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'String':
|
||||
case 'Boolean':
|
||||
case 'BigInt':
|
||||
return `Object(${stringify(thing.valueOf())})`;
|
||||
|
||||
case 'RegExp':
|
||||
const { source, flags } = thing;
|
||||
return flags
|
||||
? `new RegExp(${stringify_string(source)},"${flags}")`
|
||||
: `new RegExp(${stringify_string(source)})`;
|
||||
|
||||
case 'Date':
|
||||
return `new Date(${thing.getTime()})`;
|
||||
|
||||
case 'URL':
|
||||
return `new URL(${stringify_string(thing.toString())})`;
|
||||
|
||||
case 'URLSearchParams':
|
||||
return `new URLSearchParams(${stringify_string(thing.toString())})`;
|
||||
|
||||
case 'Array': {
|
||||
// For dense arrays (no holes), we iterate normally.
|
||||
// When we encounter the first hole, we collect own indices
|
||||
// to determine the sparseness, then decide between:
|
||||
// - Array literal with holes: [,"a",,] (default)
|
||||
// - Object.assign with a sparse-safe allocator (for very sparse arrays)
|
||||
// Only the Object.assign path avoids iterating every slot, which
|
||||
// is what protects against the DoS of e.g. `arr[1000000] = 1`.
|
||||
let has_holes = false;
|
||||
|
||||
let result = '[';
|
||||
|
||||
for (let i = 0; i < thing.length; i += 1) {
|
||||
if (i > 0) result += ',';
|
||||
|
||||
if (Object.hasOwn(thing, i)) {
|
||||
result += stringify(thing[i]);
|
||||
} else if (!has_holes) {
|
||||
// Decide between array literal and Object.assign.
|
||||
//
|
||||
// Array literal: holes are consecutive commas.
|
||||
// For example, [, "a", ,] is written as [,"a",,].
|
||||
// Each hole costs 1 char (a comma).
|
||||
//
|
||||
// Object.assign: populated indices are listed explicitly.
|
||||
// For example, [, "a", ,] would be written as
|
||||
// Object.assign(sparse(3),{1:"a"}), where sparse(n) stands
|
||||
// for the expression emitted by stringify_sparse_array(n).
|
||||
// This avoids paying per-hole, but has fixed overhead for
|
||||
// the allocator and wrapper, plus each index and colon.
|
||||
//
|
||||
// The serialized values are the same size either way, so
|
||||
// the choice comes down to the structural overhead:
|
||||
//
|
||||
// Array literal overhead:
|
||||
// 1 char per element or hole (comma separators)
|
||||
// + 2 chars for "[" and "]"
|
||||
// = L + 2
|
||||
//
|
||||
// Object.assign overhead:
|
||||
// "Object.assign(" — 14 chars
|
||||
// + allocator expression — A chars
|
||||
// + ",{" — 2 chars
|
||||
// + for each populated element:
|
||||
// index + ":" + "," — (d + 2) chars
|
||||
// + "})" — 2 chars
|
||||
// = (18 + A) + P * (d + 2)
|
||||
//
|
||||
// where L is the array length, P is the number of
|
||||
// populated elements, A is the allocator length, and d
|
||||
// is the number of digits in L (an upper bound per index).
|
||||
//
|
||||
// Object.assign is cheaper when:
|
||||
// (18 + A) + P * (d + 2) < L + 2
|
||||
const populated_keys = valid_array_indices(/** @type {any[]} */ (thing));
|
||||
const population = populated_keys.length;
|
||||
const d = String(thing.length).length;
|
||||
const array = stringify_sparse_array(thing.length);
|
||||
|
||||
const hole_cost = thing.length + 2;
|
||||
const sparse_cost = array.length + 18 + population * (d + 2);
|
||||
|
||||
if (hole_cost > sparse_cost) {
|
||||
const entries = populated_keys.map((k) => `${k}:${stringify(thing[k])}`).join(',');
|
||||
return `Object.assign(${array},{${entries}})`;
|
||||
}
|
||||
|
||||
has_holes = true;
|
||||
}
|
||||
// else: already decided on array literal, hole is just an empty slot
|
||||
// (the comma separator is all we need — no content for this position)
|
||||
}
|
||||
|
||||
const tail = thing.length === 0 || Object.hasOwn(thing, thing.length - 1) ? '' : ',';
|
||||
return result + tail + ']';
|
||||
}
|
||||
|
||||
case 'Set':
|
||||
return `new Set([${Array.from(thing, stringify).join(',')}])`;
|
||||
|
||||
case 'Map': {
|
||||
const entries = Array.from(thing, ([k, v]) => `[${stringify(k)},${stringify(v)}]`);
|
||||
return `new Map([${entries.join(',')}])`;
|
||||
}
|
||||
|
||||
case 'Int8Array':
|
||||
case 'Uint8Array':
|
||||
case 'Uint8ClampedArray':
|
||||
case 'Int16Array':
|
||||
case 'Uint16Array':
|
||||
case 'Float16Array':
|
||||
case 'Int32Array':
|
||||
case 'Uint32Array':
|
||||
case 'Float32Array':
|
||||
case 'Float64Array':
|
||||
case 'BigInt64Array':
|
||||
case 'BigUint64Array': {
|
||||
if (is_buffer(thing)) thing = new Uint8Array(thing);
|
||||
|
||||
if (thing.buffer.byteLength % thing.BYTES_PER_ELEMENT !== 0) {
|
||||
return `new ${type}(${stringify(thing.buffer)},${thing.byteOffset},${thing.length})`;
|
||||
}
|
||||
|
||||
let str = `new ${type}`;
|
||||
|
||||
if (!names.has(thing.buffer)) {
|
||||
str += `([${stringify_typed_array_elements(type, thing.buffer)}])`;
|
||||
} else {
|
||||
str += `(${stringify(thing.buffer)})`;
|
||||
}
|
||||
|
||||
// handle subarrays
|
||||
if (thing.byteLength !== thing.buffer.byteLength) {
|
||||
const start = thing.byteOffset / thing.BYTES_PER_ELEMENT;
|
||||
const end = start + thing.length;
|
||||
str += `.subarray(${start},${end})`;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
case 'DataView': {
|
||||
let str = `new DataView`;
|
||||
|
||||
if (!names.has(thing.buffer)) {
|
||||
str += `(new Uint8Array([${new Uint8Array(thing.buffer)}]).buffer`;
|
||||
} else {
|
||||
str += `(${stringify(thing.buffer)}`;
|
||||
}
|
||||
|
||||
// handle subviews
|
||||
if (thing.byteLength !== thing.buffer.byteLength) {
|
||||
str += `,${thing.byteOffset},${thing.byteLength}`;
|
||||
}
|
||||
|
||||
return str + ')';
|
||||
}
|
||||
|
||||
case 'ArrayBuffer': {
|
||||
const ui8 = new Uint8Array(thing);
|
||||
return `new Uint8Array([${ui8.toString()}]).buffer`;
|
||||
}
|
||||
|
||||
case 'Temporal.Duration':
|
||||
case 'Temporal.Instant':
|
||||
case 'Temporal.PlainDate':
|
||||
case 'Temporal.PlainTime':
|
||||
case 'Temporal.PlainDateTime':
|
||||
case 'Temporal.PlainMonthDay':
|
||||
case 'Temporal.PlainYearMonth':
|
||||
case 'Temporal.ZonedDateTime':
|
||||
return `${type}.from(${stringify_string(thing.toString())})`;
|
||||
|
||||
default:
|
||||
const keys = Object.keys(thing);
|
||||
const obj = keys.map((key) => `${safe_key(key)}:${stringify(thing[key])}`).join(',');
|
||||
const proto = Object.getPrototypeOf(thing);
|
||||
if (proto === null) {
|
||||
return keys.length > 0 ? `{${obj},__proto__:null}` : `{__proto__:null}`;
|
||||
}
|
||||
|
||||
return `{${obj}}`;
|
||||
}
|
||||
}
|
||||
|
||||
const str = stringify(value);
|
||||
|
||||
if (statements.length === 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
return `(function(){${statements.join(';')};return ${str}}())`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the elements of `buffer`, read as `type`, as a comma-separated list.
|
||||
* The view is created from `type` rather than from the serialized value's own
|
||||
* constructor, which may be a subclass like Node's `Buffer` whose `toString`
|
||||
* decodes the bytes instead of listing them.
|
||||
* `BigInt64Array`/`BigUint64Array` elements are bigints and must be written
|
||||
* with an `n` suffix, otherwise the emitted `new BigInt64Array([...])` throws.
|
||||
* @param {string} type
|
||||
* @param {ArrayBufferLike} buffer
|
||||
*/
|
||||
function stringify_typed_array_elements(type, buffer) {
|
||||
const array = new /** @type {any} */ (globalThis)[type](buffer);
|
||||
|
||||
if (type === 'BigInt64Array' || type === 'BigUint64Array') {
|
||||
return Array.from(array, (element) => `${element}n`).join(',');
|
||||
}
|
||||
|
||||
// Float arrays can hold `-0`, which `toString()` collapses to `"0"`, silently
|
||||
// losing the sign on round-trip. Emit `-0` explicitly for those elements.
|
||||
if (
|
||||
array instanceof Float32Array ||
|
||||
array instanceof Float64Array ||
|
||||
(typeof Float16Array !== 'undefined' && array instanceof Float16Array)
|
||||
) {
|
||||
return Array.from(array, (element) => (Object.is(element, -0) ? '-0' : `${element}`)).join(',');
|
||||
}
|
||||
|
||||
return array.toString();
|
||||
}
|
||||
|
||||
/** @param {string} c */
|
||||
function escape_unsafe_char(c) {
|
||||
return escaped[c] || c;
|
||||
}
|
||||
|
||||
/** @param {string} str */
|
||||
function escape_unsafe_chars(str) {
|
||||
return str.replace(unsafe_chars, escape_unsafe_char);
|
||||
}
|
||||
|
||||
/** @param {string} key */
|
||||
function safe_key(key) {
|
||||
return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? key : escape_unsafe_chars(JSON.stringify(key));
|
||||
}
|
||||
|
||||
/** @param {string} key */
|
||||
function safe_prop(key) {
|
||||
return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key)
|
||||
? `.${key}`
|
||||
: `[${escape_unsafe_chars(JSON.stringify(key))}]`;
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
function stringify_primitive(thing) {
|
||||
const type = typeof thing;
|
||||
if (type === 'string') return stringify_string(thing);
|
||||
if (thing === void 0) return 'void 0';
|
||||
if (thing === 0 && 1 / thing < 0) return '-0';
|
||||
const str = String(thing);
|
||||
if (type === 'number') return str.replace(/^(-)?0\./, '$1.');
|
||||
if (type === 'bigint') return thing + 'n';
|
||||
return str;
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
import { MAX_ARRAY_INDEX, MAX_ARRAY_LEN } from './constants.js';
|
||||
|
||||
const name_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
|
||||
const reserved_names =
|
||||
/^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/;
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
export const escaped = {
|
||||
'<': '\\u003C',
|
||||
'\\': '\\\\',
|
||||
'\b': '\\b',
|
||||
'\f': '\\f',
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\t': '\\t',
|
||||
'\u2028': '\\u2028',
|
||||
'\u2029': '\\u2029'
|
||||
};
|
||||
|
||||
export class DevalueError extends Error {
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {string[]} keys
|
||||
* @param {any} [value] - The value that failed to be serialized
|
||||
* @param {any} [root] - The root value being serialized
|
||||
*/
|
||||
constructor(message, keys, value, root) {
|
||||
super(message);
|
||||
this.name = 'DevalueError';
|
||||
this.path = keys.join('');
|
||||
this.value = value;
|
||||
this.root = root;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a Map key on the path being serialized. The key itself is recorded
|
||||
* right after the marker, so nothing is formatted or allocated per entry.
|
||||
*/
|
||||
export const MAP_KEY = Symbol();
|
||||
|
||||
/**
|
||||
* Formats the path recorded on the way to the value being serialized — a
|
||||
* property key, an array index, or `MAP_KEY` followed by a Map key — into
|
||||
* the segments a `DevalueError` joins. Only runs when an error is raised.
|
||||
* @param {any[]} keys
|
||||
* @param {(key: any) => string} format_map_key
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function format_path(keys, format_map_key) {
|
||||
/** @type {string[]} */
|
||||
const path = [];
|
||||
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const segment = keys[i];
|
||||
|
||||
if (segment === MAP_KEY) {
|
||||
path.push(`.get(${format_map_key(keys[++i])})`);
|
||||
} else if (typeof segment === 'number') {
|
||||
path.push(`[${segment}]`);
|
||||
} else {
|
||||
path.push(stringify_key(segment));
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
export function is_primitive(thing) {
|
||||
return thing === null || (typeof thing !== 'object' && typeof thing !== 'function');
|
||||
}
|
||||
|
||||
const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames(Object.prototype)
|
||||
.sort()
|
||||
.join('\0');
|
||||
|
||||
/** @param {any} thing */
|
||||
export function is_plain_object(thing) {
|
||||
const proto = Object.getPrototypeOf(thing);
|
||||
|
||||
return (
|
||||
proto === Object.prototype ||
|
||||
proto === null ||
|
||||
Object.getPrototypeOf(proto) === null ||
|
||||
Object.getOwnPropertyNames(proto).sort().join('\0') === object_proto_names
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
export function get_type(thing) {
|
||||
return Object.prototype.toString.call(thing).slice(8, -1);
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
export function is_buffer(thing) {
|
||||
return typeof Buffer !== 'undefined' && Buffer.isBuffer(thing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an array whose storage is not proportional to its declared length.
|
||||
* Touching and deleting the largest valid index forces dictionary elements
|
||||
* before setting the length; assigning .length on [] can still eagerly allocate.
|
||||
* @param {number} length
|
||||
*/
|
||||
export function stringify_sparse_array(length) {
|
||||
return `(function(a){a[${MAX_ARRAY_INDEX}]=0;delete a[${MAX_ARRAY_INDEX}];a.length=${length};return a}([]))`;
|
||||
}
|
||||
|
||||
/** Returns the compact JavaScript identifier at `index`. @param {number} index */
|
||||
export function get_name(index) {
|
||||
let name = '';
|
||||
|
||||
do {
|
||||
name = name_chars[index % name_chars.length] + name;
|
||||
index = ~~(index / name_chars.length) - 1;
|
||||
} while (index >= 0);
|
||||
|
||||
return reserved_names.test(name) ? `${name}0` : name;
|
||||
}
|
||||
|
||||
// characters `stringify_string` always escapes: quote, backslash, `<`, control
|
||||
// characters and the line/paragraph separators. Surrogates are only escaped
|
||||
// when unpaired, which `isWellFormed` detects natively, so well-formed pairs
|
||||
// such as emoji stay on the fast path
|
||||
const needs_escape = /["<\\\u0000-\u001f\u2028\u2029]/;
|
||||
|
||||
// `JSON.stringify` already emits every escape we need except these three,
|
||||
// which it leaves raw: `<` (so output is safe inside a `<script>` tag) and
|
||||
// the line/paragraph separators (kept escaped for engines predating ES2019,
|
||||
// which rejected them in string literals). It also escapes unpaired
|
||||
// surrogates as `\\udXXX`, so the result survives UTF-8 transport
|
||||
const json_leaves_raw = /[<\u2028\u2029]/g;
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
const raw_escapes = { '<': '\\u003C', '\u2028': '\\u2028', '\u2029': '\\u2029' };
|
||||
|
||||
/** @param {string} char */
|
||||
function escape_raw(char) {
|
||||
return raw_escapes[char];
|
||||
}
|
||||
|
||||
/** @param {string} str */
|
||||
export function stringify_string(str) {
|
||||
// the common case: nothing to escape, no per-character work
|
||||
if (!needs_escape.test(str) && str.isWellFormed()) return `"${str}"`;
|
||||
|
||||
return JSON.stringify(str).replace(json_leaves_raw, escape_raw);
|
||||
}
|
||||
|
||||
// Property names repeat across the values of one call and across calls, so
|
||||
// their quoted form is cached. Names come from the data being serialized, so
|
||||
// the cache is bounded in both directions: only short names are kept, and the
|
||||
// cache is emptied once it holds 1024 of them. Longer names are quoted on
|
||||
// every visit, like every other string.
|
||||
const MAX_CACHED_KEY_LENGTH = 64;
|
||||
|
||||
/** @type {Map<string, string>} */
|
||||
const quoted_keys = new Map();
|
||||
|
||||
/** @param {string} key */
|
||||
export function quote_key(key) {
|
||||
if (key.length > MAX_CACHED_KEY_LENGTH) return stringify_string(key);
|
||||
|
||||
let quoted = quoted_keys.get(key);
|
||||
|
||||
if (quoted === undefined) {
|
||||
if (quoted_keys.size >= 1024) quoted_keys.clear();
|
||||
quoted = stringify_string(key);
|
||||
quoted_keys.set(key, quoted);
|
||||
}
|
||||
|
||||
return quoted;
|
||||
}
|
||||
|
||||
/** @param {Record<string | symbol, any>} object */
|
||||
export function enumerable_symbols(object) {
|
||||
const symbols = Object.getOwnPropertySymbols(object);
|
||||
// Own symbols always have a descriptor; the optional chain only satisfies the lib's
|
||||
// `PropertyDescriptor | undefined` return type.
|
||||
return symbols.length === 0
|
||||
? symbols
|
||||
: symbols.filter((symbol) => Object.getOwnPropertyDescriptor(object, symbol)?.enumerable);
|
||||
}
|
||||
|
||||
const is_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
|
||||
|
||||
/** @param {string} key */
|
||||
function stringify_key(key) {
|
||||
return is_identifier.test(key) ? '.' + key : '[' + JSON.stringify(key) + ']';
|
||||
}
|
||||
|
||||
/** @param {number} n */
|
||||
export function is_valid_array_index(n) {
|
||||
if (!Number.isInteger(n)) return false;
|
||||
if (n < 0) return false;
|
||||
if (n > MAX_ARRAY_INDEX) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @param {number} n */
|
||||
export function is_valid_array_len(n) {
|
||||
if (!Number.isInteger(n)) return false;
|
||||
if (n < 0) return false;
|
||||
if (n > MAX_ARRAY_LEN) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @param {string} s */
|
||||
function is_valid_array_index_string(s) {
|
||||
if (s.length === 0) return false;
|
||||
if (s.length > 1 && s.charCodeAt(0) === 48) return false; // leading zero
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
const c = s.charCodeAt(i);
|
||||
if (c < 48 || c > 57) return false;
|
||||
}
|
||||
// by this point we know it's a string of digits, but it has to be within
|
||||
// the range of valid array indices
|
||||
return is_valid_array_index(+s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the leading run of valid array indices in `keys`.
|
||||
* @param {readonly string[]} keys
|
||||
*/
|
||||
function array_index_cut(keys) {
|
||||
for (var i = keys.length - 1; i >= 0; i--) {
|
||||
if (is_valid_array_index_string(keys[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the populated indices of an array.
|
||||
* @param {unknown[]} array
|
||||
*/
|
||||
export function valid_array_indices(array) {
|
||||
const keys = Object.keys(array);
|
||||
keys.length = array_index_cut(keys);
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the own enumerable string keys of an array-like value, in property
|
||||
* order, returns the leading run of them that are valid array indices.
|
||||
*
|
||||
* This is the filtering half of the `indicesOf` stringify operation,
|
||||
* exposed so that custom operations — which typically already have the keys
|
||||
* in hand, e.g. from a foreign runtime — don't have to reimplement it.
|
||||
*
|
||||
* Does not modify `keys`.
|
||||
*
|
||||
* @param {readonly string[]} keys
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function filter_array_indices(keys) {
|
||||
return keys.slice(0, array_index_cut(keys));
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
// Adapted from https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/src/he.js#L106-L134
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.fromCodePoint = void 0;
|
||||
exports.replaceCodePoint = replaceCodePoint;
|
||||
exports.decodeCodePoint = decodeCodePoint;
|
||||
const decodeMap = new Map([
|
||||
[0, 65533],
|
||||
// C1 Unicode control character reference replacements
|
||||
[128, 8364],
|
||||
[130, 8218],
|
||||
[131, 402],
|
||||
[132, 8222],
|
||||
[133, 8230],
|
||||
[134, 8224],
|
||||
[135, 8225],
|
||||
[136, 710],
|
||||
[137, 8240],
|
||||
[138, 352],
|
||||
[139, 8249],
|
||||
[140, 338],
|
||||
[142, 381],
|
||||
[145, 8216],
|
||||
[146, 8217],
|
||||
[147, 8220],
|
||||
[148, 8221],
|
||||
[149, 8226],
|
||||
[150, 8211],
|
||||
[151, 8212],
|
||||
[152, 732],
|
||||
[153, 8482],
|
||||
[154, 353],
|
||||
[155, 8250],
|
||||
[156, 339],
|
||||
[158, 382],
|
||||
[159, 376],
|
||||
]);
|
||||
/**
|
||||
* Polyfill for `String.fromCodePoint`. It is used to create a string from a Unicode code point.
|
||||
*/
|
||||
exports.fromCodePoint =
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, n/no-unsupported-features/es-builtins
|
||||
(_a = String.fromCodePoint) !== null && _a !== void 0 ? _a : ((codePoint) => {
|
||||
let output = "";
|
||||
if (codePoint > 65535) {
|
||||
codePoint -= 65536;
|
||||
output += String.fromCharCode(((codePoint >>> 10) & 1023) | 55296);
|
||||
codePoint = 56320 | (codePoint & 1023);
|
||||
}
|
||||
output += String.fromCharCode(codePoint);
|
||||
return output;
|
||||
});
|
||||
/**
|
||||
* Replace the given code point with a replacement character if it is a
|
||||
* surrogate or is outside the valid range. Otherwise return the code
|
||||
* point unchanged.
|
||||
*/
|
||||
function replaceCodePoint(codePoint) {
|
||||
var _a;
|
||||
if ((codePoint >= 55296 && codePoint <= 57343) ||
|
||||
codePoint > 1114111) {
|
||||
return 65533;
|
||||
}
|
||||
return (_a = decodeMap.get(codePoint)) !== null && _a !== void 0 ? _a : codePoint;
|
||||
}
|
||||
/**
|
||||
* Replace the code point if relevant, then convert it to a string.
|
||||
*
|
||||
* @deprecated Use `fromCodePoint(replaceCodePoint(codePoint))` instead.
|
||||
* @param codePoint The code point to decode.
|
||||
* @returns The decoded code point.
|
||||
*/
|
||||
function decodeCodePoint(codePoint) {
|
||||
return (0, exports.fromCodePoint)(replaceCodePoint(codePoint));
|
||||
}
|
||||
//# sourceMappingURL=decode-codepoint.js.map
|
||||
+568
@@ -0,0 +1,568 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.xmlDecodeTree = exports.htmlDecodeTree = exports.replaceCodePoint = exports.fromCodePoint = exports.decodeCodePoint = exports.EntityDecoder = exports.DecodingMode = void 0;
|
||||
exports.determineBranch = determineBranch;
|
||||
exports.decodeHTML = decodeHTML;
|
||||
exports.decodeHTMLAttribute = decodeHTMLAttribute;
|
||||
exports.decodeHTMLStrict = decodeHTMLStrict;
|
||||
exports.decodeXML = decodeXML;
|
||||
const decode_codepoint_js_1 = require("./decode-codepoint.js");
|
||||
const decode_data_html_js_1 = require("./generated/decode-data-html.js");
|
||||
const decode_data_xml_js_1 = require("./generated/decode-data-xml.js");
|
||||
const bin_trie_flags_js_1 = require("./internal/bin-trie-flags.js");
|
||||
var CharCodes;
|
||||
(function (CharCodes) {
|
||||
CharCodes[CharCodes["NUM"] = 35] = "NUM";
|
||||
CharCodes[CharCodes["SEMI"] = 59] = "SEMI";
|
||||
CharCodes[CharCodes["EQUALS"] = 61] = "EQUALS";
|
||||
CharCodes[CharCodes["ZERO"] = 48] = "ZERO";
|
||||
CharCodes[CharCodes["NINE"] = 57] = "NINE";
|
||||
CharCodes[CharCodes["LOWER_A"] = 97] = "LOWER_A";
|
||||
CharCodes[CharCodes["LOWER_F"] = 102] = "LOWER_F";
|
||||
CharCodes[CharCodes["LOWER_X"] = 120] = "LOWER_X";
|
||||
CharCodes[CharCodes["LOWER_Z"] = 122] = "LOWER_Z";
|
||||
CharCodes[CharCodes["UPPER_A"] = 65] = "UPPER_A";
|
||||
CharCodes[CharCodes["UPPER_F"] = 70] = "UPPER_F";
|
||||
CharCodes[CharCodes["UPPER_Z"] = 90] = "UPPER_Z";
|
||||
})(CharCodes || (CharCodes = {}));
|
||||
/** Bit that needs to be set to convert an upper case ASCII character to lower case */
|
||||
const TO_LOWER_BIT = 32;
|
||||
function isNumber(code) {
|
||||
return code >= CharCodes.ZERO && code <= CharCodes.NINE;
|
||||
}
|
||||
function isHexadecimalCharacter(code) {
|
||||
return ((code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F) ||
|
||||
(code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F));
|
||||
}
|
||||
function isAsciiAlphaNumeric(code) {
|
||||
return ((code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z) ||
|
||||
(code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z) ||
|
||||
isNumber(code));
|
||||
}
|
||||
/**
|
||||
* Checks if the given character is a valid end character for an entity in an attribute.
|
||||
*
|
||||
* Attribute values that aren't terminated properly aren't parsed, and shouldn't lead to a parser error.
|
||||
* See the example in https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state
|
||||
*/
|
||||
function isEntityInAttributeInvalidEnd(code) {
|
||||
return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code);
|
||||
}
|
||||
var EntityDecoderState;
|
||||
(function (EntityDecoderState) {
|
||||
EntityDecoderState[EntityDecoderState["EntityStart"] = 0] = "EntityStart";
|
||||
EntityDecoderState[EntityDecoderState["NumericStart"] = 1] = "NumericStart";
|
||||
EntityDecoderState[EntityDecoderState["NumericDecimal"] = 2] = "NumericDecimal";
|
||||
EntityDecoderState[EntityDecoderState["NumericHex"] = 3] = "NumericHex";
|
||||
EntityDecoderState[EntityDecoderState["NamedEntity"] = 4] = "NamedEntity";
|
||||
})(EntityDecoderState || (EntityDecoderState = {}));
|
||||
var DecodingMode;
|
||||
(function (DecodingMode) {
|
||||
/** Entities in text nodes that can end with any character. */
|
||||
DecodingMode[DecodingMode["Legacy"] = 0] = "Legacy";
|
||||
/** Only allow entities terminated with a semicolon. */
|
||||
DecodingMode[DecodingMode["Strict"] = 1] = "Strict";
|
||||
/** Entities in attributes have limitations on ending characters. */
|
||||
DecodingMode[DecodingMode["Attribute"] = 2] = "Attribute";
|
||||
})(DecodingMode || (exports.DecodingMode = DecodingMode = {}));
|
||||
/**
|
||||
* Token decoder with support of writing partial entities.
|
||||
*/
|
||||
class EntityDecoder {
|
||||
constructor(
|
||||
/** The tree used to decode entities. */
|
||||
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: False positive
|
||||
decodeTree,
|
||||
/**
|
||||
* The function that is called when a codepoint is decoded.
|
||||
*
|
||||
* For multi-byte named entities, this will be called multiple times,
|
||||
* with the second codepoint, and the same `consumed` value.
|
||||
*
|
||||
* @param codepoint The decoded codepoint.
|
||||
* @param consumed The number of bytes consumed by the decoder.
|
||||
*/
|
||||
emitCodePoint,
|
||||
/** An object that is used to produce errors. */
|
||||
errors) {
|
||||
this.decodeTree = decodeTree;
|
||||
this.emitCodePoint = emitCodePoint;
|
||||
this.errors = errors;
|
||||
/** The current state of the decoder. */
|
||||
this.state = EntityDecoderState.EntityStart;
|
||||
/** Characters that were consumed while parsing an entity. */
|
||||
this.consumed = 1;
|
||||
/**
|
||||
* The result of the entity.
|
||||
*
|
||||
* Either the result index of a numeric entity, or the codepoint of a
|
||||
* numeric entity.
|
||||
*/
|
||||
this.result = 0;
|
||||
/** The current index in the decode tree. */
|
||||
this.treeIndex = 0;
|
||||
/** The number of characters that were consumed in excess. */
|
||||
this.excess = 1;
|
||||
/** The mode in which the decoder is operating. */
|
||||
this.decodeMode = DecodingMode.Strict;
|
||||
/** The number of characters that have been consumed in the current run. */
|
||||
this.runConsumed = 0;
|
||||
}
|
||||
/** Resets the instance to make it reusable. */
|
||||
startEntity(decodeMode) {
|
||||
this.decodeMode = decodeMode;
|
||||
this.state = EntityDecoderState.EntityStart;
|
||||
this.result = 0;
|
||||
this.treeIndex = 0;
|
||||
this.excess = 1;
|
||||
this.consumed = 1;
|
||||
this.runConsumed = 0;
|
||||
}
|
||||
/**
|
||||
* Write an entity to the decoder. This can be called multiple times with partial entities.
|
||||
* If the entity is incomplete, the decoder will return -1.
|
||||
*
|
||||
* Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the
|
||||
* entity is incomplete, and resume when the next string is written.
|
||||
*
|
||||
* @param input The string containing the entity (or a continuation of the entity).
|
||||
* @param offset The offset at which the entity begins. Should be 0 if this is not the first call.
|
||||
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
||||
*/
|
||||
write(input, offset) {
|
||||
switch (this.state) {
|
||||
case EntityDecoderState.EntityStart: {
|
||||
if (input.charCodeAt(offset) === CharCodes.NUM) {
|
||||
this.state = EntityDecoderState.NumericStart;
|
||||
this.consumed += 1;
|
||||
return this.stateNumericStart(input, offset + 1);
|
||||
}
|
||||
this.state = EntityDecoderState.NamedEntity;
|
||||
return this.stateNamedEntity(input, offset);
|
||||
}
|
||||
case EntityDecoderState.NumericStart: {
|
||||
return this.stateNumericStart(input, offset);
|
||||
}
|
||||
case EntityDecoderState.NumericDecimal: {
|
||||
return this.stateNumericDecimal(input, offset);
|
||||
}
|
||||
case EntityDecoderState.NumericHex: {
|
||||
return this.stateNumericHex(input, offset);
|
||||
}
|
||||
case EntityDecoderState.NamedEntity: {
|
||||
return this.stateNamedEntity(input, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Switches between the numeric decimal and hexadecimal states.
|
||||
*
|
||||
* Equivalent to the `Numeric character reference state` in the HTML spec.
|
||||
*
|
||||
* @param input The string containing the entity (or a continuation of the entity).
|
||||
* @param offset The current offset.
|
||||
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
||||
*/
|
||||
stateNumericStart(input, offset) {
|
||||
if (offset >= input.length) {
|
||||
return -1;
|
||||
}
|
||||
if ((input.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) {
|
||||
this.state = EntityDecoderState.NumericHex;
|
||||
this.consumed += 1;
|
||||
return this.stateNumericHex(input, offset + 1);
|
||||
}
|
||||
this.state = EntityDecoderState.NumericDecimal;
|
||||
return this.stateNumericDecimal(input, offset);
|
||||
}
|
||||
/**
|
||||
* Parses a hexadecimal numeric entity.
|
||||
*
|
||||
* Equivalent to the `Hexademical character reference state` in the HTML spec.
|
||||
*
|
||||
* @param input The string containing the entity (or a continuation of the entity).
|
||||
* @param offset The current offset.
|
||||
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
||||
*/
|
||||
stateNumericHex(input, offset) {
|
||||
while (offset < input.length) {
|
||||
const char = input.charCodeAt(offset);
|
||||
if (isNumber(char) || isHexadecimalCharacter(char)) {
|
||||
// Convert hex digit to value (0-15); 'a'/'A' -> 10.
|
||||
const digit = char <= CharCodes.NINE
|
||||
? char - CharCodes.ZERO
|
||||
: (char | TO_LOWER_BIT) - CharCodes.LOWER_A + 10;
|
||||
this.result = this.result * 16 + digit;
|
||||
this.consumed++;
|
||||
offset++;
|
||||
}
|
||||
else {
|
||||
return this.emitNumericEntity(char, 3);
|
||||
}
|
||||
}
|
||||
return -1; // Incomplete entity
|
||||
}
|
||||
/**
|
||||
* Parses a decimal numeric entity.
|
||||
*
|
||||
* Equivalent to the `Decimal character reference state` in the HTML spec.
|
||||
*
|
||||
* @param input The string containing the entity (or a continuation of the entity).
|
||||
* @param offset The current offset.
|
||||
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
||||
*/
|
||||
stateNumericDecimal(input, offset) {
|
||||
while (offset < input.length) {
|
||||
const char = input.charCodeAt(offset);
|
||||
if (isNumber(char)) {
|
||||
this.result = this.result * 10 + (char - CharCodes.ZERO);
|
||||
this.consumed++;
|
||||
offset++;
|
||||
}
|
||||
else {
|
||||
return this.emitNumericEntity(char, 2);
|
||||
}
|
||||
}
|
||||
return -1; // Incomplete entity
|
||||
}
|
||||
/**
|
||||
* Validate and emit a numeric entity.
|
||||
*
|
||||
* Implements the logic from the `Hexademical character reference start
|
||||
* state` and `Numeric character reference end state` in the HTML spec.
|
||||
*
|
||||
* @param lastCp The last code point of the entity. Used to see if the
|
||||
* entity was terminated with a semicolon.
|
||||
* @param expectedLength The minimum number of characters that should be
|
||||
* consumed. Used to validate that at least one digit
|
||||
* was consumed.
|
||||
* @returns The number of characters that were consumed.
|
||||
*/
|
||||
emitNumericEntity(lastCp, expectedLength) {
|
||||
var _a;
|
||||
// Ensure we consumed at least one digit.
|
||||
if (this.consumed <= expectedLength) {
|
||||
(_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed);
|
||||
return 0;
|
||||
}
|
||||
// Figure out if this is a legit end of the entity
|
||||
if (lastCp === CharCodes.SEMI) {
|
||||
this.consumed += 1;
|
||||
}
|
||||
else if (this.decodeMode === DecodingMode.Strict) {
|
||||
return 0;
|
||||
}
|
||||
this.emitCodePoint((0, decode_codepoint_js_1.replaceCodePoint)(this.result), this.consumed);
|
||||
if (this.errors) {
|
||||
if (lastCp !== CharCodes.SEMI) {
|
||||
this.errors.missingSemicolonAfterCharacterReference();
|
||||
}
|
||||
this.errors.validateNumericCharacterReference(this.result);
|
||||
}
|
||||
return this.consumed;
|
||||
}
|
||||
/**
|
||||
* Parses a named entity.
|
||||
*
|
||||
* Equivalent to the `Named character reference state` in the HTML spec.
|
||||
*
|
||||
* @param input The string containing the entity (or a continuation of the entity).
|
||||
* @param offset The current offset.
|
||||
* @returns The number of characters that were consumed, or -1 if the entity is incomplete.
|
||||
*/
|
||||
stateNamedEntity(input, offset) {
|
||||
const { decodeTree } = this;
|
||||
let current = decodeTree[this.treeIndex];
|
||||
// The length is the number of bytes of the value, including the current byte.
|
||||
let valueLength = (current & bin_trie_flags_js_1.BinTrieFlags.VALUE_LENGTH) >> 14;
|
||||
while (offset < input.length) {
|
||||
// Handle compact runs (possibly inline): valueLength == 0 and SEMI_REQUIRED bit set.
|
||||
if (valueLength === 0 && (current & bin_trie_flags_js_1.BinTrieFlags.FLAG13) !== 0) {
|
||||
const runLength = (current & bin_trie_flags_js_1.BinTrieFlags.BRANCH_LENGTH) >> 7; /* 2..63 */
|
||||
// If we are starting a run, check the first char.
|
||||
if (this.runConsumed === 0) {
|
||||
const firstChar = current & bin_trie_flags_js_1.BinTrieFlags.JUMP_TABLE;
|
||||
if (input.charCodeAt(offset) !== firstChar) {
|
||||
return this.result === 0
|
||||
? 0
|
||||
: this.emitNotTerminatedNamedEntity();
|
||||
}
|
||||
offset++;
|
||||
this.excess++;
|
||||
this.runConsumed++;
|
||||
}
|
||||
// Check remaining characters in the run.
|
||||
while (this.runConsumed < runLength) {
|
||||
if (offset >= input.length) {
|
||||
return -1;
|
||||
}
|
||||
const charIndexInPacked = this.runConsumed - 1;
|
||||
const packedWord = decodeTree[this.treeIndex + 1 + (charIndexInPacked >> 1)];
|
||||
const expectedChar = charIndexInPacked % 2 === 0
|
||||
? packedWord & 0xff
|
||||
: (packedWord >> 8) & 0xff;
|
||||
if (input.charCodeAt(offset) !== expectedChar) {
|
||||
this.runConsumed = 0;
|
||||
return this.result === 0
|
||||
? 0
|
||||
: this.emitNotTerminatedNamedEntity();
|
||||
}
|
||||
offset++;
|
||||
this.excess++;
|
||||
this.runConsumed++;
|
||||
}
|
||||
this.runConsumed = 0;
|
||||
this.treeIndex += 1 + (runLength >> 1);
|
||||
current = decodeTree[this.treeIndex];
|
||||
valueLength = (current & bin_trie_flags_js_1.BinTrieFlags.VALUE_LENGTH) >> 14;
|
||||
}
|
||||
if (offset >= input.length)
|
||||
break;
|
||||
const char = input.charCodeAt(offset);
|
||||
/*
|
||||
* Implicit semicolon handling for nodes that require a semicolon but
|
||||
* don't have an explicit ';' branch stored in the trie. If we have
|
||||
* a value on the current node, it requires a semicolon, and the
|
||||
* current input character is a semicolon, emit the entity using the
|
||||
* current node (without descending further).
|
||||
*/
|
||||
if (char === CharCodes.SEMI &&
|
||||
valueLength !== 0 &&
|
||||
(current & bin_trie_flags_js_1.BinTrieFlags.FLAG13) !== 0) {
|
||||
return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
|
||||
}
|
||||
this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
|
||||
if (this.treeIndex < 0) {
|
||||
return this.result === 0 ||
|
||||
// If we are parsing an attribute
|
||||
(this.decodeMode === DecodingMode.Attribute &&
|
||||
// We shouldn't have consumed any characters after the entity,
|
||||
(valueLength === 0 ||
|
||||
// And there should be no invalid characters.
|
||||
isEntityInAttributeInvalidEnd(char)))
|
||||
? 0
|
||||
: this.emitNotTerminatedNamedEntity();
|
||||
}
|
||||
current = decodeTree[this.treeIndex];
|
||||
valueLength = (current & bin_trie_flags_js_1.BinTrieFlags.VALUE_LENGTH) >> 14;
|
||||
// If the branch is a value, store it and continue
|
||||
if (valueLength !== 0) {
|
||||
// If the entity is terminated by a semicolon, we are done.
|
||||
if (char === CharCodes.SEMI) {
|
||||
return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
|
||||
}
|
||||
// If we encounter a non-terminated (legacy) entity while parsing strictly, then ignore it.
|
||||
if (this.decodeMode !== DecodingMode.Strict &&
|
||||
(current & bin_trie_flags_js_1.BinTrieFlags.FLAG13) === 0) {
|
||||
this.result = this.treeIndex;
|
||||
this.consumed += this.excess;
|
||||
this.excess = 0;
|
||||
}
|
||||
}
|
||||
// Increment offset & excess for next iteration
|
||||
offset++;
|
||||
this.excess++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
* Emit a named entity that was not terminated with a semicolon.
|
||||
*
|
||||
* @returns The number of characters consumed.
|
||||
*/
|
||||
emitNotTerminatedNamedEntity() {
|
||||
var _a;
|
||||
const { result, decodeTree } = this;
|
||||
const valueLength = (decodeTree[result] & bin_trie_flags_js_1.BinTrieFlags.VALUE_LENGTH) >> 14;
|
||||
this.emitNamedEntityData(result, valueLength, this.consumed);
|
||||
(_a = this.errors) === null || _a === void 0 ? void 0 : _a.missingSemicolonAfterCharacterReference();
|
||||
return this.consumed;
|
||||
}
|
||||
/**
|
||||
* Emit a named entity.
|
||||
*
|
||||
* @param result The index of the entity in the decode tree.
|
||||
* @param valueLength The number of bytes in the entity.
|
||||
* @param consumed The number of characters consumed.
|
||||
*
|
||||
* @returns The number of characters consumed.
|
||||
*/
|
||||
emitNamedEntityData(result, valueLength, consumed) {
|
||||
const { decodeTree } = this;
|
||||
this.emitCodePoint(valueLength === 1
|
||||
? decodeTree[result] &
|
||||
~(bin_trie_flags_js_1.BinTrieFlags.VALUE_LENGTH | bin_trie_flags_js_1.BinTrieFlags.FLAG13)
|
||||
: decodeTree[result + 1], consumed);
|
||||
if (valueLength === 3) {
|
||||
// For multi-byte values, we need to emit the second byte.
|
||||
this.emitCodePoint(decodeTree[result + 2], consumed);
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
/**
|
||||
* Signal to the parser that the end of the input was reached.
|
||||
*
|
||||
* Remaining data will be emitted and relevant errors will be produced.
|
||||
*
|
||||
* @returns The number of characters consumed.
|
||||
*/
|
||||
end() {
|
||||
var _a;
|
||||
switch (this.state) {
|
||||
case EntityDecoderState.NamedEntity: {
|
||||
// Emit a named entity if we have one.
|
||||
return this.result !== 0 &&
|
||||
(this.decodeMode !== DecodingMode.Attribute ||
|
||||
this.result === this.treeIndex)
|
||||
? this.emitNotTerminatedNamedEntity()
|
||||
: 0;
|
||||
}
|
||||
// Otherwise, emit a numeric entity if we have one.
|
||||
case EntityDecoderState.NumericDecimal: {
|
||||
return this.emitNumericEntity(0, 2);
|
||||
}
|
||||
case EntityDecoderState.NumericHex: {
|
||||
return this.emitNumericEntity(0, 3);
|
||||
}
|
||||
case EntityDecoderState.NumericStart: {
|
||||
(_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed);
|
||||
return 0;
|
||||
}
|
||||
case EntityDecoderState.EntityStart: {
|
||||
// Return 0 if we have no entity.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.EntityDecoder = EntityDecoder;
|
||||
/**
|
||||
* Creates a function that decodes entities in a string.
|
||||
*
|
||||
* @param decodeTree The decode tree.
|
||||
* @returns A function that decodes entities in a string.
|
||||
*/
|
||||
function getDecoder(decodeTree) {
|
||||
let returnValue = "";
|
||||
const decoder = new EntityDecoder(decodeTree, (data) => (returnValue += (0, decode_codepoint_js_1.fromCodePoint)(data)));
|
||||
return function decodeWithTrie(input, decodeMode) {
|
||||
let lastIndex = 0;
|
||||
let offset = 0;
|
||||
while ((offset = input.indexOf("&", offset)) >= 0) {
|
||||
returnValue += input.slice(lastIndex, offset);
|
||||
decoder.startEntity(decodeMode);
|
||||
const length = decoder.write(input,
|
||||
// Skip the "&"
|
||||
offset + 1);
|
||||
if (length < 0) {
|
||||
lastIndex = offset + decoder.end();
|
||||
break;
|
||||
}
|
||||
lastIndex = offset + length;
|
||||
// If `length` is 0, skip the current `&` and continue.
|
||||
offset = length === 0 ? lastIndex + 1 : lastIndex;
|
||||
}
|
||||
const result = returnValue + input.slice(lastIndex);
|
||||
// Make sure we don't keep a reference to the final string.
|
||||
returnValue = "";
|
||||
return result;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Determines the branch of the current node that is taken given the current
|
||||
* character. This function is used to traverse the trie.
|
||||
*
|
||||
* @param decodeTree The trie.
|
||||
* @param current The current node.
|
||||
* @param nodeIdx The index right after the current node and its value.
|
||||
* @param char The current character.
|
||||
* @returns The index of the next node, or -1 if no branch is taken.
|
||||
*/
|
||||
function determineBranch(decodeTree, current, nodeIndex, char) {
|
||||
const branchCount = (current & bin_trie_flags_js_1.BinTrieFlags.BRANCH_LENGTH) >> 7;
|
||||
const jumpOffset = current & bin_trie_flags_js_1.BinTrieFlags.JUMP_TABLE;
|
||||
// Case 1: Single branch encoded in jump offset
|
||||
if (branchCount === 0) {
|
||||
return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1;
|
||||
}
|
||||
// Case 2: Multiple branches encoded in jump table
|
||||
if (jumpOffset) {
|
||||
const value = char - jumpOffset;
|
||||
return value < 0 || value >= branchCount
|
||||
? -1
|
||||
: decodeTree[nodeIndex + value] - 1;
|
||||
}
|
||||
// Case 3: Multiple branches encoded in packed dictionary (two keys per uint16)
|
||||
const packedKeySlots = (branchCount + 1) >> 1;
|
||||
/*
|
||||
* Treat packed keys as a virtual sorted array of length `branchCount`.
|
||||
* Key(i) = low byte for even i, high byte for odd i in slot i>>1.
|
||||
*/
|
||||
let lo = 0;
|
||||
let hi = branchCount - 1;
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >>> 1;
|
||||
const slot = mid >> 1;
|
||||
const packed = decodeTree[nodeIndex + slot];
|
||||
const midKey = (packed >> ((mid & 1) * 8)) & 0xff;
|
||||
if (midKey < char) {
|
||||
lo = mid + 1;
|
||||
}
|
||||
else if (midKey > char) {
|
||||
hi = mid - 1;
|
||||
}
|
||||
else {
|
||||
return decodeTree[nodeIndex + packedKeySlots + mid];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
const htmlDecoder = /* #__PURE__ */ getDecoder(decode_data_html_js_1.htmlDecodeTree);
|
||||
const xmlDecoder = /* #__PURE__ */ getDecoder(decode_data_xml_js_1.xmlDecodeTree);
|
||||
/**
|
||||
* Decodes an HTML string.
|
||||
*
|
||||
* @param htmlString The string to decode.
|
||||
* @param mode The decoding mode.
|
||||
* @returns The decoded string.
|
||||
*/
|
||||
function decodeHTML(htmlString, mode = DecodingMode.Legacy) {
|
||||
return htmlDecoder(htmlString, mode);
|
||||
}
|
||||
/**
|
||||
* Decodes an HTML string in an attribute.
|
||||
*
|
||||
* @param htmlAttribute The string to decode.
|
||||
* @returns The decoded string.
|
||||
*/
|
||||
function decodeHTMLAttribute(htmlAttribute) {
|
||||
return htmlDecoder(htmlAttribute, DecodingMode.Attribute);
|
||||
}
|
||||
/**
|
||||
* Decodes an HTML string, requiring all entities to be terminated by a semicolon.
|
||||
*
|
||||
* @param htmlString The string to decode.
|
||||
* @returns The decoded string.
|
||||
*/
|
||||
function decodeHTMLStrict(htmlString) {
|
||||
return htmlDecoder(htmlString, DecodingMode.Strict);
|
||||
}
|
||||
/**
|
||||
* Decodes an XML string, requiring all entities to be terminated by a semicolon.
|
||||
*
|
||||
* @param xmlString The string to decode.
|
||||
* @returns The decoded string.
|
||||
*/
|
||||
function decodeXML(xmlString) {
|
||||
return xmlDecoder(xmlString, DecodingMode.Strict);
|
||||
}
|
||||
var decode_codepoint_js_2 = require("./decode-codepoint.js");
|
||||
Object.defineProperty(exports, "decodeCodePoint", { enumerable: true, get: function () { return decode_codepoint_js_2.decodeCodePoint; } });
|
||||
Object.defineProperty(exports, "fromCodePoint", { enumerable: true, get: function () { return decode_codepoint_js_2.fromCodePoint; } });
|
||||
Object.defineProperty(exports, "replaceCodePoint", { enumerable: true, get: function () { return decode_codepoint_js_2.replaceCodePoint; } });
|
||||
// Re-export for use by eg. htmlparser2
|
||||
var decode_data_html_js_2 = require("./generated/decode-data-html.js");
|
||||
Object.defineProperty(exports, "htmlDecodeTree", { enumerable: true, get: function () { return decode_data_html_js_2.htmlDecodeTree; } });
|
||||
var decode_data_xml_js_2 = require("./generated/decode-data-xml.js");
|
||||
Object.defineProperty(exports, "xmlDecodeTree", { enumerable: true, get: function () { return decode_data_xml_js_2.xmlDecodeTree; } });
|
||||
//# sourceMappingURL=decode.js.map
|
||||
Generated
Vendored
+7
File diff suppressed because one or more lines are too long
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
// Generated using scripts/write-decode-map.ts
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.xmlDecodeTree = void 0;
|
||||
const decode_shared_js_1 = require("../internal/decode-shared.js");
|
||||
exports.xmlDecodeTree = (0, decode_shared_js_1.decodeBase64)("AAJhZ2xxBwARABMAFQBtAg0AAAAAAA8AcAAmYG8AcwAnYHQAPmB0ADxg9SFvdCJg");
|
||||
//# sourceMappingURL=decode-data-xml.js.map
|
||||
Generated
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BinTrieFlags = void 0;
|
||||
/**
|
||||
* Bit flags & masks for the binary trie encoding used for entity decoding.
|
||||
*
|
||||
* Bit layout (16 bits total):
|
||||
* 15..14 VALUE_LENGTH (+1 encoding; 0 => no value)
|
||||
* 13 FLAG13. If valueLength>0: semicolon required flag (implicit ';').
|
||||
* If valueLength==0: compact run flag.
|
||||
* 12..7 BRANCH_LENGTH Branch length (0 => single branch in 6..0 if jumpOffset==char) OR run length (when compact run)
|
||||
* 6..0 JUMP_TABLE Jump offset (jump table) OR single-branch char code OR first run char
|
||||
*/
|
||||
var BinTrieFlags;
|
||||
(function (BinTrieFlags) {
|
||||
BinTrieFlags[BinTrieFlags["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH";
|
||||
BinTrieFlags[BinTrieFlags["FLAG13"] = 8192] = "FLAG13";
|
||||
BinTrieFlags[BinTrieFlags["BRANCH_LENGTH"] = 8064] = "BRANCH_LENGTH";
|
||||
BinTrieFlags[BinTrieFlags["JUMP_TABLE"] = 127] = "JUMP_TABLE";
|
||||
})(BinTrieFlags || (exports.BinTrieFlags = BinTrieFlags = {}));
|
||||
//# sourceMappingURL=bin-trie-flags.js.map
|
||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.decodeBase64 = decodeBase64;
|
||||
/*
|
||||
* Shared base64 decode helper for generated decode data.
|
||||
* Assumes global atob is available.
|
||||
*/
|
||||
function decodeBase64(input) {
|
||||
const binary =
|
||||
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
||||
typeof atob === "function"
|
||||
? // Browser (and Node >=16)
|
||||
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
||||
atob(input)
|
||||
: // Older Node versions (<16)
|
||||
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
||||
typeof Buffer.from === "function"
|
||||
? // eslint-disable-next-line n/no-unsupported-features/node-builtins
|
||||
Buffer.from(input, "base64").toString("binary")
|
||||
: // eslint-disable-next-line unicorn/no-new-buffer, n/no-deprecated-api
|
||||
new Buffer(input, "base64").toString("binary");
|
||||
const evenLength = binary.length & ~1; // Round down to even length
|
||||
const out = new Uint16Array(evenLength / 2);
|
||||
for (let index = 0, outIndex = 0; index < evenLength; index += 2) {
|
||||
const lo = binary.charCodeAt(index);
|
||||
const hi = binary.charCodeAt(index + 1);
|
||||
out[outIndex++] = lo | (hi << 8);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
//# sourceMappingURL=decode-shared.js.map
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"name": "entities",
|
||||
"version": "7.0.1",
|
||||
"description": "Encode & decode XML and HTML entities with ease & speed",
|
||||
"keywords": [
|
||||
"html entities",
|
||||
"entity decoder",
|
||||
"entity encoding",
|
||||
"html decoding",
|
||||
"html encoding",
|
||||
"xml decoding",
|
||||
"xml encoding"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fb55/entities.git"
|
||||
},
|
||||
"funding": "https://github.com/fb55/entities?sponsor=1",
|
||||
"license": "BSD-2-Clause",
|
||||
"author": "Felix Boehm <me@feedic.com>",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/esm/index.d.ts",
|
||||
"default": "./dist/esm/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"default": "./dist/commonjs/index.js"
|
||||
}
|
||||
},
|
||||
"./decode": {
|
||||
"import": {
|
||||
"types": "./dist/esm/decode.d.ts",
|
||||
"default": "./dist/esm/decode.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/commonjs/decode.d.ts",
|
||||
"default": "./dist/commonjs/decode.js"
|
||||
}
|
||||
},
|
||||
"./escape": {
|
||||
"import": {
|
||||
"types": "./dist/esm/escape.d.ts",
|
||||
"default": "./dist/esm/escape.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/commonjs/escape.d.ts",
|
||||
"default": "./dist/commonjs/escape.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/commonjs/index.js",
|
||||
"module": "./dist/esm/index.js",
|
||||
"types": "./dist/commonjs/index.d.ts",
|
||||
"files": [
|
||||
"decode.js",
|
||||
"decode.d.ts",
|
||||
"escape.js",
|
||||
"escape.d.ts",
|
||||
"dist",
|
||||
"src",
|
||||
"!**/*.spec.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"build:docs": "typedoc --hideGenerator src/index.ts",
|
||||
"build:encode-trie": "node --import=tsx scripts/write-encode-map.ts",
|
||||
"build:trie": "node --import=tsx scripts/write-decode-map.ts",
|
||||
"benchmark": "node --import=tsx scripts/benchmark.ts",
|
||||
"format": "npm run format:es && npm run format:biome",
|
||||
"format:es": "npm run lint:es -- --fix",
|
||||
"format:biome": "biome check --fix .",
|
||||
"lint": "npm run lint:es && npm run lint:ts && npm run lint:biome",
|
||||
"lint:es": "eslint . --ignore-path .gitignore",
|
||||
"lint:biome": "biome check .",
|
||||
"lint:ts": "tsc --noEmit",
|
||||
"prepublishOnly": "tshy",
|
||||
"test": "npm run test:vi && npm run lint",
|
||||
"test:vi": "vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.3.11",
|
||||
"@types/node": "^25.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^8.53.1",
|
||||
"@typescript-eslint/parser": "^8.53.1",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@types/he": "^1.2.3",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-biome": "^2.1.3",
|
||||
"eslint-plugin-n": "^17.23.2",
|
||||
"eslint-plugin-unicorn": "^56.0.1",
|
||||
"he": "^1.2.0",
|
||||
"html-entities": "^2.6.0",
|
||||
"parse-entities": "^4.0.2",
|
||||
"tinybench": "^5.1.0",
|
||||
"tshy": "^3.1.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typedoc": "^0.28.16",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"tshy": {
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/__fixtures__/*",
|
||||
"**/__tests__/*",
|
||||
"**/__snapshots__/*"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./decode": "./src/decode.ts",
|
||||
"./escape": "./src/escape.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = global || self, factory(global.estreeWalker = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
// @ts-check
|
||||
/** @typedef { import('estree').BaseNode} BaseNode */
|
||||
|
||||
/** @typedef {{
|
||||
skip: () => void;
|
||||
remove: () => void;
|
||||
replace: (node: BaseNode) => void;
|
||||
}} WalkerContext */
|
||||
|
||||
class WalkerBase {
|
||||
constructor() {
|
||||
/** @type {boolean} */
|
||||
this.should_skip = false;
|
||||
|
||||
/** @type {boolean} */
|
||||
this.should_remove = false;
|
||||
|
||||
/** @type {BaseNode | null} */
|
||||
this.replacement = null;
|
||||
|
||||
/** @type {WalkerContext} */
|
||||
this.context = {
|
||||
skip: () => (this.should_skip = true),
|
||||
remove: () => (this.should_remove = true),
|
||||
replace: (node) => (this.replacement = node)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} parent
|
||||
* @param {string} prop
|
||||
* @param {number} index
|
||||
* @param {BaseNode} node
|
||||
*/
|
||||
replace(parent, prop, index, node) {
|
||||
if (parent) {
|
||||
if (index !== null) {
|
||||
parent[prop][index] = node;
|
||||
} else {
|
||||
parent[prop] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} parent
|
||||
* @param {string} prop
|
||||
* @param {number} index
|
||||
*/
|
||||
remove(parent, prop, index) {
|
||||
if (parent) {
|
||||
if (index !== null) {
|
||||
parent[prop].splice(index, 1);
|
||||
} else {
|
||||
delete parent[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @typedef { import('estree').BaseNode} BaseNode */
|
||||
/** @typedef { import('./walker.js').WalkerContext} WalkerContext */
|
||||
|
||||
/** @typedef {(
|
||||
* this: WalkerContext,
|
||||
* node: BaseNode,
|
||||
* parent: BaseNode,
|
||||
* key: string,
|
||||
* index: number
|
||||
* ) => void} SyncHandler */
|
||||
|
||||
class SyncWalker extends WalkerBase {
|
||||
/**
|
||||
*
|
||||
* @param {SyncHandler} enter
|
||||
* @param {SyncHandler} leave
|
||||
*/
|
||||
constructor(enter, leave) {
|
||||
super();
|
||||
|
||||
/** @type {SyncHandler} */
|
||||
this.enter = enter;
|
||||
|
||||
/** @type {SyncHandler} */
|
||||
this.leave = leave;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BaseNode} node
|
||||
* @param {BaseNode} parent
|
||||
* @param {string} [prop]
|
||||
* @param {number} [index]
|
||||
* @returns {BaseNode}
|
||||
*/
|
||||
visit(node, parent, prop, index) {
|
||||
if (node) {
|
||||
if (this.enter) {
|
||||
const _should_skip = this.should_skip;
|
||||
const _should_remove = this.should_remove;
|
||||
const _replacement = this.replacement;
|
||||
this.should_skip = false;
|
||||
this.should_remove = false;
|
||||
this.replacement = null;
|
||||
|
||||
this.enter.call(this.context, node, parent, prop, index);
|
||||
|
||||
if (this.replacement) {
|
||||
node = this.replacement;
|
||||
this.replace(parent, prop, index, node);
|
||||
}
|
||||
|
||||
if (this.should_remove) {
|
||||
this.remove(parent, prop, index);
|
||||
}
|
||||
|
||||
const skipped = this.should_skip;
|
||||
const removed = this.should_remove;
|
||||
|
||||
this.should_skip = _should_skip;
|
||||
this.should_remove = _should_remove;
|
||||
this.replacement = _replacement;
|
||||
|
||||
if (skipped) return node;
|
||||
if (removed) return null;
|
||||
}
|
||||
|
||||
for (const key in node) {
|
||||
const value = node[key];
|
||||
|
||||
if (typeof value !== "object") {
|
||||
continue;
|
||||
} else if (Array.isArray(value)) {
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
if (value[i] !== null && typeof value[i].type === 'string') {
|
||||
if (!this.visit(value[i], node, key, i)) {
|
||||
// removed
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value !== null && typeof value.type === "string") {
|
||||
this.visit(value, node, key, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.leave) {
|
||||
const _replacement = this.replacement;
|
||||
const _should_remove = this.should_remove;
|
||||
this.replacement = null;
|
||||
this.should_remove = false;
|
||||
|
||||
this.leave.call(this.context, node, parent, prop, index);
|
||||
|
||||
if (this.replacement) {
|
||||
node = this.replacement;
|
||||
this.replace(parent, prop, index, node);
|
||||
}
|
||||
|
||||
if (this.should_remove) {
|
||||
this.remove(parent, prop, index);
|
||||
}
|
||||
|
||||
const removed = this.should_remove;
|
||||
|
||||
this.replacement = _replacement;
|
||||
this.should_remove = _should_remove;
|
||||
|
||||
if (removed) return null;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @typedef { import('estree').BaseNode} BaseNode */
|
||||
/** @typedef { import('./walker').WalkerContext} WalkerContext */
|
||||
|
||||
/** @typedef {(
|
||||
* this: WalkerContext,
|
||||
* node: BaseNode,
|
||||
* parent: BaseNode,
|
||||
* key: string,
|
||||
* index: number
|
||||
* ) => Promise<void>} AsyncHandler */
|
||||
|
||||
class AsyncWalker extends WalkerBase {
|
||||
/**
|
||||
*
|
||||
* @param {AsyncHandler} enter
|
||||
* @param {AsyncHandler} leave
|
||||
*/
|
||||
constructor(enter, leave) {
|
||||
super();
|
||||
|
||||
/** @type {AsyncHandler} */
|
||||
this.enter = enter;
|
||||
|
||||
/** @type {AsyncHandler} */
|
||||
this.leave = leave;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BaseNode} node
|
||||
* @param {BaseNode} parent
|
||||
* @param {string} [prop]
|
||||
* @param {number} [index]
|
||||
* @returns {Promise<BaseNode>}
|
||||
*/
|
||||
async visit(node, parent, prop, index) {
|
||||
if (node) {
|
||||
if (this.enter) {
|
||||
const _should_skip = this.should_skip;
|
||||
const _should_remove = this.should_remove;
|
||||
const _replacement = this.replacement;
|
||||
this.should_skip = false;
|
||||
this.should_remove = false;
|
||||
this.replacement = null;
|
||||
|
||||
await this.enter.call(this.context, node, parent, prop, index);
|
||||
|
||||
if (this.replacement) {
|
||||
node = this.replacement;
|
||||
this.replace(parent, prop, index, node);
|
||||
}
|
||||
|
||||
if (this.should_remove) {
|
||||
this.remove(parent, prop, index);
|
||||
}
|
||||
|
||||
const skipped = this.should_skip;
|
||||
const removed = this.should_remove;
|
||||
|
||||
this.should_skip = _should_skip;
|
||||
this.should_remove = _should_remove;
|
||||
this.replacement = _replacement;
|
||||
|
||||
if (skipped) return node;
|
||||
if (removed) return null;
|
||||
}
|
||||
|
||||
for (const key in node) {
|
||||
const value = node[key];
|
||||
|
||||
if (typeof value !== "object") {
|
||||
continue;
|
||||
} else if (Array.isArray(value)) {
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
if (value[i] !== null && typeof value[i].type === 'string') {
|
||||
if (!(await this.visit(value[i], node, key, i))) {
|
||||
// removed
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value !== null && typeof value.type === "string") {
|
||||
await this.visit(value, node, key, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.leave) {
|
||||
const _replacement = this.replacement;
|
||||
const _should_remove = this.should_remove;
|
||||
this.replacement = null;
|
||||
this.should_remove = false;
|
||||
|
||||
await this.leave.call(this.context, node, parent, prop, index);
|
||||
|
||||
if (this.replacement) {
|
||||
node = this.replacement;
|
||||
this.replace(parent, prop, index, node);
|
||||
}
|
||||
|
||||
if (this.should_remove) {
|
||||
this.remove(parent, prop, index);
|
||||
}
|
||||
|
||||
const removed = this.should_remove;
|
||||
|
||||
this.replacement = _replacement;
|
||||
this.should_remove = _should_remove;
|
||||
|
||||
if (removed) return null;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @typedef { import('estree').BaseNode} BaseNode */
|
||||
/** @typedef { import('./sync.js').SyncHandler} SyncHandler */
|
||||
/** @typedef { import('./async.js').AsyncHandler} AsyncHandler */
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BaseNode} ast
|
||||
* @param {{
|
||||
* enter?: SyncHandler
|
||||
* leave?: SyncHandler
|
||||
* }} walker
|
||||
* @returns {BaseNode}
|
||||
*/
|
||||
function walk(ast, { enter, leave }) {
|
||||
const instance = new SyncWalker(enter, leave);
|
||||
return instance.visit(ast, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BaseNode} ast
|
||||
* @param {{
|
||||
* enter?: AsyncHandler
|
||||
* leave?: AsyncHandler
|
||||
* }} walker
|
||||
* @returns {Promise<BaseNode>}
|
||||
*/
|
||||
async function asyncWalk(ast, { enter, leave }) {
|
||||
const instance = new AsyncWalker(enter, leave);
|
||||
return await instance.visit(ast, null);
|
||||
}
|
||||
|
||||
exports.asyncWalk = asyncWalk;
|
||||
exports.walk = walk;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "estree-walker",
|
||||
"description": "Traverse an ESTree-compliant AST",
|
||||
"version": "2.0.2",
|
||||
"private": false,
|
||||
"author": "Rich Harris",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Rich-Harris/estree-walker"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"main": "./dist/umd/estree-walker.js",
|
||||
"module": "./dist/esm/estree-walker.js",
|
||||
"exports": {
|
||||
"require": "./dist/umd/estree-walker.js",
|
||||
"import": "./dist/esm/estree-walker.js"
|
||||
},
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"prepublishOnly": "npm run build && npm test",
|
||||
"build": "tsc && rollup -c",
|
||||
"test": "uvu test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/estree": "0.0.42",
|
||||
"rollup": "^2.10.9",
|
||||
"typescript": "^3.7.5",
|
||||
"uvu": "^0.5.1"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"types",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
Generated
Vendored
+67
@@ -0,0 +1,67 @@
|
||||
import { removeEffect, addEffect, GroupAnimationWithThen } from 'motion-dom';
|
||||
import { removeItem } from 'motion-utils';
|
||||
import { animateSequence } from './sequence.mjs';
|
||||
import { animateSubject } from './subject.mjs';
|
||||
|
||||
function isSequence(value) {
|
||||
return Array.isArray(value) && value.some(Array.isArray);
|
||||
}
|
||||
/**
|
||||
* Creates an animation function that is optionally scoped
|
||||
* to a specific element.
|
||||
*/
|
||||
function createScopedAnimate(options = {}) {
|
||||
const { scope, reduceMotion, skipAnimations } = options;
|
||||
/**
|
||||
* Implementation
|
||||
*/
|
||||
function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) {
|
||||
let animations = [];
|
||||
let animationOnComplete;
|
||||
const inherited = {};
|
||||
if (reduceMotion !== undefined)
|
||||
inherited.reduceMotion = reduceMotion;
|
||||
if (skipAnimations !== undefined)
|
||||
inherited.skipAnimations = skipAnimations;
|
||||
if (isSequence(subjectOrSequence)) {
|
||||
const { onComplete, ...sequenceOptions } = optionsOrKeyframes || {};
|
||||
if (typeof onComplete === "function") {
|
||||
animationOnComplete = onComplete;
|
||||
}
|
||||
animations = animateSequence(subjectOrSequence, { ...inherited, ...sequenceOptions }, scope);
|
||||
}
|
||||
else {
|
||||
// Extract top-level onComplete so it doesn't get applied per-value
|
||||
const { onComplete, ...rest } = options || {};
|
||||
if (typeof onComplete === "function") {
|
||||
animationOnComplete = onComplete;
|
||||
}
|
||||
animations = animateSubject(subjectOrSequence, optionsOrKeyframes, { ...inherited, ...rest }, scope);
|
||||
}
|
||||
const animation = new GroupAnimationWithThen(animations);
|
||||
if (animationOnComplete) {
|
||||
animation.finished.then(animationOnComplete);
|
||||
}
|
||||
if (scope) {
|
||||
scope.animations.push(animation);
|
||||
animation.finished.then(() => {
|
||||
removeItem(scope.animations, animation);
|
||||
});
|
||||
}
|
||||
return animation;
|
||||
}
|
||||
return scopedAnimate;
|
||||
}
|
||||
const animate = Object.assign(createScopedAnimate(), {
|
||||
/**
|
||||
* Register an effect so `animate()` can animate the subjects it
|
||||
* claims, for instance `animate.addEffect(threeEffect)`. The most
|
||||
* recently added effect is tested first. DOM elements are always
|
||||
* animated directly.
|
||||
*/
|
||||
addEffect,
|
||||
removeEffect,
|
||||
});
|
||||
|
||||
export { animate, createScopedAnimate };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
import { resolveElements } from 'motion-dom';
|
||||
import { isDOMKeyframes } from '../utils/is-dom-keyframes.mjs';
|
||||
|
||||
function resolveSubjects(subject, keyframes, scope, selectorCache) {
|
||||
if (subject == null) {
|
||||
return [];
|
||||
}
|
||||
if (typeof subject === "string" && isDOMKeyframes(keyframes)) {
|
||||
return resolveElements(subject, scope, selectorCache);
|
||||
}
|
||||
else if (subject instanceof NodeList) {
|
||||
return Array.from(subject);
|
||||
}
|
||||
else if (Array.isArray(subject)) {
|
||||
return subject.filter((s) => s != null);
|
||||
}
|
||||
else {
|
||||
return [subject];
|
||||
}
|
||||
}
|
||||
|
||||
export { resolveSubjects };
|
||||
//# sourceMappingURL=resolve-subjects.mjs.map
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
import { motionValue, spring } from 'motion-dom';
|
||||
import { createAnimationsFromSequence } from '../sequence/create.mjs';
|
||||
import { animateSubject } from './subject.mjs';
|
||||
|
||||
function animateSequence(sequence, options, scope) {
|
||||
const animations = [];
|
||||
/**
|
||||
* Pre-process: replace function segments with MotionValue segments,
|
||||
* subscribe callbacks immediately
|
||||
*/
|
||||
const processedSequence = sequence.map((segment) => {
|
||||
if (Array.isArray(segment) && typeof segment[0] === "function") {
|
||||
const callback = segment[0];
|
||||
const mv = motionValue(0);
|
||||
mv.on("change", callback);
|
||||
if (segment.length === 1) {
|
||||
return [mv, [0, 1]];
|
||||
}
|
||||
else if (segment.length === 2) {
|
||||
return [mv, [0, 1], segment[1]];
|
||||
}
|
||||
else {
|
||||
return [mv, segment[1], segment[2]];
|
||||
}
|
||||
}
|
||||
return segment;
|
||||
});
|
||||
const animationDefinitions = createAnimationsFromSequence(processedSequence, options, scope, { spring });
|
||||
animationDefinitions.forEach(({ keyframes, transition }, subject) => {
|
||||
animations.push(...animateSubject(subject, keyframes, transition));
|
||||
});
|
||||
return animations;
|
||||
}
|
||||
|
||||
export { animateSequence };
|
||||
//# sourceMappingURL=sequence.mjs.map
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
import { animateSingleValue, animateElement, visualElementStore, animateEffectSubject, propEffect, findEffect, isMotionValue } from 'motion-dom';
|
||||
import { invariant } from 'motion-utils';
|
||||
import { isDOMKeyframes } from '../utils/is-dom-keyframes.mjs';
|
||||
import { resolveSubjects } from './resolve-subjects.mjs';
|
||||
|
||||
function isSingleValue(subject, keyframes) {
|
||||
return (isMotionValue(subject) ||
|
||||
typeof subject === "number" ||
|
||||
(typeof subject === "string" && !isDOMKeyframes(keyframes)));
|
||||
}
|
||||
/**
|
||||
* Implementation
|
||||
*/
|
||||
function animateSubject(subject, keyframes, options, scope) {
|
||||
const animations = [];
|
||||
if (isSingleValue(subject, keyframes)) {
|
||||
animations.push(animateSingleValue(subject, isDOMKeyframes(keyframes)
|
||||
? keyframes.default || keyframes
|
||||
: keyframes, options ? options.default || options : options));
|
||||
}
|
||||
else {
|
||||
// Gracefully handle null/undefined subjects (e.g., from querySelector returning null)
|
||||
if (subject == null) {
|
||||
return animations;
|
||||
}
|
||||
const subjects = resolveSubjects(subject, keyframes, scope);
|
||||
const numSubjects = subjects.length;
|
||||
invariant(Boolean(numSubjects), "No valid elements provided.", "no-valid-elements");
|
||||
for (let i = 0; i < numSubjects; i++) {
|
||||
const thisSubject = subjects[i];
|
||||
const transition = { ...options };
|
||||
/**
|
||||
* Resolve stagger function if provided.
|
||||
*/
|
||||
if ("delay" in transition &&
|
||||
typeof transition.delay === "function") {
|
||||
transition.delay = transition.delay(i, numSubjects);
|
||||
}
|
||||
if (thisSubject instanceof Element) {
|
||||
/**
|
||||
* An element already owned by a VisualElement (a <motion.*>
|
||||
* component or animateLayout()) animates its values so they
|
||||
* share one renderer. Anything else is driven through
|
||||
* styleEffect (or svgEffect) and the DOM keyframe resolver,
|
||||
* without creating a VisualElement.
|
||||
*/
|
||||
animations.push(...animateElement(thisSubject, keyframes, transition, visualElementStore.get(thisSubject)));
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Registered effects (animate.addEffect) claim non-DOM
|
||||
* subjects before we fall back to writing plain properties.
|
||||
*/
|
||||
animations.push(...animateEffectSubject(findEffect(thisSubject) ?? propEffect, thisSubject, keyframes, transition));
|
||||
}
|
||||
}
|
||||
}
|
||||
return animations;
|
||||
}
|
||||
|
||||
export { animateSubject };
|
||||
//# sourceMappingURL=subject.mjs.map
|
||||
Generated
Vendored
+110
@@ -0,0 +1,110 @@
|
||||
import { resolveElements, getValueTransition, getAnimationMap, animationMapKey, getComputedStyle, fillWildcards, applyPxDefaults, NativeAnimation } from 'motion-dom';
|
||||
import { invariant, secondsToMilliseconds } from 'motion-utils';
|
||||
|
||||
function animateElements(elementOrSelector, keyframes, options, scope) {
|
||||
// Gracefully handle null/undefined elements (e.g., from querySelector returning null)
|
||||
if (elementOrSelector == null) {
|
||||
return [];
|
||||
}
|
||||
const elements = resolveElements(elementOrSelector, scope);
|
||||
const numElements = elements.length;
|
||||
invariant(Boolean(numElements), "No valid elements provided.", "no-valid-elements");
|
||||
/**
|
||||
* WAAPI doesn't support interrupting animations.
|
||||
*
|
||||
* Therefore, starting animations requires a three-step process:
|
||||
* 1. Stop existing animations (write styles to DOM)
|
||||
* 2. Resolve keyframes (read styles from DOM)
|
||||
* 3. Create new animations (write styles to DOM)
|
||||
*
|
||||
* The hybrid `animate()` function uses AsyncAnimation to resolve
|
||||
* keyframes before creating new animations, which removes style
|
||||
* thrashing. Here, we have much stricter filesize constraints.
|
||||
* Therefore we do this in a synchronous way that ensures that
|
||||
* at least within `animate()` calls there is no style thrashing.
|
||||
*
|
||||
* In the motion-native-animate-mini-interrupt benchmark this
|
||||
* was 80% faster than a single loop.
|
||||
*/
|
||||
const animationDefinitions = [];
|
||||
/**
|
||||
* Step 1: Build options and stop existing animations (write)
|
||||
*/
|
||||
for (let i = 0; i < numElements; i++) {
|
||||
const element = elements[i];
|
||||
const elementTransition = { ...options };
|
||||
/**
|
||||
* Resolve stagger function if provided.
|
||||
*/
|
||||
if (typeof elementTransition.delay === "function") {
|
||||
elementTransition.delay = elementTransition.delay(i, numElements);
|
||||
}
|
||||
for (const valueName in keyframes) {
|
||||
let valueKeyframes = keyframes[valueName];
|
||||
if (!Array.isArray(valueKeyframes)) {
|
||||
valueKeyframes = [valueKeyframes];
|
||||
}
|
||||
const valueOptions = {
|
||||
...getValueTransition(elementTransition, valueName),
|
||||
};
|
||||
valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration));
|
||||
valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay));
|
||||
/**
|
||||
* If there's an existing animation playing on this element then stop it
|
||||
* before creating a new one.
|
||||
*/
|
||||
const map = getAnimationMap(element);
|
||||
const key = animationMapKey(valueName, valueOptions.pseudoElement || "");
|
||||
const currentAnimation = map.get(key);
|
||||
currentAnimation && currentAnimation.stop();
|
||||
animationDefinitions.push({
|
||||
map,
|
||||
key,
|
||||
unresolvedKeyframes: valueKeyframes,
|
||||
options: {
|
||||
...valueOptions,
|
||||
element,
|
||||
name: valueName,
|
||||
allowFlatten: !elementTransition.type && !elementTransition.ease,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Step 2: Resolve keyframes (read)
|
||||
*/
|
||||
for (let i = 0; i < animationDefinitions.length; i++) {
|
||||
const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i];
|
||||
const { element, name, pseudoElement } = animationOptions;
|
||||
if (!pseudoElement && unresolvedKeyframes[0] === null) {
|
||||
unresolvedKeyframes[0] = getComputedStyle(element, name);
|
||||
}
|
||||
fillWildcards(unresolvedKeyframes);
|
||||
applyPxDefaults(unresolvedKeyframes, name);
|
||||
/**
|
||||
* If we only have one keyframe, explicitly read the initial keyframe
|
||||
* from the computed style. This is to ensure consistency with WAAPI behaviour
|
||||
* for restarting animations, for instance .play() after finish, when it
|
||||
* has one vs two keyframes.
|
||||
*/
|
||||
if (!pseudoElement && unresolvedKeyframes.length < 2) {
|
||||
unresolvedKeyframes.unshift(getComputedStyle(element, name));
|
||||
}
|
||||
animationOptions.keyframes = unresolvedKeyframes;
|
||||
}
|
||||
/**
|
||||
* Step 3: Create new animations (write)
|
||||
*/
|
||||
const animations = [];
|
||||
for (let i = 0; i < animationDefinitions.length; i++) {
|
||||
const { map, key, options: animationOptions } = animationDefinitions[i];
|
||||
const animation = new NativeAnimation(animationOptions);
|
||||
map.set(key, animation);
|
||||
animation.finished.finally(() => map.delete(key));
|
||||
animations.push(animation);
|
||||
}
|
||||
return animations;
|
||||
}
|
||||
|
||||
export { animateElements };
|
||||
//# sourceMappingURL=animate-elements.mjs.map
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
import { GroupAnimationWithThen } from 'motion-dom';
|
||||
import { animateElements } from './animate-elements.mjs';
|
||||
|
||||
const createScopedWaapiAnimate = (scope) => {
|
||||
function scopedAnimate(elementOrSelector, keyframes, options) {
|
||||
return new GroupAnimationWithThen(animateElements(elementOrSelector, keyframes, options, scope));
|
||||
}
|
||||
return scopedAnimate;
|
||||
};
|
||||
const animateMini = /*@__PURE__*/ createScopedWaapiAnimate();
|
||||
|
||||
export { animateMini, createScopedWaapiAnimate };
|
||||
//# sourceMappingURL=animate-style.mjs.map
|
||||
Generated
Vendored
+308
@@ -0,0 +1,308 @@
|
||||
import { isMotionValue, defaultOffset, isGenerator, createGeneratorEasing, fillOffset } from 'motion-dom';
|
||||
import { progress, secondsToMilliseconds, warning, reverseEasing, getEasingForSegment } from 'motion-utils';
|
||||
import { resolveSubjects } from '../animate/resolve-subjects.mjs';
|
||||
import { calculateRepeatDuration } from './utils/calc-repeat-duration.mjs';
|
||||
import { calcNextTime } from './utils/calc-time.mjs';
|
||||
import { addKeyframes } from './utils/edit.mjs';
|
||||
import { normalizeTimes } from './utils/normalize-times.mjs';
|
||||
import { compareByTime } from './utils/sort.mjs';
|
||||
|
||||
const defaultSegmentEasing = "easeInOut";
|
||||
const MAX_REPEAT = 20;
|
||||
function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
|
||||
const defaultDuration = defaultTransition.duration || 0.3;
|
||||
const animationDefinitions = new Map();
|
||||
const sequences = new Map();
|
||||
const elementCache = {};
|
||||
const timeLabels = new Map();
|
||||
let prevTime = 0;
|
||||
let currentTime = 0;
|
||||
let totalDuration = 0;
|
||||
/**
|
||||
* Build the timeline by mapping over the sequence array and converting
|
||||
* the definitions into keyframes and offsets with absolute time values.
|
||||
* These will later get converted into relative offsets in a second pass.
|
||||
*/
|
||||
for (let i = 0; i < sequence.length; i++) {
|
||||
const segment = sequence[i];
|
||||
/**
|
||||
* If this is a timeline label, mark it and skip the rest of this iteration.
|
||||
*/
|
||||
if (typeof segment === "string") {
|
||||
timeLabels.set(segment, currentTime);
|
||||
continue;
|
||||
}
|
||||
else if (!Array.isArray(segment)) {
|
||||
timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
|
||||
continue;
|
||||
}
|
||||
let [subject, keyframes, transition = {}] = segment;
|
||||
/**
|
||||
* If a relative or absolute time value has been specified we need to resolve
|
||||
* it in relation to the currentTime.
|
||||
*/
|
||||
if (transition.at !== undefined) {
|
||||
currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
|
||||
}
|
||||
/**
|
||||
* Keep track of the maximum duration in this definition. This will be
|
||||
* applied to currentTime once the definition has been parsed.
|
||||
*/
|
||||
let maxDuration = 0;
|
||||
const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
|
||||
const valueKeyframesAsList = keyframesAsList(valueKeyframes);
|
||||
const { delay = 0, times = defaultOffset(valueKeyframesAsList), type = defaultTransition.type || "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
|
||||
let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
|
||||
/**
|
||||
* Resolve stagger() if defined.
|
||||
*/
|
||||
const calculatedDelay = typeof delay === "function"
|
||||
? delay(elementIndex, numSubjects)
|
||||
: delay;
|
||||
/**
|
||||
* If this animation should and can use a spring, generate a spring easing function.
|
||||
*/
|
||||
const numKeyframes = valueKeyframesAsList.length;
|
||||
const createGenerator = isGenerator(type)
|
||||
? type
|
||||
: generators?.[type || "keyframes"];
|
||||
if (numKeyframes <= 2 && createGenerator) {
|
||||
/**
|
||||
* As we're creating an easing function from a spring,
|
||||
* ideally we want to generate it using the real distance
|
||||
* between the two keyframes. However this isn't always
|
||||
* possible - in these situations we use 0-100.
|
||||
*/
|
||||
let absoluteDelta = 100;
|
||||
if (numKeyframes === 2 &&
|
||||
isNumberKeyframesArray(valueKeyframesAsList)) {
|
||||
const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
|
||||
absoluteDelta = Math.abs(delta);
|
||||
}
|
||||
const springTransition = {
|
||||
...defaultTransition,
|
||||
...remainingTransition,
|
||||
};
|
||||
if (duration !== undefined) {
|
||||
springTransition.duration = secondsToMilliseconds(duration);
|
||||
}
|
||||
const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
|
||||
ease = springEasing.ease;
|
||||
duration = springEasing.duration;
|
||||
}
|
||||
duration ?? (duration = defaultDuration);
|
||||
const startTime = currentTime + calculatedDelay;
|
||||
/**
|
||||
* If there's only one time offset of 0, fill in a second with length 1
|
||||
*/
|
||||
if (times.length === 1 && times[0] === 0) {
|
||||
times[1] = 1;
|
||||
}
|
||||
/**
|
||||
* Fill out if offset if fewer offsets than keyframes
|
||||
*/
|
||||
const remainder = times.length - valueKeyframesAsList.length;
|
||||
remainder > 0 && fillOffset(times, remainder);
|
||||
/**
|
||||
* If only one value has been set, ie [1], push a null to the start of
|
||||
* the keyframe array. This will let us mark a keyframe at this point
|
||||
* that will later be hydrated with the previous value.
|
||||
*/
|
||||
valueKeyframesAsList.length === 1 &&
|
||||
valueKeyframesAsList.unshift(null);
|
||||
/**
|
||||
* Segments can't express `repeat: Infinity` or very large
|
||||
* counts — they'd leave dead time after the segment or
|
||||
* explode the keyframe array. Ignore with a warning.
|
||||
*/
|
||||
if (repeat) {
|
||||
warning(repeat < MAX_REPEAT, `Sequence segments can't repeat ${repeat} times — ignoring repeat option. Use a value below ${MAX_REPEAT} or apply repeat at the sequence level instead.`);
|
||||
}
|
||||
if (repeat && repeat < MAX_REPEAT) {
|
||||
/**
|
||||
* Express repeatDelay in units of a single iteration's duration
|
||||
* so it can be added to the per-iteration time offsets below
|
||||
* before they're normalized to 0-1.
|
||||
*/
|
||||
const repeatDelayUnits = duration > 0 ? repeatDelay / duration : 0;
|
||||
duration = calculateRepeatDuration(duration, repeat, repeatDelay);
|
||||
const originalKeyframes = [...valueKeyframesAsList];
|
||||
const originalTimes = [...times];
|
||||
ease = Array.isArray(ease) ? [...ease] : [ease];
|
||||
const originalEase = [...ease];
|
||||
/**
|
||||
* For reverse/mirror, alternate iterations play the segment
|
||||
* backwards. mirror matches JSAnimation's mirroredGenerator:
|
||||
* reversed keyframes, easings unchanged. reverse matches
|
||||
* JSAnimation's iterationProgress = 1 - p: reversed
|
||||
* keyframes, easing array reversed AND each function easing
|
||||
* mapped through reverseEasing (string easings unchanged —
|
||||
* they're resolved later by the keyframes engine).
|
||||
*/
|
||||
const isFlipping = repeatType === "reverse" || repeatType === "mirror";
|
||||
let flippedKeyframes = originalKeyframes;
|
||||
let flippedEases = originalEase;
|
||||
if (isFlipping) {
|
||||
flippedKeyframes = [...originalKeyframes].reverse();
|
||||
if (repeatType === "reverse") {
|
||||
flippedEases = [...originalEase]
|
||||
.reverse()
|
||||
.map((e) => typeof e === "function"
|
||||
? reverseEasing(e)
|
||||
: e);
|
||||
}
|
||||
}
|
||||
for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) {
|
||||
const isFlipped = isFlipping && repeatIndex % 2 === 0;
|
||||
const iterKeyframes = isFlipped
|
||||
? flippedKeyframes
|
||||
: originalKeyframes;
|
||||
const iterEase = isFlipped ? flippedEases : originalEase;
|
||||
const iterStartOffset = (repeatIndex + 1) * (1 + repeatDelayUnits);
|
||||
/**
|
||||
* If repeatDelay is set, hold the previous iteration's
|
||||
* final value through the delay by inserting a keyframe
|
||||
* at the moment the next iteration begins.
|
||||
*/
|
||||
if (repeatDelayUnits > 0) {
|
||||
valueKeyframesAsList.push(valueKeyframesAsList[valueKeyframesAsList.length - 1]);
|
||||
times.push(iterStartOffset);
|
||||
ease.push("linear");
|
||||
}
|
||||
valueKeyframesAsList.push(...iterKeyframes);
|
||||
for (let keyframeIndex = 0; keyframeIndex < iterKeyframes.length; keyframeIndex++) {
|
||||
times.push(originalTimes[keyframeIndex] + iterStartOffset);
|
||||
ease.push(keyframeIndex === 0
|
||||
? "linear"
|
||||
: getEasingForSegment(iterEase, keyframeIndex - 1));
|
||||
}
|
||||
}
|
||||
normalizeTimes(times, repeat, repeatDelayUnits);
|
||||
}
|
||||
const targetTime = startTime + duration;
|
||||
/**
|
||||
* Add keyframes, mapping offsets to absolute time.
|
||||
*/
|
||||
addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
|
||||
maxDuration = Math.max(calculatedDelay + duration, maxDuration);
|
||||
totalDuration = Math.max(targetTime, totalDuration);
|
||||
};
|
||||
if (isMotionValue(subject)) {
|
||||
const subjectSequence = getSubjectSequence(subject, sequences);
|
||||
resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
|
||||
}
|
||||
else {
|
||||
const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
|
||||
const numSubjects = subjects.length;
|
||||
/**
|
||||
* For every element in this segment, process the defined values.
|
||||
*/
|
||||
for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
|
||||
/**
|
||||
* Cast necessary, but we know these are of this type
|
||||
*/
|
||||
keyframes = keyframes;
|
||||
transition = transition;
|
||||
const thisSubject = subjects[subjectIndex];
|
||||
const subjectSequence = getSubjectSequence(thisSubject, sequences);
|
||||
for (const key in keyframes) {
|
||||
resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
prevTime = currentTime;
|
||||
currentTime += maxDuration;
|
||||
}
|
||||
/**
|
||||
* For every element and value combination create a new animation.
|
||||
*/
|
||||
sequences.forEach((valueSequences, element) => {
|
||||
for (const key in valueSequences) {
|
||||
const valueSequence = valueSequences[key];
|
||||
/**
|
||||
* Arrange all the keyframes in ascending time order.
|
||||
*/
|
||||
valueSequence.sort(compareByTime);
|
||||
const keyframes = [];
|
||||
const valueOffset = [];
|
||||
const valueEasing = [];
|
||||
/**
|
||||
* For each keyframe, translate absolute times into
|
||||
* relative offsets based on the total duration of the timeline.
|
||||
*/
|
||||
for (let i = 0; i < valueSequence.length; i++) {
|
||||
const { at, value, easing } = valueSequence[i];
|
||||
keyframes.push(value);
|
||||
valueOffset.push(progress(0, totalDuration, at));
|
||||
valueEasing.push(easing || "easeOut");
|
||||
}
|
||||
/**
|
||||
* If the first keyframe doesn't land on offset: 0
|
||||
* provide one by duplicating the initial keyframe. This ensures
|
||||
* it snaps to the first keyframe when the animation starts.
|
||||
*/
|
||||
if (valueOffset[0] !== 0) {
|
||||
valueOffset.unshift(0);
|
||||
keyframes.unshift(keyframes[0]);
|
||||
valueEasing.unshift(defaultSegmentEasing);
|
||||
}
|
||||
/**
|
||||
* If the last keyframe doesn't land on offset: 1
|
||||
* provide one with a null wildcard value. This will ensure it
|
||||
* stays static until the end of the animation.
|
||||
*/
|
||||
if (valueOffset[valueOffset.length - 1] !== 1) {
|
||||
valueOffset.push(1);
|
||||
keyframes.push(null);
|
||||
}
|
||||
if (!animationDefinitions.has(element)) {
|
||||
animationDefinitions.set(element, {
|
||||
keyframes: {},
|
||||
transition: {},
|
||||
});
|
||||
}
|
||||
const definition = animationDefinitions.get(element);
|
||||
definition.keyframes[key] = keyframes;
|
||||
/**
|
||||
* Exclude `type` from defaultTransition since springs have been
|
||||
* converted to duration-based easing functions in resolveValueSequence.
|
||||
* Including `type: "spring"` would cause JSAnimation to error when
|
||||
* the merged keyframes array has more than 2 keyframes.
|
||||
*/
|
||||
const { type: _type, ...remainingDefaultTransition } = defaultTransition;
|
||||
definition.transition[key] = {
|
||||
...remainingDefaultTransition,
|
||||
duration: totalDuration,
|
||||
ease: valueEasing,
|
||||
times: valueOffset,
|
||||
...sequenceTransition,
|
||||
};
|
||||
}
|
||||
});
|
||||
return animationDefinitions;
|
||||
}
|
||||
function getSubjectSequence(subject, sequences) {
|
||||
!sequences.has(subject) && sequences.set(subject, {});
|
||||
return sequences.get(subject);
|
||||
}
|
||||
function getValueSequence(name, sequences) {
|
||||
if (!sequences[name])
|
||||
sequences[name] = [];
|
||||
return sequences[name];
|
||||
}
|
||||
function keyframesAsList(keyframes) {
|
||||
return Array.isArray(keyframes) ? keyframes : [keyframes];
|
||||
}
|
||||
function getValueTransition(transition, key) {
|
||||
return transition && transition[key]
|
||||
? {
|
||||
...transition,
|
||||
...transition[key],
|
||||
}
|
||||
: { ...transition };
|
||||
}
|
||||
const isNumber = (keyframe) => typeof keyframe === "number";
|
||||
const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
|
||||
|
||||
export { createAnimationsFromSequence, getValueTransition };
|
||||
//# sourceMappingURL=create.mjs.map
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
function calculateRepeatDuration(duration, repeat, repeatDelay) {
|
||||
return duration * (repeat + 1) + repeatDelay * repeat;
|
||||
}
|
||||
|
||||
export { calculateRepeatDuration };
|
||||
//# sourceMappingURL=calc-repeat-duration.mjs.map
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Given a absolute or relative time definition and current/prev time state of the sequence,
|
||||
* calculate an absolute time for the next keyframes.
|
||||
*/
|
||||
function calcNextTime(current, next, prev, labels) {
|
||||
if (typeof next === "number") {
|
||||
return next;
|
||||
}
|
||||
else if (next.startsWith("-") || next.startsWith("+")) {
|
||||
return Math.max(0, current + parseFloat(next));
|
||||
}
|
||||
else if (next === "<") {
|
||||
return prev;
|
||||
}
|
||||
else if (next.startsWith("<")) {
|
||||
return Math.max(0, prev + parseFloat(next.slice(1)));
|
||||
}
|
||||
else {
|
||||
return labels.get(next) ?? current;
|
||||
}
|
||||
}
|
||||
|
||||
export { calcNextTime };
|
||||
//# sourceMappingURL=calc-time.mjs.map
|
||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
import { mixNumber } from 'motion-dom';
|
||||
import { getEasingForSegment, removeItem } from 'motion-utils';
|
||||
|
||||
function eraseKeyframes(sequence, startTime, endTime) {
|
||||
for (let i = 0; i < sequence.length; i++) {
|
||||
const keyframe = sequence[i];
|
||||
if (keyframe.at > startTime && keyframe.at < endTime) {
|
||||
removeItem(sequence, keyframe);
|
||||
// If we remove this item we have to push the pointer back one
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
|
||||
/**
|
||||
* Erase every existing value between currentTime and targetTime,
|
||||
* this will essentially splice this timeline into any currently
|
||||
* defined ones.
|
||||
*/
|
||||
eraseKeyframes(sequence, startTime, endTime);
|
||||
for (let i = 0; i < keyframes.length; i++) {
|
||||
sequence.push({
|
||||
value: keyframes[i],
|
||||
at: mixNumber(startTime, endTime, offset[i]),
|
||||
easing: getEasingForSegment(easing, i),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { addKeyframes, eraseKeyframes };
|
||||
//# sourceMappingURL=edit.mjs.map
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Take an array of times that represent repeated keyframes. For instance
|
||||
* if we have original times of [0, 0.5, 1] then our repeated times will
|
||||
* be [0, 0.5, 1, 1, 1.5, 2]. Loop over the times and scale them back
|
||||
* down to a 0-1 scale.
|
||||
*
|
||||
* `repeatDelayUnits` is the repeatDelay expressed in units of a single
|
||||
* iteration's duration, so the total span equals `(repeat + 1) + repeat * repeatDelayUnits`.
|
||||
*/
|
||||
function normalizeTimes(times, repeat, repeatDelayUnits = 0) {
|
||||
const totalUnits = repeat + 1 + repeat * repeatDelayUnits;
|
||||
for (let i = 0; i < times.length; i++) {
|
||||
times[i] = times[i] / totalUnits;
|
||||
}
|
||||
}
|
||||
|
||||
export { normalizeTimes };
|
||||
//# sourceMappingURL=normalize-times.mjs.map
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
function compareByTime(a, b) {
|
||||
if (a.at === b.at) {
|
||||
if (a.value === null)
|
||||
return 1;
|
||||
if (b.value === null)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return a.at - b.at;
|
||||
}
|
||||
}
|
||||
|
||||
export { compareByTime };
|
||||
//# sourceMappingURL=sort.mjs.map
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
function isDOMKeyframes(keyframes) {
|
||||
return typeof keyframes === "object" && !Array.isArray(keyframes);
|
||||
}
|
||||
|
||||
export { isDOMKeyframes };
|
||||
//# sourceMappingURL=is-dom-keyframes.mjs.map
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
export * from 'motion-dom';
|
||||
export { delayInSeconds as delay } from 'motion-dom';
|
||||
export * from 'motion-utils';
|
||||
export { animate, createScopedAnimate } from './animation/animate/index.mjs';
|
||||
export { animateMini } from './animation/animators/waapi/animate-style.mjs';
|
||||
export { scroll } from './render/dom/scroll/index.mjs';
|
||||
export { scrollInfo } from './render/dom/scroll/track.mjs';
|
||||
export { inView } from './render/dom/viewport/index.mjs';
|
||||
export { distance, distance2D } from './utils/distance.mjs';
|
||||
//# sourceMappingURL=dom.mjs.map
|
||||
Generated
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
import { resize } from 'motion-dom';
|
||||
import { scrollInfo } from './track.mjs';
|
||||
import { canUseNativeTimeline } from './utils/can-use-native-timeline.mjs';
|
||||
import { getTimeline } from './utils/get-timeline.mjs';
|
||||
import { offsetToViewTimelineRange } from './utils/offset-to-range.mjs';
|
||||
|
||||
function attachToAnimation(animation, options) {
|
||||
const { target, container, axis } = options;
|
||||
const range = target && offsetToViewTimelineRange(options.offset);
|
||||
/**
|
||||
* Use native timeline when:
|
||||
* - No target and no offset: ScrollTimeline
|
||||
* - Target with mappable offset: ViewTimeline with named range
|
||||
* - Otherwise: fall back to JS observe. A ScrollTimeline has no offset,
|
||||
* so page offsets are applied by the JS timeline.
|
||||
*/
|
||||
const native = canUseNativeTimeline(target) && (target ? !!range : !options.offset);
|
||||
const animations = new Map();
|
||||
const observed = new Set();
|
||||
let stopObserving;
|
||||
let reverse = false;
|
||||
/**
|
||||
* When an offset's second point comes first, its range is swapped and
|
||||
* the animation played backwards. The range is set after the direction,
|
||||
* as that's what realigns a running animation with its timeline. Chrome
|
||||
* skips that realignment when the range changes in the frame the
|
||||
* animation starts, leaving it offset for good, so it's replayed too.
|
||||
*/
|
||||
const apply = (direction, waapi) => {
|
||||
const [start, end] = range.points;
|
||||
waapi.effect.updateTiming({
|
||||
direction: reverse
|
||||
? direction === "normal"
|
||||
? "reverse"
|
||||
: "alternate-reverse"
|
||||
: direction,
|
||||
});
|
||||
Object.assign(waapi, {
|
||||
rangeStart: reverse ? end : start,
|
||||
rangeEnd: reverse ? start : end,
|
||||
});
|
||||
waapi.play();
|
||||
};
|
||||
const stops = [
|
||||
animation.attachTimeline({
|
||||
/**
|
||||
* Read only by WAAPI animations, so values driven from JS don't
|
||||
* create a native timeline they won't use.
|
||||
*/
|
||||
get timeline() {
|
||||
return native ? getTimeline(options) : undefined;
|
||||
},
|
||||
onAttach: range &&
|
||||
((waapi) => {
|
||||
const direction = waapi.effect.getTiming().direction;
|
||||
animations.set(waapi, direction);
|
||||
apply(direction, waapi);
|
||||
}),
|
||||
/**
|
||||
* Values driven from JS all track the offset with scrollInfo,
|
||||
* which measures once per frame for all of them.
|
||||
*/
|
||||
observe: (valueAnimation) => {
|
||||
valueAnimation.pause();
|
||||
observed.add(valueAnimation);
|
||||
stopObserving || (stopObserving = scrollInfo((info) => {
|
||||
observed.forEach((observedAnimation) => {
|
||||
observedAnimation.time =
|
||||
observedAnimation.iterationDuration *
|
||||
info[axis].progress;
|
||||
});
|
||||
}, options));
|
||||
return () => observed.delete(valueAnimation);
|
||||
},
|
||||
}),
|
||||
];
|
||||
if (native && range) {
|
||||
const { a, b } = range;
|
||||
const length = axis === "y" ? "clientHeight" : "clientWidth";
|
||||
/**
|
||||
* Offsets like All only run forwards when the target is longer than
|
||||
* the container, so their direction is remeasured on resize.
|
||||
*/
|
||||
const update = () => {
|
||||
if (reverse !==
|
||||
(reverse = a * target[length] + b * container[length] < 0)) {
|
||||
animations.forEach(apply);
|
||||
}
|
||||
};
|
||||
update();
|
||||
if (a * b < 0) {
|
||||
stops.push(resize(update), resize([target, container], update));
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
stops.forEach((stop) => stop());
|
||||
stopObserving?.();
|
||||
};
|
||||
}
|
||||
|
||||
export { attachToAnimation };
|
||||
//# sourceMappingURL=attach-animation.mjs.map
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
import { noop } from 'motion-utils';
|
||||
import { attachToAnimation } from './attach-animation.mjs';
|
||||
import { scrollInfo } from './track.mjs';
|
||||
|
||||
function scroll(onScroll, { axis = "y", source, container = document.scrollingElement, ...options } = {}) {
|
||||
if (!container)
|
||||
return noop;
|
||||
const optionsWithDefaults = {
|
||||
axis,
|
||||
container: source || container,
|
||||
...options,
|
||||
};
|
||||
return typeof onScroll === "function"
|
||||
? scrollInfo((info) => onScroll(info[axis].progress, info), optionsWithDefaults)
|
||||
: attachToAnimation(onScroll, optionsWithDefaults);
|
||||
}
|
||||
|
||||
export { scroll };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
Generated
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
import { clamp, progress, velocityPerSecond } from 'motion-utils';
|
||||
|
||||
/**
|
||||
* A time in milliseconds, beyond which we consider the scroll velocity to be 0.
|
||||
*/
|
||||
const maxElapsed = 50;
|
||||
const createAxisInfo = () => ({
|
||||
current: 0,
|
||||
offset: [],
|
||||
progress: 0,
|
||||
scrollLength: 0,
|
||||
targetOffset: 0,
|
||||
targetLength: 0,
|
||||
containerLength: 0,
|
||||
velocity: 0,
|
||||
});
|
||||
const createScrollInfo = () => ({
|
||||
time: 0,
|
||||
x: createAxisInfo(),
|
||||
y: createAxisInfo(),
|
||||
});
|
||||
/**
|
||||
* Also iterated with for...in as the list of axes.
|
||||
*/
|
||||
const axisKeys = {
|
||||
x: {
|
||||
length: "Width",
|
||||
position: "Left",
|
||||
},
|
||||
y: {
|
||||
length: "Height",
|
||||
position: "Top",
|
||||
},
|
||||
};
|
||||
const styles = new WeakMap();
|
||||
/**
|
||||
* Whether an axis scrolls from its end, which browsers report as a scroll
|
||||
* position running from 0 to -scrollLength (#3340). This is read from style
|
||||
* rather than the sign of the scroll position, as elastic overscroll pushes
|
||||
* the position past 0 in either kind of axis (#3791).
|
||||
*/
|
||||
function isReversed(element, axisName) {
|
||||
/**
|
||||
* The viewport takes its writing mode and direction from <body>, which
|
||||
* not every browser reflects in the root element's computed style. It
|
||||
* doesn't adopt the root element's flex layout.
|
||||
*/
|
||||
const isRoot = element === document.scrollingElement;
|
||||
const source = (isRoot && document.body) || element;
|
||||
let style = styles.get(source);
|
||||
if (!style)
|
||||
styles.set(source, (style = getComputedStyle(source)));
|
||||
const { writingMode, flexDirection } = style;
|
||||
const isInline = (axisName === "x") !== /^[vs]/.test(writingMode);
|
||||
return ((isInline
|
||||
? (style.direction === "rtl") !== (writingMode === "sideways-lr")
|
||||
: writingMode.endsWith("rl")) !==
|
||||
/**
|
||||
* A reversed flex axis flips this. The main axis is the inline axis
|
||||
* unless flex-direction is a column.
|
||||
*/
|
||||
(!isRoot &&
|
||||
style.display.includes("flex") &&
|
||||
(isInline !== (flexDirection[0] === "c")
|
||||
? flexDirection
|
||||
: style.flexWrap).endsWith("reverse")));
|
||||
}
|
||||
function updateAxisInfo(element, axisName, info, time) {
|
||||
const axis = info[axisName];
|
||||
const { length, position } = axisKeys[axisName];
|
||||
const prev = axis.current;
|
||||
const prevTime = info.time;
|
||||
const scrollPosition = element[`scroll${position}`];
|
||||
axis.current = isReversed(element, axisName)
|
||||
? -scrollPosition || 0 // Avoid -0
|
||||
: scrollPosition;
|
||||
axis.containerLength = element[`client${length}`];
|
||||
axis.targetLength = element[`scroll${length}`];
|
||||
axis.scrollLength = axis.targetLength - axis.containerLength;
|
||||
axis.offset.length = 0;
|
||||
axis.offset[0] = 0;
|
||||
axis.offset[1] = axis.scrollLength;
|
||||
axis.progress = clamp(0, 1, progress(0, axis.scrollLength, axis.current));
|
||||
const elapsed = time - prevTime;
|
||||
axis.velocity =
|
||||
elapsed > maxElapsed
|
||||
? 0
|
||||
: velocityPerSecond(axis.current - prev, elapsed);
|
||||
}
|
||||
/**
|
||||
* Measures a scroll container. Runs once per container per frame; every
|
||||
* handler on that container derives its info from the result.
|
||||
*/
|
||||
function updateScrollInfo(element, info, time) {
|
||||
for (const axis in axisKeys) {
|
||||
updateAxisInfo(element, axis, info, time);
|
||||
}
|
||||
info.time = time;
|
||||
}
|
||||
|
||||
export { axisKeys, createScrollInfo, updateScrollInfo };
|
||||
//# sourceMappingURL=info.mjs.map
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
const namedEdges = {
|
||||
start: 0,
|
||||
center: 0.5,
|
||||
end: 1,
|
||||
};
|
||||
function resolveEdge(edge, length, inset = 0) {
|
||||
let delta = 0;
|
||||
/**
|
||||
* If we have this edge defined as a preset, replace the definition
|
||||
* with the numerical value.
|
||||
*/
|
||||
if (edge in namedEdges) {
|
||||
edge = namedEdges[edge];
|
||||
}
|
||||
/**
|
||||
* Handle unit values
|
||||
*/
|
||||
if (typeof edge === "string") {
|
||||
const asNumber = parseFloat(edge);
|
||||
if (edge.endsWith("px")) {
|
||||
delta = asNumber;
|
||||
}
|
||||
else if (edge.endsWith("%")) {
|
||||
edge = asNumber / 100;
|
||||
}
|
||||
else if (edge.endsWith("vw")) {
|
||||
delta = (asNumber / 100) * document.documentElement.clientWidth;
|
||||
}
|
||||
else if (edge.endsWith("vh")) {
|
||||
delta = (asNumber / 100) * document.documentElement.clientHeight;
|
||||
}
|
||||
else {
|
||||
edge = asNumber;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the edge is defined as a number, handle as a progress value.
|
||||
*/
|
||||
if (typeof edge === "number") {
|
||||
delta = length * edge;
|
||||
}
|
||||
return inset + delta;
|
||||
}
|
||||
|
||||
export { namedEdges, resolveEdge };
|
||||
//# sourceMappingURL=edge.mjs.map
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
import { clamp, progress } from 'motion-utils';
|
||||
import { resolveOffset } from './offset.mjs';
|
||||
import { ScrollOffset } from './presets.mjs';
|
||||
|
||||
/**
|
||||
* Resolved offsets map to evenly spaced progress values, so progress is
|
||||
* derived from the segment index rather than building an interpolator.
|
||||
*/
|
||||
function offsetsToProgress(offsets, v) {
|
||||
const n = offsets.length - 1;
|
||||
if (n < 1)
|
||||
return 0;
|
||||
const reverse = offsets[0] > offsets[n];
|
||||
const at = (i) => offsets[reverse ? n - i : i];
|
||||
/**
|
||||
* Matches interpolate(), which checks for a zero-length first range
|
||||
* before reversing descending offsets.
|
||||
*/
|
||||
if (offsets[0] === offsets[1] && v < at(0))
|
||||
return reverse ? 1 : 0;
|
||||
let i = 0;
|
||||
while (i < n - 1 && v >= at(i + 1))
|
||||
i++;
|
||||
const p = (i + progress(at(i), at(i + 1), v)) / n;
|
||||
return reverse ? 1 - p : p;
|
||||
}
|
||||
function resolveOffsets(info, options) {
|
||||
const { offset: offsetDefinition = ScrollOffset.All, axis = "y" } = options;
|
||||
const axisInfo = info[axis];
|
||||
/**
|
||||
* Reset the length of the resolved offset array rather than creating a new one.
|
||||
*/
|
||||
axisInfo.offset.length = 0;
|
||||
/**
|
||||
* Populate the offset array by resolving the user's offset definition into
|
||||
* a list of pixel scroll offsets.
|
||||
*/
|
||||
const numOffsets = offsetDefinition.length;
|
||||
for (let i = 0; i < numOffsets; i++) {
|
||||
axisInfo.offset[i] = resolveOffset(offsetDefinition[i], axisInfo.containerLength, axisInfo.targetLength, axisInfo.targetOffset);
|
||||
}
|
||||
axisInfo.progress = clamp(0, 1, offsetsToProgress(axisInfo.offset, axisInfo.current));
|
||||
}
|
||||
|
||||
export { resolveOffsets };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
import { isHTMLElement } from 'motion-dom';
|
||||
|
||||
function calcInset(element, container) {
|
||||
const inset = { x: 0, y: 0 };
|
||||
let current = element;
|
||||
while (current && current !== container) {
|
||||
if (isHTMLElement(current)) {
|
||||
inset.x += current.offsetLeft;
|
||||
inset.y += current.offsetTop;
|
||||
current = current.offsetParent;
|
||||
}
|
||||
else if (current.tagName === "svg") {
|
||||
/**
|
||||
* This isn't an ideal approach to measuring the offset of <svg /> tags.
|
||||
* It would be preferable, given they behave like HTMLElements in most ways
|
||||
* to use offsetLeft/Top. But these don't exist on <svg />. Likewise we
|
||||
* can't use .getBBox() like most SVG elements as these provide the offset
|
||||
* relative to the SVG itself, which for <svg /> is usually 0x0.
|
||||
*/
|
||||
const svgBoundingBox = current.getBoundingClientRect();
|
||||
current = current.parentElement;
|
||||
const parentBoundingBox = current.getBoundingClientRect();
|
||||
inset.x += svgBoundingBox.left - parentBoundingBox.left;
|
||||
inset.y += svgBoundingBox.top - parentBoundingBox.top;
|
||||
}
|
||||
else if (current instanceof SVGGraphicsElement) {
|
||||
const { x, y } = current.getBBox();
|
||||
inset.x += x;
|
||||
inset.y += y;
|
||||
let svg = null;
|
||||
let parent = current.parentNode;
|
||||
while (!svg) {
|
||||
if (parent.tagName === "svg") {
|
||||
svg = parent;
|
||||
}
|
||||
parent = current.parentNode;
|
||||
}
|
||||
current = svg;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inset;
|
||||
}
|
||||
|
||||
export { calcInset };
|
||||
//# sourceMappingURL=inset.mjs.map
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
import { resolveEdge, namedEdges } from './edge.mjs';
|
||||
|
||||
const defaultOffset = [0, 0];
|
||||
function resolveOffset(offset, containerLength, targetLength, targetInset) {
|
||||
let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
|
||||
let targetPoint = 0;
|
||||
let containerPoint = 0;
|
||||
if (typeof offset === "number") {
|
||||
/**
|
||||
* If we're provided offset: [0, 0.5, 1] then each number x should become
|
||||
* [x, x], so we default to the behaviour of mapping 0 => 0 of both target
|
||||
* and container etc.
|
||||
*/
|
||||
offsetDefinition = [offset, offset];
|
||||
}
|
||||
else if (typeof offset === "string") {
|
||||
offset = offset.trim();
|
||||
if (offset.includes(" ")) {
|
||||
offsetDefinition = offset.split(" ");
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* If we're provided a definition like "100px" then we want to apply
|
||||
* that only to the top of the target point, leaving the container at 0.
|
||||
* Whereas a named offset like "end" should be applied to both.
|
||||
*/
|
||||
offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
|
||||
}
|
||||
}
|
||||
targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
|
||||
containerPoint = resolveEdge(offsetDefinition[1], containerLength);
|
||||
return targetPoint - containerPoint;
|
||||
}
|
||||
|
||||
export { resolveOffset };
|
||||
//# sourceMappingURL=offset.mjs.map
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
const ScrollOffset = {
|
||||
All: [
|
||||
[0, 0],
|
||||
[1, 1],
|
||||
],
|
||||
};
|
||||
|
||||
export { ScrollOffset };
|
||||
//# sourceMappingURL=presets.mjs.map
|
||||
Generated
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
import { warnOnce } from 'motion-utils';
|
||||
import { axisKeys } from './info.mjs';
|
||||
import { resolveOffsets } from './offsets/index.mjs';
|
||||
import { calcInset } from './offsets/inset.mjs';
|
||||
|
||||
function getTargetSize(target) {
|
||||
return "getBBox" in target && target.tagName !== "svg"
|
||||
? target.getBBox()
|
||||
: { width: target.clientWidth, height: target.clientHeight };
|
||||
}
|
||||
function createOnScrollHandler(container, onScroll, info, options = {}) {
|
||||
const { target } = options;
|
||||
/**
|
||||
* In development mode ensure scroll containers aren't position: static as this makes
|
||||
* it difficult to measure their relative positions. The document scrolling element
|
||||
* is exempt: offsetParent measurements naturally resolve relative to the document.
|
||||
*/
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
if (target &&
|
||||
target !== container &&
|
||||
container !== document.documentElement &&
|
||||
container !== document.scrollingElement &&
|
||||
container !== document.body) {
|
||||
warnOnce(getComputedStyle(container).position !== "static", "Please ensure that the container has a non-static position, like 'relative', 'fixed', or 'absolute' to ensure scroll offset is calculated correctly.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handlers without a target or offset are notified with the container's
|
||||
* shared info object, so they measure nothing themselves.
|
||||
*/
|
||||
const needsOwnInfo = target || options.offset;
|
||||
return {
|
||||
measure: (containerInfo) => {
|
||||
if (!needsOwnInfo)
|
||||
return;
|
||||
info.time = containerInfo.time;
|
||||
for (const key in axisKeys) {
|
||||
const axis = key;
|
||||
const { offset } = info[axis];
|
||||
Object.assign(info[axis], containerInfo[axis]);
|
||||
info[axis].offset = offset;
|
||||
}
|
||||
if (target && target !== container) {
|
||||
const inset = calcInset(target, container);
|
||||
const size = getTargetSize(target);
|
||||
info.x.targetOffset = inset.x;
|
||||
info.y.targetOffset = inset.y;
|
||||
info.x.targetLength = size.width;
|
||||
info.y.targetLength = size.height;
|
||||
}
|
||||
resolveOffsets(info, options);
|
||||
},
|
||||
notify: (containerInfo) => onScroll(needsOwnInfo ? info : containerInfo),
|
||||
};
|
||||
}
|
||||
|
||||
export { createOnScrollHandler };
|
||||
//# sourceMappingURL=on-scroll-handler.mjs.map
|
||||
Generated
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
import { resize, frame, cancelFrame, frameData } from 'motion-dom';
|
||||
import { noop } from 'motion-utils';
|
||||
import { createScrollInfo, updateScrollInfo } from './info.mjs';
|
||||
import { createOnScrollHandler } from './on-scroll-handler.mjs';
|
||||
|
||||
const scrollListeners = new WeakMap();
|
||||
const resizeListeners = new WeakMap();
|
||||
const onScrollHandlers = new WeakMap();
|
||||
const dimensionCheckProcesses = new WeakMap();
|
||||
const getEventTarget = (element) => element === document.scrollingElement ? window : element;
|
||||
function scrollInfo(onScroll, { container = document.scrollingElement, trackContentSize = false, ...options } = {}) {
|
||||
if (!container)
|
||||
return noop;
|
||||
let containerHandlers = onScrollHandlers.get(container);
|
||||
/**
|
||||
* Get the onScroll handlers for this container.
|
||||
* If one isn't found, create a new one.
|
||||
*/
|
||||
if (!containerHandlers) {
|
||||
containerHandlers = new Set();
|
||||
onScrollHandlers.set(container, containerHandlers);
|
||||
}
|
||||
/**
|
||||
* Create a new onScroll handler for the provided callback.
|
||||
*/
|
||||
const info = createScrollInfo();
|
||||
const containerHandler = createOnScrollHandler(container, onScroll, info, options);
|
||||
containerHandlers.add(containerHandler);
|
||||
/**
|
||||
* Check if there's a scroll event listener for this container.
|
||||
* If not, create one.
|
||||
*/
|
||||
if (!scrollListeners.has(container)) {
|
||||
const containerInfo = createScrollInfo();
|
||||
const measureAll = () => {
|
||||
updateScrollInfo(container, containerInfo, frameData.timestamp);
|
||||
for (const handler of containerHandlers) {
|
||||
handler.measure(containerInfo);
|
||||
}
|
||||
frame.preUpdate(notifyAll);
|
||||
};
|
||||
const notifyAll = () => {
|
||||
for (const handler of containerHandlers) {
|
||||
handler.notify(containerInfo);
|
||||
}
|
||||
};
|
||||
const listener = () => frame.read(measureAll);
|
||||
scrollListeners.set(container, listener);
|
||||
const target = getEventTarget(container);
|
||||
window.addEventListener("resize", listener);
|
||||
if (container !== document.documentElement) {
|
||||
resizeListeners.set(container, resize(container, listener));
|
||||
}
|
||||
target.addEventListener("scroll", listener);
|
||||
listener();
|
||||
}
|
||||
/**
|
||||
* Enable content size tracking if requested and not already enabled.
|
||||
*/
|
||||
if (trackContentSize && !dimensionCheckProcesses.has(container)) {
|
||||
const listener = scrollListeners.get(container);
|
||||
// Store initial scroll dimensions (object is reused to avoid allocation)
|
||||
const size = {
|
||||
width: container.scrollWidth,
|
||||
height: container.scrollHeight,
|
||||
};
|
||||
// Add frame-based scroll dimension checking to detect content changes
|
||||
const checkScrollDimensions = () => {
|
||||
const newWidth = container.scrollWidth;
|
||||
const newHeight = container.scrollHeight;
|
||||
if (size.width !== newWidth || size.height !== newHeight) {
|
||||
listener();
|
||||
size.width = newWidth;
|
||||
size.height = newHeight;
|
||||
}
|
||||
};
|
||||
// Schedule with keepAlive=true to run every frame
|
||||
const dimensionCheckProcess = frame.read(checkScrollDimensions, true);
|
||||
dimensionCheckProcesses.set(container, dimensionCheckProcess);
|
||||
}
|
||||
const listener = scrollListeners.get(container);
|
||||
frame.read(listener, false, true);
|
||||
return () => {
|
||||
cancelFrame(listener);
|
||||
/**
|
||||
* Check if we even have any handlers for this container.
|
||||
*/
|
||||
const currentHandlers = onScrollHandlers.get(container);
|
||||
if (!currentHandlers)
|
||||
return;
|
||||
currentHandlers.delete(containerHandler);
|
||||
if (currentHandlers.size)
|
||||
return;
|
||||
/**
|
||||
* If no more handlers, remove the scroll listener too. The handler
|
||||
* set goes with it, as a measure still queued from this listener
|
||||
* would otherwise notify handlers added by a later scrollInfo call.
|
||||
*/
|
||||
onScrollHandlers.delete(container);
|
||||
const scrollListener = scrollListeners.get(container);
|
||||
scrollListeners.delete(container);
|
||||
if (scrollListener) {
|
||||
getEventTarget(container).removeEventListener("scroll", scrollListener);
|
||||
resizeListeners.get(container)?.();
|
||||
window.removeEventListener("resize", scrollListener);
|
||||
}
|
||||
// Clean up scroll dimension checking
|
||||
const dimensionCheckProcess = dimensionCheckProcesses.get(container);
|
||||
if (dimensionCheckProcess) {
|
||||
cancelFrame(dimensionCheckProcess);
|
||||
dimensionCheckProcesses.delete(container);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { scrollInfo };
|
||||
//# sourceMappingURL=track.mjs.map
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import { supportsViewTimeline, supportsScrollTimeline } from 'motion-dom';
|
||||
|
||||
function canUseNativeTimeline(target) {
|
||||
if (typeof window === "undefined")
|
||||
return false;
|
||||
return target ? supportsViewTimeline() : supportsScrollTimeline();
|
||||
}
|
||||
|
||||
export { canUseNativeTimeline };
|
||||
//# sourceMappingURL=can-use-native-timeline.mjs.map
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
const timelineCache = new Map();
|
||||
/**
|
||||
* Native timelines are only attached to WAAPI animations. Offsets are
|
||||
* applied to each animation as a range, so a timeline is shared by every
|
||||
* offset.
|
||||
*/
|
||||
function getTimeline({ container, target, axis, }) {
|
||||
let containerCache = timelineCache.get(container);
|
||||
if (!containerCache) {
|
||||
containerCache = new Map();
|
||||
timelineCache.set(container, containerCache);
|
||||
}
|
||||
const targetKey = target ?? "self";
|
||||
let targetCache = containerCache.get(targetKey);
|
||||
if (!targetCache) {
|
||||
targetCache = {};
|
||||
containerCache.set(targetKey, targetCache);
|
||||
}
|
||||
return (targetCache[axis] || (targetCache[axis] = target
|
||||
? new ViewTimeline({ subject: target, axis })
|
||||
: new ScrollTimeline({ source: container, axis })));
|
||||
}
|
||||
|
||||
export { getTimeline };
|
||||
//# sourceMappingURL=get-timeline.mjs.map
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
import { resolveOffset } from '../offsets/offset.mjs';
|
||||
import { ScrollOffset } from '../offsets/presets.mjs';
|
||||
|
||||
/**
|
||||
* Resolved offsets are linear in the target and container lengths, so
|
||||
* probing them gives [target progress, container progress, pixels].
|
||||
* vw/vh can resolve to 0px, so they're rejected up front.
|
||||
*/
|
||||
const toIntersection = (o) => {
|
||||
if (/v/u.test(o))
|
||||
return [];
|
||||
const px = resolveOffset(o, 0, 0, 0);
|
||||
return [resolveOffset(o, 0, 1, 0) - px, px - resolveOffset(o, 1, 0, 0), px];
|
||||
};
|
||||
const toRange = ([t, c, px]) => !px &&
|
||||
(c === 0 || c === 1) &&
|
||||
`${c ? "entry" : "exit"}-crossing ${t * 100}%`;
|
||||
/**
|
||||
* Maps an offset to an equivalent ViewTimeline range. Returns undefined when
|
||||
* there isn't one, which signals the caller to fall back to JS-based scroll
|
||||
* tracking.
|
||||
*/
|
||||
function offsetToViewTimelineRange(offset = ScrollOffset.All) {
|
||||
if (offset.length !== 2)
|
||||
return;
|
||||
const [start, end] = offset.map(toIntersection);
|
||||
const points = [toRange(start), toRange(end)];
|
||||
const a = end[0] - start[0];
|
||||
const b = start[1] - end[1];
|
||||
if (points[0] && points[1] && (a || b)) {
|
||||
return {
|
||||
points: points,
|
||||
a,
|
||||
b,
|
||||
cover: !start[0] && a === 1 && b === 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { offsetToViewTimelineRange };
|
||||
//# sourceMappingURL=offset-to-range.mjs.map
|
||||
Generated
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
import { resolveElements } from 'motion-dom';
|
||||
|
||||
const thresholds = {
|
||||
some: 0,
|
||||
all: 1,
|
||||
};
|
||||
function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) {
|
||||
const elements = resolveElements(elementOrSelector);
|
||||
const activeIntersections = new WeakMap();
|
||||
const onIntersectionChange = (entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const onEnd = activeIntersections.get(entry.target);
|
||||
/**
|
||||
* If there's no change to the intersection, we don't need to
|
||||
* do anything here.
|
||||
*/
|
||||
if (entry.isIntersecting === Boolean(onEnd))
|
||||
return;
|
||||
if (entry.isIntersecting) {
|
||||
const newOnEnd = onStart(entry.target, entry);
|
||||
if (typeof newOnEnd === "function") {
|
||||
activeIntersections.set(entry.target, newOnEnd);
|
||||
}
|
||||
else {
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
else if (typeof onEnd === "function") {
|
||||
onEnd(entry);
|
||||
activeIntersections.delete(entry.target);
|
||||
}
|
||||
});
|
||||
};
|
||||
const observer = new IntersectionObserver(onIntersectionChange, {
|
||||
root,
|
||||
rootMargin,
|
||||
threshold: typeof amount === "number" ? amount : thresholds[amount],
|
||||
});
|
||||
elements.forEach((element) => observer.observe(element));
|
||||
return () => observer.disconnect();
|
||||
}
|
||||
|
||||
export { inView };
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
const distance = (a, b) => Math.abs(a - b);
|
||||
function distance2D(a, b) {
|
||||
// Multi-dimensional
|
||||
const xDelta = distance(a.x, b.x);
|
||||
const yDelta = distance(a.y, b.y);
|
||||
return Math.sqrt(xDelta ** 2 + yDelta ** 2);
|
||||
}
|
||||
|
||||
export { distance, distance2D };
|
||||
//# sourceMappingURL=distance.mjs.map
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"name": "framer-motion",
|
||||
"version": "14.3.1",
|
||||
"description": "A simple and powerful JavaScript animation library",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/es/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"require": "./dist/cjs/index.js",
|
||||
"import": "./dist/es/index.mjs",
|
||||
"default": "./dist/cjs/index.js"
|
||||
},
|
||||
"./debug": {
|
||||
"types": "./dist/debug.d.ts",
|
||||
"require": "./dist/cjs/debug.js",
|
||||
"import": "./dist/es/debug.mjs",
|
||||
"default": "./dist/cjs/debug.js"
|
||||
},
|
||||
"./dom/mini": {
|
||||
"types": "./dist/dom-mini.d.ts",
|
||||
"require": "./dist/cjs/dom-mini.js",
|
||||
"import": "./dist/es/dom-mini.mjs",
|
||||
"default": "./dist/cjs/dom-mini.js"
|
||||
},
|
||||
"./dom": {
|
||||
"types": "./dist/dom.d.ts",
|
||||
"require": "./dist/cjs/dom.js",
|
||||
"import": "./dist/es/dom.mjs",
|
||||
"default": "./dist/cjs/dom.js"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./dist/client.d.ts",
|
||||
"require": "./dist/cjs/client.js",
|
||||
"import": "./dist/es/client.mjs",
|
||||
"default": "./dist/cjs/client.js"
|
||||
},
|
||||
"./m": {
|
||||
"types": "./dist/m.d.ts",
|
||||
"require": "./dist/cjs/m.js",
|
||||
"import": "./dist/es/m.mjs",
|
||||
"default": "./dist/cjs/m.js"
|
||||
},
|
||||
"./mini": {
|
||||
"types": "./dist/mini.d.ts",
|
||||
"require": "./dist/cjs/mini.js",
|
||||
"import": "./dist/es/mini.mjs",
|
||||
"default": "./dist/cjs/mini.js"
|
||||
},
|
||||
"./projection": {
|
||||
"types": "./dist/projection.d.ts",
|
||||
"import": "./dist/es/projection.mjs",
|
||||
"default": "./dist/es/projection.mjs"
|
||||
},
|
||||
"./animate-view": {
|
||||
"types": "./dist/animate-view.d.ts",
|
||||
"require": "./dist/cjs/animate-view.js",
|
||||
"import": "./dist/es/animate-view.mjs",
|
||||
"default": "./dist/cjs/animate-view.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"types": "dist/index.d.ts",
|
||||
"author": "Matt Perry",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/motiondivision/motion/",
|
||||
"sideEffects": false,
|
||||
"keywords": [
|
||||
"react animation",
|
||||
"react",
|
||||
"pose",
|
||||
"react pose",
|
||||
"animation",
|
||||
"gestures",
|
||||
"drag",
|
||||
"spring",
|
||||
"popmotion",
|
||||
"framer",
|
||||
"waapi"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "yarn clean && tsc --noEmitOnError -p . && rollup -c && node ./scripts/check-bundle.js",
|
||||
"dev": "yarn watch",
|
||||
"clean": "rm -rf types dist lib",
|
||||
"test": "yarn test-server && yarn test-client",
|
||||
"test-client": "jest --config jest.config.json --max-workers=2",
|
||||
"test-server": "jest --config jest.config.ssr.json",
|
||||
"prettier": "prettier ./src/* --write",
|
||||
"watch": "concurrently -c blue,red -n tsc --noEmitOnError ,rollup --kill-others \"tsc --noEmitOnError --watch -p . --preserveWatchOutput\" \"rollup --config --watch --no-watch.clearScreen\"",
|
||||
"prepack": "yarn build && yarn measure && node ../../dev/inc/bundlesize.mjs framer-motion",
|
||||
"postpublish": "git push --tags",
|
||||
"measure": "rollup -c ./rollup.size.config.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"motion-dom": "14.3.1",
|
||||
"motion-utils": "14.1.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@thednp/dommatrix": "^2.0.11",
|
||||
"@types/three": "0.137.0",
|
||||
"concurrently": "^7.3.0",
|
||||
"jest": "^29.7.0",
|
||||
"rollup": "4.59.0",
|
||||
"three": "0.137.0",
|
||||
"typescript": "5.4.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "./dist/size-rollup-motion.js",
|
||||
"maxSize": "42.2 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/size-rollup-m.js",
|
||||
"maxSize": "5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/size-rollup-m-dom-animation.js",
|
||||
"maxSize": "28.6 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/size-rollup-m-dom-max.js",
|
||||
"maxSize": "42.3 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/size-rollup-animate.js",
|
||||
"maxSize": "22 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/size-rollup-scroll.js",
|
||||
"maxSize": "3.7 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/size-rollup-waapi-animate.js",
|
||||
"maxSize": "3.2 kB"
|
||||
}
|
||||
],
|
||||
"gitHead": "1785a8816da2682ef53bfc1fe8b3f61d86f17924"
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
exports.warning = function () { };
|
||||
exports.invariant = function () { };
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
exports.warning = function (check, message) {
|
||||
if (!check && typeof console !== 'undefined') {
|
||||
console.warn(message);
|
||||
}
|
||||
};
|
||||
exports.invariant = function (check, message) {
|
||||
if (!check) {
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "hey-listen",
|
||||
"version": "1.0.8",
|
||||
"description": "Warning and invariant dev-ex messaging.",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"module": "dist/hey-listen.es.js",
|
||||
"jsnext:main": "dist/hey-listen.es.js",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "rollup -c",
|
||||
"watch": "rollup -c -w",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Popmotion/hey-listen.git"
|
||||
},
|
||||
"keywords": [
|
||||
"warning",
|
||||
"invariant"
|
||||
],
|
||||
"author": "Matt Perry",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Popmotion/hey-listen/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Popmotion/hey-listen#readme",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^22.2.3",
|
||||
"@types/node": "^10.0.6",
|
||||
"jest": "^24.7.1",
|
||||
"rollup": "^1.9.2",
|
||||
"rollup-plugin-typescript2": "^0.14.0",
|
||||
"rollup-plugin-uglify": "^3.0.0",
|
||||
"ts-jest": "^22.4.5",
|
||||
"typescript": "^2.8.3"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"js",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
"transform": {
|
||||
"\\.(ts)$": "../node_modules/ts-jest/preprocessor.js"
|
||||
},
|
||||
"testRegex": "/_tests/.*\\.(ts|js)$",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true
|
||||
}
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
function flatHooks(configHooks, hooks = {}, parentName) {
|
||||
for (const key in configHooks) {
|
||||
const subHook = configHooks[key];
|
||||
const name = parentName ? `${parentName}:${key}` : key;
|
||||
if (typeof subHook === "object" && subHook !== null) flatHooks(subHook, hooks, name);
|
||||
else if (typeof subHook === "function") hooks[name] = subHook;
|
||||
}
|
||||
return hooks;
|
||||
}
|
||||
function mergeHooks(...hooks) {
|
||||
const finalHooks = {};
|
||||
for (const hook of hooks) {
|
||||
const flatenHook = flatHooks(hook);
|
||||
for (const key in flatenHook) if (finalHooks[key]) finalHooks[key].push(flatenHook[key]);
|
||||
else finalHooks[key] = [flatenHook[key]];
|
||||
}
|
||||
for (const key in finalHooks) if (finalHooks[key].length > 1) {
|
||||
const array = finalHooks[key];
|
||||
finalHooks[key] = (...arguments_) => serial(array, (function_) => function_(...arguments_));
|
||||
} else finalHooks[key] = finalHooks[key][0];
|
||||
return finalHooks;
|
||||
}
|
||||
function serial(tasks, function_) {
|
||||
return tasks.reduce((promise, task) => promise.then(() => function_(task)), Promise.resolve());
|
||||
}
|
||||
const createTask = /* @__PURE__ */ (() => {
|
||||
if (console.createTask) return console.createTask;
|
||||
const defaultTask = { run: (fn) => fn() };
|
||||
return () => defaultTask;
|
||||
})();
|
||||
function callHooks(hooks, args, startIndex, task) {
|
||||
for (let i = startIndex; i < hooks.length; i += 1) try {
|
||||
const result = task ? task.run(() => hooks[i](...args)) : hooks[i](...args);
|
||||
if (result && typeof result.then === "function") return Promise.resolve(result).then(() => callHooks(hooks, args, i + 1, task));
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
function serialTaskCaller(hooks, args, name) {
|
||||
if (hooks.length > 0) return callHooks(hooks, args, 0, createTask(name));
|
||||
}
|
||||
function parallelTaskCaller(hooks, args, name) {
|
||||
if (hooks.length > 0) {
|
||||
const task = createTask(name);
|
||||
return Promise.all(hooks.map((hook) => task.run(() => hook(...args))));
|
||||
}
|
||||
}
|
||||
function serialCaller(hooks, arguments_) {
|
||||
return hooks.reduce((promise, hookFunction) => promise.then(() => hookFunction(...arguments_ || [])), Promise.resolve());
|
||||
}
|
||||
function parallelCaller(hooks, args) {
|
||||
return Promise.all(hooks.map((hook) => hook(...args || [])));
|
||||
}
|
||||
function callEachWith(callbacks, arg0) {
|
||||
for (const callback of [...callbacks]) callback(arg0);
|
||||
}
|
||||
var Hookable = class {
|
||||
_hooks;
|
||||
_before;
|
||||
_after;
|
||||
_deprecatedHooks;
|
||||
_deprecatedMessages;
|
||||
constructor() {
|
||||
this._hooks = {};
|
||||
this._before = void 0;
|
||||
this._after = void 0;
|
||||
this._deprecatedMessages = void 0;
|
||||
this._deprecatedHooks = {};
|
||||
this.hook = this.hook.bind(this);
|
||||
this.callHook = this.callHook.bind(this);
|
||||
this.callHookWith = this.callHookWith.bind(this);
|
||||
}
|
||||
hook(name, function_, options = {}) {
|
||||
if (!name || typeof function_ !== "function") return () => {};
|
||||
const originalName = name;
|
||||
let dep;
|
||||
while (this._deprecatedHooks[name]) {
|
||||
dep = this._deprecatedHooks[name];
|
||||
name = dep.to;
|
||||
}
|
||||
if (dep && !options.allowDeprecated) {
|
||||
let message = dep.message;
|
||||
if (!message) message = `${originalName} hook has been deprecated` + (dep.to ? `, please use ${dep.to}` : "");
|
||||
if (!this._deprecatedMessages) this._deprecatedMessages = /* @__PURE__ */ new Set();
|
||||
if (!this._deprecatedMessages.has(message)) {
|
||||
console.warn(message);
|
||||
this._deprecatedMessages.add(message);
|
||||
}
|
||||
}
|
||||
if (!function_.name) try {
|
||||
Object.defineProperty(function_, "name", {
|
||||
get: () => "_" + name.replace(/\W+/g, "_") + "_hook_cb",
|
||||
configurable: true
|
||||
});
|
||||
} catch {}
|
||||
this._hooks[name] = this._hooks[name] || [];
|
||||
this._hooks[name].push(function_);
|
||||
return () => {
|
||||
if (function_) {
|
||||
this.removeHook(name, function_);
|
||||
function_ = void 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
hookOnce(name, function_) {
|
||||
let _unreg;
|
||||
let _function = (...arguments_) => {
|
||||
if (typeof _unreg === "function") _unreg();
|
||||
_unreg = void 0;
|
||||
_function = void 0;
|
||||
return function_(...arguments_);
|
||||
};
|
||||
_unreg = this.hook(name, _function);
|
||||
return _unreg;
|
||||
}
|
||||
removeHook(name, function_) {
|
||||
const hooks = this._hooks[name];
|
||||
if (hooks) {
|
||||
const index = hooks.indexOf(function_);
|
||||
if (index !== -1) hooks.splice(index, 1);
|
||||
if (hooks.length === 0) this._hooks[name] = void 0;
|
||||
}
|
||||
}
|
||||
clearHook(name) {
|
||||
this._hooks[name] = void 0;
|
||||
}
|
||||
deprecateHook(name, deprecated) {
|
||||
this._deprecatedHooks[name] = typeof deprecated === "string" ? { to: deprecated } : deprecated;
|
||||
const _hooks = this._hooks[name] || [];
|
||||
this._hooks[name] = void 0;
|
||||
for (const hook of _hooks) this.hook(name, hook);
|
||||
}
|
||||
deprecateHooks(deprecatedHooks) {
|
||||
for (const name in deprecatedHooks) this.deprecateHook(name, deprecatedHooks[name]);
|
||||
}
|
||||
addHooks(configHooks) {
|
||||
const hooks = flatHooks(configHooks);
|
||||
const removeFns = Object.keys(hooks).map((key) => this.hook(key, hooks[key]));
|
||||
return () => {
|
||||
for (const unreg of removeFns) unreg();
|
||||
removeFns.length = 0;
|
||||
};
|
||||
}
|
||||
removeHooks(configHooks) {
|
||||
const hooks = flatHooks(configHooks);
|
||||
for (const key in hooks) this.removeHook(key, hooks[key]);
|
||||
}
|
||||
removeAllHooks() {
|
||||
this._hooks = {};
|
||||
}
|
||||
callHook(name, ...args) {
|
||||
return this.callHookWith(serialTaskCaller, name, args);
|
||||
}
|
||||
callHookParallel(name, ...args) {
|
||||
return this.callHookWith(parallelTaskCaller, name, args);
|
||||
}
|
||||
callHookWith(caller, name, args) {
|
||||
const event = this._before || this._after ? {
|
||||
name,
|
||||
args,
|
||||
context: {}
|
||||
} : void 0;
|
||||
if (this._before) callEachWith(this._before, event);
|
||||
const result = caller(this._hooks[name] ? [...this._hooks[name]] : [], args, name);
|
||||
if (result instanceof Promise) return result.finally(() => {
|
||||
if (this._after && event) callEachWith(this._after, event);
|
||||
});
|
||||
if (this._after && event) callEachWith(this._after, event);
|
||||
return result;
|
||||
}
|
||||
beforeEach(function_) {
|
||||
this._before = this._before || [];
|
||||
this._before.push(function_);
|
||||
return () => {
|
||||
if (this._before !== void 0) {
|
||||
const index = this._before.indexOf(function_);
|
||||
if (index !== -1) this._before.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
afterEach(function_) {
|
||||
this._after = this._after || [];
|
||||
this._after.push(function_);
|
||||
return () => {
|
||||
if (this._after !== void 0) {
|
||||
const index = this._after.indexOf(function_);
|
||||
if (index !== -1) this._after.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
function createHooks() {
|
||||
return new Hookable();
|
||||
}
|
||||
var HookableCore = class {
|
||||
_hooks;
|
||||
constructor() {
|
||||
this._hooks = {};
|
||||
}
|
||||
hook(name, fn) {
|
||||
if (!name || typeof fn !== "function") return () => {};
|
||||
this._hooks[name] = this._hooks[name] || [];
|
||||
this._hooks[name].push(fn);
|
||||
return () => {
|
||||
if (fn) {
|
||||
this.removeHook(name, fn);
|
||||
fn = void 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
removeHook(name, function_) {
|
||||
const hooks = this._hooks[name];
|
||||
if (hooks) {
|
||||
const index = hooks.indexOf(function_);
|
||||
if (index !== -1) hooks.splice(index, 1);
|
||||
if (hooks.length === 0) this._hooks[name] = void 0;
|
||||
}
|
||||
}
|
||||
callHook(name, ...args) {
|
||||
const hooks = this._hooks[name];
|
||||
if (!hooks || hooks.length === 0) return;
|
||||
return callHooks(hooks.slice(), args, 0);
|
||||
}
|
||||
};
|
||||
const isBrowser = typeof window !== "undefined";
|
||||
function createDebugger(hooks, _options = {}) {
|
||||
const options = {
|
||||
inspect: isBrowser,
|
||||
group: isBrowser,
|
||||
filter: () => true,
|
||||
..._options
|
||||
};
|
||||
const _filter = options.filter;
|
||||
const filter = typeof _filter === "string" ? (name) => name.startsWith(_filter) : _filter;
|
||||
const _tag = options.tag ? `[${options.tag}] ` : "";
|
||||
const logPrefix = (event) => _tag + event.name + "".padEnd(event._id, "\0");
|
||||
const _idCtr = {};
|
||||
const unsubscribeBefore = hooks.beforeEach((event) => {
|
||||
if (filter !== void 0 && !filter(event.name)) return;
|
||||
_idCtr[event.name] = _idCtr[event.name] || 0;
|
||||
event._id = _idCtr[event.name]++;
|
||||
console.time(logPrefix(event));
|
||||
});
|
||||
const unsubscribeAfter = hooks.afterEach((event) => {
|
||||
if (filter !== void 0 && !filter(event.name)) return;
|
||||
if (options.group) console.groupCollapsed(event.name);
|
||||
if (options.inspect) console.timeLog(logPrefix(event), event.args);
|
||||
else console.timeEnd(logPrefix(event));
|
||||
if (options.group) console.groupEnd();
|
||||
_idCtr[event.name]--;
|
||||
});
|
||||
return { close: () => {
|
||||
unsubscribeBefore();
|
||||
unsubscribeAfter();
|
||||
} };
|
||||
}
|
||||
export { Hookable, HookableCore, createDebugger, createHooks, flatHooks, mergeHooks, parallelCaller, serial, serialCaller };
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "hookable",
|
||||
"version": "6.1.2",
|
||||
"description": "Awaitable hook system",
|
||||
"keywords": [
|
||||
"hook",
|
||||
"hookable",
|
||||
"plugin",
|
||||
"tapable",
|
||||
"tappable"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "unjs/hookable"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.9.6",
|
||||
"@vitest/coverage-v8": "^5",
|
||||
"changelogen": "^0.6.2",
|
||||
"esbuild": "^0.28.2",
|
||||
"eslint-config-unjs": "^0.6.2",
|
||||
"expect-type": "^1.4.0",
|
||||
"hookable-prev": "npm:hookable@^6.1.1",
|
||||
"mitata": "^1.0.34",
|
||||
"obuild": "^0.4.40",
|
||||
"oxfmt": ">=0.67.0",
|
||||
"oxlint": "^1.82.0",
|
||||
"typescript": "^6",
|
||||
"vite": "^8.3.0",
|
||||
"vitest": "^5"
|
||||
},
|
||||
"scripts": {
|
||||
"bench": "node --expose-gc --allow-natives-syntax test/bench.ts",
|
||||
"build": "obuild src/index.ts",
|
||||
"dev": "vitest",
|
||||
"lint": "oxlint . && oxfmt --check src test",
|
||||
"lint:fix": "oxlint . --fix && oxfmt src test",
|
||||
"prepublish": "pnpm build",
|
||||
"release": "pnpm test && pnpm build && changelogen --release --publish --push",
|
||||
"test": "pnpm lint && vitest run --coverage",
|
||||
"test:types": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
+21273
File diff suppressed because it is too large
Load Diff
+64
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "lucide-vue-next",
|
||||
"version": "0.475.0",
|
||||
"author": "Eric Fennis",
|
||||
"description": "A Lucide icon library package for Vue 3 applications",
|
||||
"license": "ISC",
|
||||
"homepage": "https://lucide.dev",
|
||||
"bugs": "https://github.com/lucide-icons/lucide/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lucide-icons/lucide.git",
|
||||
"directory": "packages/lucide-vue-next"
|
||||
},
|
||||
"keywords": [
|
||||
"Lucide",
|
||||
"Vue",
|
||||
"Feather",
|
||||
"Icons",
|
||||
"Icon",
|
||||
"SVG",
|
||||
"Feather Icons",
|
||||
"Fontawesome",
|
||||
"Font Awesome"
|
||||
],
|
||||
"amdName": "lucide-vue-next",
|
||||
"source": "build/lucide-vue-next.js",
|
||||
"main": "dist/cjs/lucide-vue-next.js",
|
||||
"main:umd": "dist/umd/lucide-vue-next.js",
|
||||
"module": "dist/esm/lucide-vue-next.js",
|
||||
"unpkg": "dist/umd/lucide-vue-next.min.js",
|
||||
"typings": "dist/lucide-vue-next.d.ts",
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist",
|
||||
"nuxt.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.1.6",
|
||||
"@testing-library/vue": "^8.0.3",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vue/test-utils": "2.4.5",
|
||||
"rollup": "^4.22.4",
|
||||
"rollup-plugin-dts": "^6.1.0",
|
||||
"vite": "5.1.8",
|
||||
"vitest": "^1.4.0",
|
||||
"vue": "^3.4.21",
|
||||
"@lucide/shared": "1.0.0",
|
||||
"@lucide/rollup-plugins": "1.0.0",
|
||||
"@lucide/build-icons": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm build:bundles",
|
||||
"copy:license": "cp ../../LICENSE ./LICENSE",
|
||||
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
|
||||
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mjs --renderUniqueKey --withAliases --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
|
||||
"build:bundles": "rollup -c ./rollup.config.mjs",
|
||||
"test": "pnpm build:icons && vitest run",
|
||||
"test:watch": "vitest watch",
|
||||
"version": "pnpm version --git-tag-version=false"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+206
@@ -0,0 +1,206 @@
|
||||
import { MotionGlobalConfig } from 'motion-utils';
|
||||
import { time } from '../frameloop/sync-time.mjs';
|
||||
import { JSAnimation } from './JSAnimation.mjs';
|
||||
import { getFinalKeyframe } from './keyframes/get-final.mjs';
|
||||
import { KeyframeResolver, flushKeyframeResolvers } from './keyframes/KeyframesResolver.mjs';
|
||||
import { NativeAnimationExtended } from './NativeAnimationExtended.mjs';
|
||||
import { canAnimate } from './utils/can-animate.mjs';
|
||||
import { makeAnimationInstant } from './utils/make-animation-instant.mjs';
|
||||
import { resolveStartTime } from './utils/resolve-start-time.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
import { supportsBrowserAnimation } from './waapi/supports/waapi.mjs';
|
||||
import { canGroupTransform, TransformAnimation } from './waapi/transforms/TransformAnimation.mjs';
|
||||
|
||||
class AsyncMotionValueAnimation extends WithPromise {
|
||||
constructor(options) {
|
||||
super();
|
||||
/**
|
||||
* Bound to support return animation.stop pattern
|
||||
*/
|
||||
this.stop = () => {
|
||||
if (this._animation) {
|
||||
this._animation.stop();
|
||||
this.stopTimeline?.();
|
||||
}
|
||||
this.keyframeResolver?.cancel();
|
||||
};
|
||||
this.createdAt = time.now();
|
||||
const { keyframes, name, motionValue, element } = options;
|
||||
/**
|
||||
* animateMotionValue builds a fresh options object per value, so
|
||||
* it's completed in place as keyframes resolve rather than copied.
|
||||
*/
|
||||
const optionsWithDefaults = options;
|
||||
optionsWithDefaults.autoplay ?? (optionsWithDefaults.autoplay = true);
|
||||
optionsWithDefaults.delay ?? (optionsWithDefaults.delay = 0);
|
||||
optionsWithDefaults.type ?? (optionsWithDefaults.type = "keyframes");
|
||||
optionsWithDefaults.repeat ?? (optionsWithDefaults.repeat = 0);
|
||||
optionsWithDefaults.repeatDelay ?? (optionsWithDefaults.repeatDelay = 0);
|
||||
optionsWithDefaults.repeatType ?? (optionsWithDefaults.repeatType = "loop");
|
||||
const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
|
||||
this.keyframeResolver = new KeyframeResolver$1(keyframes, (resolvedKeyframes, finalKeyframe, forced) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe, optionsWithDefaults, !forced), name, motionValue, element);
|
||||
this.keyframeResolver?.scheduleResolve();
|
||||
}
|
||||
onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
|
||||
this.keyframeResolver = undefined;
|
||||
const { name, type, velocity, delay, isHandoff, onUpdate } = options;
|
||||
this.resolvedAt = time.now();
|
||||
/**
|
||||
* If we can't animate this value with the resolved keyframes
|
||||
* then we should complete it immediately.
|
||||
*/
|
||||
let canAnimateValue = true;
|
||||
if (!canAnimate(keyframes, name, type, velocity)) {
|
||||
canAnimateValue = false;
|
||||
if (MotionGlobalConfig.instantAnimations || !delay) {
|
||||
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
||||
}
|
||||
keyframes[0] = keyframes[keyframes.length - 1];
|
||||
makeAnimationInstant(options);
|
||||
options.repeat = 0;
|
||||
}
|
||||
const { onComplete } = options;
|
||||
options.finalKeyframe = finalKeyframe;
|
||||
options.keyframes = keyframes;
|
||||
/**
|
||||
* JSAnimation and NativeAnimation call onComplete exactly when
|
||||
* their own `finished` resolves, so this replaces a promise chain
|
||||
* per value with a callback.
|
||||
*/
|
||||
options.onComplete = () => {
|
||||
onComplete?.();
|
||||
this.notifyFinished();
|
||||
};
|
||||
/**
|
||||
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
||||
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
||||
* optimised animation.
|
||||
*
|
||||
* Also skip WAAPI when keyframes aren't animatable, as the resolved
|
||||
* values may not be valid CSS and would trigger browser warnings.
|
||||
*/
|
||||
const useWaapi = canAnimateValue && !isHandoff && supportsBrowserAnimation(options);
|
||||
/**
|
||||
* Independent transforms (x, scale etc) on an HTML element are
|
||||
* composed into one WAAPI transform animation per element.
|
||||
*/
|
||||
const isGroupedTransform = !useWaapi && canGroupTransform(options);
|
||||
/**
|
||||
* Resolve startTime for the animation. A startTime passed in options
|
||||
* (an optimised appear handoff syncing to its WAAPI animation) takes
|
||||
* precedence.
|
||||
*
|
||||
* *Ideally*, we would use the createdAt time as t=0 as the following
|
||||
* frame would then be the first frame of the animation in progress,
|
||||
* which would feel snappier. If keyframes resolved on a later frame,
|
||||
* and long after creation, we start from then instead.
|
||||
*
|
||||
* If they resolved immediately, a JSAnimation picks its own start
|
||||
* time and resolves it the same way on its first frame, so every
|
||||
* animation started in that moment stays in sync. WAAPI has no frame
|
||||
* of ours to do that on.
|
||||
*/
|
||||
if (sync &&
|
||||
(useWaapi ||
|
||||
isGroupedTransform ||
|
||||
this.resolvedAt !== this.createdAt)) {
|
||||
options.startTime ?? (options.startTime = resolveStartTime(this.createdAt, this.resolvedAt));
|
||||
}
|
||||
let animation;
|
||||
if (useWaapi) {
|
||||
/**
|
||||
* The resolver needed the VisualElement, WAAPI needs the DOM
|
||||
* element. JSAnimation reads neither, so this is safe to
|
||||
* leave in place if we fall back to it.
|
||||
*/
|
||||
options.element = options.motionValue?.owner?.current;
|
||||
try {
|
||||
animation = new NativeAnimationExtended(options);
|
||||
}
|
||||
catch {
|
||||
animation = new JSAnimation(options);
|
||||
}
|
||||
}
|
||||
else if (isGroupedTransform) {
|
||||
animation = new TransformAnimation(options);
|
||||
}
|
||||
else {
|
||||
animation = new JSAnimation(options);
|
||||
}
|
||||
if (this.pendingTimeline) {
|
||||
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
||||
this.pendingTimeline = undefined;
|
||||
}
|
||||
this._animation = animation;
|
||||
}
|
||||
get finished() {
|
||||
return this._animation ? this._animation.finished : super.finished;
|
||||
}
|
||||
then(onResolve, _onReject) {
|
||||
return this.finished.finally(onResolve).then(() => { });
|
||||
}
|
||||
/**
|
||||
* The value now, if the animation runs on the compositor.
|
||||
*/
|
||||
liveValue() {
|
||||
return this._animation?.liveValue?.();
|
||||
}
|
||||
get animation() {
|
||||
if (!this._animation) {
|
||||
this.keyframeResolver?.resume();
|
||||
flushKeyframeResolvers();
|
||||
}
|
||||
return this._animation;
|
||||
}
|
||||
get duration() {
|
||||
return this.animation.duration;
|
||||
}
|
||||
get iterationDuration() {
|
||||
return this.animation.iterationDuration;
|
||||
}
|
||||
get time() {
|
||||
return this.animation.time;
|
||||
}
|
||||
set time(newTime) {
|
||||
this.animation.time = newTime;
|
||||
}
|
||||
get speed() {
|
||||
return this.animation.speed;
|
||||
}
|
||||
get state() {
|
||||
return this.animation.state;
|
||||
}
|
||||
set speed(newSpeed) {
|
||||
this.animation.speed = newSpeed;
|
||||
}
|
||||
get startTime() {
|
||||
return this.animation.startTime;
|
||||
}
|
||||
attachTimeline(timeline) {
|
||||
if (this._animation) {
|
||||
this.stopTimeline = this.animation.attachTimeline(timeline);
|
||||
}
|
||||
else {
|
||||
this.pendingTimeline = timeline;
|
||||
}
|
||||
return () => this.stop();
|
||||
}
|
||||
play() {
|
||||
this.animation.play();
|
||||
}
|
||||
pause() {
|
||||
this.animation.pause();
|
||||
}
|
||||
complete() {
|
||||
this.animation.complete();
|
||||
}
|
||||
cancel() {
|
||||
if (this._animation) {
|
||||
this.animation.cancel();
|
||||
}
|
||||
this.keyframeResolver?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
export { AsyncMotionValueAnimation };
|
||||
//# sourceMappingURL=AsyncMotionValueAnimation.mjs.map
|
||||
Generated
Vendored
+154
@@ -0,0 +1,154 @@
|
||||
import { time } from '../frameloop/sync-time.mjs';
|
||||
import { calcGeneratorVelocity } from './generators/utils/velocity.mjs';
|
||||
import { resolveStartTime } from './utils/resolve-start-time.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
import { frame, cancelFrame } from '../frameloop/frame.mjs';
|
||||
|
||||
/**
|
||||
* Every running follower ticks from this one frame callback.
|
||||
*/
|
||||
const active = new Set();
|
||||
const tickAll = ({ timestamp }) => {
|
||||
active.forEach((animation) => animation.tick(timestamp));
|
||||
};
|
||||
/**
|
||||
* A lean animation for values that follow a target (`followValue`,
|
||||
* `springValue`): a single numeric generator, retargeted in place as
|
||||
* the target moves and ticked from a frame callback shared by every
|
||||
* follower. Only implements the controls `MotionValue.animation`
|
||||
* exposes. Repeat options are ignored: a follower only ever heads for
|
||||
* its latest target.
|
||||
*/
|
||||
class FollowAnimation extends WithPromise {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.state = "idle";
|
||||
this.startTime = 0;
|
||||
this.currentTime = 0;
|
||||
/**
|
||||
* Whether the first tick has fired `onPlay`.
|
||||
*/
|
||||
this.started = false;
|
||||
/**
|
||||
* A target set since the last tick, applied at the next one so any
|
||||
* number of sets per frame cost one retarget.
|
||||
*/
|
||||
this.hasNextTarget = false;
|
||||
this.nextTarget = 0;
|
||||
/**
|
||||
* Bound to the instance so `animation.stop` can be passed around.
|
||||
*/
|
||||
this.stop = () => {
|
||||
if (this.state === "idle")
|
||||
return;
|
||||
this.teardown();
|
||||
this.options.onStop?.();
|
||||
};
|
||||
this.options = options;
|
||||
this.factory = options.type;
|
||||
this.generator = this.factory(options);
|
||||
const { driver } = options;
|
||||
if (driver)
|
||||
this.driver = driver((timestamp) => this.tick(timestamp));
|
||||
this.startTime = this.now();
|
||||
this.state = "running";
|
||||
if (this.driver) {
|
||||
this.driver.start();
|
||||
}
|
||||
else {
|
||||
active.size || frame.update(tickAll, true);
|
||||
active.add(this);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Aim at a new target from wherever the animation has reached by the
|
||||
* next tick. `velocity` fixes the velocity to steer with, otherwise
|
||||
* the generator's is used.
|
||||
*/
|
||||
setTarget(target, velocity) {
|
||||
this.nextTarget = target;
|
||||
this.nextVelocity = velocity;
|
||||
this.hasNextTarget = true;
|
||||
}
|
||||
/**
|
||||
* Steer towards new keyframes from the current position and velocity.
|
||||
* Springs are updated in place; other generators are recreated.
|
||||
*/
|
||||
retarget(keyframes, velocity) {
|
||||
const { options, generator } = this;
|
||||
options.keyframes = keyframes;
|
||||
options.velocity = velocity;
|
||||
this.startTime = this.now();
|
||||
this.currentTime = 0;
|
||||
if (generator.retarget) {
|
||||
generator.retarget(keyframes, velocity);
|
||||
}
|
||||
else {
|
||||
this.generator = this.factory(options);
|
||||
}
|
||||
}
|
||||
tick(timestamp) {
|
||||
const { options, hasNextTarget: retargeted } = this;
|
||||
const { delay = 0, onUpdate, onPlay } = options;
|
||||
// Retargets happen on a tick, so only the first start can lag
|
||||
if (!this.started) {
|
||||
this.startTime = resolveStartTime(this.startTime, timestamp);
|
||||
}
|
||||
const elapsed = Math.round(timestamp - this.startTime) - delay;
|
||||
const t = (this.currentTime = Math.max(0, elapsed));
|
||||
const state = this.generator.next(t);
|
||||
// Hold the origin through the delay, even if the generator is already at rest
|
||||
const value = elapsed < 0 ? options.keyframes[0] : state.value;
|
||||
if (retargeted) {
|
||||
/**
|
||||
* Steer from where the current trajectory has reached this
|
||||
* frame, then render that position as the new origin.
|
||||
*/
|
||||
this.hasNextTarget = false;
|
||||
const { keyframes } = options;
|
||||
keyframes[0] = value;
|
||||
keyframes[1] = this.nextTarget;
|
||||
this.retarget(keyframes, this.nextVelocity ?? this.getGeneratorVelocity());
|
||||
}
|
||||
if (retargeted || !this.started) {
|
||||
this.started = true;
|
||||
onPlay?.();
|
||||
}
|
||||
/**
|
||||
* Callbacks may stop this animation or set a new target, so check
|
||||
* the live state rather than what was read at the top of the tick.
|
||||
*/
|
||||
if (this.state !== "running")
|
||||
return;
|
||||
onUpdate?.(value);
|
||||
if (state.done &&
|
||||
elapsed >= 0 &&
|
||||
!retargeted &&
|
||||
!this.hasNextTarget &&
|
||||
this.state === "running") {
|
||||
this.notifyFinished();
|
||||
this.teardown();
|
||||
this.state = "finished";
|
||||
options.onComplete?.();
|
||||
}
|
||||
}
|
||||
getGeneratorVelocity() {
|
||||
return calcGeneratorVelocity(this.generator, this.currentTime, this.options.velocity);
|
||||
}
|
||||
now() {
|
||||
return this.driver ? this.driver.now() : time.now();
|
||||
}
|
||||
teardown() {
|
||||
this.state = "idle";
|
||||
if (this.driver) {
|
||||
this.driver.stop();
|
||||
}
|
||||
else {
|
||||
active.delete(this);
|
||||
active.size || cancelFrame(tickAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { FollowAnimation };
|
||||
//# sourceMappingURL=FollowAnimation.mjs.map
|
||||
Generated
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
class GroupAnimation {
|
||||
constructor(animations) {
|
||||
// Bound to accommodate common `return animation.stop` pattern
|
||||
this.stop = () => this.runAll("stop");
|
||||
this.animations = animations.filter(Boolean);
|
||||
}
|
||||
get finished() {
|
||||
return Promise.all(this.animations.map((animation) => animation.finished));
|
||||
}
|
||||
/**
|
||||
* TODO: Filter out cancelled or stopped animations before returning
|
||||
*/
|
||||
getAll(propName) {
|
||||
return this.animations[0][propName];
|
||||
}
|
||||
setAll(propName, newValue) {
|
||||
for (let i = 0; i < this.animations.length; i++) {
|
||||
this.animations[i][propName] = newValue;
|
||||
}
|
||||
}
|
||||
attachTimeline(timeline) {
|
||||
const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline));
|
||||
return () => {
|
||||
subscriptions.forEach((cancel, i) => {
|
||||
cancel && cancel();
|
||||
this.animations[i].stop();
|
||||
});
|
||||
};
|
||||
}
|
||||
get time() {
|
||||
return this.getAll("time");
|
||||
}
|
||||
set time(time) {
|
||||
this.setAll("time", time);
|
||||
}
|
||||
get speed() {
|
||||
return this.getAll("speed");
|
||||
}
|
||||
set speed(speed) {
|
||||
this.setAll("speed", speed);
|
||||
}
|
||||
get state() {
|
||||
return this.getAll("state");
|
||||
}
|
||||
get startTime() {
|
||||
return this.getAll("startTime");
|
||||
}
|
||||
get duration() {
|
||||
return getMax(this.animations, "duration");
|
||||
}
|
||||
get iterationDuration() {
|
||||
return getMax(this.animations, "iterationDuration");
|
||||
}
|
||||
runAll(methodName) {
|
||||
this.animations.forEach((controls) => controls[methodName]());
|
||||
}
|
||||
play() {
|
||||
this.runAll("play");
|
||||
}
|
||||
pause() {
|
||||
this.runAll("pause");
|
||||
}
|
||||
cancel() {
|
||||
this.runAll("cancel");
|
||||
}
|
||||
complete() {
|
||||
this.runAll("complete");
|
||||
}
|
||||
}
|
||||
function getMax(animations, propName) {
|
||||
let max = 0;
|
||||
for (let i = 0; i < animations.length; i++) {
|
||||
const value = animations[i][propName];
|
||||
if (value !== null && value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
export { GroupAnimation };
|
||||
//# sourceMappingURL=GroupAnimation.mjs.map
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import { GroupAnimation } from './GroupAnimation.mjs';
|
||||
|
||||
class GroupAnimationWithThen extends GroupAnimation {
|
||||
then(onResolve, _onReject) {
|
||||
return this.finished.finally(onResolve).then(() => { });
|
||||
}
|
||||
}
|
||||
|
||||
export { GroupAnimationWithThen };
|
||||
//# sourceMappingURL=GroupAnimationWithThen.mjs.map
|
||||
Generated
Vendored
+418
@@ -0,0 +1,418 @@
|
||||
import { invariant, pipe, clamp, millisecondsToSeconds, secondsToMilliseconds } from 'motion-utils';
|
||||
import { time } from '../frameloop/sync-time.mjs';
|
||||
import { mix } from '../utils/mix/index.mjs';
|
||||
import { frameloopDriver } from './drivers/frame.mjs';
|
||||
import { inertia } from './generators/inertia.mjs';
|
||||
import { keyframes } from './generators/keyframes.mjs';
|
||||
import { calcGeneratorDuration } from './generators/utils/calc-duration.mjs';
|
||||
import { calcGeneratorVelocity } from './generators/utils/velocity.mjs';
|
||||
import { getFinalKeyframe } from './keyframes/get-final.mjs';
|
||||
import { replaceTransitionType } from './utils/replace-transition-type.mjs';
|
||||
import { resolveStartTime } from './utils/resolve-start-time.mjs';
|
||||
import { notifyAnimationStart } from './utils/notify-inspector.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
|
||||
const percentToProgress = (percent) => percent / 100;
|
||||
const round = (value) => Math.round(value * 10000) / 10000;
|
||||
class JSAnimation extends WithPromise {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.state = "idle";
|
||||
this.startTime = null;
|
||||
this.isStopped = false;
|
||||
/**
|
||||
* The current time of the animation.
|
||||
*/
|
||||
this.currentTime = 0;
|
||||
/**
|
||||
* The time at which the animation was paused.
|
||||
*/
|
||||
this.holdTime = null;
|
||||
/**
|
||||
* Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
|
||||
*/
|
||||
this.playbackSpeed = 1;
|
||||
/**
|
||||
* Reusable state object for the delay phase to avoid
|
||||
* allocating a new object every frame.
|
||||
*/
|
||||
this.delayState = {
|
||||
done: false,
|
||||
value: undefined,
|
||||
};
|
||||
/**
|
||||
* This method is bound to the instance to fix a pattern where
|
||||
* animation.stop is returned as a reference from a useEffect.
|
||||
*/
|
||||
this.stop = () => {
|
||||
const { motionValue } = this.options;
|
||||
if (motionValue && motionValue.updatedAt !== time.now()) {
|
||||
this.tick(time.now());
|
||||
}
|
||||
this.isStopped = true;
|
||||
if (this.state === "idle")
|
||||
return;
|
||||
this.teardown();
|
||||
this.options.onStop?.();
|
||||
};
|
||||
this.options = options;
|
||||
this.initAnimation();
|
||||
this.play();
|
||||
if (options.autoplay === false)
|
||||
this.pause();
|
||||
notifyAnimationStart(this, this.options);
|
||||
}
|
||||
initAnimation() {
|
||||
const { options } = this;
|
||||
replaceTransitionType(options);
|
||||
const { type = keyframes, repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = options;
|
||||
let { keyframes: keyframes$1 } = options;
|
||||
const generatorFactory = type || keyframes;
|
||||
if (process.env.NODE_ENV !== "production" &&
|
||||
generatorFactory !== keyframes) {
|
||||
invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`, "spring-two-frames");
|
||||
}
|
||||
if (generatorFactory !== keyframes &&
|
||||
typeof keyframes$1[0] !== "number") {
|
||||
this.mixKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
|
||||
keyframes$1 = [0, 100];
|
||||
}
|
||||
const generator = generatorFactory(keyframes$1 === options.keyframes
|
||||
? options
|
||||
: { ...options, keyframes: keyframes$1 });
|
||||
/**
|
||||
* If we have a mirror repeat type we need to create a second generator that outputs the
|
||||
* mirrored (not reversed) animation and later ping pong between the two generators.
|
||||
*/
|
||||
if (repeatType === "mirror") {
|
||||
this.mirroredGenerator = generatorFactory({
|
||||
...options,
|
||||
keyframes: [...keyframes$1].reverse(),
|
||||
velocity: -velocity,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* If duration is undefined and we have repeat options,
|
||||
* we need to calculate a duration from the generator.
|
||||
*
|
||||
* We set it to the generator itself to cache the duration.
|
||||
* Any timeline resolver will need to have already precalculated
|
||||
* the duration by this step.
|
||||
*/
|
||||
if (generator.calculatedDuration === null) {
|
||||
generator.calculatedDuration = calcGeneratorDuration(generator);
|
||||
}
|
||||
const { calculatedDuration } = generator;
|
||||
this.calculatedDuration = calculatedDuration;
|
||||
this.resolvedDuration = calculatedDuration + repeatDelay;
|
||||
this.totalDuration = this.resolvedDuration * (repeat + 1) - repeatDelay;
|
||||
this.generator = generator;
|
||||
}
|
||||
updateTime(timestamp) {
|
||||
const animationTime = Math.round(timestamp - this.startTime) * this.playbackSpeed;
|
||||
// Update currentTime
|
||||
if (this.holdTime !== null) {
|
||||
this.currentTime = this.holdTime;
|
||||
}
|
||||
else {
|
||||
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
||||
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
||||
// example.
|
||||
this.currentTime = animationTime;
|
||||
}
|
||||
}
|
||||
tick(timestamp, sample = false) {
|
||||
const { generator, totalDuration, calculatedDuration } = this;
|
||||
if (this.startTime === null)
|
||||
return generator.next(0);
|
||||
const { delay = 0, keyframes, type, onUpdate, finalKeyframe, } = this.options;
|
||||
if (this.startTime === this.pendingStartTime) {
|
||||
this.startTime = resolveStartTime(this.startTime, timestamp);
|
||||
}
|
||||
this.pendingStartTime = undefined;
|
||||
/**
|
||||
* requestAnimationFrame timestamps can come through as lower than
|
||||
* the startTime as set by performance.now(). Here we prevent this,
|
||||
* though in the future it could be possible to make setting startTime
|
||||
* a pending operation that gets resolved here.
|
||||
*/
|
||||
if (this.speed > 0) {
|
||||
this.startTime = Math.min(this.startTime, timestamp);
|
||||
}
|
||||
else if (this.speed < 0) {
|
||||
this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
|
||||
}
|
||||
if (sample) {
|
||||
this.currentTime = timestamp;
|
||||
}
|
||||
else {
|
||||
this.updateTime(timestamp);
|
||||
}
|
||||
// Rebase on delay
|
||||
const timeWithoutDelay = this.currentTime - delay * (this.playbackSpeed >= 0 ? 1 : -1);
|
||||
const isInDelayPhase = this.playbackSpeed >= 0
|
||||
? timeWithoutDelay < 0
|
||||
: timeWithoutDelay > totalDuration;
|
||||
this.currentTime = Math.max(timeWithoutDelay, 0);
|
||||
// If this animation has finished, set the current time to the total duration.
|
||||
if (this.state === "finished" && this.holdTime === null) {
|
||||
this.currentTime = totalDuration;
|
||||
}
|
||||
const state = this.generate(this.currentTime, isInDelayPhase);
|
||||
let { done } = state;
|
||||
if (!isInDelayPhase && calculatedDuration !== null) {
|
||||
done =
|
||||
this.playbackSpeed >= 0
|
||||
? this.currentTime >= totalDuration
|
||||
: this.currentTime <= 0;
|
||||
}
|
||||
const isAnimationFinished = this.holdTime === null &&
|
||||
(this.state === "finished" || (this.state === "running" && done));
|
||||
// TODO: The exception for inertia could be cleaner here
|
||||
if (isAnimationFinished && type !== inertia) {
|
||||
state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
||||
}
|
||||
if (onUpdate) {
|
||||
onUpdate(state.value);
|
||||
}
|
||||
if (isAnimationFinished) {
|
||||
this.finish();
|
||||
}
|
||||
return state;
|
||||
}
|
||||
/**
|
||||
* This animation's time, including delay, at a timestamp.
|
||||
*/
|
||||
at(timestamp) {
|
||||
return (this.holdTime ??
|
||||
round((timestamp - this.startTime) * this.playbackSpeed));
|
||||
}
|
||||
/**
|
||||
* The value at a timestamp, without changing this animation's state.
|
||||
* If resolved, it ends at the last resolved keyframe, which is in the
|
||||
* same unit as the others, rather than at finalKeyframe.
|
||||
*/
|
||||
sampleAt(timestamp, resolved) {
|
||||
const { options, totalDuration } = this;
|
||||
const t = this.at(timestamp) - (options.delay || 0);
|
||||
return t >= totalDuration && options.type !== inertia
|
||||
? getFinalKeyframe(options.keyframes, options, resolved ? undefined : options.finalKeyframe)
|
||||
: this.generate(Math.max(t, 0), t < 0).value;
|
||||
}
|
||||
/**
|
||||
* The generator's state `currentTime` ms after the delay, without
|
||||
* changing the animation's own state.
|
||||
*/
|
||||
generate(currentTime, isInDelayPhase) {
|
||||
const { generator, totalDuration, mixKeyframes, mirroredGenerator, resolvedDuration, } = this;
|
||||
const { keyframes, repeat, repeatType, repeatDelay } = this.options;
|
||||
let elapsed = currentTime;
|
||||
let frameGenerator = generator;
|
||||
if (repeat) {
|
||||
/**
|
||||
* Get the current progress (0-1) of the animation. If t is >
|
||||
* than duration we'll get values like 2.5 (midway through the
|
||||
* third iteration)
|
||||
*/
|
||||
const progress = Math.min(currentTime, totalDuration) / resolvedDuration;
|
||||
/**
|
||||
* Get the current iteration (0 indexed). For instance the floor of
|
||||
* 2.5 is 2.
|
||||
*/
|
||||
let currentIteration = Math.floor(progress);
|
||||
/**
|
||||
* Get the current progress of the iteration by taking the remainder
|
||||
* so 2.5 is 0.5 through iteration 2
|
||||
*/
|
||||
let iterationProgress = progress % 1.0;
|
||||
/**
|
||||
* If iteration progress is 1 we count that as the end
|
||||
* of the previous iteration.
|
||||
*/
|
||||
if (!iterationProgress && progress >= 1) {
|
||||
iterationProgress = 1;
|
||||
}
|
||||
iterationProgress === 1 && currentIteration--;
|
||||
currentIteration = Math.min(currentIteration, repeat + 1);
|
||||
/**
|
||||
* Reverse progress if we're not running in "normal" direction
|
||||
*/
|
||||
const isOddIteration = Boolean(currentIteration % 2);
|
||||
if (isOddIteration) {
|
||||
if (repeatType === "reverse") {
|
||||
iterationProgress = 1 - iterationProgress;
|
||||
if (repeatDelay) {
|
||||
iterationProgress -= repeatDelay / resolvedDuration;
|
||||
}
|
||||
}
|
||||
else if (repeatType === "mirror") {
|
||||
frameGenerator = mirroredGenerator;
|
||||
}
|
||||
}
|
||||
elapsed = clamp(0, 1, iterationProgress) * resolvedDuration;
|
||||
}
|
||||
/**
|
||||
* If we're in negative time, set state as the initial keyframe.
|
||||
* This prevents delay: x, duration: 0 animations from finishing
|
||||
* instantly.
|
||||
*/
|
||||
let state;
|
||||
if (isInDelayPhase) {
|
||||
this.delayState.value = keyframes[0];
|
||||
state = this.delayState;
|
||||
}
|
||||
else {
|
||||
state = frameGenerator.next(elapsed);
|
||||
}
|
||||
if (mixKeyframes && !isInDelayPhase) {
|
||||
state.value = mixKeyframes(state.value);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
/**
|
||||
* Allows the returned animation to be awaited or promise-chained. Currently
|
||||
* resolves when the animation finishes at all but in a future update could/should
|
||||
* reject if its cancels.
|
||||
*/
|
||||
then(resolve, reject) {
|
||||
return this.finished.then(resolve, reject);
|
||||
}
|
||||
get duration() {
|
||||
return millisecondsToSeconds(this.calculatedDuration);
|
||||
}
|
||||
get iterationDuration() {
|
||||
const { delay = 0 } = this.options || {};
|
||||
return this.duration + millisecondsToSeconds(delay);
|
||||
}
|
||||
get time() {
|
||||
return millisecondsToSeconds(this.currentTime);
|
||||
}
|
||||
set time(newTime) {
|
||||
newTime = secondsToMilliseconds(newTime);
|
||||
this.currentTime = newTime;
|
||||
if (this.startTime === null ||
|
||||
this.holdTime !== null ||
|
||||
this.playbackSpeed === 0) {
|
||||
this.holdTime = newTime;
|
||||
}
|
||||
else if (this.driver) {
|
||||
this.startTime = this.driver.now() - newTime / this.playbackSpeed;
|
||||
}
|
||||
if (this.driver) {
|
||||
this.driver.start(false);
|
||||
}
|
||||
else {
|
||||
this.startTime = 0;
|
||||
this.state = "paused";
|
||||
this.holdTime = newTime;
|
||||
this.tick(newTime);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the generator's velocity at the current time in units/second.
|
||||
* Uses the analytical derivative when available (springs), avoiding
|
||||
* the MotionValue's frame-dependent velocity estimation.
|
||||
*/
|
||||
getGeneratorVelocity() {
|
||||
return calcGeneratorVelocity(this.generator, this.currentTime, this.options.velocity);
|
||||
}
|
||||
get speed() {
|
||||
return this.playbackSpeed;
|
||||
}
|
||||
set speed(newSpeed) {
|
||||
const hasChanged = this.playbackSpeed !== newSpeed;
|
||||
if (hasChanged && this.driver) {
|
||||
this.updateTime(time.now());
|
||||
}
|
||||
this.playbackSpeed = newSpeed;
|
||||
if (hasChanged && this.driver) {
|
||||
this.time = millisecondsToSeconds(this.currentTime);
|
||||
}
|
||||
}
|
||||
play() {
|
||||
if (this.isStopped)
|
||||
return;
|
||||
const { driver = frameloopDriver, startTime } = this.options;
|
||||
if (!this.driver) {
|
||||
this.driver = driver((timestamp) => this.tick(timestamp));
|
||||
}
|
||||
this.options.onPlay?.();
|
||||
const now = this.driver.now();
|
||||
if (this.state === "finished") {
|
||||
this.updateFinished();
|
||||
this.startTime = now;
|
||||
}
|
||||
else if (this.holdTime !== null) {
|
||||
this.startTime = now - this.holdTime / (this.playbackSpeed || 1);
|
||||
}
|
||||
else if (!this.startTime) {
|
||||
this.startTime = startTime ?? (this.pendingStartTime = now);
|
||||
}
|
||||
if (this.state === "finished" && this.speed < 0) {
|
||||
this.startTime += this.calculatedDuration;
|
||||
}
|
||||
this.holdTime = null;
|
||||
/**
|
||||
* Set playState to running only after we've used it in
|
||||
* the previous logic.
|
||||
*/
|
||||
this.state = "running";
|
||||
this.driver.start();
|
||||
}
|
||||
pause() {
|
||||
this.state = "paused";
|
||||
this.updateTime(time.now());
|
||||
this.holdTime = this.currentTime;
|
||||
}
|
||||
complete() {
|
||||
if (this.state !== "running") {
|
||||
this.play();
|
||||
}
|
||||
this.state = "finished";
|
||||
this.holdTime = null;
|
||||
}
|
||||
finish() {
|
||||
this.notifyFinished();
|
||||
this.teardown();
|
||||
this.state = "finished";
|
||||
this.options.onComplete?.();
|
||||
}
|
||||
cancel() {
|
||||
this.holdTime = null;
|
||||
this.startTime = 0;
|
||||
this.tick(0);
|
||||
this.teardown();
|
||||
this.options.onCancel?.();
|
||||
}
|
||||
teardown() {
|
||||
this.state = "idle";
|
||||
this.stopDriver();
|
||||
this.startTime = this.holdTime = null;
|
||||
}
|
||||
stopDriver() {
|
||||
if (!this.driver)
|
||||
return;
|
||||
this.driver.stop();
|
||||
this.driver = undefined;
|
||||
}
|
||||
sample(sampleTime) {
|
||||
this.startTime = 0;
|
||||
return this.tick(sampleTime, true);
|
||||
}
|
||||
attachTimeline(timeline) {
|
||||
if (this.options.allowFlatten) {
|
||||
this.options.type = "keyframes";
|
||||
this.options.ease = "linear";
|
||||
this.initAnimation();
|
||||
}
|
||||
this.driver?.stop();
|
||||
return timeline.observe(this);
|
||||
}
|
||||
}
|
||||
// Legacy function support
|
||||
function animateValue(options) {
|
||||
return new JSAnimation(options);
|
||||
}
|
||||
|
||||
export { JSAnimation, animateValue, round };
|
||||
//# sourceMappingURL=JSAnimation.mjs.map
|
||||
Generated
Vendored
+178
@@ -0,0 +1,178 @@
|
||||
import { invariant, millisecondsToSeconds, secondsToMilliseconds, noop } from 'motion-utils';
|
||||
import { setStyle } from '../render/dom/style-set.mjs';
|
||||
import { supportsScrollTimeline } from '../utils/supports/scroll-timeline.mjs';
|
||||
import { getFinalKeyframe } from './keyframes/get-final.mjs';
|
||||
import { WithPromise } from './utils/WithPromise.mjs';
|
||||
import { notifyAnimationStart } from './utils/notify-inspector.mjs';
|
||||
import { startWaapiAnimation } from './waapi/start-waapi-animation.mjs';
|
||||
import { applyGeneratorOptions } from './waapi/utils/apply-generator.mjs';
|
||||
|
||||
/**
|
||||
* NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
|
||||
*/
|
||||
class NativeAnimation extends WithPromise {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.finishedTime = null;
|
||||
this.isStopped = false;
|
||||
/**
|
||||
* Tracks a manually-set start time that takes precedence over WAAPI's
|
||||
* dynamic startTime. This is cleared when play() or time setter is called,
|
||||
* allowing WAAPI to take over timing.
|
||||
*/
|
||||
this.manualStartTime = null;
|
||||
if (!options)
|
||||
return;
|
||||
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
|
||||
this.isPseudoElement = Boolean(pseudoElement);
|
||||
this.allowFlatten = allowFlatten;
|
||||
this.options = options;
|
||||
invariant(typeof options.type !== "string", `Mini animate() doesn't support "type" as a string.`, "mini-spring");
|
||||
const transition = applyGeneratorOptions(options);
|
||||
this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
|
||||
if (transition.autoplay === false) {
|
||||
this.animation.pause();
|
||||
}
|
||||
this.animation.onfinish = () => {
|
||||
this.finishedTime = this.time;
|
||||
if (!pseudoElement) {
|
||||
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
||||
if (this.updateMotionValue) {
|
||||
this.updateMotionValue(keyframe);
|
||||
}
|
||||
/**
|
||||
* If we can, we want to commit the final style as set by the user,
|
||||
* rather than the computed keyframe value supplied by the animation.
|
||||
* We always do this, even when a motion value is present, to prevent
|
||||
* a visual flash in Firefox where the WAAPI animation's fill is removed
|
||||
* during cancel() before the scheduled render can apply the correct value.
|
||||
*/
|
||||
setStyle(element, name, keyframe);
|
||||
this.animation.cancel();
|
||||
}
|
||||
onComplete?.();
|
||||
this.notifyFinished();
|
||||
};
|
||||
notifyAnimationStart(this, options, transition);
|
||||
}
|
||||
play() {
|
||||
if (this.isStopped)
|
||||
return;
|
||||
this.manualStartTime = null;
|
||||
this.animation.play();
|
||||
if (this.state === "finished") {
|
||||
this.updateFinished();
|
||||
}
|
||||
}
|
||||
pause() {
|
||||
this.animation.pause();
|
||||
}
|
||||
complete() {
|
||||
this.animation.finish?.();
|
||||
}
|
||||
cancel() {
|
||||
try {
|
||||
this.animation.cancel();
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
stop() {
|
||||
if (this.isStopped)
|
||||
return;
|
||||
this.isStopped = true;
|
||||
const { state } = this;
|
||||
if (state === "idle" || state === "finished") {
|
||||
return;
|
||||
}
|
||||
if (this.updateMotionValue) {
|
||||
this.updateMotionValue();
|
||||
}
|
||||
else {
|
||||
this.commitStyles();
|
||||
}
|
||||
if (!this.isPseudoElement)
|
||||
this.cancel();
|
||||
}
|
||||
/**
|
||||
* WAAPI doesn't natively have any interruption capabilities.
|
||||
*
|
||||
* In this method, we commit styles back to the DOM before cancelling
|
||||
* the animation.
|
||||
*
|
||||
* This is designed to be overridden by NativeAnimationExtended, which
|
||||
* will create a renderless JS animation and sample it twice to calculate
|
||||
* its current value, "previous" value, and therefore allow
|
||||
* Motion to also correctly calculate velocity for any subsequent animation
|
||||
* while deferring the commit until the next animation frame.
|
||||
*/
|
||||
commitStyles() {
|
||||
const element = this.options?.element;
|
||||
if (!this.isPseudoElement && element?.isConnected) {
|
||||
this.animation.commitStyles?.();
|
||||
}
|
||||
}
|
||||
get duration() {
|
||||
const duration = this.animation.effect?.getComputedTiming?.().duration || 0;
|
||||
return millisecondsToSeconds(Number(duration));
|
||||
}
|
||||
get iterationDuration() {
|
||||
const { delay = 0 } = this.options || {};
|
||||
return this.duration + millisecondsToSeconds(delay);
|
||||
}
|
||||
get time() {
|
||||
return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
|
||||
}
|
||||
set time(newTime) {
|
||||
const wasFinished = this.finishedTime !== null;
|
||||
this.manualStartTime = null;
|
||||
this.finishedTime = null;
|
||||
this.animation.currentTime = secondsToMilliseconds(newTime);
|
||||
if (wasFinished) {
|
||||
this.animation.pause();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The playback speed of the animation.
|
||||
* 1 = normal speed, 2 = double speed, 0.5 = half speed.
|
||||
*/
|
||||
get speed() {
|
||||
return this.animation.playbackRate;
|
||||
}
|
||||
set speed(newSpeed) {
|
||||
// Allow backwards playback after finishing
|
||||
if (newSpeed < 0)
|
||||
this.finishedTime = null;
|
||||
this.animation.playbackRate = newSpeed;
|
||||
}
|
||||
get state() {
|
||||
return this.finishedTime !== null
|
||||
? "finished"
|
||||
: this.animation.playState;
|
||||
}
|
||||
get startTime() {
|
||||
return this.manualStartTime ?? Number(this.animation.startTime);
|
||||
}
|
||||
set startTime(newStartTime) {
|
||||
this.manualStartTime = this.animation.startTime = newStartTime;
|
||||
}
|
||||
/**
|
||||
* Attaches a timeline to the animation, for instance the `ScrollTimeline`.
|
||||
*/
|
||||
attachTimeline({ timeline, onAttach, observe, }) {
|
||||
if (this.allowFlatten) {
|
||||
this.animation.effect?.updateTiming({ easing: "linear" });
|
||||
}
|
||||
this.animation.onfinish = null;
|
||||
if (timeline && supportsScrollTimeline()) {
|
||||
this.animation.timeline = timeline;
|
||||
onAttach?.(this.animation);
|
||||
return noop;
|
||||
}
|
||||
else {
|
||||
return observe(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { NativeAnimation };
|
||||
//# sourceMappingURL=NativeAnimation.mjs.map
|
||||
Generated
Vendored
+115
@@ -0,0 +1,115 @@
|
||||
import { clamp } from 'motion-utils';
|
||||
import { time } from '../frameloop/sync-time.mjs';
|
||||
import { setStyle } from '../render/dom/style-set.mjs';
|
||||
import { JSAnimation } from './JSAnimation.mjs';
|
||||
import { NativeAnimation } from './NativeAnimation.mjs';
|
||||
import { replaceTransitionType } from './utils/replace-transition-type.mjs';
|
||||
import { replaceStringEasing } from './waapi/utils/unsupported-easing.mjs';
|
||||
|
||||
/**
|
||||
* 10ms is chosen here as it strikes a balance between smooth
|
||||
* results (more than one keyframe per frame at 60fps) and
|
||||
* keyframe quantity.
|
||||
*/
|
||||
const sampleDelta = 10; //ms
|
||||
class NativeAnimationExtended extends NativeAnimation {
|
||||
constructor(options) {
|
||||
/**
|
||||
* The base NativeAnimation function only supports a subset
|
||||
* of Motion easings, and WAAPI also only supports some
|
||||
* easing functions via string/cubic-bezier definitions.
|
||||
*
|
||||
* This function replaces those unsupported easing functions
|
||||
* with a JS easing function. This will later get compiled
|
||||
* to a linear() easing function.
|
||||
*/
|
||||
replaceStringEasing(options);
|
||||
/**
|
||||
* Ensure we replace the transition type with a generator function
|
||||
* before passing to WAAPI.
|
||||
*
|
||||
* TODO: Does this have a better home? It could be shared with
|
||||
* JSAnimation.
|
||||
*/
|
||||
replaceTransitionType(options);
|
||||
super(options);
|
||||
/**
|
||||
* Only set startTime when the animation should autoplay.
|
||||
* Setting startTime on a paused WAAPI animation unpauses it
|
||||
* (per the WAAPI spec), which breaks autoplay: false.
|
||||
*/
|
||||
if (options.startTime !== undefined && options.autoplay !== false) {
|
||||
this.startTime = options.startTime;
|
||||
}
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* The animation's time now, in ms. Wall-clock rather than WAAPI's
|
||||
* currentTime while running, as under CPU load currentTime may not
|
||||
* reflect the actual elapsed time, causing incorrect sampling and
|
||||
* visual jumps.
|
||||
*/
|
||||
elapsed() {
|
||||
return this.state === "running"
|
||||
? (time.now() - this.startTime) * this.speed
|
||||
: this.time * 1000;
|
||||
}
|
||||
/**
|
||||
* The value at a time, from a renderless JS animation.
|
||||
*/
|
||||
sample(sampleTime) {
|
||||
if (!this.sampler) {
|
||||
const { motionValue, onUpdate, onComplete, onPlay, onStop, element, ...options } = this.options;
|
||||
/**
|
||||
* Stop it straight away so its driver doesn't run every frame.
|
||||
* It can still be sampled.
|
||||
*/
|
||||
this.sampler = new JSAnimation({ ...options, autoplay: false });
|
||||
this.sampler.stop();
|
||||
}
|
||||
return this.sampler.sample(Math.max(0, sampleTime)).value;
|
||||
}
|
||||
/**
|
||||
* The value now, as WAAPI doesn't set the motion value while it runs.
|
||||
*/
|
||||
liveValue() {
|
||||
const { state } = this;
|
||||
if (state === "running" || state === "paused") {
|
||||
return this.sample(this.elapsed());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* WAAPI doesn't natively have any interruption capabilities.
|
||||
*
|
||||
* Rather than read committed styles back out of the DOM, we can
|
||||
* sample a renderless JS animation twice to calculate its current
|
||||
* value, "previous" value, and therefore allow Motion to calculate
|
||||
* velocity for any subsequent animation.
|
||||
*/
|
||||
updateMotionValue(value) {
|
||||
const { motionValue, element, name } = this.options;
|
||||
if (!motionValue)
|
||||
return;
|
||||
if (value !== undefined) {
|
||||
motionValue.set(value);
|
||||
return;
|
||||
}
|
||||
const { speed } = this;
|
||||
const sampleTime = this.elapsed();
|
||||
const delta = this.state === "running" && speed
|
||||
? clamp(0, sampleDelta, sampleTime)
|
||||
: 0;
|
||||
const current = this.sample(sampleTime);
|
||||
/**
|
||||
* Write the estimated value to inline style so it persists
|
||||
* after cancel(), covering the async gap before the next
|
||||
* animation starts.
|
||||
*/
|
||||
if (element && name)
|
||||
setStyle(element, name, current);
|
||||
motionValue.setWithVelocity(this.sample(sampleTime - delta * Math.sign(speed)), current, delta / Math.abs(speed || 1));
|
||||
}
|
||||
}
|
||||
|
||||
export { NativeAnimationExtended, sampleDelta };
|
||||
//# sourceMappingURL=NativeAnimationExtended.mjs.map
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import { NativeAnimation } from './NativeAnimation.mjs';
|
||||
import { notifyAnimationStart } from './utils/notify-inspector.mjs';
|
||||
|
||||
class NativeAnimationWrapper extends NativeAnimation {
|
||||
constructor(animation) {
|
||||
super();
|
||||
this.animation = animation;
|
||||
animation.onfinish = () => {
|
||||
this.finishedTime = this.time;
|
||||
this.notifyFinished();
|
||||
};
|
||||
/**
|
||||
* The optionless super() returns before NativeAnimation's own
|
||||
* notify, so wrapped animations (view transition cross-fades)
|
||||
* report here, after the wrapped WAAPI animation is attached.
|
||||
*/
|
||||
notifyAnimationStart(this, {});
|
||||
}
|
||||
}
|
||||
|
||||
export { NativeAnimationWrapper };
|
||||
//# sourceMappingURL=NativeAnimationWrapper.mjs.map
|
||||
Generated
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
import { invariant, removeItem } from 'motion-utils';
|
||||
import { positionalKeys } from '../../render/utils/keys-position.mjs';
|
||||
import { motionValue } from '../../value/index.mjs';
|
||||
import { animateMotionValue } from '../interfaces/motion-value.mjs';
|
||||
import { frame } from '../../frameloop/frame.mjs';
|
||||
|
||||
/**
|
||||
* Effects registered via `animate.addEffect()`, most recent first.
|
||||
*/
|
||||
const effects = [];
|
||||
function addEffect(effect) {
|
||||
invariant(typeof effect.test === "function" && typeof effect.read === "function", "Effects passed to animate.addEffect() need test() and read().", "effect-missing-test");
|
||||
removeEffect(effect);
|
||||
effects.unshift(effect);
|
||||
}
|
||||
function removeEffect(effect) {
|
||||
removeItem(effects, effect);
|
||||
}
|
||||
function findEffect(subject) {
|
||||
return effects.find((effect) => effect.test(subject));
|
||||
}
|
||||
/**
|
||||
* Animate each key in `keyframes` on the motion value `getValue` returns
|
||||
* for it. `element` is what the keyframe resolver uses to read, render
|
||||
* and measure the subject when the effect defers resolution to it.
|
||||
*/
|
||||
function animateValues(getValue, keyframes, transition = {}, element) {
|
||||
const animations = [];
|
||||
const { velocity, path } = transition;
|
||||
const reduceMotion = transition.reduceMotion ?? element?.shouldReduceMotion;
|
||||
/**
|
||||
* Only elements have x/y to move along a path. path claims them by
|
||||
* deleting them, so hand it a copy.
|
||||
*/
|
||||
if (path?.animateValues && element) {
|
||||
keyframes = { ...keyframes };
|
||||
path.animateValues(getValue, keyframes, transition, 0, animations);
|
||||
}
|
||||
for (const key in keyframes) {
|
||||
if (key === "transition" || key === "transitionEnd")
|
||||
continue;
|
||||
const target = keyframes[key];
|
||||
if (target === undefined)
|
||||
continue;
|
||||
const value = getValue(key);
|
||||
/**
|
||||
* If the value is already at the defined target, skip the animation.
|
||||
* We still re-assert the value via frame.update to take precedence
|
||||
* over any stale transitionEnd callbacks from previous animations.
|
||||
*/
|
||||
const current = value.get();
|
||||
if (current !== undefined &&
|
||||
!value.isAnimating() &&
|
||||
!Array.isArray(target) &&
|
||||
target === current &&
|
||||
!velocity) {
|
||||
frame.update(() => value.set(target));
|
||||
continue;
|
||||
}
|
||||
value.start(animateMotionValue(key, value, target, reduceMotion && positionalKeys.has(key)
|
||||
? { type: false }
|
||||
: transition, element));
|
||||
value.animation &&
|
||||
animations.push(value.animation);
|
||||
}
|
||||
const { transitionEnd } = keyframes;
|
||||
if (transitionEnd) {
|
||||
const applyTransitionEnd = () => frame.update(() => {
|
||||
for (const key in transitionEnd) {
|
||||
getValue(key).set(transitionEnd[key]);
|
||||
}
|
||||
});
|
||||
animations.length
|
||||
? Promise.all(animations).then(applyTransitionEnd)
|
||||
: applyTransitionEnd();
|
||||
}
|
||||
return animations;
|
||||
}
|
||||
/**
|
||||
* Animate the keys of `subject` via `effect`. Motion values are created on
|
||||
* first animation and bound to the subject through the effect for the
|
||||
* rest of its life.
|
||||
*
|
||||
* Without `element`, a new value is seeded synchronously from the first
|
||||
* keyframe or `effect.read()`. With it, the value starts undefined and the
|
||||
* keyframe resolver reads the subject (batched, with unit conversion by
|
||||
* measurement) via `element`.
|
||||
*/
|
||||
function animateEffectSubject(effect, subject, keyframes, transition, element) {
|
||||
return animateValues((key) => {
|
||||
let value = effect.get(subject, key);
|
||||
if (!value) {
|
||||
let initial;
|
||||
if (!element) {
|
||||
const target = keyframes[key];
|
||||
initial =
|
||||
firstKeyframe(target) ??
|
||||
effect.read(subject, key, target);
|
||||
invariant(initial !== undefined, `"${key}" can't be read from the animated subject. Provide [from, to] keyframes.`, "effect-unreadable-value");
|
||||
}
|
||||
value = motionValue(initial, { owner: element });
|
||||
effect(subject, { [key]: value });
|
||||
}
|
||||
return value;
|
||||
}, keyframes, transition, element);
|
||||
}
|
||||
function firstKeyframe(target) {
|
||||
const first = Array.isArray(target) ? target[0] : undefined;
|
||||
return first === null ? undefined : first;
|
||||
}
|
||||
|
||||
export { addEffect, animateEffectSubject, animateValues, findEffect, removeEffect };
|
||||
//# sourceMappingURL=effects.mjs.map
|
||||
Generated
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
import { styleSubjectEffect } from '../../effects/style/index.mjs';
|
||||
import { svgSubjectEffect } from '../../effects/svg/index.mjs';
|
||||
import { measureViewportBox } from '../../projection/utils/measure.mjs';
|
||||
import { isSVGElement } from '../../utils/is-svg-element.mjs';
|
||||
import { DOMKeyframesResolver } from '../keyframes/DOMKeyframesResolver.mjs';
|
||||
import { animateValues, animateEffectSubject } from './effects.mjs';
|
||||
|
||||
const noProps = {};
|
||||
const getElementEffect = (element) => (isSVGElement(element)
|
||||
? svgSubjectEffect
|
||||
: styleSubjectEffect);
|
||||
/**
|
||||
* Presents an effect bound to a DOM element in the shape the DOM keyframe
|
||||
* resolver and WAAPI expect, which is that of a VisualElement. Holds no
|
||||
* state: everything delegates to the effect. Goes once the resolver takes
|
||||
* an effect and subject directly.
|
||||
*/
|
||||
class EffectSubject {
|
||||
constructor(effect, current) {
|
||||
this.effect = effect;
|
||||
this.current = current;
|
||||
this.KeyframeResolver = DOMKeyframesResolver;
|
||||
}
|
||||
getValue(key) {
|
||||
return this.effect.get(this.current, key);
|
||||
}
|
||||
readValue(key, target) {
|
||||
return this.effect.read(this.current, key, target);
|
||||
}
|
||||
render() {
|
||||
this.effect.flush(this.current);
|
||||
}
|
||||
measureViewportBox() {
|
||||
return measureViewportBox(this.current);
|
||||
}
|
||||
getProps() {
|
||||
return noProps;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Animate a DOM element's styles. HTML elements are bound through
|
||||
* styleEffect, SVG through svgEffect, and both are resolved with the DOM
|
||||
* keyframe resolver so reads are batched, units are converted by
|
||||
* measurement and eligible values run on WAAPI.
|
||||
*
|
||||
* Pass the element's VisualElement (a <motion.*> component or an
|
||||
* animateLayout() node) to animate its values instead, so the two keep
|
||||
* sharing values and a renderer.
|
||||
*/
|
||||
function animateElement(element, keyframes, transition, visualElement) {
|
||||
if (visualElement) {
|
||||
return animateValues((key) => visualElement.getValue(key, null), keyframes, transition, visualElement);
|
||||
}
|
||||
const effect = getElementEffect(element);
|
||||
return animateEffectSubject(effect, element, keyframes, transition, new EffectSubject(effect, element));
|
||||
}
|
||||
/**
|
||||
* Hand the values styleEffect/svgEffect are rendering on `element`,
|
||||
* whether bound by animate() or directly, to a VisualElement that now
|
||||
* owns the element (e.g. created by animateLayout()), so a single
|
||||
* renderer drives them alongside its own values.
|
||||
*/
|
||||
function handOffElementState(element, visualElement) {
|
||||
const state = getElementEffect(element).state(element);
|
||||
if (!state)
|
||||
return;
|
||||
const { transformKeys } = state;
|
||||
state.release().forEach((value, key) => {
|
||||
/**
|
||||
* The style effect derives transform (from the bound transform
|
||||
* keys) and transformBox itself; a VisualElement builds its own.
|
||||
*/
|
||||
const derived = key === "transformBox" ||
|
||||
(key === "transform" && transformKeys?.length);
|
||||
derived || visualElement.addValue(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
export { animateElement, handOffElementState };
|
||||
//# sourceMappingURL=element.mjs.map
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
import { animateMotionValue } from '../interfaces/motion-value.mjs';
|
||||
import { motionValue } from '../../value/index.mjs';
|
||||
import { isMotionValue } from '../../value/utils/is-motion-value.mjs';
|
||||
|
||||
function animateSingleValue(value, keyframes, options) {
|
||||
const motionValue$1 = isMotionValue(value) ? value : motionValue(value);
|
||||
motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options));
|
||||
return motionValue$1.animation;
|
||||
}
|
||||
|
||||
export { animateSingleValue };
|
||||
//# sourceMappingURL=single-value.mjs.map
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
import { time } from '../../frameloop/sync-time.mjs';
|
||||
import { frameData } from '../../frameloop/frame-data.mjs';
|
||||
import { cancelFrame, frame } from '../../frameloop/frame.mjs';
|
||||
|
||||
const frameloopDriver = (update) => {
|
||||
const passTimestamp = ({ timestamp }) => update(timestamp);
|
||||
return {
|
||||
start: (keepAlive = true) => frame.update(passTimestamp, keepAlive),
|
||||
stop: () => cancelFrame(passTimestamp),
|
||||
/**
|
||||
* If we're processing this frame we can use the
|
||||
* framelocked timestamp to keep things in sync.
|
||||
*/
|
||||
now: () => (frameData.isProcessing ? frameData.timestamp : time.now()),
|
||||
};
|
||||
};
|
||||
|
||||
export { frameloopDriver };
|
||||
//# sourceMappingURL=frame.mjs.map
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
import { spring } from './spring.mjs';
|
||||
|
||||
function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
|
||||
const origin = keyframes[0];
|
||||
const state = {
|
||||
done: false,
|
||||
value: origin,
|
||||
};
|
||||
const isOutOfBounds = (v) => v < min || v > max;
|
||||
const nearestBoundary = (v) => {
|
||||
if (min === undefined)
|
||||
return max;
|
||||
if (max === undefined)
|
||||
return min;
|
||||
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
||||
};
|
||||
let amplitude = power * velocity;
|
||||
const ideal = origin + amplitude;
|
||||
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
||||
/**
|
||||
* If the target has changed we need to re-calculate the amplitude, otherwise
|
||||
* the animation will start from the wrong position.
|
||||
*/
|
||||
if (target !== ideal)
|
||||
amplitude = target - origin;
|
||||
const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
|
||||
const applyFriction = (t) => {
|
||||
const delta = calcDelta(t);
|
||||
state.done = Math.abs(delta) <= restDelta;
|
||||
state.value = state.done ? target : target + delta;
|
||||
};
|
||||
/**
|
||||
* Ideally this would resolve for t in a stateless way, we could
|
||||
* do that by always precalculating the animation but as we know
|
||||
* this will be done anyway we can assume that spring will
|
||||
* be discovered during that.
|
||||
*/
|
||||
let timeReachedBoundary;
|
||||
let spring$1;
|
||||
const checkCatchBoundary = (t) => {
|
||||
if (!isOutOfBounds(state.value))
|
||||
return;
|
||||
timeReachedBoundary = t;
|
||||
spring$1 = spring({
|
||||
keyframes: [state.value, nearestBoundary(state.value)],
|
||||
/**
|
||||
* The friction curve is target + calcDelta(t), so its exact
|
||||
* derivative is -calcDelta(t) / timeConstant in units/ms,
|
||||
* converted here to the units/second expected by spring.
|
||||
*/
|
||||
velocity: (-calcDelta(t) / timeConstant) * 1000,
|
||||
damping: bounceDamping,
|
||||
stiffness: bounceStiffness,
|
||||
restDelta,
|
||||
restSpeed,
|
||||
});
|
||||
};
|
||||
checkCatchBoundary(0);
|
||||
return {
|
||||
calculatedDuration: null,
|
||||
next: (t) => {
|
||||
/**
|
||||
* We need to resolve the friction to figure out if we need a
|
||||
* spring but we don't want to do this twice per frame. So here
|
||||
* we flag if we updated for this frame and later if we did
|
||||
* we can skip doing it again.
|
||||
*/
|
||||
let hasUpdatedFrame = false;
|
||||
if (!spring$1 && timeReachedBoundary === undefined) {
|
||||
hasUpdatedFrame = true;
|
||||
applyFriction(t);
|
||||
checkCatchBoundary(t);
|
||||
}
|
||||
/**
|
||||
* If we have a spring and the provided t is beyond the moment the friction
|
||||
* animation crossed the min/max boundary, use the spring.
|
||||
*/
|
||||
if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
|
||||
return spring$1.next(t - timeReachedBoundary);
|
||||
}
|
||||
else {
|
||||
!hasUpdatedFrame && applyFriction(t);
|
||||
return state;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { inertia };
|
||||
//# sourceMappingURL=inertia.mjs.map
|
||||
Generated
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
import { easeInOut, isEasingArray, easingDefinitionToFunction, MotionGlobalConfig, clamp } from 'motion-utils';
|
||||
import { interpolate } from '../../utils/interpolate.mjs';
|
||||
import { mix } from '../../utils/mix/index.mjs';
|
||||
import { defaultOffset } from '../keyframes/offsets/default.mjs';
|
||||
import { convertOffsetToTimes } from '../keyframes/offsets/time.mjs';
|
||||
|
||||
function defaultEasing(values, easing) {
|
||||
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
||||
}
|
||||
function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
|
||||
/**
|
||||
* Easing functions can be externally defined as strings. Here we convert them
|
||||
* into actual functions.
|
||||
*/
|
||||
const easingFunctions = isEasingArray(ease)
|
||||
? ease.map(easingDefinitionToFunction)
|
||||
: easingDefinitionToFunction(ease) || easeInOut;
|
||||
/**
|
||||
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
||||
* to reduce GC during animation.
|
||||
*/
|
||||
const state = {
|
||||
done: false,
|
||||
value: keyframeValues[0],
|
||||
};
|
||||
/**
|
||||
* Fast setup path for two keyframes one easing.
|
||||
*/
|
||||
if (keyframeValues.length === 2 &&
|
||||
!Array.isArray(easingFunctions) &&
|
||||
(!times ||
|
||||
times.length !== 2 ||
|
||||
(times[0] === 0 && times[1] === 1))) {
|
||||
const [origin, target] = keyframeValues;
|
||||
const mixer = origin === target
|
||||
? undefined
|
||||
: (MotionGlobalConfig.mix || mix)(origin, target);
|
||||
return {
|
||||
calculatedDuration: duration,
|
||||
next: (t) => {
|
||||
state.value = mixer
|
||||
? mixer(easingFunctions(duration > 0 ? clamp(0, 1, t / duration) : 1))
|
||||
: target;
|
||||
state.done = t >= duration;
|
||||
return state;
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create a times array based on the provided 0-1 offsets
|
||||
*/
|
||||
const absoluteTimes = convertOffsetToTimes(
|
||||
// Only use the provided offsets if they're the correct length
|
||||
// TODO Maybe we should warn here if there's a length mismatch
|
||||
times && times.length === keyframeValues.length
|
||||
? times
|
||||
: defaultOffset(keyframeValues), duration);
|
||||
const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
|
||||
ease: Array.isArray(easingFunctions)
|
||||
? easingFunctions
|
||||
: defaultEasing(keyframeValues, easingFunctions),
|
||||
});
|
||||
return {
|
||||
calculatedDuration: duration,
|
||||
next: (t) => {
|
||||
state.value = mapTimeToKeyframe(t);
|
||||
state.done = t >= duration;
|
||||
return state;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { defaultEasing, keyframes };
|
||||
//# sourceMappingURL=keyframes.mjs.map
|
||||
Generated
Vendored
+399
@@ -0,0 +1,399 @@
|
||||
import { millisecondsToSeconds, secondsToMilliseconds, warning, clamp } from 'motion-utils';
|
||||
import { generateLinearEasing } from '../waapi/utils/linear.mjs';
|
||||
import { calcGeneratorDuration, maxGeneratorDuration } from './utils/calc-duration.mjs';
|
||||
import { createGeneratorEasing } from './utils/create-generator-easing.mjs';
|
||||
|
||||
const springDefaults = {
|
||||
// Default spring physics
|
||||
stiffness: 100,
|
||||
damping: 10,
|
||||
mass: 1.0,
|
||||
// Default duration/bounce-based options
|
||||
duration: 800, // in ms
|
||||
bounce: 0.3,
|
||||
visualDuration: 0.3, // in seconds
|
||||
// Rest thresholds
|
||||
restSpeed: {
|
||||
granular: 0.01,
|
||||
default: 2,
|
||||
},
|
||||
restDelta: {
|
||||
granular: 0.005,
|
||||
default: 0.5,
|
||||
},
|
||||
// Limits
|
||||
minDuration: 0.01, // in seconds
|
||||
maxDuration: 10.0, // in seconds
|
||||
minDamping: 0.05,
|
||||
};
|
||||
/**
|
||||
* Maps bounce to a damping ratio as SwiftUI does: 0 is critically damped, and
|
||||
* it approaches undamped towards 1 and infinitely overdamped towards -1. Both
|
||||
* ends are limited so the spring still moves and settles.
|
||||
*/
|
||||
const bounceToDampingRatio = (bounce) => bounce < 0
|
||||
? 1 / Math.max(1 + bounce, springDefaults.minDamping)
|
||||
: Math.max(1 - bounce, springDefaults.minDamping);
|
||||
/**
|
||||
* Scales an overdamped spring's undamped frequency so that, at its defining
|
||||
* time, it has as far left to go as the critically damped spring that's
|
||||
* criticalFreqTime radians in by then. A negative bounce then changes the
|
||||
* shape of the curve, not when it arrives. 1 at a damping ratio of 1, so
|
||||
* there's no jump at a bounce of 0.
|
||||
*
|
||||
* Solved by Newton on the remaining distance (times fast - slow), which is
|
||||
* convex from criticalFreqTime onwards. criticalFreqTime is always below the
|
||||
* answer, so it converges from below without overshooting.
|
||||
*/
|
||||
function overdampedFreqScale(dampingRatio, criticalFreqTime) {
|
||||
if (!(dampingRatio > 1))
|
||||
return 1;
|
||||
const root = Math.sqrt(dampingRatio * dampingRatio - 1);
|
||||
const slow = dampingRatio - root;
|
||||
const fast = dampingRatio + root;
|
||||
const target = 2 * root * Math.exp(-criticalFreqTime) * (1 + criticalFreqTime);
|
||||
return (approximateRoot((freqTime) => fast * Math.exp(-slow * freqTime) -
|
||||
slow * Math.exp(-fast * freqTime) -
|
||||
target, (freqTime) => Math.exp(-fast * freqTime) - Math.exp(-slow * freqTime), criticalFreqTime) / criticalFreqTime);
|
||||
}
|
||||
function calcAngularFreq(undampedFreq, dampingRatio) {
|
||||
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
||||
}
|
||||
const rootIterations = 12;
|
||||
function approximateRoot(envelope, derivative, initialGuess) {
|
||||
let result = initialGuess;
|
||||
for (let i = 1; i < rootIterations; i++) {
|
||||
result = result - envelope(result) / derivative(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* This is ported from the Framer implementation of duration-based spring resolution.
|
||||
*/
|
||||
const safeMin = 0.001;
|
||||
/**
|
||||
* Assumes zero initial velocity and the default mass: time-defined springs
|
||||
* ignore inherited velocity and only resolve without valid physics. Returns
|
||||
* NaN physics when the root search doesn't converge.
|
||||
*/
|
||||
function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, }) {
|
||||
let envelope;
|
||||
let derivative;
|
||||
warning(duration <= secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less", "spring-duration-limit");
|
||||
const dampingRatio = bounceToDampingRatio(bounce);
|
||||
duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, millisecondsToSeconds(duration));
|
||||
if (dampingRatio < 1) {
|
||||
/**
|
||||
* Underdamped spring
|
||||
*/
|
||||
envelope = (undampedFreq) => {
|
||||
const exponentialDecay = undampedFreq * dampingRatio;
|
||||
const delta = exponentialDecay * duration;
|
||||
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
||||
const c = Math.exp(-delta);
|
||||
return safeMin - (exponentialDecay / b) * c;
|
||||
};
|
||||
derivative = (undampedFreq) => {
|
||||
const exponentialDecay = undampedFreq * dampingRatio;
|
||||
const delta = exponentialDecay * duration;
|
||||
const e = dampingRatio *
|
||||
dampingRatio *
|
||||
undampedFreq *
|
||||
undampedFreq *
|
||||
duration;
|
||||
const f = Math.exp(-delta);
|
||||
const g = calcAngularFreq(undampedFreq * undampedFreq, dampingRatio);
|
||||
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
||||
return (factor * -e * f) / g;
|
||||
};
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Critically-damped spring. Overdamped springs reuse this root,
|
||||
* scaled below.
|
||||
*/
|
||||
envelope = (undampedFreq) => {
|
||||
const a = Math.exp(-undampedFreq * duration);
|
||||
const b = undampedFreq * duration + 1;
|
||||
return -safeMin + a * b;
|
||||
};
|
||||
derivative = (undampedFreq) => {
|
||||
const a = Math.exp(-undampedFreq * duration);
|
||||
const b = -undampedFreq * (duration * duration);
|
||||
return a * b;
|
||||
};
|
||||
}
|
||||
const initialGuess = 5 / duration;
|
||||
const criticalFreq = approximateRoot(envelope, derivative, initialGuess);
|
||||
const undampedFreq = criticalFreq *
|
||||
overdampedFreqScale(dampingRatio, criticalFreq * duration);
|
||||
const stiffness = undampedFreq * undampedFreq;
|
||||
return {
|
||||
stiffness,
|
||||
damping: dampingRatio * 2 * Math.sqrt(stiffness),
|
||||
duration: secondsToMilliseconds(duration),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Spring physics must be finite. stiffness and mass are also divisors so must
|
||||
* be positive, whereas a damping of 0 is a valid, perpetually oscillating
|
||||
* spring. Relational rather than Number.isFinite so numeric strings still
|
||||
* coerce.
|
||||
*/
|
||||
const isValidPhysics = (value, canBeZero) => (canBeZero ? value >= 0 : value > 0) && value < Infinity;
|
||||
/**
|
||||
* Returns value if it's usable spring physics, otherwise undefined so callers
|
||||
* fall back to the default. An explicit `undefined`, e.g. from a forwarded
|
||||
* optional prop, must fall back too. Invalid physics would otherwise resolve
|
||||
* to NaN spring values that never report done.
|
||||
*/
|
||||
function resolvePhysics(value, canBeZero) {
|
||||
if (isValidPhysics(value, canBeZero))
|
||||
return value;
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
warning(value === undefined, "Spring stiffness and mass must be positive, damping 0 or greater", "spring-invalid-physics");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function getSpringOptions(options) {
|
||||
/**
|
||||
* Resolve physics before choosing between physics- and duration-based
|
||||
* resolution, so an invalid stiffness doesn't also silently discard a
|
||||
* valid duration/bounce.
|
||||
*/
|
||||
const validStiffness = resolvePhysics(options.stiffness);
|
||||
const validDamping = resolvePhysics(options.damping, true);
|
||||
const validMass = resolvePhysics(options.mass);
|
||||
const springOptions = {
|
||||
...options,
|
||||
stiffness: validStiffness ?? springDefaults.stiffness,
|
||||
damping: validDamping ?? springDefaults.damping,
|
||||
mass: validMass ?? springDefaults.mass,
|
||||
isResolvedFromDuration: false,
|
||||
// stiffness/damping/mass overrides duration/bounce
|
||||
isTimeDefined: (validStiffness ?? validDamping ?? validMass) === undefined &&
|
||||
(options.duration !== undefined || options.bounce !== undefined),
|
||||
};
|
||||
if (springOptions.isTimeDefined) {
|
||||
if (options.visualDuration) {
|
||||
const dampingRatio = bounceToDampingRatio(options.bounce || 0);
|
||||
const root = ((2 * Math.PI) / (options.visualDuration * 1.2)) *
|
||||
overdampedFreqScale(dampingRatio, (2 * Math.PI) / 1.2);
|
||||
springOptions.stiffness = root * root;
|
||||
springOptions.damping =
|
||||
2 * dampingRatio * Math.sqrt(springOptions.stiffness);
|
||||
}
|
||||
else {
|
||||
Object.assign(springOptions, findSpring(springOptions));
|
||||
springOptions.isResolvedFromDuration = true;
|
||||
}
|
||||
/**
|
||||
* Time options can degenerate: a NaN bounce gives a NaN damping, an
|
||||
* infinite visualDuration a 0 stiffness, and findSpring NaN for both
|
||||
* when it doesn't converge. Replace the two together, so the
|
||||
* relationship duration resolution establishes between them is never
|
||||
* left half-overwritten.
|
||||
*/
|
||||
if (!isValidPhysics(springOptions.stiffness) ||
|
||||
!isValidPhysics(springOptions.damping, true)) {
|
||||
springOptions.stiffness = springDefaults.stiffness;
|
||||
springOptions.damping = springDefaults.damping;
|
||||
}
|
||||
}
|
||||
return springOptions;
|
||||
}
|
||||
function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
|
||||
const options = typeof optionsOrVisualDuration !== "object"
|
||||
? {
|
||||
visualDuration: optionsOrVisualDuration,
|
||||
keyframes: [0, 1],
|
||||
bounce,
|
||||
}
|
||||
: optionsOrVisualDuration;
|
||||
const origin = options.keyframes[0];
|
||||
const target = options.keyframes[options.keyframes.length - 1];
|
||||
/**
|
||||
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
||||
* to reduce GC during animation.
|
||||
*/
|
||||
const state = { done: false, value: origin };
|
||||
const { stiffness, damping, mass, duration, isResolvedFromDuration, isTimeDefined, } = getSpringOptions({ ...options });
|
||||
/**
|
||||
* Time-defined springs ignore inherited velocity. Velocity from
|
||||
* interrupted animations causes massive oscillation on small-range
|
||||
* animations.
|
||||
*/
|
||||
const inheritVelocity = (velocity) => isTimeDefined ? 0 : -millisecondsToSeconds(velocity);
|
||||
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
||||
const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
|
||||
const decay = dampingRatio * undampedAngularFreq;
|
||||
/**
|
||||
* Everything that changes when the spring is retargeted: written by
|
||||
* retarget() and update(), read by the resolvers. Grouped on one object,
|
||||
* like the coefficients (c) below. Writing doubles to object fields
|
||||
* measured marginally faster than to captured let variables in optimised
|
||||
* code; neither allocates, so this is a grouping choice, not a GC one.
|
||||
*/
|
||||
const s = {
|
||||
target,
|
||||
delta: target - origin,
|
||||
velocity: inheritVelocity(options.velocity || 0) || 0,
|
||||
restSpeed: 0,
|
||||
restDelta: 0,
|
||||
};
|
||||
/**
|
||||
* If we're working on a granular scale, use smaller defaults for determining
|
||||
* when the spring is finished.
|
||||
*
|
||||
* These defaults have been selected empirically based on what strikes a good
|
||||
* ratio between feeling good and finishing as soon as changes are imperceptible.
|
||||
*/
|
||||
const setRestThresholds = () => {
|
||||
const isGranularScale = Math.abs(s.delta) < 5;
|
||||
s.restSpeed =
|
||||
options.restSpeed ||
|
||||
(isGranularScale
|
||||
? springDefaults.restSpeed.granular
|
||||
: springDefaults.restSpeed.default);
|
||||
s.restDelta =
|
||||
options.restDelta ||
|
||||
(isGranularScale
|
||||
? springDefaults.restDelta.granular
|
||||
: springDefaults.restDelta.default);
|
||||
};
|
||||
setRestThresholds();
|
||||
let resolveSpring;
|
||||
let resolveVelocity;
|
||||
/**
|
||||
* Derives the coefficients that depend on origin, target and initial
|
||||
* velocity. Called once now and again whenever the spring is retargeted.
|
||||
*/
|
||||
let update;
|
||||
if (dampingRatio < 1) {
|
||||
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
||||
/**
|
||||
* A is the position coefficient, sinC/cosC the coefficients of the
|
||||
* analytical derivative (px/ms). The exp/sin/cos terms depend only
|
||||
* on t, so they're memoized by t independently of the target: a
|
||||
* spring retargeted every frame samples the same t each frame and
|
||||
* skips the transcendentals entirely.
|
||||
*/
|
||||
const c = { A: 0, sinC: 0, cosC: 0, t: -1, env: 0, sin: 0, cos: 0 };
|
||||
update = () => {
|
||||
c.A = (s.velocity + decay * s.delta) / angularFreq;
|
||||
c.sinC = decay * c.A + s.delta * angularFreq;
|
||||
c.cosC = decay * s.delta - c.A * angularFreq;
|
||||
};
|
||||
const sample = (t) => {
|
||||
if (t !== c.t) {
|
||||
c.t = t;
|
||||
c.env = Math.exp(-decay * t);
|
||||
c.sin = Math.sin(angularFreq * t);
|
||||
c.cos = Math.cos(angularFreq * t);
|
||||
}
|
||||
};
|
||||
// Underdamped spring
|
||||
resolveSpring = (t) => {
|
||||
sample(t);
|
||||
return s.target - c.env * (c.A * c.sin + s.delta * c.cos);
|
||||
};
|
||||
resolveVelocity = (t) => {
|
||||
sample(t);
|
||||
return c.env * (c.sinC * c.sin + c.cosC * c.cos);
|
||||
};
|
||||
}
|
||||
else if (dampingRatio === 1) {
|
||||
// Critically damped spring
|
||||
resolveSpring = (t) => s.target -
|
||||
Math.exp(-undampedAngularFreq * t) *
|
||||
(s.delta + (s.velocity + undampedAngularFreq * s.delta) * t);
|
||||
// Analytical derivative of critically damped spring (px/ms)
|
||||
const c = { C: 0 };
|
||||
update = () => {
|
||||
c.C = s.velocity + undampedAngularFreq * s.delta;
|
||||
};
|
||||
resolveVelocity = (t) => Math.exp(-undampedAngularFreq * t) *
|
||||
(undampedAngularFreq * c.C * t - s.velocity);
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Overdamped spring: the sum of a slow and a fast decaying
|
||||
* exponential, so no term can overflow however heavily it's damped.
|
||||
*/
|
||||
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
||||
const slow = decay - dampedAngularFreq;
|
||||
const fast = decay + dampedAngularFreq;
|
||||
/**
|
||||
* Physics-defined springs keep a non-physical limit: once
|
||||
* dampedAngularFreq * t passes 300, both terms decay at the damping
|
||||
* rate. Heavily overdamped drag springs (dragElastic: 0,
|
||||
* dragTransition) rely on this to settle almost at once rather than
|
||||
* creep at stiffness / damping. Negative bounce needs the exact
|
||||
* curve, so time-defined springs are exempt.
|
||||
*/
|
||||
const limit = isTimeDefined ? Infinity : 300 / dampedAngularFreq;
|
||||
const decayAt = (rate, t) => Math.exp(t > limit ? -rate * limit - decay * (t - limit) : -rate * t);
|
||||
const c = { S: 0, F: 0 };
|
||||
update = () => {
|
||||
const P = (s.velocity + decay * s.delta) / dampedAngularFreq;
|
||||
c.S = (s.delta + P) / 2;
|
||||
c.F = (s.delta - P) / 2;
|
||||
};
|
||||
resolveSpring = (t) => s.target - c.S * decayAt(slow, t) - c.F * decayAt(fast, t);
|
||||
// Analytical derivative of overdamped spring (px/ms)
|
||||
resolveVelocity = (t) => slow * c.S * decayAt(slow, t) + fast * c.F * decayAt(fast, t);
|
||||
}
|
||||
update();
|
||||
const calculatedDuration = isResolvedFromDuration ? duration || null : null;
|
||||
const generator = {
|
||||
calculatedDuration,
|
||||
/**
|
||||
* Aim the spring at a new target from its current position and
|
||||
* velocity, reusing the resolved physics and closures.
|
||||
*/
|
||||
retarget: (keyframes, newVelocity) => {
|
||||
s.target = keyframes[keyframes.length - 1];
|
||||
s.delta = s.target - keyframes[0];
|
||||
s.velocity = inheritVelocity(newVelocity);
|
||||
// Default thresholds depend on the scale of the new delta
|
||||
if (!(options.restSpeed && options.restDelta))
|
||||
setRestThresholds();
|
||||
// Invalidate any duration lazily cached by JSAnimation
|
||||
generator.calculatedDuration = calculatedDuration;
|
||||
state.done = false;
|
||||
update();
|
||||
},
|
||||
velocity: (t) => secondsToMilliseconds(resolveVelocity(t)),
|
||||
next: (t) => {
|
||||
const current = resolveSpring(t);
|
||||
if (!isResolvedFromDuration) {
|
||||
const currentVelocity = secondsToMilliseconds(resolveVelocity(t));
|
||||
state.done =
|
||||
Math.abs(currentVelocity) <= s.restSpeed &&
|
||||
Math.abs(s.target - current) <= s.restDelta;
|
||||
}
|
||||
else {
|
||||
state.done = t >= duration;
|
||||
}
|
||||
state.value = state.done ? s.target : current;
|
||||
return state;
|
||||
},
|
||||
toString: () => {
|
||||
const easingDuration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
|
||||
const easing = generateLinearEasing((progress) => generator.next(easingDuration * progress).value, easingDuration, 30);
|
||||
return easingDuration + "ms " + easing;
|
||||
},
|
||||
toTransition: () => { },
|
||||
};
|
||||
return generator;
|
||||
}
|
||||
spring.applyToOptions = (options) => {
|
||||
const generatorOptions = createGeneratorEasing(options, 100, spring);
|
||||
options.ease = generatorOptions.ease;
|
||||
options.duration = secondsToMilliseconds(generatorOptions.duration);
|
||||
options.type = "keyframes";
|
||||
return options;
|
||||
};
|
||||
|
||||
export { spring };
|
||||
//# sourceMappingURL=spring.mjs.map
|
||||
apps/web/.output/server/node_modules/motion-dom/dist/es/animation/generators/utils/calc-duration.mjs
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Implement a practical max duration for keyframe generation
|
||||
* to prevent infinite loops
|
||||
*/
|
||||
const maxGeneratorDuration = 20000;
|
||||
function calcGeneratorDuration(generator, timeStep = 50, maxDuration = maxGeneratorDuration, keyframes) {
|
||||
let duration = 0;
|
||||
let state = generator.next(duration);
|
||||
keyframes?.push(state.value);
|
||||
while (!state.done && duration < maxDuration) {
|
||||
duration += timeStep;
|
||||
state = generator.next(duration);
|
||||
keyframes?.push(state.value);
|
||||
}
|
||||
return duration >= maxDuration ? Infinity : duration;
|
||||
}
|
||||
|
||||
export { calcGeneratorDuration, maxGeneratorDuration };
|
||||
//# sourceMappingURL=calc-duration.mjs.map
|
||||
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
import { millisecondsToSeconds } from 'motion-utils';
|
||||
import { calcGeneratorDuration, maxGeneratorDuration } from './calc-duration.mjs';
|
||||
|
||||
/**
|
||||
* Create a progress => progress easing function from a generator.
|
||||
*/
|
||||
function createGeneratorEasing(options, scale = 100, createGenerator) {
|
||||
/**
|
||||
* The easing is generated over 0 - scale, but velocity is in the
|
||||
* value's own units, so scale it to match. Otherwise an interrupted
|
||||
* spring hands off the wrong speed, or direction. Keyframes with
|
||||
* units (colours, filters, mixed units) give NaN, which is falsy,
|
||||
* so their velocity passes through unscaled.
|
||||
*/
|
||||
const { keyframes: k, velocity: v } = options;
|
||||
const delta = k && k[k.length - 1] - k[0];
|
||||
const generator = createGenerator({
|
||||
...options,
|
||||
velocity: v && delta ? (v * scale) / delta : v,
|
||||
keyframes: [0, scale],
|
||||
});
|
||||
const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
|
||||
return {
|
||||
type: "keyframes",
|
||||
ease: (progress) => {
|
||||
return generator.next(duration * progress).value / scale;
|
||||
},
|
||||
duration: millisecondsToSeconds(duration),
|
||||
};
|
||||
}
|
||||
|
||||
export { createGeneratorEasing };
|
||||
//# sourceMappingURL=create-generator-easing.mjs.map
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
function isGenerator(type) {
|
||||
return typeof type === "function" && "applyToOptions" in type;
|
||||
}
|
||||
|
||||
export { isGenerator };
|
||||
//# sourceMappingURL=is-generator.mjs.map
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
import { velocityPerSecond } from 'motion-utils';
|
||||
|
||||
const velocitySampleDuration = 5; // ms
|
||||
function getGeneratorVelocity(resolveValue, t, current) {
|
||||
const prevT = Math.max(t - velocitySampleDuration, 0);
|
||||
return velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
||||
}
|
||||
/**
|
||||
* A generator's velocity at time t in units/second: analytical where the
|
||||
* generator provides it (springs), otherwise by finite difference. Before
|
||||
* the animation has started this is its initial velocity.
|
||||
*/
|
||||
function calcGeneratorVelocity(generator, t, initialVelocity = 0) {
|
||||
if (t <= 0)
|
||||
return initialVelocity;
|
||||
return generator.velocity
|
||||
? generator.velocity(t)
|
||||
: getGeneratorVelocity((s) => generator.next(s).value, t, generator.next(t).value);
|
||||
}
|
||||
|
||||
export { calcGeneratorVelocity, getGeneratorVelocity };
|
||||
//# sourceMappingURL=velocity.mjs.map
|
||||
Generated
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
import { secondsToMilliseconds, MotionGlobalConfig } from 'motion-utils';
|
||||
import { AsyncMotionValueAnimation } from '../AsyncMotionValueAnimation.mjs';
|
||||
import { JSAnimation } from '../JSAnimation.mjs';
|
||||
import { getValueTransition } from '../utils/get-value-transition.mjs';
|
||||
import { makeAnimationInstant } from '../utils/make-animation-instant.mjs';
|
||||
import { getDefaultTransition } from '../utils/default-transitions.mjs';
|
||||
import { getFinalKeyframe } from '../keyframes/get-final.mjs';
|
||||
import { isTransitionDefined } from '../utils/is-transition-defined.mjs';
|
||||
import { frame } from '../../frameloop/frame.mjs';
|
||||
|
||||
const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
|
||||
const valueTransition = getValueTransition(transition, name) || {};
|
||||
/**
|
||||
* Most transition values are currently completely overwritten by value-specific
|
||||
* transitions. In the future it'd be nicer to blend these transitions. But for now
|
||||
* delay actually does inherit from the root transition if not value-specific.
|
||||
*/
|
||||
const delay = valueTransition.delay || transition.delay || 0;
|
||||
/**
|
||||
* Elapsed isn't a public transition option but can be passed through from
|
||||
* optimized appear effects in milliseconds.
|
||||
*/
|
||||
let { elapsed = 0 } = transition;
|
||||
elapsed = elapsed - secondsToMilliseconds(delay);
|
||||
const options = {
|
||||
keyframes: Array.isArray(target) ? target : [null, target],
|
||||
ease: "easeOut",
|
||||
velocity: value.getVelocity(),
|
||||
...valueTransition,
|
||||
delay: -elapsed,
|
||||
onUpdate: (v) => {
|
||||
value.set(v);
|
||||
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
||||
},
|
||||
onComplete: () => {
|
||||
onComplete();
|
||||
valueTransition.onComplete && valueTransition.onComplete();
|
||||
},
|
||||
name,
|
||||
motionValue: value,
|
||||
element: isHandoff ? undefined : element,
|
||||
hasOnUpdate: !!valueTransition.onUpdate,
|
||||
};
|
||||
/**
|
||||
* If there's no transition defined for this value, we can generate
|
||||
* unique transition settings for this value.
|
||||
*/
|
||||
if (!isTransitionDefined(valueTransition)) {
|
||||
Object.assign(options, getDefaultTransition(name, options));
|
||||
}
|
||||
/**
|
||||
* Both WAAPI and our internal animation functions use durations
|
||||
* as defined by milliseconds, while our external API defines them
|
||||
* as seconds.
|
||||
*/
|
||||
options.duration && (options.duration = secondsToMilliseconds(options.duration));
|
||||
options.repeatDelay && (options.repeatDelay = secondsToMilliseconds(options.repeatDelay));
|
||||
/**
|
||||
* Support deprecated way to set initial value. Prefer keyframe syntax.
|
||||
*/
|
||||
if (options.from !== undefined) {
|
||||
options.keyframes[0] = options.from;
|
||||
}
|
||||
let shouldSkip = false;
|
||||
if (options.type === false ||
|
||||
(options.duration === 0 && !options.repeatDelay)) {
|
||||
makeAnimationInstant(options);
|
||||
if (options.delay === 0) {
|
||||
shouldSkip = true;
|
||||
}
|
||||
}
|
||||
if (MotionGlobalConfig.instantAnimations ||
|
||||
MotionGlobalConfig.skipAnimations ||
|
||||
element?.shouldSkipAnimations ||
|
||||
valueTransition.skipAnimations) {
|
||||
shouldSkip = true;
|
||||
makeAnimationInstant(options);
|
||||
options.delay = 0;
|
||||
}
|
||||
/**
|
||||
* If the transition type or easing has been explicitly set by the user
|
||||
* then we don't want to allow flattening the animation.
|
||||
*/
|
||||
options.allowFlatten = !valueTransition.type && !valueTransition.ease;
|
||||
/**
|
||||
* If we can or must skip creating the animation, and apply only
|
||||
* the final keyframe, do so. We also check once keyframes are resolved but
|
||||
* this early check prevents the need to create an animation at all.
|
||||
*/
|
||||
if (shouldSkip && !isHandoff && value.get() !== undefined) {
|
||||
const finalKeyframe = getFinalKeyframe(options.keyframes, valueTransition);
|
||||
if (finalKeyframe !== undefined) {
|
||||
frame.update(() => {
|
||||
options.onUpdate(finalKeyframe);
|
||||
options.onComplete();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
return valueTransition.isSync
|
||||
? new JSAnimation(options)
|
||||
: new AsyncMotionValueAnimation(options);
|
||||
};
|
||||
|
||||
export { animateMotionValue };
|
||||
//# sourceMappingURL=motion-value.mjs.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user