* Remplacement de unzipit par zip.js

* Générer un fichier qui est valide et s'ouvre dans LibreOffice

* blip bloop, smaller browser bundle

* uninstall xlsx
This commit is contained in:
David Bruant 2025-03-25 22:06:53 +01:00 committed by GitHub
parent 05190774c4
commit a3a2393217
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 463 additions and 225 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@ node_modules/
build/*
.~lock*
stats.html

382
package-lock.json generated
View File

@ -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",

View File

@ -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"
}
}

View File

@ -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()

View File

@ -1,7 +1,7 @@
<script>
//@ts-check
import {tableRawContentToObjects, tableWithoutEmptyRows, getODSTableRawContent, getXLSXTableRawContent} from './browser.js'
import {tableRawContentToObjects, tableWithoutEmptyRows, getODSTableRawContent, getXLSXTableRawContent, createOdsFile} from './browser.js'
const ODS_TYPE = "application/vnd.oasis.opendocument.spreadsheet";
const XLSX_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
@ -31,6 +31,9 @@
$: tableObjectSheets = tableRawContent && tableRawContent.then(tableWithoutEmptyRows).then(tableRawContentToObjects) || []
$: Promise.resolve(tableObjectSheets).then(x => console.log('tableObjectSheets', x))
// ligne inutile qui utilise createOdsFile pour l'importer dans le bundle
$: tableRawContent && tableRawContent.then(createOdsFile).then(ab => console.log('length', ab.byteLength))
</script>
<h1>Import fichier .ods et .xslx</h1>

View File

@ -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<SheetName, SheetRawContent>} sheetsData
*/
export function createOdsFile(sheetsData){
return _createOdsFile(sheetsData, createDocument, serializeToString)
}
export {
// table-level exports

View File

@ -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 = `<?xml version="1.0" encoding="UTF-8"?>
<office:document-styles
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
office:version="1.2">
<office:styles/>
<office:automatic-styles/>
<office:master-styles/>
</office:document-styles>`;
const manifestXml = `<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest manifest:version="1.2" xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
<manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.spreadsheet" manifest:full-path="/"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
</manifest:manifest>`;
/**
* Crée un fichier .ods à partir d'un Map de feuilles de calcul
* @param {Map<SheetName, SheetRawContent>} sheetsData
* @param {typeof DOMImplementation.prototype.createDocument} createDocument
* @param {typeof XMLSerializer.prototype.serializeToString} serializeToString
* @returns {Promise<ArrayBuffer>}
*/
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});
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<SheetName, SheetRawContent>} 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;
}
return write(workbook, {bookType: 'ods', type: 'array'});
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';
}

View File

@ -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<SheetName, SheetRawContent>} sheetsData
*/
export function createOdsFile(sheetsData){
return _createOdsFile(sheetsData, createDocument, serializeToString)
}
export {
// table-level exports

View File

@ -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<Map<SheetName, SheetRawContent>>}
*/
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<Entry['filename'], Entry>} */
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<Map<SheetName, SheetRawContent>>}
*/
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<Entry['filename'], Entry>} */
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');

View File

@ -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<SheetName, SheetRawContent>} */
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)