From 1e59826ab3b14ce2350d63953fe63eba248ba26f Mon Sep 17 00:00:00 2001 From: hadestructhor <60148800+hadestructhor@users.noreply.github.com> Date: Sat, 4 Jan 2025 17:39:34 +0100 Subject: [PATCH] feat: Dockerfile multistage --- .dockerignore | 8 + .woodpecker/.workflow.yml | 11 +- Dockerfile | 15 +- README.md | 108 ++++++- assets/react-multistage-size.png | Bin 0 -> 12879 bytes assets/react-simple-size.png | Bin 0 -> 10092 bytes package-lock.json | 531 ++++++++++++++++++++++++++++++- package.json | 3 +- 8 files changed, 661 insertions(+), 15 deletions(-) create mode 100644 .dockerignore create mode 100644 assets/react-multistage-size.png create mode 100644 assets/react-simple-size.png diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..aaa4c37 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +npm-debug.log +Dockerfile +.dockerignore +.git +.gitignore +build +assets \ No newline at end of file diff --git a/.woodpecker/.workflow.yml b/.woodpecker/.workflow.yml index 64e2f35..6aeeb33 100644 --- a/.woodpecker/.workflow.yml +++ b/.woodpecker/.workflow.yml @@ -1,16 +1,10 @@ when: - event: [ tag, manual, push, pull_request ] - branch: main steps: - - name: npm install - image: node:18 - commands: - - npm install - name: docker build and publish when: - event: [tag, manual, push, pull_request] - branch: main image: docker environment: DOCKER_USERNAME: @@ -21,6 +15,5 @@ steps: - /var/run/docker.sock:/var/run/docker.sock commands: - docker login forgejo.transprot.eu -u $DOCKER_USERNAME -p $DOCKER_PASSWORD - - docker build -t 'forgejo.transprot.eu/public/react-simple:${CI_PIPELINE_NUMBER}' -t 'forgejo.transprot.eu/public/react-simple:latest' . - - docker push forgejo.transprot.eu/public/react-simple:${CI_PIPELINE_NUMBER} - - docker push forgejo.transprot.eu/public/react-simple:latest \ No newline at end of file + - docker build --no-cache -t 'forgejo.transprot.eu/public/react-multistage:latest' . + - docker push forgejo.transprot.eu/public/react-multistage:latest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a6aa0d9..db90bcb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,14 @@ -FROM node:18 -COPY . . +# Build stage of the application +FROM node:18 AS build +WORKDIR /app +COPY package*.json ./ RUN npm install +COPY . . +RUN npm run build + +# Production stage to run the application +FROM node:18 AS production +COPY --from=build /app/build /prod +RUN npm install --global serve EXPOSE 3000 -CMD ["npm", "start"] \ No newline at end of file +CMD ["serve", "prod/"] \ No newline at end of file diff --git a/README.md b/README.md index edab771..0efbde1 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,59 @@ docker image ls | from ssv | where repository starts-with 'react' Here's the result of running this command that shows how shamefully big the image is: ![Size of the image](./assets/react-simple-size.png) +### First optimisation: using a .dockerignore file and multi-stage build + +The first optimization that we can do is using a `.dockerignore` file. Just like a `.gitignore` file, a `.dockerignore` file allows us to exclude file and folders that we don't want to include in the docker image that we are building. + +Here's the `.dockerignore` file that I added to this simple project to mainly exclude the dark hole that node_modules are `node_modules`: +``` +node_modules +npm-debug.log +Dockerfile +.dockerignore +.git +.gitignore +build +assets +``` + +In our context, this command only optimizes the build time and not the build size, as the `309 MB` that the node_modules folder occupies are not needed to build the docker imagen but as we run the `npm install` command in any case inside the Dockerfile, this folder is generated and included from inside the Dockerfile. + +A better optimisation of the image is to use multi-stage build. Multi-stage builds allows us to include only what is necessary to run the image, and to not include the build files or node_modules for example. + +Here's the new version of the `Dockerfile` using multi-stage build: +```Dockerfile +# Build stage of the application +FROM node:18 AS build +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . +RUN npm run build + +# Production stage to run the application +FROM node:18 AS production +COPY --from=build /app/build /prod +RUN npm install --global serve +EXPOSE 3000 +CMD ["serve", "prod/"] +``` + +You can run the following command to build the image: +```shell +docker build -t 'react-multistage' . +``` + +You can run the following command to verify that the new image size is smaller than the previous one: +```shell +docker image ls +``` + +I am still usig Nushell on my end to have a pretty print and filter on the images that I want which gives me the following output: +![Multistage image size](./assets/react-multistage-size.png) + +The image now weighs `44 MB` less, which is 1.27 times smaller, and a whopping 21.67% reduction in size! + # Français ## Comment réduire la taille de vos images 💿 docker 🐳 ? @@ -72,4 +125,57 @@ docker image ls | from ssv | where repository starts-with 'react' ``` Voici un aperçu de ce que cette commande m'affiche avec le poids de l'image assez désastreux: -![Taille de cette image](./assets/react-simple-size.png) \ No newline at end of file +![Taille de cette image](./assets/react-simple-size.png) + +### Première optimisation: utilisation de .dockerignore et multi-stage + +Une des première optimisation que l'on peut faire est d'utiliser un fichier `.dockerignore`. Tout comme un fichier `.gitignore`, le fichier `.dockerignore` permet de ne pas include certains dossiers ou fichier qui ne sont pas nécessaire pour l'image que l'on construit. + +Voici le `.dockerignore` que j'ai rajouté à ce simple projet pour notamment éviter d'inclure le trou noir qu'est le dossier `node_modules`: +``` +node_modules +npm-debug.log +Dockerfile +.dockerignore +.git +.gitignore +build +assets +``` + +Cependant, ce que cette commande optimise est le temps du build de l'image, car les `309 MB` qu'occupe actuellement ce dossier ne sont pas nécessaire à inclure dans l'image lors du build de celle-ci, mais comme nous lançons la commande `npm install` dans tous les cas dans le Dockerfile, ils sont réinstallé et ré-inclut dans l'image. + +Une meilleure optimisation de l'image est d'utiliser le multi-stage build. Le multi-stage build permet de n'inclure que ce qui est nécessaire pour exécuter votre programme et de ne pas inclure les fichier de build ou les node_modules par exemple. + +Voici la nouvelle version du `Dockerfile` découpé en multi-stage: +```Dockerfile +# Build stage of the application +FROM node:18 AS build +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . +RUN npm run build + +# Production stage to run the application +FROM node:18 AS production +COPY --from=build /app/build /prod +RUN npm install --global serve +EXPOSE 3000 +CMD ["serve", "prod/"] +``` + +Vous pouvez lancer la commande suivante pour construire l'image: +```shell +docker build -t 'react-multistage' . +``` + +Vous pouvez relancer la comande suivante pour vérifier que la taille a bien diminué cette fois-ci: +```shell +docker image ls +``` + +Pour ma part j'utilise toujours Nushell pour avoir cette belle présentation des résultats filtré: +![Taille de l'image multistage](./assets/react-multistage-size.png) + +L'image pèse maintenant `44 MB` de moins, on a une image 1.27 fois plus petite, soit une diminution de la taille de 21.67% ! \ No newline at end of file diff --git a/assets/react-multistage-size.png b/assets/react-multistage-size.png new file mode 100644 index 0000000000000000000000000000000000000000..74a2df0b12e6b63bc50025abcf878af5b6ab9205 GIT binary patch literal 12879 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 8" } }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -5128,6 +5164,137 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, + "node_modules/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5368,6 +5535,21 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -5470,6 +5652,35 @@ "node": ">=0.10.0" } }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "license": "MIT", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -6314,6 +6525,15 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "license": "MIT" }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -9387,6 +9607,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -11808,6 +12040,12 @@ "node": ">=0.10.0" } }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "license": "(WTFPL OR MIT)" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -13541,6 +13779,30 @@ "node": ">=0.10.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", @@ -13929,6 +14191,28 @@ "node": ">=4" } }, + "node_modules/registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "license": "MIT", + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", @@ -14520,6 +14804,100 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz", + "integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==", + "license": "MIT", + "dependencies": { + "@zeit/schemas": "2.36.0", + "ajv": "8.12.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.7.4", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" + }, + "bin": { + "serve": "build/main.js" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-handler/node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/serve-handler/node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", @@ -14613,6 +14991,88 @@ "node": ">= 0.8.0" } }, + "node_modules/serve/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/serve/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve/node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/serve/node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/serve/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -16268,6 +16728,16 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -16835,6 +17305,65 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 3e520d1..710411a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "cra-template": "1.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-scripts": "5.0.1" + "react-scripts": "5.0.1", + "serve": "^14.2.4" }, "scripts": { "start": "react-scripts start",