diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 3168e78c7..8dfe41078 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -24,9 +24,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- with:
- # checkout is pinned to dev branch here
- ref: dev
- name: Setup Node
uses: actions/setup-node@v3
with:
diff --git a/packages/three-vrm-animation/README.md b/packages/three-vrm-animation/README.md
new file mode 100644
index 000000000..c9b5757ff
--- /dev/null
+++ b/packages/three-vrm-animation/README.md
@@ -0,0 +1,9 @@
+# @pixiv/three-vrm-animation
+
+The implementation of VRM Animation
+
+[GitHub Repository](https://github.com/pixiv/three-vrm/tree/dev/packages/three-vrm-animation)
+
+[Examples](https://pixiv.github.io/three-vrm/packages/three-vrm-animation/examples)
+
+[Documentation](https://pixiv.github.io/three-vrm/packages/three-vrm-animation/docs)
diff --git a/packages/three-vrm-animation/examples/.editorconfig b/packages/three-vrm-animation/examples/.editorconfig
new file mode 100644
index 000000000..0b225afd4
--- /dev/null
+++ b/packages/three-vrm-animation/examples/.editorconfig
@@ -0,0 +1,2 @@
+[*]
+indent_style = tab
diff --git a/packages/three-vrm-animation/examples/.eslintrc b/packages/three-vrm-animation/examples/.eslintrc
new file mode 100644
index 000000000..48e061bbc
--- /dev/null
+++ b/packages/three-vrm-animation/examples/.eslintrc
@@ -0,0 +1,11 @@
+{
+ "root": true,
+ "plugins": [
+ "html"
+ ],
+ "extends": "mdcs",
+ "rules": {
+ "padded-blocks":"off",
+ "no-unused-vars": "off"
+ }
+}
diff --git a/packages/three-vrm-animation/examples/dnd.html b/packages/three-vrm-animation/examples/dnd.html
new file mode 100644
index 000000000..1af2f326d
--- /dev/null
+++ b/packages/three-vrm-animation/examples/dnd.html
@@ -0,0 +1,281 @@
+
+
+
+
+
+ three-vrm-animation example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/three-vrm-animation/examples/index.html b/packages/three-vrm-animation/examples/index.html
new file mode 100644
index 000000000..494a2151d
--- /dev/null
+++ b/packages/three-vrm-animation/examples/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ three-vrm-animation examples
+
+
+
+
+
+
+ loader-plugin.html
+ Import a VRM Animation from gltf
+
+
+ dnd.html
+ A slightly advanced example with a drag-and-drop capability
+
+
+
diff --git a/packages/three-vrm-animation/examples/loader-plugin.html b/packages/three-vrm-animation/examples/loader-plugin.html
new file mode 100644
index 000000000..050f33f71
--- /dev/null
+++ b/packages/three-vrm-animation/examples/loader-plugin.html
@@ -0,0 +1,151 @@
+
+
+
+
+
+ three-vrm-animation example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/three-vrm-animation/examples/models/VRM1_Constraint_Twist_Sample.vrm b/packages/three-vrm-animation/examples/models/VRM1_Constraint_Twist_Sample.vrm
new file mode 100644
index 000000000..8ee8717df
Binary files /dev/null and b/packages/three-vrm-animation/examples/models/VRM1_Constraint_Twist_Sample.vrm differ
diff --git a/packages/three-vrm-animation/examples/models/test.vrma b/packages/three-vrm-animation/examples/models/test.vrma
new file mode 100644
index 000000000..25f0f1b8a
Binary files /dev/null and b/packages/three-vrm-animation/examples/models/test.vrma differ
diff --git a/packages/three-vrm-animation/package.json b/packages/three-vrm-animation/package.json
new file mode 100644
index 000000000..33555518d
--- /dev/null
+++ b/packages/three-vrm-animation/package.json
@@ -0,0 +1,62 @@
+{
+ "name": "@pixiv/three-vrm-animation",
+ "version": "2.0.10",
+ "description": "The implementation of VRM Animation",
+ "license": "MIT",
+ "author": "pixiv",
+ "files": [
+ "/lib/",
+ "/ts*/",
+ "/types/",
+ "LICENSE"
+ ],
+ "main": "lib/three-vrm-animation.js",
+ "module": "lib/three-vrm-animation.module.js",
+ "types": "types/index.d.ts",
+ "typesVersions": {
+ "<3.9": {
+ "*": [
+ "ts3.4/*"
+ ]
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixiv/three-vrm.git",
+ "directory": "packages/three-vrm-animation"
+ },
+ "scripts": {
+ "version": "yarn all",
+ "all": "yarn lint && yarn clean && yarn build && yarn docs",
+ "dev": "cross-env NODE_ENV=development rollup -w -c",
+ "clean": "rimraf docs/ lib/ ts*/ types/",
+ "build": "yarn build-dev && yarn build-prod && yarn build-types",
+ "build-dev": "cross-env NODE_ENV=development rollup -c",
+ "build-prod": "cross-env NODE_ENV=production rollup -c",
+ "build-types": "tsc --declaration --declarationDir ./types --emitDeclarationOnly && downlevel-dts types ts3.4/types",
+ "docs": "typedoc --entryPoints ./src/index.ts --out docs",
+ "lint": "eslint \"src/**/*.{ts,tsx}\" && yarn lint-examples && prettier \"src/**/*.{ts,tsx}\" --check",
+ "lint-examples": "eslint \"examples/**/*.{ts,tsx,js,html}\" --rule \"padded-blocks: error\"",
+ "lint-fix": "eslint \"src/**/*.{ts,tsx}\" --fix && eslint \"examples/**/*.{ts,tsx,js,html}\" --fix && prettier \"src/**/*.{ts,tsx}\" --write"
+ },
+ "lint-staged": {
+ "./src/**/*.{ts,tsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ]
+ },
+ "dependencies": {
+ "@pixiv/three-vrm-core": "2.0.10",
+ "@pixiv/types-vrmc-vrm-1.0": "2.0.8",
+ "@pixiv/types-vrmc-vrm-animation-1.0": "2.0.10"
+ },
+ "devDependencies": {
+ "@types/three": "^0.160.0",
+ "lint-staged": "13.1.2",
+ "three": "^0.160.0"
+ },
+ "peerDependencies": {
+ "@types/three": "^0.160.0",
+ "three": "^0.160.0"
+ }
+}
diff --git a/packages/three-vrm-animation/rollup.config.js b/packages/three-vrm-animation/rollup.config.js
new file mode 100644
index 000000000..927b077aa
--- /dev/null
+++ b/packages/three-vrm-animation/rollup.config.js
@@ -0,0 +1,96 @@
+/* eslint-env node */
+
+import packageJson from './package.json';
+import serve from 'rollup-plugin-serve';
+import { terser } from 'rollup-plugin-terser';
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import typescript from '@rollup/plugin-typescript';
+
+// == constants ====================================================================================
+/** copyright text */
+const copyright = '(c) 2023 pixiv Inc.';
+
+/** name of the license */
+const licenseName = 'MIT License';
+
+/** url of the license */
+const licenseUri = 'https://github.com/pixiv/three-vrm/blob/release/LICENSE';
+
+/** output name of the module */
+const globalName = 'THREE_VRM_ANIMATION';
+
+/** filename of output */
+const filename = 'lib/three-vrm-animation';
+
+// == envs =========================================================================================
+const NODE_ENV = process.env.NODE_ENV;
+const DEV = NODE_ENV === 'development';
+const WATCH = process.env.ROLLUP_WATCH === 'true';
+
+// == banner =======================================================================================
+const bannerTextDev = `/*!
+ * ${packageJson.name} v${packageJson.version}
+ * ${packageJson.description}
+ *
+ * Copyright ${copyright}
+ * ${packageJson.name} is distributed under ${licenseName}
+ * ${licenseUri}
+ */`;
+
+const bannerTextProd = `/*! ${copyright} - ${licenseUri} */`;
+
+// == module =======================================================================================
+/** will be used to inject the stuff into THREE */
+const outro = `Object.assign(THREE, exports);`;
+
+// == serve ========================================================================================
+const serveOptions = {
+ contentBase: '.',
+ port: process.env.PORT ?? 10001,
+};
+
+// == config =======================================================================================
+function createOutputOptions( { esm } ) {
+ let file = filename;
+ file += esm ? '.module' : '';
+ file += DEV ? '' : '.min';
+ file += '.js';
+
+ return {
+ file,
+ format: esm ? 'esm' : 'umd',
+ name: esm ? undefined : globalName,
+ banner: DEV ? bannerTextDev : bannerTextProd,
+ globals: esm ? undefined : { three: 'THREE' },
+ sourcemap: DEV ? 'inline' : false,
+ plugins: [
+ ...( DEV ? [] : [
+ terser(),
+ ] ),
+ ...( WATCH ? [
+ serve( serveOptions )
+ ] : [] ),
+ ],
+ outro: esm ? undefined : outro,
+ };
+}
+
+function createConfig( output ) {
+ return {
+ input: 'src/index.ts',
+ output,
+ external: [ 'three' ],
+ plugins: [
+ typescript(),
+ nodeResolve(),
+ ],
+ };
+};
+
+// == output =======================================================================================
+export default [
+ createConfig( [
+ createOutputOptions( {} ),
+ createOutputOptions( { esm: true } ),
+ ] ),
+];
diff --git a/packages/three-vrm-animation/src/VRMAnimation.ts b/packages/three-vrm-animation/src/VRMAnimation.ts
new file mode 100644
index 000000000..b09f53683
--- /dev/null
+++ b/packages/three-vrm-animation/src/VRMAnimation.ts
@@ -0,0 +1,40 @@
+import * as THREE from 'three';
+import type { VRMExpressionPresetName, VRMHumanBoneName } from '@pixiv/three-vrm-core';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import type { createVRMAnimationClip } from './createVRMAnimationClip';
+
+/**
+ * Represents a single VRM Animation.
+ * You probably want to create an AnimationClip using {@link createVRMAnimationClip}.
+ */
+export class VRMAnimation {
+ public duration: number;
+ public restHipsPosition: THREE.Vector3;
+
+ public humanoidTracks: {
+ translation: Map<'hips', THREE.VectorKeyframeTrack>;
+ rotation: Map;
+ };
+ public expressionTracks: {
+ preset: Map;
+ custom: Map;
+ };
+ public lookAtTrack: THREE.QuaternionKeyframeTrack | null;
+
+ public constructor() {
+ this.duration = 0.0;
+ this.restHipsPosition = new THREE.Vector3();
+
+ this.humanoidTracks = {
+ translation: new Map(),
+ rotation: new Map(),
+ };
+
+ this.expressionTracks = {
+ preset: new Map(),
+ custom: new Map(),
+ };
+
+ this.lookAtTrack = null;
+ }
+}
diff --git a/packages/three-vrm-animation/src/VRMAnimationLoaderPlugin.ts b/packages/three-vrm-animation/src/VRMAnimationLoaderPlugin.ts
new file mode 100644
index 000000000..c498fa7e0
--- /dev/null
+++ b/packages/three-vrm-animation/src/VRMAnimationLoaderPlugin.ts
@@ -0,0 +1,271 @@
+import * as THREE from 'three';
+import { GLTF, GLTFLoaderPlugin, GLTFParser } from 'three/examples/jsm/loaders/GLTFLoader';
+import { GLTF as GLTFSchema } from '@gltf-transform/core';
+import { VRMCVRMAnimation } from '@pixiv/types-vrmc-vrm-animation-1.0';
+import type { VRMHumanBoneName } from '@pixiv/three-vrm-core';
+import { VRMExpressionPresetName, VRMHumanBoneParentMap } from '@pixiv/three-vrm-core';
+import { VRMAnimation } from './VRMAnimation';
+import { arrayChunk } from './utils/arrayChunk';
+
+const MAT4_IDENTITY = /*@__PURE__*/ new THREE.Matrix4();
+
+const _v3A = /*@__PURE__*/ new THREE.Vector3();
+const _quatA = /*@__PURE__*/ new THREE.Quaternion();
+const _quatB = /*@__PURE__*/ new THREE.Quaternion();
+const _quatC = /*@__PURE__*/ new THREE.Quaternion();
+
+/**
+ * Possible spec versions it recognizes.
+ */
+const POSSIBLE_SPEC_VERSIONS = /*@__PURE__*/ new Set(['1.0', '1.0-draft']);
+
+const vrmExpressionPresetNameSet: Set = /*@__PURE__*/ new Set(Object.values(VRMExpressionPresetName));
+
+interface VRMAnimationLoaderPluginNodeMap {
+ humanoidIndexToName: Map;
+ expressionsIndexToName: Map;
+ lookAtIndex: number | null;
+}
+
+type VRMAnimationLoaderPluginWorldMatrixMap = Map;
+
+/**
+ * A plugin of GLTFLoader that imports {@link VRMAnimation}s from a `VRMC_vrm_animation` extension and gltf animations.
+ */
+export class VRMAnimationLoaderPlugin implements GLTFLoaderPlugin {
+ public readonly parser: GLTFParser;
+
+ public constructor(parser: GLTFParser) {
+ this.parser = parser;
+ }
+
+ public get name(): string {
+ return 'VRMC_vrm_animation';
+ }
+
+ public async afterRoot(gltf: GLTF): Promise {
+ const defGltf = gltf.parser.json as GLTFSchema.IGLTF;
+ const defExtensionsUsed = defGltf.extensionsUsed;
+
+ if (defExtensionsUsed == null || defExtensionsUsed.indexOf(this.name) == -1) {
+ return;
+ }
+
+ const defExtension = defGltf.extensions?.[this.name] as VRMCVRMAnimation | undefined;
+
+ if (defExtension == null) {
+ return;
+ }
+
+ const specVersion = defExtension.specVersion;
+ if (!POSSIBLE_SPEC_VERSIONS.has(specVersion)) {
+ console.warn(`VRMAnimationLoaderPlugin: Unknown VRMC_vrm_animation spec version: ${specVersion}`);
+ return;
+ }
+ if (specVersion === '1.0-draft') {
+ console.warn(
+ 'VRMAnimationLoaderPlugin: Using a draft spec version: 1.0-draft. Some behaviors may be different. Consider updating the animation file.',
+ );
+ }
+
+ const nodeMap = this._createNodeMap(defExtension);
+ const worldMatrixMap = await this._createBoneWorldMatrixMap(gltf, defExtension);
+
+ const hipsNode = defExtension.humanoid?.humanBones['hips']?.node;
+ const hips = hipsNode != null ? ((await gltf.parser.getDependency('node', hipsNode)) as THREE.Object3D) : null;
+
+ const restHipsPosition = new THREE.Vector3();
+ hips?.getWorldPosition(restHipsPosition);
+
+ const clips = gltf.animations;
+ const animations: VRMAnimation[] = clips.map((clip, iAnimation) => {
+ const defAnimation = defGltf.animations![iAnimation];
+
+ const animation = this._parseAnimation(clip, defAnimation, nodeMap, worldMatrixMap);
+ animation.restHipsPosition = restHipsPosition;
+
+ return animation;
+ });
+
+ gltf.userData.vrmAnimations = animations;
+ }
+
+ private _createNodeMap(defExtension: VRMCVRMAnimation): VRMAnimationLoaderPluginNodeMap {
+ const humanoidIndexToName: Map = new Map();
+ const expressionsIndexToName: Map = new Map();
+
+ // humanoid
+ const humanBones = defExtension.humanoid?.humanBones;
+
+ if (humanBones) {
+ Object.entries(humanBones).forEach(([name, bone]) => {
+ const node = bone?.node;
+ if (node != null) {
+ humanoidIndexToName.set(node, name as VRMHumanBoneName);
+ }
+ });
+ }
+
+ // expressions
+ const preset = defExtension.expressions?.preset;
+
+ if (preset) {
+ Object.entries(preset).forEach(([name, expression]) => {
+ const node = expression?.node;
+ if (node != null) {
+ expressionsIndexToName.set(node, name);
+ }
+ });
+ }
+
+ const custom = defExtension.expressions?.custom;
+
+ if (custom) {
+ Object.entries(custom).forEach(([name, expression]) => {
+ const { node } = expression;
+ expressionsIndexToName.set(node, name);
+ });
+ }
+
+ // lookAt
+ const lookAtIndex = defExtension.lookAt?.node ?? null;
+
+ return { humanoidIndexToName, expressionsIndexToName, lookAtIndex };
+ }
+
+ private async _createBoneWorldMatrixMap(
+ gltf: GLTF,
+ defExtension: VRMCVRMAnimation,
+ ): Promise {
+ // update the entire hierarchy first
+ gltf.scene.updateWorldMatrix(false, true);
+
+ const threeNodes = (await gltf.parser.getDependencies('node')) as THREE.Object3D[];
+
+ const worldMatrixMap: VRMAnimationLoaderPluginWorldMatrixMap = new Map();
+
+ for (const [boneName, humanBone] of Object.entries(defExtension.humanoid.humanBones)) {
+ const node = humanBone?.node;
+ if (node != null) {
+ const threeNode = threeNodes[node];
+ worldMatrixMap.set(boneName as VRMHumanBoneName, threeNode.matrixWorld);
+
+ if (boneName === 'hips') {
+ worldMatrixMap.set('hipsParent', threeNode.parent?.matrixWorld ?? MAT4_IDENTITY);
+ }
+ }
+ }
+
+ return worldMatrixMap;
+ }
+
+ private _parseAnimation(
+ animationClip: THREE.AnimationClip,
+ defAnimation: GLTFSchema.IAnimation,
+ nodeMap: VRMAnimationLoaderPluginNodeMap,
+ worldMatrixMap: VRMAnimationLoaderPluginWorldMatrixMap,
+ ): VRMAnimation {
+ const tracks = animationClip.tracks;
+ const defChannels = defAnimation.channels;
+
+ const result = new VRMAnimation();
+
+ result.duration = animationClip.duration;
+
+ defChannels.forEach((channel, iChannel) => {
+ const { node, path } = channel.target;
+ const origTrack = tracks[iChannel];
+
+ if (node == null) {
+ return;
+ }
+
+ // humanoid
+ const boneName = nodeMap.humanoidIndexToName.get(node);
+ if (boneName != null) {
+ let parentBoneName: VRMHumanBoneName | 'hipsParent' | null = VRMHumanBoneParentMap[boneName];
+ while (parentBoneName != null && worldMatrixMap.get(parentBoneName) == null) {
+ parentBoneName = VRMHumanBoneParentMap[parentBoneName];
+ }
+ parentBoneName ?? (parentBoneName = 'hipsParent');
+
+ if (path === 'translation') {
+ if (boneName !== 'hips') {
+ console.warn(
+ `The loading animation contains a translation track for ${boneName}, which is not permitted in the VRMC_vrm_animation spec. ignoring the track`,
+ );
+ } else {
+ const hipsParentWorldMatrix = worldMatrixMap.get('hipsParent')!;
+
+ const trackValues = arrayChunk(origTrack.values, 3).flatMap((v) =>
+ _v3A.fromArray(v).applyMatrix4(hipsParentWorldMatrix).toArray(),
+ );
+
+ const track = origTrack.clone();
+ track.values = new Float32Array(trackValues);
+
+ result.humanoidTracks.translation.set(boneName, track);
+ }
+ } else if (path === 'rotation') {
+ // a = p^-1 * a' * p * c
+ // a' = p * p^-1 * a' * p * c * c^-1 * p^-1
+ // = p * a * c^-1 * p^-1
+
+ const worldMatrix = worldMatrixMap.get(boneName)!;
+ const parentWorldMatrix = worldMatrixMap.get(parentBoneName)!;
+
+ worldMatrix.decompose(_v3A, _quatA, _v3A);
+ _quatA.invert();
+
+ parentWorldMatrix.decompose(_v3A, _quatB, _v3A);
+
+ const trackValues = arrayChunk(origTrack.values, 4).flatMap((q) =>
+ _quatC.fromArray(q).premultiply(_quatB).multiply(_quatA).toArray(),
+ );
+
+ const track = origTrack.clone();
+ track.values = new Float32Array(trackValues);
+
+ result.humanoidTracks.rotation.set(boneName, track);
+ } else {
+ throw new Error(`Invalid path "${path}"`);
+ }
+ return;
+ }
+
+ // expressions
+ const expressionName = nodeMap.expressionsIndexToName.get(node);
+ if (expressionName != null) {
+ if (path === 'translation') {
+ const times = origTrack.times;
+ const values = new Float32Array(origTrack.values.length / 3);
+ for (let i = 0; i < values.length; i++) {
+ values[i] = origTrack.values[3 * i];
+ }
+
+ const newTrack = new THREE.NumberKeyframeTrack(`${expressionName}.weight`, times as any, values as any);
+
+ if (vrmExpressionPresetNameSet.has(expressionName)) {
+ result.expressionTracks.preset.set(expressionName as VRMExpressionPresetName, newTrack);
+ } else {
+ result.expressionTracks.custom.set(expressionName, newTrack);
+ }
+ } else {
+ throw new Error(`Invalid path "${path}"`);
+ }
+ return;
+ }
+
+ // lookAt
+ if (node === nodeMap.lookAtIndex) {
+ if (path === 'rotation') {
+ result.lookAtTrack = origTrack;
+ } else {
+ throw new Error(`Invalid path "${path}"`);
+ }
+ }
+ });
+
+ return result;
+ }
+}
diff --git a/packages/three-vrm-animation/src/VRMAnimationLoaderPluginOptions.ts b/packages/three-vrm-animation/src/VRMAnimationLoaderPluginOptions.ts
new file mode 100644
index 000000000..0129fb523
--- /dev/null
+++ b/packages/three-vrm-animation/src/VRMAnimationLoaderPluginOptions.ts
@@ -0,0 +1,7 @@
+export interface VRMAnimationLoaderPluginOptions {
+ /**
+ * Specify a desired frame rate in case if the animation need to be baked.
+ * Animation bake occurs when the lookAt target is not a direct child of the root.
+ */
+ bakeFrameRate?: number;
+}
diff --git a/packages/three-vrm-animation/src/VRMLookAtQuaternionProxy.ts b/packages/three-vrm-animation/src/VRMLookAtQuaternionProxy.ts
new file mode 100644
index 000000000..554bd4297
--- /dev/null
+++ b/packages/three-vrm-animation/src/VRMLookAtQuaternionProxy.ts
@@ -0,0 +1,39 @@
+import { VRMLookAt } from '@pixiv/three-vrm-core';
+import * as THREE from 'three';
+
+const RAD2DEG = 180 / Math.PI;
+
+const _eulerA = /*@__PURE__*/ new THREE.Euler();
+
+export class VRMLookAtQuaternionProxy extends THREE.Object3D {
+ public readonly vrmLookAt: VRMLookAt;
+ public override readonly type: string | 'VRMLookAtQuaternionProxy';
+
+ public constructor(lookAt: VRMLookAt) {
+ super();
+
+ this.vrmLookAt = lookAt;
+
+ this.type = 'VRMLookAtQuaternionProxy';
+
+ // See: https://github.com/mrdoob/three.js/blob/r158/src/core/Object3D.js#L65
+ const prevRotationOnChangeCallback = this.rotation._onChangeCallback;
+ this.rotation._onChange(() => {
+ prevRotationOnChangeCallback();
+ this._applyToLookAt();
+ });
+
+ const prevQuaternionOnChangeCallback = this.quaternion._onChangeCallback;
+ this.quaternion._onChange(() => {
+ prevQuaternionOnChangeCallback();
+ this._applyToLookAt();
+ });
+ }
+
+ private _applyToLookAt(): void {
+ _eulerA.setFromQuaternion(this.quaternion, VRMLookAt.EULER_ORDER);
+
+ this.vrmLookAt.yaw = RAD2DEG * _eulerA.y;
+ this.vrmLookAt.pitch = RAD2DEG * _eulerA.x;
+ }
+}
diff --git a/packages/three-vrm-animation/src/createVRMAnimationClip.ts b/packages/three-vrm-animation/src/createVRMAnimationClip.ts
new file mode 100644
index 000000000..1dfa8a8a4
--- /dev/null
+++ b/packages/three-vrm-animation/src/createVRMAnimationClip.ts
@@ -0,0 +1,150 @@
+import * as THREE from 'three';
+import type {
+ VRMCore,
+ VRMExpressionManager,
+ VRMExpressionPresetName,
+ VRMHumanBoneName,
+ VRMHumanoid,
+} from '@pixiv/three-vrm-core';
+import type { VRMAnimation } from './VRMAnimation';
+import { VRMLookAtQuaternionProxy } from './VRMLookAtQuaternionProxy';
+
+export function createVRMAnimationHumanoidTracks(
+ vrmAnimation: VRMAnimation,
+ humanoid: VRMHumanoid,
+ metaVersion: '0' | '1',
+): {
+ translation: Map<'hips', THREE.VectorKeyframeTrack>;
+ rotation: Map;
+} {
+ const translation = new Map<'hips', THREE.VectorKeyframeTrack>();
+ const rotation = new Map();
+
+ for (const [name, origTrack] of vrmAnimation.humanoidTracks.rotation.entries()) {
+ const nodeName = humanoid.getNormalizedBoneNode(name)?.name;
+
+ if (nodeName != null) {
+ const track = new THREE.QuaternionKeyframeTrack(
+ `${nodeName}.quaternion`,
+ origTrack.times,
+ origTrack.values.map((v, i) => (metaVersion === '0' && i % 2 === 0 ? -v : v)),
+ );
+ rotation.set(name, track);
+ }
+ }
+
+ for (const [name, origTrack] of vrmAnimation.humanoidTracks.translation.entries()) {
+ const nodeName = humanoid.getNormalizedBoneNode(name)?.name;
+
+ if (nodeName != null) {
+ const animationY = vrmAnimation.restHipsPosition.y;
+ const humanoidY = humanoid.normalizedRestPose.hips!.position![1];
+ const scale = humanoidY / animationY;
+
+ const track = origTrack.clone();
+ track.values = track.values.map((v, i) => (metaVersion === '0' && i % 3 !== 1 ? -v : v) * scale);
+ track.name = `${nodeName}.position`;
+ translation.set(name, track);
+ }
+ }
+
+ return { translation, rotation };
+}
+
+export function createVRMAnimationExpressionTracks(
+ vrmAnimation: VRMAnimation,
+ expressionManager: VRMExpressionManager,
+): {
+ preset: Map;
+ custom: Map;
+} {
+ const preset = new Map();
+ const custom = new Map();
+
+ for (const [name, origTrack] of vrmAnimation.expressionTracks.preset.entries()) {
+ const trackName = expressionManager.getExpressionTrackName(name);
+
+ if (trackName != null) {
+ const track = origTrack.clone();
+ track.name = trackName;
+ preset.set(name, track);
+ }
+ }
+
+ for (const [name, origTrack] of vrmAnimation.expressionTracks.custom.entries()) {
+ const trackName = expressionManager.getExpressionTrackName(name);
+
+ if (trackName != null) {
+ const track = origTrack.clone();
+ track.name = trackName;
+ custom.set(name, track);
+ }
+ }
+
+ return { preset, custom };
+}
+
+export function createVRMAnimationLookAtTrack(
+ vrmAnimation: VRMAnimation,
+ trackName: string,
+): THREE.KeyframeTrack | null {
+ if (vrmAnimation.lookAtTrack == null) {
+ return null;
+ }
+
+ const track = vrmAnimation.lookAtTrack.clone();
+ track.name = trackName;
+ return track;
+}
+
+/**
+ * Create an AnimationClip out of the given VRMAnimation and the VRM.
+ *
+ * @param vrmAnimation A {@link VRMAnimation}.
+ * @param vrm A {@link VRMCore}.
+ * @returns An AnimationClip
+ */
+export function createVRMAnimationClip(vrmAnimation: VRMAnimation, vrm: VRMCore): THREE.AnimationClip {
+ const tracks: THREE.KeyframeTrack[] = [];
+
+ const humanoidTracks = createVRMAnimationHumanoidTracks(vrmAnimation, vrm.humanoid, vrm.meta.metaVersion);
+ tracks.push(...humanoidTracks.translation.values());
+ tracks.push(...humanoidTracks.rotation.values());
+
+ if (vrm.expressionManager != null) {
+ const expressionTracks = createVRMAnimationExpressionTracks(vrmAnimation, vrm.expressionManager);
+ tracks.push(...expressionTracks.preset.values());
+ tracks.push(...expressionTracks.custom.values());
+ }
+
+ if (vrm.lookAt != null) {
+ // search VRMLookAtQuaternionProxy
+ let proxy = vrm.scene.children.find((obj) => obj instanceof VRMLookAtQuaternionProxy);
+
+ if (proxy == null) {
+ // if not found, create a new one
+ console.warn(
+ 'createVRMAnimationClip: VRMLookAtQuaternionProxy is not found. Creating a new one automatically. To suppress this warning, create a VRMLookAtQuaternionProxy manually',
+ );
+
+ proxy = new VRMLookAtQuaternionProxy(vrm.lookAt);
+ proxy.name = 'VRMLookAtQuaternionProxy';
+ vrm.scene.add(proxy);
+ } else if (proxy.name == null) {
+ // if found but name is not set, set the name automatically
+ console.warn(
+ 'createVRMAnimationClip: VRMLookAtQuaternionProxy is found but its name is not set. Setting the name automatically. To suppress this warning, set the name manually',
+ );
+
+ proxy.name = 'VRMLookAtQuaternionProxy';
+ }
+
+ // create a track
+ const track = createVRMAnimationLookAtTrack(vrmAnimation, `${proxy.name}.quaternion`);
+ if (track != null) {
+ tracks.push(track);
+ }
+ }
+
+ return new THREE.AnimationClip('Clip', vrmAnimation.duration, tracks);
+}
diff --git a/packages/three-vrm-animation/src/index.ts b/packages/three-vrm-animation/src/index.ts
new file mode 100644
index 000000000..0ff6ad3a8
--- /dev/null
+++ b/packages/three-vrm-animation/src/index.ts
@@ -0,0 +1,9 @@
+export {
+ createVRMAnimationHumanoidTracks,
+ createVRMAnimationExpressionTracks,
+ createVRMAnimationLookAtTrack,
+ createVRMAnimationClip,
+} from './createVRMAnimationClip';
+export { VRMAnimation } from './VRMAnimation';
+export { VRMAnimationLoaderPlugin } from './VRMAnimationLoaderPlugin';
+export { VRMLookAtQuaternionProxy } from './VRMLookAtQuaternionProxy';
diff --git a/packages/three-vrm-animation/src/utils/arrayChunk.ts b/packages/three-vrm-animation/src/utils/arrayChunk.ts
new file mode 100644
index 000000000..ab5a845b9
--- /dev/null
+++ b/packages/three-vrm-animation/src/utils/arrayChunk.ts
@@ -0,0 +1,30 @@
+/**
+ * ```js
+ * arrayChunk( [ 1, 2, 3, 4, 5, 6 ], 2 )
+ * // will be
+ * [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]
+ * ```
+ */
+export function arrayChunk(array: ArrayLike, every: number): T[][] {
+ const N = array.length;
+
+ const ret: T[][] = [];
+
+ let current: T[] = [];
+ let remaining = 0;
+
+ for (let i = 0; i < N; i++) {
+ const el = array[i];
+
+ if (remaining <= 0) {
+ remaining = every;
+ current = [];
+ ret.push(current);
+ }
+
+ current.push(el);
+ remaining--;
+ }
+
+ return ret;
+}
diff --git a/packages/three-vrm-animation/tsconfig.json b/packages/three-vrm-animation/tsconfig.json
new file mode 100644
index 000000000..7513033f0
--- /dev/null
+++ b/packages/three-vrm-animation/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": ["./src"]
+}
diff --git a/packages/three-vrm-materials-mtoon/src/MToonMaterial.ts b/packages/three-vrm-materials-mtoon/src/MToonMaterial.ts
index 07930d8ca..83aab2a6e 100644
--- a/packages/three-vrm-materials-mtoon/src/MToonMaterial.ts
+++ b/packages/three-vrm-materials-mtoon/src/MToonMaterial.ts
@@ -446,7 +446,7 @@ export class MToonMaterial extends THREE.ShaderMaterial {
uvAnimationRotationPhase: { value: 0.0 },
},
parameters.uniforms ?? {},
- ]) as any;
+ ]) as typeof MToonMaterial.prototype.uniforms;
// == finally compile the shader program =======================================================
this.setValues(parameters);
diff --git a/packages/types-vrmc-vrm-animation-1.0/README.md b/packages/types-vrmc-vrm-animation-1.0/README.md
new file mode 100644
index 000000000..db31f3d74
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/README.md
@@ -0,0 +1,13 @@
+# @pixiv/types-vrmc-vrm-animation-1.0
+
+Type definitions of `VRM_vrm_animation` 1.0 schema.
+
+https://github.com/vrm-c/vrm-specification/tree/master/specification/VRMC_vrm_animation-1.0
+
+There should not be any implementation in this package. Just type definitions of the schema.
+
+The extension root is named `VRMCVRMAnimation` .
+
+[GitHub Repository](https://github.com/pixiv/three-vrm/tree/dev/packages/types-vrmc-vrm-animation-1.0)
+
+[Documentation](https://pixiv.github.io/three-vrm/packages/types-vrmc-vrm-animation-1.0/docs)
diff --git a/packages/types-vrmc-vrm-animation-1.0/package.json b/packages/types-vrmc-vrm-animation-1.0/package.json
new file mode 100644
index 000000000..dcaa05cf7
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "@pixiv/types-vrmc-vrm-animation-1.0",
+ "version": "2.0.10",
+ "description": "Type definitions of VRMC_vrm_animation 1.0 schema",
+ "license": "MIT",
+ "author": "pixiv",
+ "files": [
+ "/ts*/",
+ "/types/",
+ "LICENSE"
+ ],
+ "main": "",
+ "types": "types/index.d.ts",
+ "typesVersions": {
+ "<3.9": {
+ "*": [
+ "ts3.4/*"
+ ]
+ }
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixiv/three-vrm.git",
+ "directory": "packages/types-vrmc-vrm-animation-1.0"
+ },
+ "scripts": {
+ "version": "yarn all",
+ "all": "yarn lint && yarn clean && yarn build && yarn docs",
+ "clean": "rimraf docs/ ts*/ types/",
+ "build": "tsc --declaration --declarationDir ./types --emitDeclarationOnly && downlevel-dts types ts3.4/types",
+ "docs": "typedoc --entryPoints ./src/index.ts --out docs",
+ "lint": "eslint \"src/**/*.{ts,tsx}\" && prettier \"src/**/*.{ts,tsx}\" --check",
+ "lint-fix": "eslint \"src/**/*.{ts,tsx}\" --fix && prettier \"src/**/*.{ts,tsx}\" --write"
+ },
+ "lint-staged": {
+ "./src/**/*.{ts,tsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ]
+ },
+ "dependencies": {
+ "@pixiv/types-vrmc-vrm-1.0": "2.0.3"
+ }
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/Expression.ts b/packages/types-vrmc-vrm-animation-1.0/src/Expression.ts
new file mode 100644
index 000000000..6e5f48201
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/Expression.ts
@@ -0,0 +1,12 @@
+/**
+ * Represents a single expression.
+ */
+export interface Expression {
+ /**
+ * Represents a single glTF node mapped to this expression.
+ */
+ node: number;
+
+ extensions?: { [name: string]: any };
+ extras?: any;
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/Expressions.ts b/packages/types-vrmc-vrm-animation-1.0/src/Expressions.ts
new file mode 100644
index 000000000..5f4471906
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/Expressions.ts
@@ -0,0 +1,21 @@
+import type { Expression } from './Expression';
+import type { ExpressionPresetName } from '@pixiv/types-vrmc-vrm-1.0';
+
+/**
+ * An object which maps expressions to nodes.
+ */
+export interface Expressions {
+ /**
+ * An object that contains definitions of preset expressions.
+ */
+ preset?: {
+ [preset in ExpressionPresetName]?: Expression;
+ };
+
+ /**
+ * An object that contains definitions of custom expressions.
+ */
+ custom?: {
+ [key: string]: Expression;
+ };
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/Humanoid.ts b/packages/types-vrmc-vrm-animation-1.0/src/Humanoid.ts
new file mode 100644
index 000000000..fe6e58a1d
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/Humanoid.ts
@@ -0,0 +1,11 @@
+import { HumanoidHumanBones } from './HumanoidHumanBones';
+
+/**
+ * An object which describes about humanoid bones.
+ */
+export interface Humanoid {
+ /**
+ * An object which maps humanoid bones to nodes.
+ */
+ humanBones: HumanoidHumanBones;
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/HumanoidHumanBone.ts b/packages/types-vrmc-vrm-animation-1.0/src/HumanoidHumanBone.ts
new file mode 100644
index 000000000..91f25977b
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/HumanoidHumanBone.ts
@@ -0,0 +1,12 @@
+/**
+ * Represents a single bone of a Humanoid.
+ */
+export interface HumanoidHumanBone {
+ /**
+ * Represents a single glTF node mapped to this humanBone.
+ */
+ node: number;
+
+ extensions?: { [name: string]: any };
+ extras?: any;
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/HumanoidHumanBones.ts b/packages/types-vrmc-vrm-animation-1.0/src/HumanoidHumanBones.ts
new file mode 100644
index 000000000..c6ffdc0bc
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/HumanoidHumanBones.ts
@@ -0,0 +1,9 @@
+import type { HumanoidHumanBone } from './HumanoidHumanBone';
+import type { HumanoidHumanBoneName } from '@pixiv/types-vrmc-vrm-1.0';
+
+/**
+ * An object which maps humanoid bones to nodes.
+ */
+export type HumanoidHumanBones = {
+ [key in HumanoidHumanBoneName]: HumanoidHumanBone;
+};
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/LookAt.ts b/packages/types-vrmc-vrm-animation-1.0/src/LookAt.ts
new file mode 100644
index 000000000..9fcd2ede6
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/LookAt.ts
@@ -0,0 +1,12 @@
+/**
+ * An object which maps a eye gaze point to a node.
+ */
+export interface LookAt {
+ /**
+ * Represents a single glTF node represents the eye gaze point.
+ */
+ node: number;
+
+ extensions?: { [name: string]: any };
+ extras?: any;
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/VRMCVRMAnimation.ts b/packages/types-vrmc-vrm-animation-1.0/src/VRMCVRMAnimation.ts
new file mode 100644
index 000000000..7f9f6fa9e
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/VRMCVRMAnimation.ts
@@ -0,0 +1,31 @@
+import type { Expressions } from './Expressions';
+import type { Humanoid } from './Humanoid';
+import type { LookAt } from './LookAt';
+
+/**
+ * glTF extension that defines humanoid animations.
+ */
+export interface VRMCVRMAnimation {
+ /**
+ * Specification version of VRMC_vrm_animation
+ */
+ specVersion: '1.0' | '1.0-draft';
+
+ /**
+ * An object which describes about humanoid bones.
+ */
+ humanoid: Humanoid;
+
+ /**
+ * An object which maps expressions to nodes.
+ */
+ expressions?: Expressions;
+
+ /**
+ * An object which maps a eye gaze point to a node.
+ */
+ lookAt?: LookAt;
+
+ extensions?: { [name: string]: any };
+ extras?: any;
+}
diff --git a/packages/types-vrmc-vrm-animation-1.0/src/index.ts b/packages/types-vrmc-vrm-animation-1.0/src/index.ts
new file mode 100644
index 000000000..ff5d2fe06
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/src/index.ts
@@ -0,0 +1,7 @@
+export type { Expression } from './Expression';
+export type { Expressions } from './Expressions';
+export type { Humanoid } from './Humanoid';
+export type { HumanoidHumanBone } from './HumanoidHumanBone';
+export type { HumanoidHumanBones } from './HumanoidHumanBones';
+export type { LookAt } from './LookAt';
+export type { VRMCVRMAnimation } from './VRMCVRMAnimation';
diff --git a/packages/types-vrmc-vrm-animation-1.0/tsconfig.json b/packages/types-vrmc-vrm-animation-1.0/tsconfig.json
new file mode 100644
index 000000000..5bac6173e
--- /dev/null
+++ b/packages/types-vrmc-vrm-animation-1.0/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": [
+ "./src"
+ ]
+}
diff --git a/yarn.lock b/yarn.lock
index f00324051..3fdace5f4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1191,6 +1191,11 @@
node-addon-api "^3.2.1"
node-gyp-build "^4.3.0"
+"@pixiv/types-vrmc-vrm-1.0@2.0.3":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/@pixiv/types-vrmc-vrm-1.0/-/types-vrmc-vrm-1.0-2.0.3.tgz#6f3674b955d54c0c5489bc57e0e19552d0c613e1"
+ integrity sha512-RMP34Bk1qLFQv/CRB1Zqvn2qMFfWQfP2Hms5QrrfoBsW9XroZdEe0zPLNbGmUPYh4F7VtBb+B7+RCuymRtpehA==
+
"@pkgjs/parseargs@^0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
@@ -1647,7 +1652,7 @@ ansi-colors@^4.1.1:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
-ansi-escapes@^4.2.1:
+ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
@@ -2259,6 +2264,22 @@ cli-spinners@^2.5.0:
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a"
integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==
+cli-truncate@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+ integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
+ dependencies:
+ slice-ansi "^3.0.0"
+ string-width "^4.2.0"
+
+cli-truncate@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389"
+ integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==
+ dependencies:
+ slice-ansi "^5.0.0"
+ string-width "^5.0.0"
+
cli-truncate@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a"
@@ -2377,7 +2398,7 @@ colorette@^1.2.2:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
-colorette@^2.0.20:
+colorette@^2.0.19, colorette@^2.0.20:
version "2.0.20"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
@@ -2407,6 +2428,11 @@ commander@^2.20.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+commander@^9.4.1:
+ version "9.5.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
+ integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
+
common-ancestor-path@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7"
@@ -3228,6 +3254,21 @@ execa@^5.0.0:
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
+execa@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20"
+ integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.1"
+ human-signals "^3.0.1"
+ is-stream "^3.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^5.1.0"
+ onetime "^6.0.0"
+ signal-exit "^3.0.7"
+ strip-final-newline "^3.0.0"
+
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -3619,7 +3660,7 @@ get-stream@^5.0.0:
dependencies:
pump "^3.0.0"
-get-stream@^6.0.0:
+get-stream@^6.0.0, get-stream@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -3951,6 +3992,11 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+human-signals@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5"
+ integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==
+
human-signals@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
@@ -5097,6 +5143,11 @@ libnpmpublish@7.1.4:
sigstore "^1.4.0"
ssri "^10.0.1"
+lilconfig@2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4"
+ integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==
+
lilconfig@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc"
@@ -5112,6 +5163,25 @@ lines-and-columns@~2.0.3:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b"
integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==
+lint-staged@13.1.2:
+ version "13.1.2"
+ resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.1.2.tgz#443636a0cfd834d5518d57d228130dc04c83d6fb"
+ integrity sha512-K9b4FPbWkpnupvK3WXZLbgu9pchUJ6N7TtVZjbaPsoizkqFUDkUReUL25xdrCljJs7uLUF3tZ7nVPeo/6lp+6w==
+ dependencies:
+ cli-truncate "^3.1.0"
+ colorette "^2.0.19"
+ commander "^9.4.1"
+ debug "^4.3.4"
+ execa "^6.1.0"
+ lilconfig "2.0.6"
+ listr2 "^5.0.5"
+ micromatch "^4.0.5"
+ normalize-path "^3.0.0"
+ object-inspect "^1.12.2"
+ pidtree "^0.6.0"
+ string-argv "^0.3.1"
+ yaml "^2.1.3"
+
lint-staged@^15.1.0:
version "15.2.0"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.0.tgz#3111534ca58096a3c8f70b044b6e7fe21b36f859"
@@ -5140,6 +5210,20 @@ listr2@8.0.0:
rfdc "^1.3.0"
wrap-ansi "^9.0.0"
+listr2@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23"
+ integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==
+ dependencies:
+ cli-truncate "^2.1.0"
+ colorette "^2.0.19"
+ log-update "^4.0.0"
+ p-map "^4.0.0"
+ rfdc "^1.3.0"
+ rxjs "^7.8.0"
+ through "^2.3.8"
+ wrap-ansi "^7.0.0"
+
load-json-file@6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-6.2.0.tgz#5c7770b42cafa97074ca2848707c61662f4251a1"
@@ -5210,6 +5294,16 @@ log-symbols@^4.1.0:
chalk "^4.1.0"
is-unicode-supported "^0.1.0"
+log-update@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+ integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+ dependencies:
+ ansi-escapes "^4.3.0"
+ cli-cursor "^3.1.0"
+ slice-ansi "^4.0.0"
+ wrap-ansi "^6.2.0"
+
log-update@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.0.0.tgz#0ddeb7ac6ad658c944c1de902993fce7c33f5e59"
@@ -5372,7 +5466,7 @@ meshoptimizer@~0.18.1:
resolved "https://registry.yarnpkg.com/meshoptimizer/-/meshoptimizer-0.18.1.tgz#cdb90907f30a7b5b1190facd3b7ee6b7087797d8"
integrity sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==
-micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4:
+micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@@ -6033,6 +6127,11 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
+object-inspect@^1.12.2:
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
+ integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
+
object-visit@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
@@ -6403,7 +6502,7 @@ picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-pidtree@0.6.0:
+pidtree@0.6.0, pidtree@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
@@ -6959,6 +7058,13 @@ rxjs@^7.5.5:
dependencies:
tslib "^2.1.0"
+rxjs@^7.8.0:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
+ integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
+ dependencies:
+ tslib "^2.1.0"
+
safe-buffer@^5.1.0, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
@@ -7135,6 +7241,15 @@ slash@3.0.0, slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+slice-ansi@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+ integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
slice-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
@@ -7339,7 +7454,7 @@ static-extend@^0.1.1:
define-property "^0.2.5"
object-copy "^0.1.0"
-string-argv@0.3.2:
+string-argv@0.3.2, string-argv@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
@@ -7370,7 +7485,7 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
-string-width@^5.0.1, string-width@^5.1.2:
+string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
@@ -7618,7 +7733,7 @@ through2@^4.0.0:
dependencies:
readable-stream "3"
-through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6:
+through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@@ -8288,7 +8403,7 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-yaml@2.3.4:
+yaml@2.3.4, yaml@^2.1.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2"
integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==