diff --git a/.devcontainer/entrypoint.sh b/.devcontainer/entrypoint.sh new file mode 100644 index 000000000..b10e94e18 --- /dev/null +++ b/.devcontainer/entrypoint.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Source emsdk environment +if [ -f "${EMSDK_ROOT}/emsdk_env.sh" ]; then + source "${EMSDK_ROOT}/emsdk_env.sh" +fi + +# Go to workdir +cd /usr/src/app + +exec "$@" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..99a0f9ab0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules/* +dist/* +build/* +docs/build/* +.vscode +.git +.gitignore +.DS_Store diff --git a/.editorconfig b/.editorconfig index 40ce86e45..5ca9d6d4d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -25,10 +25,25 @@ insert_final_newline = true max_line_length = 120 # ------------------------- -# Makefile +# CMake # ------------------------- -[Makefile] -indent_style = tab +[CMakeLists.txt] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + +[*.cmake] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 # ------------------------- # JavaScript / React @@ -130,3 +145,48 @@ max_line_length = off [LICENSE] max_line_length = off trim_trailing_whitespace = false + +# ------------------------- +# Bash / Shell scripts +# ------------------------- +[*.{sh,bash}] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 +# img2num is in bash +[img2num] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + +# ------------------------- +# Batch scripts +# ------------------------- +[*.{bat,cmd}] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + +# ------------------------- +# PowerShell scripts +# ------------------------- +[*.ps1] +indent_style = space +indent_size = 2 +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 diff --git a/.gitignore b/.gitignore index d49901ff3..d79137f52 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,8 @@ dist-ssr *:Zone.Identifier src/data/contributor-credits.json + +# CMake build artifacts +src/wasm/cmake-build/ +src/wasm/**/CMakeCache.txt +src/wasm/**/CMakeFiles/ diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 000000000..02474aa9b --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,42 @@ +# ------------------------ +# Img2Num Dockerfile.dev +# ------------------------ +FROM node:22-bullseye + +ENV EMSDK_VERSION=4.0.10 +ENV EMSDK_DIR=/opt/emsdk + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + python3 \ + git \ + curl \ + unzip \ + pkg-config \ + bash \ + ca-certificates \ + libvips-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install EMSDK once, cached by Docker layers +RUN git clone --depth 1 https://github.com/emscripten-core/emsdk.git $EMSDK_DIR \ +&& cd $EMSDK_DIR \ +&& git fetch --tags \ +&& git checkout $EMSDK_VERSION \ +&& ./emsdk install $EMSDK_VERSION \ +&& ./emsdk activate $EMSDK_VERSION + +ENV PATH=$EMSDK_DIR:$EMSDK_DIR/upstream/emscripten:$PATH +ENV EMSDK=$EMSDK_DIR + +RUN npm install -g npm@11 + +WORKDIR /usr/src/app + +ENV CHOKIDAR_USEPOLLING=true +ENV CHOKIDAR_INTERVAL=100 + +EXPOSE 5173 3000 + +CMD ["bash"] diff --git a/README.md b/README.md index b7e15e358..e50799b5a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ - - + > [!CAUTION] > ⚠️⚠️⚠️ **Breaking Change in [PR #93](https://github.com/Ryan-Millard/Img2Num/pull/93)** ⚠️⚠️⚠️ > diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..da81d0e6c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +services: + dev: + image: ryanmillard/img2num-dev:latest + build: + context: . + dockerfile: Dockerfile.dev + container_name: img2num-dev + working_dir: /usr/src/app + volumes: + - .:/usr/src/app:cached + - node_modules:/usr/src/app/node_modules + - docs_node_modules:/usr/src/app/docs/node_modules + environment: + CHOKIDAR_USEPOLLING: "true" + CHOKIDAR_INTERVAL: "100" + ports: + - "5173:5173" # Vite dev + - "4173:4173" # Vite preview + - "3000:3000" # Docusaurus dev + stdin_open: true + tty: true + +volumes: + node_modules: + docs_node_modules: diff --git a/docs/docs/guidelines/CONTRIBUTING.md b/docs/docs/guidelines/CONTRIBUTING.md index 2760f22fd..b768ae4ae 100644 --- a/docs/docs/guidelines/CONTRIBUTING.md +++ b/docs/docs/guidelines/CONTRIBUTING.md @@ -32,7 +32,7 @@ The [Project Scripts](../project-scripts/overview.md) section shows all of the a ## Additional Resources - [README](https://github.com/Ryan-Millard/Img2Num/blob/main/README.md) -- [Orchestrator Makefile](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/Makefile) +- [Orchestrator CMakeLists.txt](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/CMakeLists.txt) - [Vite Config](https://github.com/Ryan-Millard/Img2Num/blob/main/vite.config.js) - [ESLint Config](https://github.com/Ryan-Millard/Img2Num/blob/main/eslint.config.js) diff --git a/docs/docs/guidelines/coding-style.md b/docs/docs/guidelines/coding-style.md index 78397528b..6cee45e86 100644 --- a/docs/docs/guidelines/coding-style.md +++ b/docs/docs/guidelines/coding-style.md @@ -6,7 +6,7 @@ sidebar_position: 3 ## 🌐General Rules - **Follow `.editorconfig`** exactly: - - Indent: **2 spaces** (except Makefile: tabs) + - Indent: **2 spaces** - Charset: **UTF-8** - Line endings: **LF** - Max line length: 120 (off for JSON, Markdown, lock files, images) @@ -93,9 +93,6 @@ namespace exampleNamespace - Final newline: true (lock files: false) - Max line length: off -## 📝 Makefiles -- Indent: **tabs only** - ## 🖼 Images / Binary Assets - Charset: binary - Do not trim trailing whitespace diff --git a/docs/docs/index.md b/docs/docs/index.md index 8971fe426..06134baec 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -10,8 +10,6 @@ Img2Num is a lightweight, high-performance C++ library (compiled to WebAssembly) Whether you're a beginner or an advanced user, you'll find everything you need here to get started and master Img2Num. ---- - ## Features - ⚡ **Fast & Efficient**: Optimized for large images. @@ -19,29 +17,26 @@ Whether you're a beginner or an advanced user, you'll find everything you need h - 🌐 **WebAssembly Ready**: Run directly in the browser. - 🔧 **Developer-Friendly**: Easy-to-use API for rapid integration. ---- - ## Getting Started -1. **Installation** - Follow the [installation guide](./introduction/getting-started.md) to set up Img2Num quickly. - -2. **Usage Examples** - Learn how to process images with our [code examples](./). - -3. **Advanced Topics** - Explore performance tips, SIMD intrinsics, and optimization tricks in [advanced guides](./). - ---- +Follow the [installation guide](./introduction/getting-started.md) to set up Img2Num quickly. ## Quick Links - [Overview](./introduction/getting-started.md) – Project introduction and setup - [Changelog](/changelog) – Track releases and updates -- [Examples](./) – Ready-to-use snippets and demos -- [FAQ](./) – Common questions answered --- -Enjoy using **Img2Num**! -For issues or contributions, visit our [GitHub repository](https://github.com/Ryan-Millard/Img2Num). +We hope you enjoy using **Img2Num**! +For issues or contributions, visit our +[GitHub repository](https://github.com/Ryan-Millard/Img2Num/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22). + +:::important +If you spot something wrong in the documentation or elsewhere, please help the community by opening an issue for it! + +Issue links: +- [Bug Report](https://github.com/Ryan-Millard/Img2Num/issues/new?template=bug_report.yml) +- [Refactor / Code Quality Improvement](https://github.com/Ryan-Millard/Img2Num/issues/new?template=refactor.yml) +- [Blank Issue](https://github.com/Ryan-Millard/Img2Num/issues/new) +::: diff --git a/docs/docs/introduction/getting-started.md b/docs/docs/introduction/getting-started.md index 9187647cb..7f6c42737 100644 --- a/docs/docs/introduction/getting-started.md +++ b/docs/docs/introduction/getting-started.md @@ -7,16 +7,164 @@ sidebar_position: 2 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import DockerHomepage from './img/docker-desktop-homepage.jpg'; +import DockerSettings from './img/docker-desktop-settings-button-location.jpg'; +import DockerWslSetup from './img/docker-desktop-wsl-integration-setup.jpg'; +import DockerResources from './img/docker-desktop-resources-button-location.jpg'; +import DockerWslButton from './img/docker-desktop-wsl-integration-button-location.jpg'; + ## Getting Started This section covers how to run the application for the first time - from installation to first run. ### Requirements -Before you start installing anything, make sure you have the below installed +:::tip +We highly recommend that you consider both options below (the Docker route and the local route) in case +you run into problems at a later stage. + +The Docker route is the fastest and easiest to set up, +so you should definitely start with that! +::: + +Before you start installing anything, make sure you have the below installed. + +- [Git](https://git-scm.com/install/): Used to download the code and make contributions. + + + +#### Installing Docker + +The section below will guide you through installing Docker on your operating system. + + + + + 1. Install a **POSIX environment** + + + :::important We **highly recommend using WSL2**. + While Docker Desktop can run without it, + WSL2 gives a much smoother experience and works best with the POSIX-oriented scripts in Img2Num. + WSL, in general, is much faster than Hyper-V is with Docker. + ::: + + ```powershell title="Run This in Powershell" + wsl --install + ``` + This will install Ubuntu by default. You can use other distributions if you prefer. + See [Microsoft's documentation](https://learn.microsoft.com/en-us/windows/wsl/install) to + find out more about installing WSL. + + + If you don't have WSL (or a POSIX shell), + it is recommended that you at least download [**Git Bash**](https://git-scm.com/install/windows) + or another POSIX shell. WSL is still faster, so you should consider installing it. + + This is because **Img2Num's code is best suited for POSIX shells**. We have tried our + best to support other shells, but there is a chance that you will run into + problems if you don't use a POSIX shell because most of our developers use POSIX shells. + + + If you are using CMD or PowerShell, you can still run the same Docker commands (although they will be slower), + but **POSIX shells like WSL/Git Bash are recommended** for the best compatibility with Img2Num scripts. + + + + 2. Download and install **Docker Desktop** from [https://www.docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop). + + + During installation, make sure: + - "Use WSL2 instead of Hyper-V" is selected. + - Your preferred Linux distribution (e.g., Ubuntu) is enabled. + + + During installation, make sure: + - "Use Hyper-V of WSL2 instead" is selected. + + After installation, do the following: + 1. Open Docker Desktop. + Docker Desktop Homepage + 2. Click "Settings" in the top right of the screen. + Docker Settings Button Location + 3. Click "Resources" in the sidebar. + Docker WSL Integration Setup + 4. Select "WSL Integration". + Docker Resources Button Location + 5. Select "Enable integration with my default WSL distro" and select the other distributions you want to enable Docker for. + Docker WSL Integration Button Location + + + During installation, make sure: + - "Use Hyper-V of WSL2 instead" is selected. + + After installation, do the following: + 1. Open Docker Desktop. + Docker Desktop Homepage + 2. Click "Settings" in the top right of the screen. + Docker Settings Button Location + 3. Click "Resources" in the sidebar. + Docker WSL Integration Setup + 4. Select "WSL Integration". + Docker Resources Button Location + 5. Select "Enable integration with my default WSL distro" and select the other distributions you want to enable Docker for. + Docker WSL Integration Button Location + + + + 3. Verify the installation in your **WSL terminal or Git Bash**: + ```bash + docker --version + docker compose version + ``` + + :::danger Docker not working? + Make sure to keep Docker Desktop open while you're using Docker because it needs to be open to run containers. + ::: + + + + + ### Installing Docker on macOS + + 1. Download and install **Docker Desktop** from [https://www.docker.com/products/docker-desktop](https://www.docker.com/products/docker-desktop). + + 2. Open Docker Desktop and ensure it is running. + + 3. Verify installation in Terminal: + ```bash + docker --version + docker compose version + ``` + + + + + + ### Installing Docker on Linux + + 1. Install Docker and Docker Compose via your package manager. For Ubuntu/Debian: + ```bash + sudo apt update + sudo apt install -y docker.io docker-compose + sudo systemctl enable --now docker + sudo usermod -aG docker $USER + ``` + > You may need to log out and back in for the group change to take effect. + + 2. Verify installation: + ```bash + docker --version + docker compose version + ``` + + + + -- [Git](https://git-scm.com/install/) - - Used to download the code and make contributions. + + + - [Node.js](https://nodejs.org/en/download/) version 20.0 or higher - When installing Node.js, you are recommended to check all checkboxes related to dependencies. - [Emscripten](https://emscripten.org/docs/getting_started/downloads.html) version 4.0.10 or higher @@ -84,6 +232,9 @@ These all need to be set in your PATH variable and must be accessible in your te ::: + + + ### Cloning the repository This step will guide you through downloading (cloning) the repository's code into a folder, named `Img2Num`. @@ -100,27 +251,128 @@ This section will help you install all the required dependencies. You can choose to only install the dependencies for one portion of the app, but it is recommended that you install the dependencies for both if you want all the functionality. - - ```bash title="Install all dependendencies" - # Install main app's dependencies - cd Img2Num - npm install - - # Install documentation site's dependencies - npm install --prefix ./docs - ``` + + + + :::caution Your Current Working Directory Matters! + In order to call the `img2num` script that we will be using for Docker in the following sections, + you need to stay in the root directory of the project since that is where the file is. + ::: + + + ```bash title="Install all dependencies" + cd Img2Num + chmod +x ./img2num + ./img2num npm install + + ./img2num npm install --prefix ./docs + ``` + + + + ```bash title="Install all dependencies" + cd Img2Num + .\img2num.bat npm install + + .\img2num.bat npm install --prefix ./docs + ``` + + + + ```bash title="Install all dependencies" + cd Img2Num + .\img2num.ps1 npm install + + .\img2num.ps1 npm install --prefix ./docs + ``` + + + + + ```bash title="Install all dependencies" + # Install main app's dependencies + cd Img2Num + npm install + + # Install documentation site's dependencies + npm install --prefix ./docs + ``` + + - ```bash title="Install only the main app's dependencies" - cd Img2Num - npm install - ``` + + + + + ```bash title="Install all dependencies" + cd Img2Num + chmod +x ./img2num + ./img2num npm install + ``` + + + + ```bash title="Install all dependencies" + cd Img2Num + .\img2num.bat npm install + ``` + + + + ```bash title="Install all dependencies" + cd Img2Num + .\img2num.ps1 npm install + ``` + + + + + ```bash title="Install only the main app's dependencies" + cd Img2Num + npm install + ``` + + - ```bash title="Install only the documentation site's dependencies" - npm install --prefix ./docs - ``` + + + + + + ```bash title="Install all dependencies" + cd Img2Num + chmod +x ./img2num + ./img2num npm install --prefix ./docs + ``` + + + + ```bash title="Install all dependencies" + cd Img2Num + .\img2num.bat npm install --prefix ./docs + ``` + + + + ```bash title="Install all dependencies" + cd Img2Num + .\img2num.ps1 npm install --prefix ./docs + ``` + + + + + ```bash title="Install only the documentation site's dependencies" + cd Img2Num + npm install --prefix ./docs + ``` + + + + @@ -129,37 +381,141 @@ You can choose to only install the dependencies for one portion of the app, but This section will help you run both the main application and the documentation site for the first time. - - From the project's root, run: - - ```bash title="Concurrently run both the Vite development and Docs servers" - npm run dev:all - ``` + + + + + + From the project's root, run: + ```bash title="Concurrently run both the Vite development and Docs servers" + ./img2num dev:all + ``` + + :::tip + You can always run `./img2num -h` or `./img2num --help` to see a full list of available commands and scripts. + + It is a wrapper script that allows you to use scripts defined in [`package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/package.json) + and [`docs/package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/docs/package.json). + ::: + + + From the project's root, run: + ```bash title="Concurrently run both the Vite development and Docs servers" + .\img2num.bat dev:all + ``` + + :::tip + You can always run `.\img2num.bat -h` or `.\img2num.bat --help` to see a full list of available commands and scripts. + + It is a wrapper script that allows you to use scripts defined in [`package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/package.json) + and [`docs/package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/docs/package.json). + ::: + + + From the project's root, run: + ```bash title="Concurrently run both the Vite development and Docs servers" + .\img2num.ps1 dev:all + ``` + + :::tip + You can always run `.\img2num.ps1 -h` or `.\img2num.ps1 --help` to see a full list of available commands and scripts. + + It is a wrapper script that allows you to use scripts defined in [`package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/package.json) + and [`docs/package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/docs/package.json). + ::: + + + + + From the project's root, run: + ```bash title="Concurrently run both the Vite development and Docs servers" + npm run dev:all + ``` + + :::tip + You can always run `npm run help` to see a full list of available commands and scripts which allows you to fuzzy find specific scripts. + ::: + + - - From the project's root, run: - - ```bash title="Run the Vite development server" - npm run dev - ``` - + + + + + + From the project's root, run: + ```bash title="Run the Vite development server" + ./img2num dev + ``` + + + From the project's root, run: + ```bash title="Run the Vite development server" + .\img2num.bat dev + ``` + + + From the project's root, run: + ```bash title="Run the Vite development server" + .\img2num.ps1 dev + ``` + + + + + From the project's root, run: + ```bash title="Run the Vite development server" + npm run dev + ``` + + - - From the `docs/` folder, run: - - ```bash title="Run the Docs server" - npm run start - ``` - -
**OR**
- - From the project's root, run: - ```bash - npm run docs start - ``` + + :::note + The [documentation site](https://ryan-millard.github.io/Img2Num/info/) works like an isolated app. + As a result, there are two options for each depending on where you are in the file system. + ::: + + + + + + ```bash title="Run the Docs server" + ./img2num docs start + ``` + + + + ```bash title="Run the Docs server" + .\img2num.bat docs start + ``` + + + + ```bash title="Run the Docs server" + .\img2num.ps1 docs start + ``` + + + + + From the project's root: + ```bash title="Run the Docs server" + npm run docs start + ``` + +
**OR**
+ + From inside the `docs/` folder: + ```bash title="Run the Docs server" + npm run start + ``` +
+
+
### Further Information diff --git a/docs/docs/introduction/img/docker-desktop-homepage.jpg b/docs/docs/introduction/img/docker-desktop-homepage.jpg new file mode 100644 index 000000000..0744f176b Binary files /dev/null and b/docs/docs/introduction/img/docker-desktop-homepage.jpg differ diff --git a/docs/docs/introduction/img/docker-desktop-resources-button-location.jpg b/docs/docs/introduction/img/docker-desktop-resources-button-location.jpg new file mode 100644 index 000000000..7eb569ed2 Binary files /dev/null and b/docs/docs/introduction/img/docker-desktop-resources-button-location.jpg differ diff --git a/docs/docs/introduction/img/docker-desktop-settings-button-location.jpg b/docs/docs/introduction/img/docker-desktop-settings-button-location.jpg new file mode 100644 index 000000000..3baf35f3d Binary files /dev/null and b/docs/docs/introduction/img/docker-desktop-settings-button-location.jpg differ diff --git a/docs/docs/introduction/img/docker-desktop-wsl-integration-button-location.jpg b/docs/docs/introduction/img/docker-desktop-wsl-integration-button-location.jpg new file mode 100644 index 000000000..e701a0bd2 Binary files /dev/null and b/docs/docs/introduction/img/docker-desktop-wsl-integration-button-location.jpg differ diff --git a/docs/docs/introduction/img/docker-desktop-wsl-integration-setup.jpg b/docs/docs/introduction/img/docker-desktop-wsl-integration-setup.jpg new file mode 100644 index 000000000..c15aa2987 Binary files /dev/null and b/docs/docs/introduction/img/docker-desktop-wsl-integration-setup.jpg differ diff --git a/docs/docs/introduction/usage.md b/docs/docs/introduction/usage.md index b9c9e9ab7..1d50de2cc 100644 --- a/docs/docs/introduction/usage.md +++ b/docs/docs/introduction/usage.md @@ -6,8 +6,6 @@ sidebar_position: 1 import hedgeSleep from '@site/static/img/pixel_art_hedgehog/sleep/sleep.gif'; -# Getting Started with Img2Num - Follow these steps to start turning images into color-by-number templates: ## 1. Accessing the site diff --git a/docs/docs/project-scripts/build.md b/docs/docs/project-scripts/build.md index f6dd3bd39..37659b1c0 100644 --- a/docs/docs/project-scripts/build.md +++ b/docs/docs/project-scripts/build.md @@ -45,7 +45,7 @@ Runs the WASM release build: make -C src/wasm build ``` -Creates an optimized WebAssembly module using the `build` script available in the [orchestrator Makefile](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/Makefile). +Creates an optimized WebAssembly module using the `build` script available in the [orchestrator CMakeLists.txt](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/CMakeLists.txt). ## `npm run build-wasm:debug` @@ -55,4 +55,4 @@ Runs the debug build: make -C src/wasm debug ``` -Includes debug symbols and no optimizations. Recommended for debugging logic issues inside C++ code. Also uses a script from the [orchestrator Makefile](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/Makefile). +Includes debug symbols and no optimizations. Recommended for debugging logic issues inside C++ code. Also uses a script from the [orchestrator CMakeLists.txt](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/CMakeLists.txt). diff --git a/docs/docs/project-scripts/clean.md b/docs/docs/project-scripts/clean.md index 168610f57..fb3695a8e 100644 --- a/docs/docs/project-scripts/clean.md +++ b/docs/docs/project-scripts/clean.md @@ -19,7 +19,7 @@ Ensures the project returns to a “fresh” state. Removes the Vite build output directory: ```bash -rm -rf dist +rimraf dist ``` ## `npm run clean-wasm` @@ -27,7 +27,9 @@ rm -rf dist Delegates to: ```bash -make -C src/wasm clean +node scripts/build-wasm.js --clean ``` -This calls the [orchestrator Makefile's](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/Makefile) `clean` script, which calls the `clean` script in every WASM module. +`scripts/build-wasm.js` handles the deletion of build files. + +This calls the [orchestrator CMakeLists.txt's](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/CMakeLists.txt) `clean` script, which calls the `clean` script in every WASM module. diff --git a/docs/docs/project-scripts/overview.md b/docs/docs/project-scripts/overview.md index d1dba49d0..b2c756746 100644 --- a/docs/docs/project-scripts/overview.md +++ b/docs/docs/project-scripts/overview.md @@ -9,7 +9,7 @@ Img2Num provides several `npm scripts` that help with development, building, for This section explains each script category in detail: - Development scripts -- WASM build scripts (handled by the [orchestrator Makefile](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/Makefile), but wrapped by `npm` in `package.json`) +- WASM build scripts (handled by the [orchestrator CMakeLists.txt](https://github.com/Ryan-Millard/Img2Num/blob/main/src/wasm/CMakeLists.txt), but wrapped by `npm` in `package.json`) - Build & preview scripts - Cleanup scripts - Formatting scripts diff --git a/docs/docs/reference/wasm/development-workflow.md b/docs/docs/reference/wasm/development-workflow.md index 464d1bd1d..6adad1dbe 100644 --- a/docs/docs/reference/wasm/development-workflow.md +++ b/docs/docs/reference/wasm/development-workflow.md @@ -11,53 +11,94 @@ This repository configures a Vite plugin (`watch-cpp-and-build-wasm`) that watch Important points: * The watcher registers `src/wasm/**/*.{cpp,h}` with Vite's watcher so edits trigger rebuilds. -* The build uses the root `src/wasm/Makefile` which iterates modules and calls each module's `Makefile`. +* The build uses the root `src/wasm/CMakeLists.txt` which iterates through the modules and calls each module's `CMakeLists.txt`. * For faster local iteration use `npm run dev:debug` — this runs `make debug` and launches the dev server. -## Root Makefile contract - -The root `src/wasm/Makefile` (provided in the repo) implements three main targets: - -```makefile -.PHONY: build debug clean - -build: - # for each module with a Makefile => make -C $$module - -debug: - # same but pass `debug` target to sub-makefiles - -clean: - # invoke `make clean` in each module +## Root CMakeLists.txt contract + +The root `src/wasm/CMakeLists.txt` (provided in the repo) implements two main targets, `build` & `debug`. +Submodule CMakeLists.txt must therefore support at least `build` (default) & `debug` targets. + +## Example submodule CMakeLists.txt template (recommended) + +```CMakeLists.txt +# ================================================================= +# WASM Module - CMake Build Configuration +# ================================================================= + +# Get module name from directory +get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +# Capitalize first letter for export name (e.g., "example" -> "Example") +string(SUBSTRING ${MODULE_NAME} 0 1 FIRST_LETTER) +string(TOUPPER ${FIRST_LETTER} FIRST_LETTER_UPPER) +string(SUBSTRING ${MODULE_NAME} 1 -1 REST_OF_NAME) +set(CAP_MODULE_NAME "${FIRST_LETTER_UPPER}${REST_OF_NAME}") + +# Collect source files (recursively) +file(GLOB_RECURSE SRC_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" +) + +# Output directory +set(BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build") +set(OUT_JS "${BUILD_DIR}/index.js") + +# Create executable target (Emscripten produces .js + .wasm) +add_executable(${MODULE_NAME}_wasm ${SRC_FILES}) + +# Include directories +target_include_directories(${MODULE_NAME}_wasm PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +# Shared Emscripten options +set(COMMON_FLAGS + "SHELL:-s MODULARIZE=1" + "SHELL:-s EXPORT_ES6=1" + "SHELL:-s EXIT_RUNTIME=1" + "SHELL:-s ENVIRONMENT=web" + "SHELL:-s EXPORTED_FUNCTIONS=['_malloc','_free']" + "SHELL:-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','getValue','setValue','HEAPU8']" + "SHELL:-s INITIAL_MEMORY=1024MB" + "SHELL:-s MAXIMUM_MEMORY=2048MB" + "SHELL:-s ALLOW_MEMORY_GROWTH=1" + "SHELL:-s EXPORT_NAME=create${CAP_MODULE_NAME}Module" +) + +# Apply common flags +target_link_options(${MODULE_NAME}_wasm PRIVATE ${COMMON_FLAGS}) + +# Build-type specific flags +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${MODULE_NAME}_wasm PRIVATE -O0 -g4) + target_link_options(${MODULE_NAME}_wasm PRIVATE + "SHELL:-s ASSERTIONS=2" + -g4 + ) +else() + target_compile_options(${MODULE_NAME}_wasm PRIVATE -O3) + target_link_options(${MODULE_NAME}_wasm PRIVATE + "SHELL:-s SINGLE_FILE=0" + ) +endif() + +# Set output location and name +set_target_properties(${MODULE_NAME}_wasm PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${BUILD_DIR}" + OUTPUT_NAME "index" + SUFFIX ".js" +) + +message(STATUS "Module '${MODULE_NAME}' configured (export: create${CAP_MODULE_NAME}Module)") ``` -Submodule Makefiles must therefore support at least `build` (default), `debug`, and `clean` targets. - -## Example submodule Makefile template (recommended) - -```makefile -# src/wasm/modules//Makefile -EMCC ?= emcc -CXXFLAGS_RELEASE = -O3 -s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -CXXFLAGS_DEBUG = -g -O0 -s ASSERTIONS=1 -s DEMANGLE_SUPPORT=1 -SRC = $(wildcard src/*.cpp) -OUT_DIR = build - -.PHONY: all build debug clean - -all: build - -build: - mkdir -p $(OUT_DIR) - $(EMCC) $(SRC) -o $(OUT_DIR)/index.js $(CXXFLAGS_RELEASE) --bind - -debug: - mkdir -p $(OUT_DIR) - $(EMCC) $(SRC) -o $(OUT_DIR)/index.js $(CXXFLAGS_DEBUG) --bind - -clean: - rm -rf $(OUT_DIR) -``` +:::note +1. Place your C++ headers in include/ and sources in src/ +2. Use exported functions via ccall/cwrap in JS +3. Adjust memory flags if your module needs more/less WASM memory +4. For module-specific Emscripten options, add them before target_link_options +::: This template compiles all `.cpp` files under `src/` into `build/index.js` + `build/index.wasm` using simple flags. Tailor flags and link-time options to your needs. diff --git a/docs/docs/reference/wasm/how-to-add-a-module.md b/docs/docs/reference/wasm/how-to-add-a-module.md index 60cf435bf..b01422930 100644 --- a/docs/docs/reference/wasm/how-to-add-a-module.md +++ b/docs/docs/reference/wasm/how-to-add-a-module.md @@ -7,9 +7,9 @@ sidebar_position: 6 # Step-by-step: add a new module 1. Create a new directory: `src/wasm/modules//`. - - Don't be daft - make sure to replace `` with the actual name your module should be given. -2. Add `src/`, `include/` and a `Makefile` that obeys the root Makefile contract (targets: `build`, `debug`, `clean`). -3. Make sure the module's `Makefile` writes output to `build/` with `index.js` and `index.wasm` (the repo's alias generator expects `modules/{name}/build`). + - Replace `` with your actual module name (lowercase, e.g., `audio`, `filters`). +2. Add `src/`, `include/` directories and a `CMakeLists.txt` file. +3. Make sure the module's `CMakeLists.txt` writes output to `build/` with `index.js` and `index.wasm` (the repo's alias generator expects `modules/{name}/build`). 4. `vite.config.js` will automatically find the module and create an alias `@wasm-` on next `vite` start (or rebuild of the config). Example usage: ```js @@ -17,25 +17,106 @@ sidebar_position: 6 await init(); ``` -5. Commit the `Makefile` and source files; do not commit `build/` artifacts unless you want to vendor the WASM for static hosting without building. +5. Commit the `CMakeLists.txt` and source files; do not commit `build/` artifacts unless you want to vendor the WASM for static hosting without building. -# Minimal module Makefile (copy/paste) +# Minimal module CMakeLists.txt (copy/paste) -```makefile -EMCC ?= emcc -SRC = $(wildcard src/*.cpp) -OUT_DIR = build +```cmake +# ============================================= +# WASM Module - CMake Build Configuration +# ============================================= -.PHONY: build debug clean +# Get module name from directory +get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) -build: - mkdir -p $(OUT_DIR) - $(EMCC) $(SRC) -o $(OUT_DIR)/index.js -O3 -s WASM=1 --bind +# Capitalize first letter for export name (e.g., "audio" -> "Audio") +string(SUBSTRING ${MODULE_NAME} 0 1 FIRST_LETTER) +string(TOUPPER ${FIRST_LETTER} FIRST_LETTER_UPPER) +string(SUBSTRING ${MODULE_NAME} 1 -1 REST_OF_NAME) +set(CAP_MODULE_NAME "${FIRST_LETTER_UPPER}${REST_OF_NAME}") -debug: - mkdir -p $(OUT_DIR) - $(EMCC) $(SRC) -o $(OUT_DIR)/index.js -g -O0 -s ASSERTIONS=1 --bind +# Collect source files +file(GLOB_RECURSE SRC_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" +) -clean: - rm -rf $(OUT_DIR) +# Output directory +set(BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build") + +# Create executable target (Emscripten produces .js + .wasm) +add_executable(${MODULE_NAME}_wasm ${SRC_FILES}) + +# Include directories +target_include_directories(${MODULE_NAME}_wasm PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +# Shared Emscripten options +# Adjust INITIAL_MEMORY and MAXIMUM_MEMORY based on your module's needs +set(COMMON_FLAGS + "SHELL:-s MODULARIZE=1" + "SHELL:-s EXPORT_ES6=1" + "SHELL:-s EXIT_RUNTIME=1" + "SHELL:-s ENVIRONMENT=web" + "SHELL:-s EXPORTED_FUNCTIONS=['_malloc','_free']" + "SHELL:-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','getValue','setValue','HEAPU8']" + "SHELL:-s INITIAL_MEMORY=256MB" + "SHELL:-s MAXIMUM_MEMORY=512MB" + "SHELL:-s ALLOW_MEMORY_GROWTH=1" + "SHELL:-s EXPORT_NAME=create${CAP_MODULE_NAME}Module" +) + +# Apply common flags +target_link_options(${MODULE_NAME}_wasm PRIVATE ${COMMON_FLAGS}) + +# Build-type specific flags +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${MODULE_NAME}_wasm PRIVATE -O0 -g4) + target_link_options(${MODULE_NAME}_wasm PRIVATE + "SHELL:-s ASSERTIONS=2" + -g4 + ) +else() + target_compile_options(${MODULE_NAME}_wasm PRIVATE -O3) + target_link_options(${MODULE_NAME}_wasm PRIVATE + "SHELL:-s SINGLE_FILE=0" + ) +endif() + +# Set output location and name +set_target_properties(${MODULE_NAME}_wasm PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${BUILD_DIR}" + OUTPUT_NAME "index" + SUFFIX ".js" +) + +message(STATUS "Module '${MODULE_NAME}' configured (export: create${CAP_MODULE_NAME}Module)") ``` + +# Directory structure + +Your new module should look like: + +``` +src/wasm/modules// +├── CMakeLists.txt +├── include/ +│ └── your_header.h +├── src/ +│ └── main.cpp +└── build/ # Generated (gitignored) + ├── index.js + └── index.wasm +``` + +# Building + +Once added, the module is automatically discovered by the root `CMakeLists.txt`. Simply run: + +```bash +npm run build-wasm # Release build +npm run build-wasm:debug # Debug build with source maps +npm run clean-wasm # Clean all build artifacts +``` + +The build system works identically on Windows, macOS, and Linux. diff --git a/docs/docs/reference/wasm/modules/image/overview.md b/docs/docs/reference/wasm/modules/image/overview.md index 86009e11f..c55f6a01a 100644 --- a/docs/docs/reference/wasm/modules/image/overview.md +++ b/docs/docs/reference/wasm/modules/image/overview.md @@ -11,10 +11,7 @@ The **Image** WASM module provides core image-processing functionality for Img2N ## Structure ``` src/wasm/modules/image/ -├── Makefile -├── build -│   ├── index.js -│   └── index.wasm +├── CMakeLists.txt ├── include │   ├── Image.h │   ├── Pixel.h diff --git a/docs/docs/reference/wasm/overview.md b/docs/docs/reference/wasm/overview.md index 1e4ddbc27..003ffe03b 100644 --- a/docs/docs/reference/wasm/overview.md +++ b/docs/docs/reference/wasm/overview.md @@ -28,9 +28,9 @@ This repo ships native C++ image-processing code compiled to WebAssembly (WASM) The important pieces are: -* [`src/wasm/`](https://github.com/Ryan-Millard/Img2Num/tree/main/src/wasm) — centralized place for a **root Makefile** and a `modules/` directory containing one or more WASM modules (example: `image`). +* [`src/wasm/`](https://github.com/Ryan-Millard/Img2Num/tree/main/src/wasm) — centralized place for a **root CMakeLists.txt** and a `modules/` directory containing one or more WASM modules (example: `image`). * [`vite.config.js`](https://github.com/Ryan-Millard/Img2Num/blob/main/vite.config.js) — contains alias generation so you can `import` built WASM outputs using `@wasm-{module-name}` and a dev-time watcher that triggers rebuilds when `.cpp` / `.h` change. -* [`package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/package.json) scripts — `npm run build-wasm`, `npm run build-wasm:debug`, `npm run clean-wasm`, which delegate to the Makefile in `src/wasm/`. +* [`package.json`](https://github.com/Ryan-Millard/Img2Num/blob/main/package.json) scripts — `npm run build-wasm`, `npm run build-wasm:debug`, `npm run clean-wasm`, which delegate to the CMakeLists.txt in `src/wasm/`. The design goals: @@ -41,7 +41,7 @@ The design goals: ### Quick commands -```bash title="Build all WASM modules (production flags chosen by module Makefiles)" +```bash title="Build all WASM modules" npm run build-wasm ``` diff --git a/docs/docs/reference/wasm/setup-and-dependencies.md b/docs/docs/reference/wasm/setup-and-dependencies.md index 19a96ef66..6d21a22f9 100644 --- a/docs/docs/reference/wasm/setup-and-dependencies.md +++ b/docs/docs/reference/wasm/setup-and-dependencies.md @@ -1,42 +1,148 @@ --- -id: wasm-setup-depencenciess +id: wasm-setup-dependencies title: Setup & Dependencies sidebar_position: 2 --- -# Prerequisites +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -To build the WASM modules you will need a working Emscripten toolchain (emsdk) available on your `PATH` and a Unix-y shell environment (bash / WSL / macOS). Steps in brief: +## Prerequisites -1. Install Emscripten (emsdk) — follow the official Emscripten docs for your OS. -2. Activate emsdk and ensure `emcc`/`em++` are available in the shell used by `npm`/`make`. -3. Optionally install `clang-format` and `cmake` if module Makefiles rely on them. +### Frontend-Only Development + +If you are working on the React/TypeScript frontend, documentation, or any non-WASM features, simply install Node.js and run `npm install` or `pnpm install`. The following npm scripts work cross-platform: + +- **Development**: `dev`, `dev:all`, `preview` +- **Building**: `build-js` (JavaScript/React build only) +- **Code Quality**: `lint`, `lint:fix`, `lint:style`, `format`, `format-js`, `format-wasm` +- **Documentation**: `docs` (all docs scripts) +- **Utilities**: `help`, `clean-js`, `release` + +### Full Development (Including WASM) + +WASM development now works natively on **all platforms** including Windows. You need: + +1. **Node.js** (v20.11+ or v22 LTS) +2. **CMake** (v3.16+) +3. **Emscripten SDK** (emsdk) + +**WASM scripts** (cross-platform): + +- `build-wasm`, `build-wasm:debug` +- `clean-wasm` +- `build`, `clean` (these chain WASM builds) +- `dev:debug`, `dev:all:debug` + +## WASM Build Setup + +### Step 1: Install CMake + + + + ```cmd + winget install Kitware.CMake + ``` + + + ```bash + sudo apt install cmake + ``` + + + ```bash + brew install cmake + ``` + + + +### Step 2: Install Emscripten + +```bash +# Clone emsdk +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk + +# Install and activate latest +./emsdk install latest +./emsdk activate latest + + +``` + + + + ```cmd title="CMD" + emsdk_env.bat + ``` + ```PowerShell title="PowerShell" + ./emsdk_env.ps1 + ``` + + + ```bash title="Add to PATH (run this in each new terminal, or add to your shell profile)" + source ./emsdk_env.sh + ``` + + + ```bash title="Add to PATH (run this in each new terminal, or add to your shell profile)" + source ./emsdk_env.sh + ``` + + :::tip -For Windows development we recommend WSL2 + Ubuntu and activating emsdk inside WSL for consistent results. +Add the emsdk environment script to your shell profile (`.bashrc`, `.zshrc`, or Windows equivalent) so `emcc` is always available. ::: -:::danger Help Wanted! -The support for developers on Windows for this repository is not great. Thus, we need **your help**! -[Issue #80](https://github.com/Ryan-Millard/Img2Num/issues/80) is up for grabs for anyone will to take on this task. -::: +### Step 3: Verify Installation + +```bash +cmake --version # Should show 3.16+ +emcc --version # Should show Emscripten version +``` ## How package.json ties into builds -The repo ships npm scripts that call `make` in the `src/wasm` directory. Example snippets from `package.json`: +The repo ships npm scripts that use a cross-platform Node.js build script with CMake: ```json "scripts": { - "build-wasm": "make -C src/wasm build", - "build-wasm:debug": "make -C src/wasm debug", - "clean-wasm": "make -C src/wasm clean" + "build-wasm": "node scripts/build-wasm.js", + "build-wasm:debug": "node scripts/build-wasm.js --debug", + "clean-wasm": "node scripts/build-wasm.js --clean" } ``` -Use the `npm` scripts when developing locally or in CI; they make the JS side independent from the exact make command. +The build script: +1. Verifies Emscripten is installed +2. Runs `emcmake cmake` to configure the build +3. Runs `cmake --build` to compile all WASM modules +4. Outputs to each module's `build/` directory + +:::note +Use the `npm` scripts when developing locally or in CI; they abstract away the build system details. +::: ## Environment variables -* `NODE_ENV=production` — some parts of `vite.config.js` only trigger a WASM build when building for production; during dev the plugin also triggers builds but only as configured. -* `EMSDK` / `EMCC` — if you maintain multiple SDK installs, ensure the correct one is on `PATH` when running `npm run build-wasm`. +- `NODE_ENV=production` — some parts of `vite.config.js` only trigger a WASM build when building for production; during dev the plugin also triggers builds but only as configured. +- `EMSDK` / `EMCC` — if you maintain multiple SDK installs, ensure the correct one is on `PATH` when running `npm run build-wasm`. + +## Build System Architecture + +The WASM modules use CMake for cross-platform compatibility: + +```txt +src/wasm/ +├── CMakeLists.txt # Root orchestrator (auto-discovers modules) +├── cmake-build/ # CMake build artifacts (gitignored) +└── modules/ + └── image/ + ├── CMakeLists.txt # Module build configuration + ├── src/ # C++ source files + ├── include/ # Header files + └── build/ # Output (index.js + index.wasm) +``` +This replaces the previous Makefile-based system (before [#93](https://github.com/Ryan-Millard/Img2Num/pull/93)) and works identically on Windows, macOS, and Linux. diff --git a/docs/package-lock.json b/docs/package-lock.json index 9423ba745..318bb4db1 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -251,7 +251,6 @@ "integrity": "sha512-22SHEEVNjZfFWkFks3P6HilkR3rS7a6GjnCIqR22Zz4HNxdfT0FG+RE7efTcFVfLUkTTMQQybvaUcwMrHXYa7Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@algolia/client-common": "5.46.0", "@algolia/requester-browser-xhr": "5.46.0", @@ -397,7 +396,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2233,7 +2231,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2256,7 +2253,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2366,7 +2362,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2788,7 +2783,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3727,7 +3721,6 @@ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", "license": "MIT", - "peer": true, "dependencies": { "@docusaurus/core": "3.9.2", "@docusaurus/logger": "3.9.2", @@ -4481,7 +4474,6 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", "license": "MIT", - "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -4821,7 +4813,6 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5463,7 +5454,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -5823,7 +5813,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5910,7 +5899,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -5957,7 +5945,6 @@ "integrity": "sha512-7ML6fa2K93FIfifG3GMWhDEwT5qQzPTmoHKCTvhzGEwdbQ4n0yYUWZlLYT75WllTGJCJtNUI0C1ybN4BCegqvg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@algolia/abtesting": "1.12.0", "@algolia/client-abtesting": "5.46.0", @@ -6423,7 +6410,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6725,7 +6711,6 @@ "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", @@ -7424,7 +7409,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -7744,7 +7728,6 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10" } @@ -8166,7 +8149,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -9347,7 +9329,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14114,7 +14095,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14676,7 +14656,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -15580,7 +15559,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -16370,7 +16348,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -16453,7 +16430,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -16463,7 +16439,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz", "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -16520,7 +16495,6 @@ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/react": "*" }, @@ -16549,7 +16523,6 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -18514,8 +18487,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/type-fest": { "version": "2.19.0", @@ -18939,7 +18911,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -19197,7 +19168,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -19790,7 +19760,6 @@ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/docs/package.json b/docs/package.json index f66424c4a..3077f4f5b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -95,12 +95,12 @@ "scripts": { "help": "node scripts/help.js", "docusaurus": "docusaurus", - "start": "docusaurus start", + "start": "docusaurus start --host 0.0.0.0 --poll 1000", "build": "docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", - "serve": "docusaurus serve", + "serve": "docusaurus serve --host 0.0.0.0", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "validate-scripts": "npm run --prefix .. validate-scripts" diff --git a/img2num b/img2num new file mode 100755 index 000000000..4324c96e4 --- /dev/null +++ b/img2num @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +MODE="$1" +shift || true + +ensure_container() { + # Check if the dev service container is running, start it if not + container_id=$(docker compose ps -q dev) + + # Container does not exist → create & start + if [ -z "$container_id" ]; then + echo "Starting dev container..." + docker compose up -d dev + return + fi + + # Container exists but is stopped → start it + if ! docker inspect -f '{{.State.Running}}' "$container_id" 2>/dev/null | grep -q true; then + echo "Dev container exists but is stopped. Starting..." + docker compose start dev + fi +} + +# Run a command inside the dev container +run_in_container() { + ensure_container + docker compose exec dev "$@" +} + +# Main command dispatch +case "$MODE" in + # Commands that map directly to npm scripts + dev|dev:all|dev:debug|dev:all:debug|build|build-js|build-wasm|build-wasm:debug|preview|docs|lint|lint:fix|lint:style|format|format-js|format-wasm|clean|clean-js|clean-wasm|help) + if [ "$MODE" = "docs" ]; then + YELLOW="" + MAGENTA="" + RESET="" + # Check if terminal supports colors + if [ -t 1 ] && command -v tput >/dev/null 2>&1 && [ "$(tput colors)" -ge 8 ]; then + YELLOW="$(tput setaf 3)" + MAGENTA="$(tput setaf 5)" + RESET="$(tput sgr0)" + fi + + # Informative warning about Docusaurus port forwarding + echo -e "${YELLOW}[INFO] Docusaurus is running inside the container, listening on all interfaces (0.0.0.0).${RESET}" + echo -e "${YELLOW}[INFO] You cannot use the 0.0.0.0 link directly.${RESET}" + echo -e "${YELLOW}[INFO] Access the site in your browser via: ${MAGENTA}http://localhost:3000/Img2Num/info/${RESET}" + fi + + run_in_container npm run "$MODE" "$@" + ;; + + # Arbitrary npm commands + npm) + run_in_container npm "$@" + ;; + + # Open a shell in the container + sh|shell|bash|term|terminal) + run_in_container bash + ;; + + # Docker management shortcuts + stop) docker compose stop ;; + restart) docker compose restart ;; + down) docker compose down ;; + purge|destroy) + docker compose down --volumes --remove-orphans + [ "$MODE" = "destroy" ] && docker rmi img2num-dev:latest + ;; + + # Tail logs + logs) docker compose logs -f ;; + + # Fallback usage + -h|--help|*) + EXIT_CODE=0 + echo + if [ "$MODE" != "-h" ] && [ "$MODE" != "--help" ]; then + EXIT_CODE=1 + echo "Unknown command used." + echo + fi + + cat < + +Commands: + NPM Scripts (use help to see more info about each script): + build|build-js|build-wasm|build-wasm:debug + clean|clean-js|clean-wasm + dev|dev:all|dev:debug|dev:all:debug + docs + format|format-js|format-wasm + help + lint|lint:fix|lint:style + preview + + Using NPM Directly: + npm Run arbitrary npm command + + Open Container Terminal: + sh|shell|bash Opens bash terminal in Docker container + + Docker Maintenance: + stop Stops running Docker container (keeps containers, volumes & networks). + restart Restarts running Docker container. + down Stops running Docker container (keeps volumes). + purge Stops running Docker container (removes everything, including orphans). + destroy Same as purge, but deletes Docker image. + logs Displays any relevant Docker logs. +EOF + exit $EXIT_CODE + ;; +esac diff --git a/img2num.bat b/img2num.bat new file mode 100644 index 000000000..522fb2e8b --- /dev/null +++ b/img2num.bat @@ -0,0 +1,8 @@ +@echo off +REM img2num.bat - CMD wrapper for img2num.ps1 (PowerShell script) + +REM Get the directory of the batch file +SET "SCRIPT_DIR=%~dp0" + +REM Call the PowerShell script with all arguments using Windows PowerShell +powershell -ExecutionPolicy Bypass -File "%SCRIPT_DIR%img2num.ps1" %* diff --git a/img2num.ps1 b/img2num.ps1 new file mode 100644 index 000000000..640b99e2b --- /dev/null +++ b/img2num.ps1 @@ -0,0 +1,143 @@ +#!/usr/bin/env pwsh +# ====================================================================== +# ⚠️ Recommended: use Bash / WSL2 for full compatibility +# ====================================================================== +param( + [string]$Mode = "", + [Parameter(ValueFromRemainingArguments=$true)] + [string[]]$RemainingArgs +) + +function Ensure-Container { + # Get container ID (may exist but be stopped) + $containerId = docker compose ps -q dev + + # Container does not exist → create & start + if (-not $containerId) { + Write-Host "Starting dev container..." + docker compose up -d dev + return + } + + # Container exists but is stopped → start it + $running = docker inspect -f '{{.State.Running}}' $containerId 2>$null + if ($running -ne "true") { + Write-Host "Dev container exists but is stopped. Starting..." + docker compose start dev + } +} + +# Run a command inside the dev container +function Run-InContainer { + param([string[]]$CmdArgs) + + Ensure-Container + docker compose exec dev @CmdArgs +} + +# ------------------------------- +# Main command dispatch +# ------------------------------- +switch ($Mode) { + + # NPM scripts + { $_ -in @( + "dev","dev:all","dev:debug","dev:all:debug", + "build","build-js","build-wasm","build-wasm:debug", + "preview","docs", + "lint","lint:fix","lint:style", + "format","format-js","format-wasm", + "clean","clean-js","clean-wasm", + "help" + ) } { + + if ($Mode -eq "docs") { + $YELLOW = $MAGENTA = $RESET = "" + # Check if terminal supports colors + $supportsColor = $Host.UI.SupportsVirtualTerminal + if ($supportsColor) { + $YELLOW = "`e[33m" + $MAGENTA = "`e[35m" + $RESET = "`e[0m" + } + + Write-Host "${YELLOW}[INFO] Docusaurus is running inside the container, listening on all interfaces (0.0.0.0).${RESET}" + Write-Host "${YELLOW}[INFO] You cannot use the 0.0.0.0 link directly.${RESET}" + Write-Host "${YELLOW}[INFO] Access the site in your browser via: ${MAGENTA}http://localhost:3000/Img2Num/info/${RESET}" + } + $cmd = @("npm", "run", $Mode) + $RemainingArgs + Run-InContainer -CmdArgs $cmd + } + + # Arbitrary npm + "npm" { + $cmd = @("npm") + $RemainingArgs + Run-InContainer -CmdArgs $cmd + } + + # Open a shell in the container + { $_ -in @("sh","shell","bash","term","terminal") } { + Run-InContainer @("bash") + } + + # Docker maintenance + "stop" { docker compose stop } + "restart" { docker compose restart } + "down" { docker compose down } + + { $_ -in @("purge","destroy") } { + docker compose down --volumes --remove-orphans + if ($Mode -eq "destroy") { + docker rmi img2num-dev:latest + } + } + + # Logs + "logs" { + docker compose logs -f + } + + # Help / fallback + default { + $EXIT_CODE = 0 + Write-Host "" + + if ($Mode -ne "" -and $Mode -notin @("-h","--help")) { + $EXIT_CODE = 1 + Write-Host "Unknown command used." + Write-Host "" + } + + Write-Host @" +Usage: + ./img2num + +Commands: + NPM Scripts (use help to see more info about each script): + build|build-js|build-wasm|build-wasm:debug + clean|clean-js|clean-wasm + dev|dev:all|dev:debug|dev:all:debug + docs + format|format-js|format-wasm + help + lint|lint:fix|lint:style + preview + + Using NPM Directly: + npm Run arbitrary npm command + + Open Container Terminal: + sh|shell|bash Opens bash terminal in Docker container + + Docker Maintenance: + stop Stops running Docker container (keeps containers, volumes & networks). + restart Restarts running Docker container. + down Stops running Docker container (keeps volumes). + purge Stops running Docker container (removes everything, including orphans). + destroy Same as purge, but deletes Docker image. + logs Displays any relevant Docker logs. +"@ + + exit $EXIT_CODE + } +} diff --git a/package-lock.json b/package-lock.json index 4f072277d..47ff24543 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "globals": "^16.5.0", "jsdom": "^27.3.0", "prettier": "^3.7.4", + "rimraf": "^6.1.2", "standard-version": "^9.5.0", "vite": "^7.2.7", "vite-imagetools": "^9.0.2", @@ -144,7 +145,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -462,7 +462,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -509,7 +508,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1701,6 +1699,29 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2496,7 +2517,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/chai": { "version": "5.2.3", @@ -2550,7 +2572,6 @@ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2561,7 +2582,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2742,7 +2762,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2971,7 +2990,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -3714,7 +3732,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dot-prop": { "version": "5.3.0", @@ -3942,7 +3961,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5024,7 +5042,6 @@ "integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", @@ -5295,6 +5312,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -5637,6 +5655,16 @@ "node": ">= 6" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -5813,6 +5841,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5890,6 +5925,33 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -5970,7 +6032,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6012,6 +6073,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -6027,6 +6089,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -6039,7 +6102,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/process-nextick-args": { "version": "2.0.1", @@ -6117,7 +6181,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6127,7 +6190,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6446,13 +6508,66 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -7141,7 +7256,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7357,7 +7471,6 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -7472,7 +7585,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7486,7 +7598,6 @@ "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.15", "@vitest/mocker": "4.0.15", @@ -7821,7 +7932,6 @@ "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 0d207dfc7..b086495fb 100644 --- a/package.json +++ b/package.json @@ -131,11 +131,11 @@ "preview": "vite preview", "build": "npm run build-wasm && vite build", "build-js": "vite build", - "build-wasm": "make -C src/wasm build", - "build-wasm:debug": "make -C src/wasm debug", + "build-wasm": "node scripts/build-wasm.js", + "build-wasm:debug": "node scripts/build-wasm.js --debug", "clean": "npm run clean-wasm && npm run clean-js", - "clean-js": "rm -rf dist", - "clean-wasm": "make -C src/wasm clean", + "clean-js": "rimraf dist", + "clean-wasm": "node scripts/build-wasm.js --clean", "format": "npm run format-js && npm run format-wasm", "format-js": "prettier --write .", "format-wasm": "node scripts/format-wasm.js", @@ -178,6 +178,7 @@ "globals": "^16.5.0", "jsdom": "^27.3.0", "prettier": "^3.7.4", + "rimraf": "^6.1.2", "standard-version": "^9.5.0", "vite": "^7.2.7", "vite-imagetools": "^9.0.2", diff --git a/scripts/build-wasm.js b/scripts/build-wasm.js new file mode 100644 index 000000000..e871c243b --- /dev/null +++ b/scripts/build-wasm.js @@ -0,0 +1,231 @@ +#!/usr/bin/env node +/** + * Cross-platform WASM build script using CMake + Emscripten + * + * Usage: + * node scripts/build-wasm.js [--debug] [--clean] + * + * Options: + * --debug Build with debug flags (unoptimized, with source maps) + * --clean Remove build artifacts before building + */ + +import { execFileSync } from 'node:child_process'; +import { existsSync, rmSync, mkdirSync, readdirSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { platform } from 'node:os'; + +const WASM_DIR = resolve(import.meta.dirname, '..', 'src', 'wasm'); +const BUILD_DIR = join(WASM_DIR, 'cmake-build'); +const MODULES_DIR = join(WASM_DIR, 'modules'); + +const VALID_ARGS = ['--debug', '--clean']; +const args = process.argv.slice(2); + +// Validate arguments +const unknownArgs = args.filter((arg) => !VALID_ARGS.includes(arg)); +if (unknownArgs.length > 0) { + console.error(`Unknown argument(s): ${unknownArgs.join(', ')}`); + console.error(`Valid arguments: ${VALID_ARGS.join(', ')}`); + process.exit(1); +} + +const isDebug = args.includes('--debug'); +const isClean = args.includes('--clean'); + +const isWindows = platform() === 'win32'; + +/** + * Run a command with arguments (no shell) + */ +function run(cmd, cmdArgs, options = {}) { + const fullCmd = [cmd, ...cmdArgs].join(' '); + console.log(`\n> ${fullCmd}\n`); + + try { + execFileSync(cmd, cmdArgs, { + stdio: 'inherit', + cwd: options.cwd || process.cwd(), + env: { ...process.env, ...options.env }, + }); + } catch (error) { + console.error(`Command failed: ${fullCmd}`); + console.error(` Exit code: ${error.status ?? 'unknown'}`); + if (error.signal) { + console.error(` Signal: ${error.signal}`); + } + if (error.message) { + console.error(` Message: ${error.message}`); + } + process.exit(error.status || 1); + } +} + +/** + * Check if emcmake is available + */ +function checkEmscripten() { + const emcc = isWindows ? 'emcc.bat' : 'emcc'; + try { + execFileSync(emcc, ['--version'], { stdio: 'pipe' }); + return true; + } catch (error) { + // ENOENT means the command was not found (expected when Emscripten not installed) + if (error.code === 'ENOENT') { + return false; + } + // For other errors, log diagnostics and return false + console.error(`Error checking for Emscripten (${emcc}):`); + console.error(` Error code: ${error.code ?? 'unknown'}`); + if (error.status !== undefined) { + console.error(` Exit status: ${error.status}`); + } + if (error.message) { + console.error(` Message: ${error.message}`); + } + return false; + } +} + +/** + * Safely remove a directory with proper error handling + * @param {string} dir - Directory path to remove + * @returns {boolean} - true if successful, false if failed + */ +function safeRemoveDir(dir) { + if (!existsSync(dir)) { + return true; // Nothing to remove + } + + try { + rmSync(dir, { recursive: true, force: false }); + console.log(` Removed: ${dir}`); + return true; + } catch (error) { + console.error(` Failed to remove: ${dir}`); + console.error(` Error code: ${error.code ?? 'unknown'}`); + if (error.message) { + console.error(` Message: ${error.message}`); + } + console.log("You may need to forcefully remove it."); + return false; + } +} + +/** + * Discover all module directories dynamically + * @returns {string[]} - Array of module names + */ +function discoverModules() { + if (!existsSync(MODULES_DIR)) { + return []; + } + + try { + const entries = readdirSync(MODULES_DIR, { withFileTypes: true }); + return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name); + } catch (error) { + console.error(`Failed to read modules directory: ${MODULES_DIR}`); + console.error(` Error: ${error.message}`); + return []; + } +} + +/** + * Clean build directories + */ +function clean() { + console.log('Cleaning build directories...'); + + const failedDirs = []; + + // Remove CMake build directory + if (!safeRemoveDir(BUILD_DIR)) { + failedDirs.push(BUILD_DIR); + } + + // Discover and remove all module build directories dynamically + const modules = discoverModules(); + for (const moduleName of modules) { + const moduleBuildDir = join(MODULES_DIR, moduleName, 'build'); + if (!safeRemoveDir(moduleBuildDir)) { + failedDirs.push(moduleBuildDir); + } + } + + if (failedDirs.length > 0) { + console.error('\nClean completed with errors. Failed to remove:'); + for (const dir of failedDirs) { + console.error(` - ${dir}`); + } + process.exit(1); + } + + console.log('Clean complete.'); +} + +/** + * Main build function + */ +function build() { + console.log(`\n🔧 Building WASM modules (${isDebug ? 'Debug' : 'Release'})...\n`); + + // Check Emscripten + if (!checkEmscripten()) { + console.error('❌ Emscripten not found in PATH.'); + console.error(''); + console.error('Please install Emscripten:'); + console.error(' 1. git clone https://github.com/emscripten-core/emsdk.git'); + console.error(' 2. cd emsdk && ./emsdk install latest && ./emsdk activate latest'); + console.error(' 3. source ./emsdk_env.sh (or emsdk_env.bat on Windows)'); + console.error(''); + console.error('See: https://emscripten.org/docs/getting_started/'); + process.exit(1); + } + + // Create build directory + if (!existsSync(BUILD_DIR)) { + try { + mkdirSync(BUILD_DIR, { recursive: true }); + } catch (error) { + console.error(`Failed to create build directory: ${BUILD_DIR}`); + console.error(` Error code: ${error.code ?? 'unknown'}`); + if (error.message) { + console.error(` Message: ${error.message}`); + } + console.error(''); + console.error('Possible causes:'); + console.error(' - Insufficient permissions to create directory'); + console.error(' - Parent directory does not exist and cannot be created'); + console.error(' - Disk is full or read-only'); + process.exit(1); + } + } + + // Configure with CMake via emcmake + const buildType = isDebug ? 'Debug' : 'Release'; + const emcmake = isWindows ? 'emcmake.bat' : 'emcmake'; + + run(emcmake, [ + 'cmake', + '-S', WASM_DIR, + '-B', BUILD_DIR, + `-DCMAKE_BUILD_TYPE=${buildType}`, + ]); + + // Build + run('cmake', ['--build', BUILD_DIR, '--parallel', '--config', buildType]); + + console.log('\n✅ WASM build complete!\n'); +} + +// Main +if (isClean && args.length === 1) { + // Only clean, don't build + clean(); +} else { + if (isClean) { + clean(); + } + build(); +} diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt new file mode 100644 index 000000000..02db3d4cd --- /dev/null +++ b/src/wasm/CMakeLists.txt @@ -0,0 +1,39 @@ +# ================================================================= +# WASM Root Orchestrator - CMake Build System +# Cross-platform replacement for Makefile +# Added in https://github.com/Ryan-Millard/Img2Num/pull/93/changes +# ================================================================= + +cmake_minimum_required(VERSION 3.16) +project(Img2NumWASM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Verify we're building with Emscripten +if(NOT EMSCRIPTEN) + message(FATAL_ERROR + "This project must be built with Emscripten.\n" + "Use: emcmake cmake .. && cmake --build .\n" + "See docs for installation: https://emscripten.org/docs/getting_started/" + ) +endif() + +# Build type defaults to Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Emscripten: ${EMSCRIPTEN_ROOT_PATH}") + +# Auto-discover all modules with CMakeLists.txt +file(GLOB MODULE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/modules/*") +foreach(MODULE_DIR ${MODULE_DIRS}) + if(IS_DIRECTORY ${MODULE_DIR} AND EXISTS "${MODULE_DIR}/CMakeLists.txt") + get_filename_component(MODULE_NAME ${MODULE_DIR} NAME) + message(STATUS "Adding module: ${MODULE_NAME}") + add_subdirectory(${MODULE_DIR}) + endif() +endforeach() + diff --git a/src/wasm/Makefile b/src/wasm/Makefile deleted file mode 100644 index d7a430b8d..000000000 --- a/src/wasm/Makefile +++ /dev/null @@ -1,66 +0,0 @@ -# ============================================= -# WASM Root Orchestrator Makefile -# ============================================= - -# ANSI color codes -# TODO: Remove below colors after #93 is merged on 22 Dec 2025 - # see https://github.com/Ryan-Millard/Img2Num/pull/93 -RED := \033[1;31m -RESET := \033[0m - -MODULES_DIR := modules -MODULE_PATHS := $(wildcard $(MODULES_DIR)/*) - -# TODO: Remove prewarn after #93 is merged on 22 Dec 2025 -.PHONY: build debug clean help prewarn - -# This target prints the warning -# TODO: Remove prewarn after #93 is merged on 22 Dec 2025 -prewarn: - @echo "\t${RED}" - @echo "\t======================================================" - @echo "\t| ⚠️ WARNING: MAKEFILE BEHAVIOR IS CHANGING! ⚠️ |" - @echo "\t| |" - @echo "\t| Read PR #107 before running: |" - @echo "\t| https://github.com/Ryan-Millard/Img2Num/pull/107 |" - @echo "\t| |" - @echo "\t| Make will be replaced with CMake on 22 Dec 2025. |" - @echo "\t======================================================" - @echo "\t${RESET}" - -# Build all modules -# TODO: Remove prewarn after #93 is merged on 22 Dec 2025 -build: prewarn - @for module in $(MODULE_PATHS); do \ - if [ -f $$module/Makefile ]; then \ - echo "Building module: $$module"; \ - $(MAKE) -C $$module; \ - fi; \ - done - -# Build all modules in debug mode -# TODO: Remove prewarn after #93 is merged on 22 Dec 2025 -debug: prewarn - @for module in modules/*; do \ - if [ -f $$module/Makefile ]; then \ - echo "Debug‐building $$module"; \ - $(MAKE) -C $$module debug; \ - fi; \ - done - -# Clean all build dirs -# TODO: Remove prewarn after #93 is merged on 22 Dec 2025 -clean: prewarn - @for module in $(MODULE_PATHS); do \ - if [ -f $$module/Makefile ]; then \ - echo "Cleaning module: $$module"; \ - $(MAKE) -C $$module clean; \ - fi; \ - done - -# TODO: Remove prewarn after #93 is merged on 22 Dec 2025 -help: prewarn - @echo "Available targets:" - @echo " make build - Build all modules" - @echo " make debug - Clean all modules" - @echo " make clean - Clean all modules" diff --git a/src/wasm/modules/image/CMakeLists.txt b/src/wasm/modules/image/CMakeLists.txt new file mode 100644 index 000000000..3391f7aa5 --- /dev/null +++ b/src/wasm/modules/image/CMakeLists.txt @@ -0,0 +1,71 @@ +# ============================================= +# Image WASM Module - CMake Build Configuration +# Cross-platform replacement for Makefile +# Added in https://github.com/Ryan-Millard/Img2Num/pull/93/changes +# ============================================= + +# Get module name from directory +get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +# Capitalize first letter for export name (e.g., "image" -> "Image") +string(SUBSTRING ${MODULE_NAME} 0 1 FIRST_LETTER) +string(TOUPPER ${FIRST_LETTER} FIRST_LETTER_UPPER) +string(SUBSTRING ${MODULE_NAME} 1 -1 REST_OF_NAME) +set(CAP_MODULE_NAME "${FIRST_LETTER_UPPER}${REST_OF_NAME}") + +# Collect source files +file(GLOB_RECURSE SRC_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" +) + +# Output directory +set(BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build") +set(OUT_JS "${BUILD_DIR}/index.js") + +# Create executable target (Emscripten produces .js + .wasm) +add_executable(${MODULE_NAME}_wasm ${SRC_FILES}) + +# Include directories +target_include_directories(${MODULE_NAME}_wasm PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +# Shared Emscripten options +set(COMMON_FLAGS + "SHELL:-s MODULARIZE=1" + "SHELL:-s EXPORT_ES6=1" + "SHELL:-s EXIT_RUNTIME=1" + "SHELL:-s ENVIRONMENT=web" + "SHELL:-s EXPORTED_FUNCTIONS=['_malloc','_free']" + "SHELL:-s EXPORTED_RUNTIME_METHODS=['ccall','cwrap','getValue','setValue','HEAPU8']" + "SHELL:-s INITIAL_MEMORY=1024MB" + "SHELL:-s MAXIMUM_MEMORY=2048MB" + "SHELL:-s ALLOW_MEMORY_GROWTH=1" + "SHELL:-s EXPORT_NAME=create${CAP_MODULE_NAME}Module" +) + +# Apply common flags +target_link_options(${MODULE_NAME}_wasm PRIVATE ${COMMON_FLAGS}) + +# Build-type specific flags +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(${MODULE_NAME}_wasm PRIVATE -O0 -g4) + target_link_options(${MODULE_NAME}_wasm PRIVATE + "SHELL:-s ASSERTIONS=2" + -g4 + ) +else() + target_compile_options(${MODULE_NAME}_wasm PRIVATE -O3) + target_link_options(${MODULE_NAME}_wasm PRIVATE + "SHELL:-s SINGLE_FILE=0" + ) +endif() + +# Set output location and name +set_target_properties(${MODULE_NAME}_wasm PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${BUILD_DIR}" + OUTPUT_NAME "index" + SUFFIX ".js" +) + +message(STATUS "Module '${MODULE_NAME}' configured (export: create${CAP_MODULE_NAME}Module)") diff --git a/src/wasm/modules/image/Makefile b/src/wasm/modules/image/Makefile deleted file mode 100644 index e2f5e10ea..000000000 --- a/src/wasm/modules/image/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -# Directories -INCLUDE_DIR := include -BUILD_DIR := build - -# Detect module name -MODULE_NAME := $(notdir $(CURDIR)) -CAP_MODULE_NAME := $(shell echo $(MODULE_NAME) | sed 's/^./\U&/') - -# Sources & output -SRC_FILES := $(shell find . -type f -name '*.cpp' ! -path './$(BUILD_DIR)/*') -OUT_JS := $(BUILD_DIR)/index.js - -# Shared Emscripten options -COMMON_FLAGS := \ - -s MODULARIZE=1 \ - -s EXPORT_ES6=1 \ - -s EXIT_RUNTIME=1 \ - -s ENVIRONMENT=web \ - -I$(INCLUDE_DIR) \ - -s EXPORTED_FUNCTIONS="['_malloc','_free']" \ - -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","getValue","setValue","HEAPU8"]' \ - -s INITIAL_MEMORY=1024MB \ - -s MAXIMUM_MEMORY=2048MB \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s ASSERTIONS=2 - -# Release vs Debug flags -RELEASE_FLAGS := -O3 -s SINGLE_FILE=0 -DEBUG_FLAGS := -O0 -g4 -s ASSERTIONS=2 - -# By default, FLAGS = RELEASE_FLAGS -FLAGS := $(RELEASE_FLAGS) - -.PHONY: all debug clean - -all: $(OUT_JS) - -debug: FLAGS := $(DEBUG_FLAGS) -debug: all - -$(OUT_JS): $(SRC_FILES) - @mkdir -p $(BUILD_DIR) - @echo "ΓåÆ Building $(MODULE_NAME) [flags: $(FLAGS)]" - emcc $^ $(COMMON_FLAGS) $(FLAGS) \ - -s EXPORT_NAME="create$(CAP_MODULE_NAME)Module" \ - -o $@ - -clean: - rm -rf $(BUILD_DIR) diff --git a/vite.config.js b/vite.config.js index 0534a4464..e741686ad 100644 --- a/vite.config.js +++ b/vite.config.js @@ -41,6 +41,8 @@ async function buildWasmModules() { export default defineConfig({ base: '/Img2Num/', // important for GitHub Pages server: { + host: '0.0.0.0', // Allow connections from outside Docker + port: 5173, // Match docker-compose port watch: { ignored: ['src/wasm/**/*.js', 'src/wasm/**/*.wasm'], },