diff --git a/bun.lock b/bun.lock index c6acaa5..83241e7 100644 --- a/bun.lock +++ b/bun.lock @@ -9,9 +9,9 @@ }, "devDependencies": { "@biomejs/biome": "^2.4", - "@figma/plugin-typings": "^1.124", - "@rspack/cli": "^2.0.0", - "@rspack/core": "^2.0.0", + "@figma/plugin-typings": "^1.126", + "@rspack/cli": "^2.0.3", + "@rspack/core": "^2.0.3", "@types/bun": "^1.3", "husky": "^9.1", "typescript": "^6.0", @@ -19,23 +19,23 @@ }, }, "packages": { - "@biomejs/biome": ["@biomejs/biome@2.4.12", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.12", "@biomejs/cli-darwin-x64": "2.4.12", "@biomejs/cli-linux-arm64": "2.4.12", "@biomejs/cli-linux-arm64-musl": "2.4.12", "@biomejs/cli-linux-x64": "2.4.12", "@biomejs/cli-linux-x64-musl": "2.4.12", "@biomejs/cli-win32-arm64": "2.4.12", "@biomejs/cli-win32-x64": "2.4.12" }, "bin": { "biome": "bin/biome" } }, "sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA=="], + "@biomejs/biome": ["@biomejs/biome@2.4.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.15", "@biomejs/cli-darwin-x64": "2.4.15", "@biomejs/cli-linux-arm64": "2.4.15", "@biomejs/cli-linux-arm64-musl": "2.4.15", "@biomejs/cli-linux-x64": "2.4.15", "@biomejs/cli-linux-x64-musl": "2.4.15", "@biomejs/cli-win32-arm64": "2.4.15", "@biomejs/cli-win32-x64": "2.4.15" }, "bin": { "biome": "bin/biome" } }, "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.12", "", { "os": "linux", "cpu": "x64" }, "sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.12", "", { "os": "linux", "cpu": "x64" }, "sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.12", "", { "os": "win32", "cpu": "x64" }, "sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.15", "", { "os": "win32", "cpu": "x64" }, "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ=="], "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], @@ -43,43 +43,43 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], - "@figma/plugin-typings": ["@figma/plugin-typings@1.124.0", "", {}, "sha512-dZ7w5TKz8WGAncwev6G5UdX5UrMImnPw7QlSAh3vqOY1trdFL1PUKDtEpWRqB65hfMdfa8X0NuaGM0Z+2ak7QQ=="], + "@figma/plugin-typings": ["@figma/plugin-typings@1.126.0", "", {}, "sha512-7Y8HPIHWgMUoT1L7InyUe5X6S+gPUSlVLi7TQvwnRev6PjDwwNkM/oSio2t69EvsJ+TRPSJSvO7xph+KHIcWeQ=="], "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], - "@rspack/binding": ["@rspack/binding@2.0.0", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "2.0.0", "@rspack/binding-darwin-x64": "2.0.0", "@rspack/binding-linux-arm64-gnu": "2.0.0", "@rspack/binding-linux-arm64-musl": "2.0.0", "@rspack/binding-linux-x64-gnu": "2.0.0", "@rspack/binding-linux-x64-musl": "2.0.0", "@rspack/binding-wasm32-wasi": "2.0.0", "@rspack/binding-win32-arm64-msvc": "2.0.0", "@rspack/binding-win32-ia32-msvc": "2.0.0", "@rspack/binding-win32-x64-msvc": "2.0.0" } }, "sha512-WA2f9eQpejkvf5Vrnf6wNCn1m8RT1p08NjgOZpKhsCzr0uBjWeRvGduawlrFFHZh/jPnWZTVaVdQ08FEAWbwGw=="], + "@rspack/binding": ["@rspack/binding@2.0.3", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "2.0.3", "@rspack/binding-darwin-x64": "2.0.3", "@rspack/binding-linux-arm64-gnu": "2.0.3", "@rspack/binding-linux-arm64-musl": "2.0.3", "@rspack/binding-linux-x64-gnu": "2.0.3", "@rspack/binding-linux-x64-musl": "2.0.3", "@rspack/binding-wasm32-wasi": "2.0.3", "@rspack/binding-win32-arm64-msvc": "2.0.3", "@rspack/binding-win32-ia32-msvc": "2.0.3", "@rspack/binding-win32-x64-msvc": "2.0.3" } }, "sha512-4exVNhGhW5RFHjK87XeTKbkA/qAgI5NHJlT1jNqiJv0gcUXLqTOEU3w7f8+f9zUo4JMFvPc0c9veOi4M19YYTg=="], - "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@2.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ICBHDKYyndFqljLhjxvKfWWZu39RJSH2jkSmbceXl0kmptLSE0cLWpvk+eGSzLqtxKN0jVchwCw+5P5mWCzwAw=="], + "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@2.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4UyCjLJwU/WxR6K1/gG4u3+jUsoaRHJ5rNu9fto/UbvrItwdlVNULChAApqZFw6mcSetMddSjSICeuj5pSB6sA=="], - "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@2.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-YQ96LMmzIzhZt9cZWUDWXSxS9UWWHWoLxJyZ5f42DSaVPVelBg5ThbVORDwOP5QDA2xFXj60rVnmmcZLzg/aDA=="], + "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@2.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-K3evrbTgZNa8emEqk+AjDtbuoXZp5tPZz3pcEgETxuu3KanW8Zu+Fb+TUp1DEUcL0xOmHPPox8H2cZ3pF4Zmug=="], - "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ufn33gzkIV7JY69k6vJQEdOzRvBqThIgH46pwXksHSMwRZp8IbJhXfyYIAVsRWCk8fXpr9t1nAvCDvJXT2EeyA=="], + "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@2.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-aPLDaaTtX1wqjLYAIHc2MGDQZtv1Hbjx47oaaefbWz5GbAnSA4P8jdYIeeGRyrqvQ0WqJXIWXgT0d/iXtes00A=="], - "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-CZbvFKlNY9UC0C+Czz6i8JFCzGpuL9oX8gEqcJA1+84Y6eEEBH50UiTzeCewxKW3dOofkZdvT5vgNMXz6aMUmg=="], + "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@2.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-0WulUQPop6vmSDfrTxghmVlm+6crU8/XqD2f0dOWbEniZVuDZJ5/Y/cBqTRyk3rjl0vrmUv3lc87/t7UgQJQSw=="], - "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-dPjFGpoCvZfFpJBsWAUR+PR7mWYxpou6L026qIOpAVkz7WiTzErwKD3P1jVrpP4dM9yLb3fVE+PHHjTglhTJ4g=="], + "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@2.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-fAhiMuV5omT53YMft+f3Y9euAFgspuyBAk9ZpeW2buL2TkuUMwP07adhhvQfKdQ5gpELfzmjQaRDGqaIT8UWiA=="], - "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4fgDTMWt0mJDiugdia2mdOjTbnm7yM1Drzl1JpPqlUlOr113byOhc+qgN57LURSGypz2yz/h/Zad7/UnVAxYJw=="], + "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@2.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-0kcuFoZ8vy2iNWoISFOZt+/Ujo7LRLrzE7h07AV5r+oN/mv+/v14Sd/8NUtDIScCkrYOszYq/QS31e6t0UrVfw=="], - "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@2.0.0", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "1.1.4" }, "cpu": "none" }, "sha512-ANk73ZKtPrZf9gdtyRK2nQUfhi1uXoC5P2KF89pyVAE8+zcoLBnYtZGYpWa/cmNi5BcO5g4Z+v2l1UA3bUPLQQ=="], + "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@2.0.3", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "1.1.4" }, "cpu": "none" }, "sha512-x2fsw7GzNZEnw444ikj4/b8kVjM0Y0TllxmizHpYZ9gmaQrOk5OXo9RQdz+l4zzoGors0l2IZP5Cc4GJNCaSoQ=="], - "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@2.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-IHZFRtJ85ONbM+BCtF4TeYXS2Fu9X0IJS2phX1rPibYq9iEtHGfBt4cNlnsJPhbPAXVvi4Oli/yiLRJ1zxtCIg=="], + "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@2.0.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-jqlxuVPdrgMuwj/HEjSkC/jmhl4fAuKyob36zJXq2uAusn2FRJ4kClGe1fLFpfxRXFVQAWwlAOwLJg8T0suuaA=="], - "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@2.0.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-n4tbIqacq/FhNJflMlgZV50AeQFTLh5hnDS3v4W+rJWa3IW1VfgB0+XppdeW+Dqhw7QcMIsCmro01kwNdlXZDQ=="], + "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@2.0.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QM4JEuyk5QaZ5gnvnAIaCwVQzCkrD2E4Sud77kx/MVGDsRkcOlMx3blMC5QNHPDamRmWGk+7314YOQvRhKuWyg=="], - "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@2.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-cJOgikIW2t3S+42TQZsv+DJriJt2m6lnUk+pUFu/fO93rrMvNrx8gfMxR8W5zDTreBX0cfMx2pw6EVmyi/YzsQ=="], + "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@2.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-vSQNnAy0wswG6AfNRuArTHQBiXOXl+A9ddQxBFup4PMHUzXxKtsBLQzw7BgFC0EgrPeHbt+30j7sXVZKYukj4A=="], - "@rspack/cli": ["@rspack/cli@2.0.0", "", { "peerDependencies": { "@rspack/core": "^2.0.0-0", "@rspack/dev-server": "^2.0.0-0" }, "optionalPeers": ["@rspack/dev-server"], "bin": { "rspack": "bin/rspack.js" } }, "sha512-drBB/biz6NHvjEqKLRzJmdPTn/6rg1TLLOY1o7lOySA1nLYoao5h+33TFRCKwpioEigpdAldw35crCLlQ/GHbQ=="], + "@rspack/cli": ["@rspack/cli@2.0.3", "", { "peerDependencies": { "@rspack/core": "^2.0.0-0", "@rspack/dev-server": "^2.0.0-0" }, "optionalPeers": ["@rspack/dev-server"], "bin": { "rspack": "bin/rspack.js" } }, "sha512-h/Xbkupx82UaPr5Ye3hNORi5eXmpEGSPri7WkEOrKIcT+Y3h603kujCQziNIPX3G4UURWPiksIArp1GBTF+A9w=="], - "@rspack/core": ["@rspack/core@2.0.0", "", { "dependencies": { "@rspack/binding": "2.0.0" }, "peerDependencies": { "@module-federation/runtime-tools": "^0.24.1 || ^2.0.0", "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@module-federation/runtime-tools", "@swc/helpers"] }, "sha512-WD1mJM9LbZ7Z399Rbv9dE3BNEV0+3sE5OzDdzV8hOxUb3mX++ynK5n9kil8w60B6nGdcKeV9ly5aN4PgqiwWUg=="], + "@rspack/core": ["@rspack/core@2.0.3", "", { "dependencies": { "@rspack/binding": "2.0.3" }, "peerDependencies": { "@module-federation/runtime-tools": "^0.24.1 || ^2.0.0", "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@module-federation/runtime-tools", "@swc/helpers"] }, "sha512-2ufO/8FHIA/lX6UOgSsKPhpDvHr0sh9lYq/n/LsIZsTwu3973BGbu2fg1Akvuu3rEnskPqXjsqH2EPBzEA42uA=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], - "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], - "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@types/node": ["@types/node@25.7.0", "", { "dependencies": { "undici-types": "~7.21.0" } }, "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg=="], - "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], @@ -111,7 +111,7 @@ "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + "undici-types": ["undici-types@7.21.0", "", {}, "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], } diff --git a/package.json b/package.json index fa2a6c5..85247e8 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,9 @@ "author": "", "license": "", "devDependencies": { - "@figma/plugin-typings": "^1.124", - "@rspack/cli": "^2.0.0", - "@rspack/core": "^2.0.0", + "@figma/plugin-typings": "^1.126", + "@rspack/cli": "^2.0.3", + "@rspack/core": "^2.0.3", "husky": "^9.1", "typescript": "^6.0", diff --git a/src/commands/devup/__tests__/index.test.ts b/src/commands/devup/__tests__/index.test.ts index 7cc846d..874b2cb 100644 --- a/src/commands/devup/__tests__/index.test.ts +++ b/src/commands/devup/__tests__/index.test.ts @@ -17,8 +17,9 @@ import * as textSegmentToTypographyModule from '../../../utils/text-segment-to-t import * as textStyleToTypographyModule from '../../../utils/text-style-to-typography' import * as uploadFileModule from '../../../utils/upload-file' import * as variableAliasToValueModule from '../../../utils/variable-alias-to-value' +import { findDuplicateVariableNames } from '../export-devup' import { exportDevup, importDevup } from '../index' -import type { DevupTypography } from '../types' +import type { Devup, DevupTypography } from '../types' import * as downloadXlsxModule from '../utils/download-devup-xlsx' import * as getColorCollectionModule from '../utils/get-devup-color-collection' import * as uploadXlsxModule from '../utils/upload-devup-xlsx' @@ -723,6 +724,116 @@ describe('devup commands', () => { expect(payload.theme?.typography).toBeUndefined() }) + test('exportDevup notifies and skips json download when variable names collide across collections', async () => { + getColorCollectionSpy = spyOn( + getColorCollectionModule, + 'getDevupColorCollection', + ).mockResolvedValue(null) + spyOn(rgbaToHexModule, 'rgbaToHex').mockReturnValue('#ff0000') + spyOn(optimizeHexModule, 'optimizeHex').mockImplementation((v) => v) + + const colorVariable = { + name: 'title', + resolvedType: 'COLOR', + valuesByMode: { m1: { r: 1, g: 0, b: 0, a: 1 } }, + } as unknown as Variable + const floatVariable = { + name: 'title', + resolvedType: 'FLOAT', + valuesByMode: { m1: 16 }, + } as unknown as Variable + const variablesById: Record = { + color1: colorVariable, + float1: floatVariable, + } + + const notifyMock = mock(() => {}) + ;(globalThis as { figma?: unknown }).figma = { + util: { rgba: (v: unknown) => v }, + loadAllPagesAsync: async () => {}, + getLocalTextStylesAsync: async () => [], + getLocalEffectStylesAsync: async () => [], + root: { findAllWithCriteria: () => [], children: [] }, + variables: { + getVariableByIdAsync: async (id: string) => variablesById[id] ?? null, + getLocalVariableCollectionsAsync: async () => [ + { + modes: [{ modeId: 'm1', name: 'Light' }], + variableIds: ['color1', 'float1'], + }, + ], + }, + notify: notifyMock, + } as unknown as typeof figma + + await exportDevup('json', false) + + expect(notifyMock).toHaveBeenCalledTimes(1) + const [message, options] = notifyMock.mock.calls[0] as unknown as [ + string, + { error?: boolean; timeout?: number }, + ] + expect(message).toContain('"title"') + expect(message).toContain('colors') + expect(message).toContain('length') + expect(options).toMatchObject({ error: true }) + expect(downloadFileMock).not.toHaveBeenCalled() + }) + + test('exportDevup notifies and skips excel download when variable names collide across collections', async () => { + getColorCollectionSpy = spyOn( + getColorCollectionModule, + 'getDevupColorCollection', + ).mockResolvedValue(null) + spyOn(rgbaToHexModule, 'rgbaToHex').mockReturnValue('#ff0000') + spyOn(optimizeHexModule, 'optimizeHex').mockImplementation((v) => v) + styleNameToTypographySpy = spyOn( + styleNameToTypographyModule, + 'styleNameToTypography', + ).mockReturnValue({ level: 0, name: 'title' }) + textStyleToTypographySpy = spyOn( + textStyleToTypographyModule, + 'textStyleToTypography', + ).mockResolvedValue({ fontFamily: 'Inter' } as unknown as DevupTypography) + + const colorVariable = { + name: 'title', + resolvedType: 'COLOR', + valuesByMode: { m1: { r: 1, g: 0, b: 0, a: 1 } }, + } as unknown as Variable + const variablesById: Record = { color1: colorVariable } + + const notifyMock = mock(() => {}) + ;(globalThis as { figma?: unknown }).figma = { + util: { rgba: (v: unknown) => v }, + loadAllPagesAsync: async () => {}, + getLocalTextStylesAsync: async () => [ + { id: 'style1', name: 'title' } as unknown as TextStyle, + ], + getLocalEffectStylesAsync: async () => [], + root: { findAllWithCriteria: () => [], children: [] }, + variables: { + getVariableByIdAsync: async (id: string) => variablesById[id] ?? null, + getLocalVariableCollectionsAsync: async () => [ + { + modes: [{ modeId: 'm1', name: 'Light' }], + variableIds: ['color1'], + }, + ], + }, + notify: notifyMock, + } as unknown as typeof figma + + await exportDevup('excel', false) + + expect(notifyMock).toHaveBeenCalledTimes(1) + const [message] = notifyMock.mock.calls[0] as unknown as [string] + expect(message).toContain('"title"') + expect(message).toContain('colors') + expect(message).toContain('typography') + expect(downloadXlsxMock).not.toHaveBeenCalled() + }) + test('importDevup creates colors and typography from json', async () => { getColorCollectionSpy = spyOn( getColorCollectionModule, @@ -1015,3 +1126,68 @@ describe('devup commands', () => { expect(styleObj.lineHeight).toMatchObject({ unit: 'PIXELS', value: 18 }) }) }) + +describe('findDuplicateVariableNames', () => { + test('returns empty map when no theme is defined', () => { + expect(findDuplicateVariableNames({}).size).toBe(0) + }) + + test('returns empty map when each name lives in a single category', () => { + const devup: Devup = { + theme: { + colors: { light: { primary: '#fff' }, dark: { primary: '#000' } }, + length: { default: { gutter: '16px' } }, + shadows: { default: { card: '0 0 1px #000' } }, + typography: { heading: { fontFamily: 'Inter' } }, + }, + } + expect(findDuplicateVariableNames(devup).size).toBe(0) + }) + + test('reports a name shared between colors and length', () => { + const devup: Devup = { + theme: { + colors: { light: { title: '#fff' } }, + length: { default: { title: '16px' } }, + }, + } + const duplicates = findDuplicateVariableNames(devup) + expect(duplicates.get('title')).toEqual(['colors', 'length']) + }) + + test('reports a name shared between typography and shadows', () => { + const devup: Devup = { + theme: { + shadows: { default: { card: '0 0 1px #000' } }, + typography: { card: { fontFamily: 'Inter' } }, + }, + } + const duplicates = findDuplicateVariableNames(devup) + expect(duplicates.get('card')).toEqual(['shadows', 'typography']) + }) + + test('reports a name occurring in three categories', () => { + const devup: Devup = { + theme: { + colors: { light: { brand: '#fff' } }, + length: { default: { brand: '16px' } }, + typography: { brand: { fontFamily: 'Inter' } }, + }, + } + const duplicates = findDuplicateVariableNames(devup) + expect(duplicates.get('brand')).toEqual(['colors', 'length', 'typography']) + }) + + test('deduplicates a name appearing in multiple themes of the same category', () => { + const devup: Devup = { + theme: { + colors: { + light: { title: '#fff' }, + dark: { title: '#000' }, + }, + }, + } + // Same category — not a cross-collection duplicate. + expect(findDuplicateVariableNames(devup).size).toBe(0) + }) +}) diff --git a/src/commands/devup/export-devup.ts b/src/commands/devup/export-devup.ts index 4290405..b3d875e 100644 --- a/src/commands/devup/export-devup.ts +++ b/src/commands/devup/export-devup.ts @@ -560,6 +560,70 @@ function effectStyleToCssShadow(style: EffectStyle): string | null { return parts.length > 0 ? parts.join(', ') : null } +type DevupCategoryName = 'colors' | 'length' | 'shadows' | 'typography' + +/** + * Find variable names that appear in more than one Devup category. + * + * devup.json keys all flow into a single token namespace at consumption time, + * so a name shared between e.g. `colors.title` and `length.title` collapses + * into one ambiguous reference. Surfacing duplicates at export time prevents + * silent overwrite on the next `importDevup` round-trip. + */ +export function findDuplicateVariableNames( + devup: Devup, +): Map { + const occurrences = new Map() + + const record = (category: DevupCategoryName, name: string) => { + let categories = occurrences.get(name) + if (!categories) { + categories = [] + occurrences.set(name, categories) + } + if (!categories.includes(category)) { + categories.push(category) + } + } + + if (devup.theme?.colors) { + for (const themeEntries of Object.values(devup.theme.colors)) { + for (const key of Object.keys(themeEntries)) { + record('colors', key) + } + } + } + if (devup.theme?.length) { + for (const themeEntries of Object.values(devup.theme.length)) { + for (const key of Object.keys(themeEntries)) { + record('length', key) + } + } + } + if (devup.theme?.shadows) { + for (const themeEntries of Object.values(devup.theme.shadows)) { + for (const key of Object.keys(themeEntries)) { + record('shadows', key) + } + } + } + if (devup.theme?.typography) { + for (const key of Object.keys(devup.theme.typography)) { + record('typography', key) + } + } + + const duplicates = new Map() + for (const [name, categories] of occurrences) { + if (categories.length > 1) { + duplicates.set(name, categories) + } + } + return duplicates +} + +const DUPLICATE_NOTIFY_TIMEOUT = 5000 + export async function exportDevup( output: 'json' | 'excel', treeshaking: boolean = true, @@ -570,6 +634,18 @@ export async function exportDevup( perfEnd('exportDevup()', t) console.info(perfReport()) + const duplicates = findDuplicateVariableNames(devup) + if (duplicates.size > 0) { + const details = Array.from(duplicates.entries()) + .map(([name, categories]) => `"${name}" (${categories.join(', ')})`) + .join(', ') + figma.notify( + `Duplicate variable name(s) across collections: ${details}. Rename them before exporting to avoid devup.json conflicts.`, + { timeout: DUPLICATE_NOTIFY_TIMEOUT, error: true }, + ) + return + } + switch (output) { case 'json': return downloadFile('devup.json', JSON.stringify(devup))