diff --git a/.gitignore b/.gitignore index f8e98d2..08d407f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ node_modules/ build/* -.~lock* \ No newline at end of file +.~lock* + +stats.html \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 599f966..5ee887c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "0.10.0", "dependencies": { "@xmldom/xmldom": "^0.8.10", - "unzipit": "^1.4.3", - "xlsx": "^0.18.5" + "@zip.js/zip.js": "^2.7.57" }, "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", @@ -22,6 +21,7 @@ "rollup": "^4.18.0", "rollup-plugin-css-only": "^4.5.2", "rollup-plugin-svelte": "^7.1.6", + "rollup-plugin-visualizer": "^5.14.0", "sass": "^1.58.3", "svelte": "^4.2.9", "svelte-preprocess": "^5.1.3" @@ -611,6 +611,17 @@ "node": ">=10.0.0" } }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.57", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -650,15 +661,6 @@ "node": ">=0.4.0" } }, - "node_modules/adler-32": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", - "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1072,19 +1074,6 @@ "node": ">=16" } }, - "node_modules/cfb": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", - "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", - "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "crc-32": "~1.2.0" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1270,15 +1259,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/codepage": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", - "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1382,18 +1362,6 @@ "node": ">= 0.4.0" } }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -1449,6 +1417,16 @@ "node": ">=0.10.0" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1711,15 +1689,6 @@ } } }, - "node_modules/frac": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", - "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -2323,6 +2292,22 @@ "node": ">= 0.4" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2440,6 +2425,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2944,6 +2942,24 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -3428,6 +3444,60 @@ "node": ">= 8.0.0" } }, + "node_modules/rollup-plugin-visualizer": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.14.0.tgz", + "integrity": "sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "open": "^8.4.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "rolldown": "1.x", + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, "node_modules/rollup/node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -3719,18 +3789,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/ssf": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", - "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", - "license": "Apache-2.0", - "dependencies": { - "frac": "~1.1.2" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -4131,17 +4189,6 @@ "node": ">= 0.8.0" } }, - "node_modules/unzipit": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unzipit/-/unzipit-1.4.3.tgz", - "integrity": "sha512-gsq2PdJIWWGhx5kcdWStvNWit9FVdTewm4SEG7gFskWs+XCVaULt9+BwuoBtJiRE8eo3L1IPAOrbByNLtLtIlg==", - "dependencies": { - "uzip-module": "^1.0.2" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -4154,11 +4201,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/uzip-module": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/uzip-module/-/uzip-module-1.0.3.tgz", - "integrity": "sha512-AMqwWZaknLM77G+VPYNZLEruMGWGzyigPK3/Whg99B3S6vGHuqsyl5ZrOv1UUF3paGK1U6PM0cnayioaryg/fA==" - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4277,24 +4319,6 @@ "node": ">=8" } }, - "node_modules/wmf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", - "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/word": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", - "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -4426,27 +4450,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/xlsx": { - "version": "0.18.5", - "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", - "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", - "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "cfb": "~1.2.1", - "codepage": "~1.15.0", - "crc-32": "~1.2.1", - "ssf": "~0.11.2", - "wmf": "~1.0.1", - "word": "~0.3.0" - }, - "bin": { - "xlsx": "bin/xlsx.njs" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -4922,6 +4925,11 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==" }, + "@zip.js/zip.js": { + "version": "2.7.57", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4950,11 +4958,6 @@ "acorn": "^8.11.0" } }, - "adler-32": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", - "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==" - }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -5265,15 +5268,6 @@ "nofilter": "^3.1.0" } }, - "cfb": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", - "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", - "requires": { - "adler-32": "~1.3.0", - "crc-32": "~1.2.0" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5419,11 +5413,6 @@ } } }, - "codepage": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", - "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==" - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5511,11 +5500,6 @@ "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true }, - "crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" - }, "css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -5559,6 +5543,12 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -5749,11 +5739,6 @@ "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true }, - "frac": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", - "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" - }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -6207,6 +6192,12 @@ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -6291,6 +6282,15 @@ "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", "dev": true }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -6666,6 +6666,17 @@ "wrappy": "1" } }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -7013,6 +7024,32 @@ } } }, + "rollup-plugin-visualizer": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.14.0.tgz", + "integrity": "sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==", + "dev": true, + "requires": { + "open": "^8.4.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "dependencies": { + "picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7224,14 +7261,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "ssf": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", - "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", - "requires": { - "frac": "~1.1.2" - } - }, "stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -7495,14 +7524,6 @@ "qs": "^6.4.0" } }, - "unzipit": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unzipit/-/unzipit-1.4.3.tgz", - "integrity": "sha512-gsq2PdJIWWGhx5kcdWStvNWit9FVdTewm4SEG7gFskWs+XCVaULt9+BwuoBtJiRE8eo3L1IPAOrbByNLtLtIlg==", - "requires": { - "uzip-module": "^1.0.2" - } - }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -7515,11 +7536,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "uzip-module": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/uzip-module/-/uzip-module-1.0.3.tgz", - "integrity": "sha512-AMqwWZaknLM77G+VPYNZLEruMGWGzyigPK3/Whg99B3S6vGHuqsyl5ZrOv1UUF3paGK1U6PM0cnayioaryg/fA==" - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -7619,16 +7635,6 @@ } } }, - "wmf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", - "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==" - }, - "word": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", - "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -7728,20 +7734,6 @@ } } }, - "xlsx": { - "version": "0.18.5", - "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", - "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", - "requires": { - "adler-32": "~1.3.0", - "cfb": "~1.2.1", - "codepage": "~1.15.0", - "crc-32": "~1.2.1", - "ssf": "~0.11.2", - "wmf": "~1.0.1", - "word": "~0.3.0" - } - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 15d990f..abe4282 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,13 @@ "rollup": "^4.18.0", "rollup-plugin-css-only": "^4.5.2", "rollup-plugin-svelte": "^7.1.6", + "rollup-plugin-visualizer": "^5.14.0", "sass": "^1.58.3", "svelte": "^4.2.9", "svelte-preprocess": "^5.1.3" }, "dependencies": { "@xmldom/xmldom": "^0.8.10", - "unzipit": "^1.4.3", - "xlsx": "^0.18.5" + "@zip.js/zip.js": "^2.7.57" } } diff --git a/rollup.config.js b/rollup.config.js index bc83f6f..48a0946 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,6 +4,7 @@ import commonjs from '@rollup/plugin-commonjs'; import terser from '@rollup/plugin-terser'; import css from 'rollup-plugin-css-only'; import sveltePreprocess from 'svelte-preprocess' +import { visualizer } from "rollup-plugin-visualizer"; const production = !process.env.ROLLUP_WATCH; @@ -37,6 +38,7 @@ export default { }), commonjs(), + visualizer(), // If we're building for production (npm run build // instead of npm run dev), minify //production && terser() diff --git a/scripts/App.svelte b/scripts/App.svelte index 8de4bc2..eee0e85 100644 --- a/scripts/App.svelte +++ b/scripts/App.svelte @@ -1,7 +1,7 @@

Import fichier .ods et .xslx

diff --git a/scripts/browser.js b/scripts/browser.js index d79b77d..b2e5a77 100644 --- a/scripts/browser.js +++ b/scripts/browser.js @@ -5,6 +5,10 @@ import { _getXLSXTableRawContent } from './shared.js' +import {_createOdsFile} from './createOdsFile.js' + +/** @import {SheetCellRawContent, SheetName, SheetRawContent} from './types.js' */ + function parseXML(str){ return (new DOMParser()).parseFromString(str, 'application/xml'); @@ -27,7 +31,26 @@ export function getXLSXTableRawContent(xlsxArrBuff){ } -export {createOdsFile} from './createOdsFile.js' +/** @type { typeof DOMImplementation.prototype.createDocument } */ +const createDocument = function createDocument(...args){ + // @ts-ignore + return document.implementation.createDocument(...args) +} + +const serializer = new XMLSerializer() + +/** @type { typeof XMLSerializer.prototype.serializeToString } */ +const serializeToString = function serializeToString(node){ + return serializer.serializeToString(node) +} + +/** + * @param {Map} sheetsData + */ +export function createOdsFile(sheetsData){ + return _createOdsFile(sheetsData, createDocument, serializeToString) +} + export { // table-level exports diff --git a/scripts/createOdsFile.js b/scripts/createOdsFile.js index 888b30b..5d8a96c 100644 --- a/scripts/createOdsFile.js +++ b/scripts/createOdsFile.js @@ -1,24 +1,161 @@ -//@ts-check +import { ZipWriter, BlobWriter, TextReader } from '@zip.js/zip.js'; -import {write, utils} from 'xlsx' -import {tableRawContentToValues} from './shared.js' +/** @import {SheetCellRawContent, SheetName, SheetRawContent} from './types.js' */ -/** @import {SheetName, SheetRawContent} from './types.js' */ +const stylesXml = ` + + + + +`; + +const manifestXml = ` + + + + +`; /** * Crée un fichier .ods à partir d'un Map de feuilles de calcul * @param {Map} sheetsData + * @param {typeof DOMImplementation.prototype.createDocument} createDocument + * @param {typeof XMLSerializer.prototype.serializeToString} serializeToString * @returns {Promise} */ -export async function createOdsFile(sheetsData) { - const workbook = utils.book_new(); +export async function _createOdsFile(sheetsData, createDocument, serializeToString) { + // Create a new zip writer + const zipWriter = new ZipWriter(new BlobWriter('application/vnd.oasis.opendocument.spreadsheet')); - const sheetsDataValues = tableRawContentToValues(sheetsData) + zipWriter.add( + "mimetype", + new TextReader("application/vnd.oasis.opendocument.spreadsheet"), + { + compressionMethod: 0, + level: 0, + dataDescriptor: false, + extendedTimestamp: false, + } + ); - for(const [sheetName, table] of sheetsDataValues){ - const worksheet = utils.aoa_to_sheet(table); - utils.book_append_sheet(workbook, worksheet, sheetName); - } + const contentXml = generateContentFileXMLString(sheetsData, createDocument, serializeToString); + zipWriter.add("content.xml", new TextReader(contentXml), {level: 9}); - return write(workbook, {bookType: 'ods', type: 'array'}); + zipWriter.add("styles.xml", new TextReader(stylesXml)); + + zipWriter.add('META-INF/manifest.xml', new TextReader(manifestXml)); + + // Close the zip writer and get the ArrayBuffer + const zipFile = await zipWriter.close(); + return zipFile.arrayBuffer(); } + + +/** + * Generate the content.xml file with spreadsheet data + * @param {Map} sheetsData + * @param {typeof DOMImplementation.prototype.createDocument} createDocument + * @param {typeof XMLSerializer.prototype.serializeToString} serializeToString + * @returns {string} + */ +function generateContentFileXMLString(sheetsData, createDocument, serializeToString) { + const doc = createDocument('urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'office:document-content'); + const root = doc.documentElement; + + // Set up namespaces + root.setAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'); + root.setAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'); + root.setAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'); + root.setAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0'); + root.setAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0'); + root.setAttribute('office:version', '1.2'); + + const bodyNode = doc.createElement('office:body'); + root.appendChild(bodyNode); + + const spreadsheetNode = doc.createElement('office:spreadsheet'); + bodyNode.appendChild(spreadsheetNode); + + // Iterate through sheets + sheetsData.forEach((sheetData, sheetName) => { + const tableNode = doc.createElement('table:table'); + tableNode.setAttribute('table:name', sheetName); + spreadsheetNode.appendChild(tableNode); + + const columnNode = doc.createElement('table:table-column'); + tableNode.appendChild(columnNode); + + // Iterate through rows + sheetData.forEach((row) => { + const rowNode = doc.createElement('table:table-row'); + tableNode.appendChild(rowNode); + + // Iterate through cells in row + row.forEach((cell) => { + const cellNode = doc.createElement('table:table-cell'); + const cellType = convertCellType(cell.type); + cellNode.setAttribute('office:value-type', cellType); + + // Add value attribute based on type + if (cell.value !== null && cell.value !== undefined) { + switch (cellType) { + case 'float': + cellNode.setAttribute('office:value', cell.value.toString()); + break; + case 'percentage': + cellNode.setAttribute('office:value', cell.value.toString()); + cellNode.setAttribute('office:value-type', 'percentage'); + break; + case 'date': + cellNode.setAttribute('office:date-value', cell.value.toString()); + break; + case 'boolean': + cellNode.setAttribute('office:boolean-value', cell.value ? 'true' : 'false'); + break; + default: + const textNode = doc.createElement('text:p'); + textNode.textContent = cell.value.toString(); + cellNode.appendChild(textNode); + break; + } + + if (cellType !== 'string') { + const textNode = doc.createElement('text:p'); + textNode.textContent = cell.value.toString(); + cellNode.appendChild(textNode); + } + } + + rowNode.appendChild(cellNode); + }); + }); + }); + + return serializeToString(doc); +} + +/** + * Convert cell type to OpenDocument format type + * @param {SheetCellRawContent['type']} type + * @returns {SheetCellRawContent['type']} + */ +function convertCellType(type) { + const typeMap = { + 'float': 'float', + 'percentage': 'percentage', + 'currency': 'currency', + 'date': 'date', + 'time': 'time', + 'boolean': 'boolean', + 'string': 'string', + 'n': 'float', + 's': 'string', + 'd': 'date', + 'b': 'boolean' + }; + return typeMap[type] || 'string'; +} + diff --git a/scripts/node.js b/scripts/node.js index a48e788..5096c40 100644 --- a/scripts/node.js +++ b/scripts/node.js @@ -1,11 +1,14 @@ //@ts-check -import {DOMParser} from '@xmldom/xmldom' +import {DOMParser, DOMImplementation, XMLSerializer} from '@xmldom/xmldom' import { _getODSTableRawContent, _getXLSXTableRawContent } from './shared.js' +import { _createOdsFile } from './createOdsFile.js' + +/** @import {SheetCellRawContent, SheetName, SheetRawContent} from './types.js' */ function parseXML(str){ @@ -29,7 +32,27 @@ export function getXLSXTableRawContent(xlsxArrBuff){ return _getXLSXTableRawContent(xlsxArrBuff, parseXML) } -export {createOdsFile} from './createOdsFile.js' +const implementation = new DOMImplementation() + +/** @type { typeof DOMImplementation.prototype.createDocument } */ +const createDocument = function createDocument(...args){ + // @ts-ignore + return implementation.createDocument(...args) +} + +const serializer = new XMLSerializer() + +/** @type { typeof XMLSerializer.prototype.serializeToString } */ +const serializeToString = function serializeToString(node){ + return serializer.serializeToString(node) +} + +/** + * @param {Map} sheetsData + */ +export function createOdsFile(sheetsData){ + return _createOdsFile(sheetsData, createDocument, serializeToString) +} export { // table-level exports diff --git a/scripts/shared.js b/scripts/shared.js index 599d7b7..78d89e4 100644 --- a/scripts/shared.js +++ b/scripts/shared.js @@ -1,8 +1,10 @@ //@ts-check -import { unzip } from 'unzipit'; +import { Uint8ArrayReader, ZipReader, TextWriter } from '@zip.js/zip.js'; +/** @import {Entry} from '@zip.js/zip.js'*/ /** @import {SheetName, SheetRawContent, SheetRowRawContent, SheetCellRawContent} from './types.js' */ + // https://dom.spec.whatwg.org/#interface-node const TEXT_NODE = 3 @@ -48,11 +50,30 @@ function extraxtODSCellText(cell) { * @returns {Promise>} */ export async function _getODSTableRawContent(arrayBuffer, parseXML) { - const zip = await unzip(arrayBuffer); - const entries = zip.entries; + const zipDataReader = new Uint8ArrayReader(new Uint8Array(arrayBuffer)); + const zipReader = new ZipReader(zipDataReader); + const zipEntries = await zipReader.getEntries() + await zipReader.close(); + + /** @type {Map} */ + const entryByFilename = new Map() + for(const entry of zipEntries){ + const filename = entry.filename + entryByFilename.set(filename, entry) + } + + const contentXmlEntry = entryByFilename.get('content.xml') + + if(!contentXmlEntry){ + throw new TypeError(`entry 'content.xml' manquante dans le zip`) + } // Extract the content.xml file which contains the spreadsheet data - const contentXml = await entries['content.xml'].text(); + + //@ts-ignore + const contentXml = await contentXmlEntry.getData(new TextWriter()); + //console.log('contentXml', contentXml); + const contentDoc = parseXML(contentXml); const tableMap = new Map(); @@ -109,29 +130,61 @@ export async function _getODSTableRawContent(arrayBuffer, parseXML) { * @returns {Promise>} */ export async function _getXLSXTableRawContent(arrayBuffer, parseXML) { - const zip = await unzip(arrayBuffer); - const entries = zip.entries; + const zipDataReader = new Uint8ArrayReader(new Uint8Array(arrayBuffer)); + const zipReader = new ZipReader(zipDataReader); + const zipEntries = await zipReader.getEntries() + await zipReader.close(); + + /** @type {Map} */ + const entryByFilename = new Map() + for(const entry of zipEntries){ + const filename = entry.filename + entryByFilename.set(filename, entry) + } + + const sharedStringsEntry = entryByFilename.get('xl/sharedStrings.xml') + + if(!sharedStringsEntry){ + throw new TypeError(`entry 'xl/sharedStrings.xml' manquante dans le zip`) + } + + //@ts-ignore + const sharedStringsXml = await sharedStringsEntry.getData(new TextWriter()); - const sharedStringsXml = await entries['xl/sharedStrings.xml'].text(); const sharedStringsDoc = parseXML(sharedStringsXml); const sharedStrings = Array.from(sharedStringsDoc.getElementsByTagName('sst')[0].getElementsByTagName('si')).map(si => si.textContent); // Get sheet names and their corresponding XML files - const workbookXml = await entries['xl/workbook.xml'].text(); + const workbookEntry = entryByFilename.get('xl/workbook.xml') + + if(!workbookEntry){ + throw new TypeError(`entry 'xl/workbook.xml' manquante dans le zip`) + } + + //@ts-ignore + const workbookXml = await workbookEntry.getData(new TextWriter()); const workbookDoc = parseXML(workbookXml); const sheets = Array.from(workbookDoc.getElementsByTagName('sheets')[0].getElementsByTagName('sheet')); const sheetNames = sheets.map(sheet => sheet.getAttribute('name')); const sheetIds = sheets.map(sheet => sheet.getAttribute('r:id')); // Read the relations to get the actual filenames for each sheet - const workbookRelsXml = await entries['xl/_rels/workbook.xml.rels'].text(); + const workbookRelsEntry = entryByFilename.get('xl/_rels/workbook.xml.rels') + + if(!workbookRelsEntry){ + throw new TypeError(`entry 'xl/_rels/workbook.xml.rels' manquante dans le zip`) + } + + //@ts-ignore + const workbookRelsXml = await workbookRelsEntry.getData(new TextWriter()); const workbookRelsDoc = parseXML(workbookRelsXml); const sheetRels = Array.from(workbookRelsDoc.getElementsByTagName('Relationship')); const sheetFiles = sheetIds.map(id => sheetRels.find(rel => rel.getAttribute('Id') === id).getAttribute('Target').replace('worksheets/', '')); // Read each sheet's XML and extract data in parallel const sheetDataPs = sheetFiles.map((sheetFile, index) => ( - entries[`xl/worksheets/${sheetFile}`].text().then(sheetXml => { + // @ts-ignore + entryByFilename.get(`xl/worksheets/${sheetFile}`).getData(new TextWriter()).then(sheetXml => { const sheetDoc = parseXML(sheetXml); const rows = sheetDoc.getElementsByTagName('sheetData')[0].getElementsByTagName('row'); diff --git a/tests/create-ods-file.js b/tests/create-ods-file.js index 8ff8977..35f8054 100644 --- a/tests/create-ods-file.js +++ b/tests/create-ods-file.js @@ -2,7 +2,10 @@ import test from 'ava'; import {getODSTableRawContent, createOdsFile} from '../scripts/node.js' +/** @import {SheetName, SheetRawContent} from '../scripts/types.js' */ + test('basic file creation', async t => { + /** @type {Map} */ const content = new Map([ [ 'La feuille', @@ -15,7 +18,7 @@ test('basic file creation', async t => { ] ]) - // @ts-ignore + const odsFile = await createOdsFile(content) const parsedContent = await getODSTableRawContent(odsFile)