From b8d8f545b0144e97b0c4abc53c0841d39beff049 Mon Sep 17 00:00:00 2001 From: Jonathan Hornung Date: Fri, 15 Aug 2025 08:57:49 +0200 Subject: [PATCH] Replace Makefile w/ something that reads the libs metadata Fixes #122 --- Makefile | 285 ----------------------------------------- README.md | 50 ++++++-- build-libs.js | 324 +++++++++++++++++++++++++++++++++++++++++++++++ libs-config.json | 248 ++++++++++++++++++++++++++++++++++++ package.json | 10 +- 5 files changed, 616 insertions(+), 301 deletions(-) delete mode 100644 Makefile create mode 100755 build-libs.js create mode 100644 libs-config.json diff --git a/Makefile b/Makefile deleted file mode 100644 index fcdc3ac..0000000 --- a/Makefile +++ /dev/null @@ -1,285 +0,0 @@ -# Pin WASM build to a version known to work -WASM_BUILD_URL=https://files.openscad.org/playground/OpenSCAD-2025.03.25.wasm24456-WebAssembly-web.zip -# WASM_SNAPSHOT_JS_URL=https://files.openscad.org/snapshots/.snapshot_wasm.js -# WASM_BUILD_URL=$(shell curl ${WASM_SNAPSHOT_JS_URL} 2>/dev/null | grep https | sed -E "s/.*(https:[^']+)'.*/\1/" ) - -SINGLE_BRANCH_MAIN=--branch main --single-branch -SINGLE_BRANCH=--branch master --single-branch -SHALLOW=--depth 1 - -SHELL:=/usr/bin/env bash -WASM_BUILD=Release - -all: public - -.PHONY: public wasm -public: \ - src/wasm \ - public/openscad.js \ - public/openscad.wasm \ - public/libraries/fonts.zip \ - public/libraries/openscad.zip \ - public/libraries/NopSCADlib.zip \ - public/libraries/BOSL.zip \ - public/libraries/BOSL2.zip \ - public/libraries/boltsparts.zip \ - public/libraries/OpenSCAD-Snippet.zip \ - public/libraries/funcutils.zip \ - public/libraries/FunctionalOpenSCAD.zip \ - public/libraries/YAPP_Box.zip \ - public/libraries/MCAD.zip \ - public/libraries/smooth-prim.zip \ - public/libraries/plot-function.zip \ - public/libraries/openscad-tray.zip \ - public/libraries/closepoints.zip \ - public/libraries/Stemfie_OpenSCAD.zip \ - public/libraries/pathbuilder.zip \ - public/libraries/openscad_attachable_text3d.zip \ - public/libraries/brailleSCAD.zip \ - public/libraries/UB.scad.zip \ - public/libraries/lasercut.zip - -clean: - rm -fR libs build - rm -fR public/openscad.{js,wasm} - rm -fR public/libraries/*.zip - rm -fR src/wasm - -dist/index.js: public - npm run build - -dist/openscad-worker.js: src/openscad-worker.ts src/wasm/openscad.js - npx rollup -c - -src/wasm: libs/openscad-wasm - rm -f src/wasm - ln -sf "$(shell pwd)/libs/openscad-wasm" src/wasm - -wasm: libs/openscad - ( cd libs/openscad && ./scripts/wasm-base-docker-run.sh emcmake cmake -B build -DCMAKE_BUILD_TYPE=$(WASM_BUILD) -DEXPERIMENTAL=1 ) - ( cd libs/openscad && ./scripts/wasm-base-docker-run.sh /bin/bash -c "cmake --build build -j || cmake --build build -j2 || cmake --build build" ) - mkdir -p libs/openscad-wasm - cp libs/openscad/build/openscad.* libs/openscad-wasm/ - -libs/openscad-wasm: - mkdir -p libs/openscad-wasm - wget ${WASM_BUILD_URL} -O libs/openscad-wasm.zip - ( cd libs/openscad-wasm && unzip ../openscad-wasm.zip ) - -public/openscad.js: libs/openscad-wasm libs/openscad-wasm/openscad.js - ln -sf libs/openscad-wasm/openscad.js public/openscad.js - -public/openscad.wasm: libs/openscad-wasm libs/openscad-wasm/openscad.wasm - ln -sf libs/openscad-wasm/openscad.wasm public/openscad.wasm - -# Var w/ noto fonts -NOTO_FONTS=\ - libs/noto/NotoNaskhArabic-Bold.ttf \ - libs/noto/NotoNaskhArabic-Regular.ttf \ - libs/noto/NotoSans-Bold.ttf \ - libs/noto/NotoSans-Italic.ttf \ - libs/noto/NotoSans-Regular.ttf \ - libs/noto/NotoSansArmenian-Bold.ttf \ - libs/noto/NotoSansArmenian-Regular.ttf \ - libs/noto/NotoSansBalinese-Regular.ttf \ - libs/noto/NotoSansBengali-Bold.ttf \ - libs/noto/NotoSansBengali-Regular.ttf \ - libs/noto/NotoSansDevanagari-Bold.ttf \ - libs/noto/NotoSansDevanagari-Regular.ttf \ - libs/noto/NotoSansEthiopic-Bold.ttf \ - libs/noto/NotoSansEthiopic-Regular.ttf \ - libs/noto/NotoSansGeorgian-Bold.ttf \ - libs/noto/NotoSansGeorgian-Regular.ttf \ - libs/noto/NotoSansGujarati-Bold.ttf \ - libs/noto/NotoSansGujarati-Regular.ttf \ - libs/noto/NotoSansGurmukhi-Bold.ttf \ - libs/noto/NotoSansGurmukhi-Regular.ttf \ - libs/noto/NotoSansHebrew-Bold.ttf \ - libs/noto/NotoSansHebrew-Regular.ttf \ - libs/noto/NotoSansJavanese-Regular.ttf \ - libs/noto/NotoSansKannada-Bold.ttf \ - libs/noto/NotoSansKannada-Regular.ttf \ - libs/noto/NotoSansKhmer-Bold.ttf \ - libs/noto/NotoSansKhmer-Regular.ttf \ - libs/noto/NotoSansLao-Bold.ttf \ - libs/noto/NotoSansLao-Regular.ttf \ - libs/noto/NotoSansMongolian-Regular.ttf \ - libs/noto/NotoSansMyanmar-Bold.ttf \ - libs/noto/NotoSansMyanmar-Regular.ttf \ - libs/noto/NotoSansOriya-Bold.ttf \ - libs/noto/NotoSansOriya-Regular.ttf \ - libs/noto/NotoSansSinhala-Bold.ttf \ - libs/noto/NotoSansSinhala-Regular.ttf \ - libs/noto/NotoSansTamil-Bold.ttf \ - libs/noto/NotoSansTamil-Regular.ttf \ - libs/noto/NotoSansThai-Bold.ttf \ - libs/noto/NotoSansThai-Regular.ttf \ - libs/noto/NotoSansTibetan-Bold.ttf \ - libs/noto/NotoSansTibetan-Regular.ttf \ - libs/noto/NotoSansTifinagh-Regular.ttf \ - -# Way too big for now, also can't make them work yet: -# libs/noto/NotoSansCJKtc-Bold.otf -# libs/noto/NotoSansCJKtc-Regular.otf - -public/libraries/fonts.zip: $(NOTO_FONTS) libs/liberation - mkdir -p public/libraries - zip -r $@ -j fonts.conf libs/noto/*.ttf libs/liberation/{*.ttf,LICENSE,AUTHORS} - -libs/noto/%.ttf: - mkdir -p libs/noto - wget https://github.com/openmaptiles/fonts/raw/master/noto-sans/$(notdir $@) -O $@ - -libs/noto/%.otf: - mkdir -p libs/noto - wget https://github.com/openmaptiles/fonts/raw/master/noto-sans/$(notdir $@) -O $@ - -libs/liberation: - git clone --recurse https://github.com/shantigilbert/liberation-fonts-ttf.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -libs/openscad: - git clone --recurse https://github.com/openscad/openscad.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/openscad.zip: libs/openscad - mkdir -p public/libraries - ( cd libs/openscad ; zip -r - `find examples -name '*.scad' | grep -v tests` ) > public/libraries/openscad.zip - -libs/BOSL2: - git clone --recurse https://github.com/BelfrySCAD/BOSL2.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/BOSL2.zip: libs/BOSL2 - mkdir -p public/libraries - ( cd libs/BOSL2 ; zip -r ../../public/libraries/BOSL2.zip *.scad LICENSE examples ) - -libs/BOSL: - git clone --recurse https://github.com/revarbat/BOSL.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/BOSL.zip: libs/BOSL - mkdir -p public/libraries - ( cd libs/BOSL ; zip -r ../../public/libraries/BOSL.zip *.scad LICENSE ) - -libs/NopSCADlib: - git clone --recurse https://github.com/nophead/NopSCADlib.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/NopSCADlib.zip: libs/NopSCADlib - mkdir -p public/libraries - ( cd libs/NopSCADlib ; zip -r ../../public/libraries/NopSCADlib.zip `find . -name '*.scad'` COPYING ) - -libs/funcutils: - git clone --recurse https://github.com/thehans/funcutils.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/funcutils.zip: libs/funcutils - mkdir -p public/libraries - ( cd libs/funcutils ; zip -r ../../public/libraries/funcutils.zip *.scad LICENSE ) - -libs/FunctionalOpenSCAD: - git clone --recurse https://github.com/thehans/FunctionalOpenSCAD.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/FunctionalOpenSCAD.zip: libs/FunctionalOpenSCAD - mkdir -p public/libraries - ( cd libs/FunctionalOpenSCAD ; zip -r ../../public/libraries/FunctionalOpenSCAD.zip *.scad LICENSE ) - -libs/YAPP_Box: - git clone --recurse https://github.com/mrWheel/YAPP_Box.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/YAPP_Box.zip: libs/YAPP_Box - mkdir -p public/libraries - ( cd libs/YAPP_Box ; zip -r ../../public/libraries/YAPP_Box.zip `find . -name '*.scad'` LICENSE ) - -libs/MCAD: - git clone --recurse https://github.com/openscad/MCAD.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/MCAD.zip: libs/MCAD - mkdir -p public/libraries - ( cd libs/MCAD ; zip -r ../../public/libraries/MCAD.zip *.scad bitmap/*.scad LICENSE ) - -libs/boltsparts: - git clone --recurse https://github.com/boltsparts/boltsparts.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/boltsparts.zip: libs/boltsparts - mkdir -p public/libraries - ( cd libs/boltsparts/openscad ; zip -r ../../../public/libraries/boltsparts.zip `find . -name '*.scad' | grep -v tests` ../LICENSE ) - -libs/OpenSCAD-Snippet: - git clone --recurse https://github.com/AngeloNicoli/OpenSCAD-Snippet.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/OpenSCAD-Snippet.zip: libs/OpenSCAD-Snippet - mkdir -p public/libraries - ( cd libs/OpenSCAD-Snippet ; zip -r ../../public/libraries/OpenSCAD-Snippet.zip `find . -name '*.scad'` LICENSE ) - -libs/Stemfie_OpenSCAD: - git clone --recurse https://github.com/Cantareus/Stemfie_OpenSCAD.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/Stemfie_OpenSCAD.zip: libs/Stemfie_OpenSCAD - mkdir -p public/libraries - ( cd libs/Stemfie_OpenSCAD ; zip -r ../../public/libraries/Stemfie_OpenSCAD.zip *.scad LICENSE ) - -libs/pathbuilder: - git clone --recurse https://github.com/dinther/pathbuilder.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/pathbuilder.zip: libs/pathbuilder - mkdir -p public/libraries - ( cd libs/pathbuilder ; zip -r ../../public/libraries/pathbuilder.zip *.scad demo/*.scad LICENSE ) - -libs/openscad_attachable_text3d: - git clone --recurse https://github.com/jon-gilbert/openscad_attachable_text3d.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/openscad_attachable_text3d.zip: libs/openscad_attachable_text3d - mkdir -p public/libraries - ( cd libs/openscad_attachable_text3d ; zip -r ../../public/libraries/openscad_attachable_text3d.zip *.scad LICENSE ) - -libs/brailleSCAD: - git clone --recurse https://github.com/BelfrySCAD/brailleSCAD.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/brailleSCAD.zip: libs/brailleSCAD - mkdir -p public/libraries - ( cd libs/brailleSCAD ; zip -r ../../public/libraries/brailleSCAD.zip *.scad LICENSE ) - -# libs/threads: -# git clone --recurse https://github.com/rcolyer/threads.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -# public/libraries/threads.zip: libs/threads -# mkdir -p public/libraries -# ( cd libs/threads ; zip -r ../../public/libraries/threads.zip *.scad LICENSE.txt ) - -libs/smooth-prim: - git clone --recurse https://github.com/rcolyer/smooth-prim.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/smooth-prim.zip: libs/smooth-prim - mkdir -p public/libraries - ( cd libs/smooth-prim ; zip -r ../../public/libraries/smooth-prim.zip *.scad LICENSE.txt ) - -libs/plot-function: - git clone --recurse https://github.com/rcolyer/plot-function.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/plot-function.zip: libs/plot-function - mkdir -p public/libraries - ( cd libs/plot-function ; zip -r ../../public/libraries/plot-function.zip *.scad LICENSE.txt ) - -libs/closepoints: - git clone --recurse https://github.com/rcolyer/closepoints.git ${SHALLOW} ${SINGLE_BRANCH} $@ - -public/libraries/closepoints.zip: libs/closepoints - mkdir -p public/libraries - ( cd libs/closepoints ; zip -r ../../public/libraries/closepoints.zip *.scad LICENSE.txt ) - -libs/UB.scad: - git clone --recurse https://github.com/UBaer21/UB.scad.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/UB.scad.zip: libs/UB.scad - mkdir -p public/libraries - ( cd libs/UB.scad ; zip -r ../../public/libraries/UB.scad.zip libraries/*.scad LICENSE examples/UBexamples ) - -libs/openscad-tray: - git clone --recurse https://github.com/sofian/openscad-tray.git ${SHALLOW} ${SINGLE_BRANCH_MAIN} $@ - -public/libraries/openscad-tray.zip: libs/openscad-tray - mkdir -p public/libraries - ( cd libs/openscad-tray ; zip -r ../../public/libraries/openscad-tray.zip *.scad LICENSE ) - -libs/lasercut: - git clone --recurse https://github.com/bmsleight/lasercut.git ${SHALLOW} ${SINGLE_BRANCH} $@ -public/libraries/lasercut.zip: libs/lasercut - mkdir -p public/libraries - ( cd libs/lasercut ; zip -r ../../public/libraries/lasercut.zip *.scad LICENSE ) - diff --git a/README.md b/README.md index bbc30e0..4c1831c 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Licenses: see [LICENSES](./LICENSE). - Animation rendering (And other formats than STL) - [x] Compress URL fragment - [x] Mobile (iOS) editing support: switch to https://www.npmjs.com/package/react-codemirror ? -- [ ] Replace Makefile w/ something that reads the libs metadata +- [x] Replace Makefile w/ something that reads the libs metadata - [ ] Merge modifiers rendering code to openscad - Model /home fs in shared state. have two clear paths: /libraries for builtins, and /home for user data. State pointing to /libraries paths needs not store the data except if there's overrides (flagged as modifications in the file picker) - Drag and drop of files (SCAD, STL, etc) and Zip archives. For assets, auto insert the corresponding import. @@ -49,10 +49,12 @@ Licenses: see [LICENSES](./LICENSE). ## Building Prerequisites: -* wget -* GNU make +* wget or curl +* Node.js (>=18.12.0) * npm -* Docker able to run amd64 containers. If running on a different platform (including Silicon Mac), you can add support for amd64 images through QEMU with: +* git +* zip +* Docker able to run amd64 containers (only needed if building WASM from source). If running on a different platform (including Silicon Mac), you can add support for amd64 images through QEMU with: ```bash docker run --privileged --rm tonistiigi/binfmt --install all @@ -61,27 +63,26 @@ Prerequisites: Local dev: ```bash -make public +npm run build:libs # Download WASM and build all OpenSCAD libraries npm install -npm start +npm run start # http://localhost:4000/ ``` Local prod (test both the different inlining and serving under a prefix): ```bash -make public +npm run build:libs # Download WASM and build all OpenSCAD libraries npm install -npm run start:prod +npm run start:production # http://localhost:3000/dist/ ``` Deployment (edit "homepage" in `package.json` to match your deployment root!): ```bash -make public +npm run build:all # Build libraries and compile the application npm install -NODE_ENV=production npm run build rm -fR ../ochafik.github.io/openscad2 && cp -R dist ../ochafik.github.io/openscad2 # Now commit and push changes, wait for site update and enjoy! @@ -104,24 +105,45 @@ rm -fR ../ochafik.github.io/openscad2 && cp -R dist ../ochafik.github.io/opensca - Build WASM binary (add `WASM_BUILD=Debug` argument if you'd like to debug any cryptic crashes): ```bash - make wasm + npm run build:libs:wasm ``` - Then continue the build: ```bash - make public - npm start + npm run build:libs + npm run start ``` ## Adding OpenSCAD libraries You'll need to update 3 files (search for BOSL2 for an example): -- [Makefile](./Makefile): to pull the library's code (optionally alias some files for easier imports) and package it as a `.zip` archive +- [libs-config.json](./libs-config.json): to add the library's metadata including repository URL, branch, and files to include/exclude in the zip archive - [src/fs/zip-archives.ts](./src/fs/zip-archives.ts): to use the `.zip` archive in the UI (both for file explorer and automatic imports mounting) - [LICENSE.md](./LICENSE.md): most libraries require proper disclosure of their usage and of their license. If a license is unique, paste it in full, otherwise, link to one of the standard ones already there. +### Library Configuration Format + +In `libs-config.json`, add an entry like this: + +```json +{ + "name": "LibraryName", + "repo": "https://github.com/user/repo.git", + "branch": "main", + "zipIncludes": ["*.scad", "LICENSE", "examples"], + "zipExcludes": ["**/tests/**"], + "workingDir": "." +} +``` + +Available build commands: +- `npm run build:libs` - Build all libraries +- `npm run build:libs:clean` - Clean all build artifacts +- `npm run build:libs:wasm` - Download/build just the WASM binary +- `npm run build:libs:fonts` - Download/build just the fonts + Send us a PR, then once it's merged request an update to the hosted https://ochafik.com/openscad2 demo. diff --git a/build-libs.js b/build-libs.js new file mode 100755 index 0000000..3981ba8 --- /dev/null +++ b/build-libs.js @@ -0,0 +1,324 @@ +#!/usr/bin/env node + +import fs from 'fs/promises'; +import { createWriteStream, existsSync } from 'fs'; +import path from 'path'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import https from 'https'; +import { pipeline } from 'stream/promises'; + +const execAsync = promisify(exec); + +const CONFIG_FILE = 'libs-config.json'; +const LIBS_DIR = 'libs'; +const PUBLIC_LIBS_DIR = 'public/libraries'; +const SRC_WASM_DIR = 'src/wasm'; + +class LibsBuilder { + constructor() { + this.config = null; + } + + async loadConfig() { + try { + const configContent = await fs.readFile(CONFIG_FILE, 'utf-8'); + this.config = JSON.parse(configContent); + } catch (error) { + console.error(`Failed to load config from ${CONFIG_FILE}:`, error.message); + process.exit(1); + } + } + + async ensureDir(dirPath) { + try { + await fs.mkdir(dirPath, { recursive: true }); + } catch (error) { + if (error.code !== 'EEXIST') { + throw error; + } + } + } + + async downloadFile(url, outputPath) { + console.log(`Downloading ${url} to ${outputPath}`); + + return new Promise((resolve, reject) => { + https.get(url, (response) => { + if (response.statusCode === 302 || response.statusCode === 301) { + // Handle redirects + return this.downloadFile(response.headers.location, outputPath) + .then(resolve) + .catch(reject); + } + + if (response.statusCode !== 200) { + reject(new Error(`Failed to download: ${response.statusCode}`)); + return; + } + + const fileStream = createWriteStream(outputPath); + pipeline(response, fileStream) + .then(resolve) + .catch(reject); + }).on('error', reject); + }); + } + + async cloneRepo(repo, targetDir, branch = 'master', shallow = true) { + const cloneArgs = [ + 'clone', + '--recurse', + shallow ? '--depth 1' : '', + `--branch ${branch}`, + '--single-branch', + repo, + targetDir + ].filter(Boolean); + + console.log(`Cloning ${repo} to ${targetDir}`); + try { + await execAsync(`git ${cloneArgs.join(' ')}`); + } catch (error) { + console.error(`Failed to clone ${repo}:`, error.message); + throw error; + } + } + + async createZip(sourceDir, outputPath, includes = [], excludes = [], workingDir = '.') { + await this.ensureDir(path.dirname(outputPath)); + + const fullSourceDir = path.join(sourceDir, workingDir); + + // Build find command for includes + let findCmd = ''; + if (includes.length > 0) { + const findPatterns = includes.map(pattern => { + if (pattern.includes('**/*.')) { + // Pattern like "examples/**/*.scad" + const parts = pattern.split('/'); + const dir = parts[0]; + const filePattern = parts[parts.length - 1]; + return `-path "./${dir}/*" -name "${filePattern}"`; + } else if (pattern.includes('**')) { + // Pattern like "**/*.scad" + const filePattern = pattern.replace('**/', ''); + return `-name "${filePattern}"`; + } else if (pattern.includes('*')) { + // Pattern like "*.scad" + return `-name "${pattern}"`; + } else if (pattern.includes('/')) { + // Path pattern like "bitmap/*.scad" + return `-path "./${pattern}"`; + } else { + // Direct file/directory name + return `-name "${pattern}" -o -path "./${pattern}/*"`; + } + }).join(' -o '); + findCmd = `find . \\( ${findPatterns} \\)`; + } else { + findCmd = 'find . -name "*.scad"'; + } + + // Add excludes + if (excludes.length > 0) { + const excludePatterns = excludes.map(pattern => { + const cleanPattern = pattern.replace('**/', '').replace('/**', ''); + return `-not -path "*/${cleanPattern}*"`; + }).join(' '); + findCmd += ` ${excludePatterns}`; + } + + const zipCmd = `cd ${fullSourceDir} && ${findCmd} | zip -r ${path.resolve(outputPath)} -@`; + + console.log(`Creating zip: ${outputPath}`); + console.log(`Zip command: ${zipCmd}`); + try { + await execAsync(zipCmd); + } catch (error) { + console.error(`Failed to create zip ${outputPath}:`, error.message); + throw error; + } + } + + async buildWasm() { + const { wasmBuild } = this.config; + const wasmDir = wasmBuild.target; + const wasmZip = `${wasmDir}.zip`; + + // Create libs directory + await this.ensureDir(LIBS_DIR); + + // Download WASM if not exists + if (!existsSync(wasmDir)) { + await this.ensureDir(wasmDir); + + // Download WASM zip + await this.downloadFile(wasmBuild.url, wasmZip); + + // Extract WASM zip + console.log(`Extracting WASM to ${wasmDir}`); + await execAsync(`cd ${wasmDir} && unzip ../${path.basename(wasmZip)}`); + } + + // Create symlinks for public files + await this.ensureDir('public'); + + const jsTarget = 'public/openscad.js'; + const wasmTarget = 'public/openscad.wasm'; + + // Remove existing symlinks/files + try { + await fs.unlink(jsTarget); + } catch { + // ignore - file doesn't exist + } + try { + await fs.unlink(wasmTarget); + } catch { + // ignore - file doesn't exist + } + + // Create new symlinks - use relative paths for portability + await fs.symlink(path.relative('public', path.join(wasmDir, 'openscad.js')), jsTarget); + await fs.symlink(path.relative('public', path.join(wasmDir, 'openscad.wasm')), wasmTarget); + + // Create src/wasm symlink + try { + await fs.unlink(SRC_WASM_DIR); + } catch { + // ignore - file doesn't exist + } + await fs.symlink(path.relative('src', wasmDir), SRC_WASM_DIR); + + console.log('WASM setup completed'); + } + + async buildFonts() { + const { fonts } = this.config; + const notoDir = path.join(LIBS_DIR, 'noto'); + const liberationDir = path.join(LIBS_DIR, 'liberation'); + + await this.ensureDir(notoDir); + + // Download Noto fonts + for (const font of fonts.notoFonts) { + const fontPath = path.join(notoDir, font); + if (!existsSync(fontPath)) { + const url = fonts.notoBaseUrl + font; + await this.downloadFile(url, fontPath); + } + } + + // Clone liberation fonts if not exists + if (!existsSync(liberationDir)) { + await this.cloneRepo(fonts.liberationRepo, liberationDir, fonts.liberationBranch); + } + + // Create fonts zip + const fontsZip = path.join(PUBLIC_LIBS_DIR, 'fonts.zip'); + await this.ensureDir(PUBLIC_LIBS_DIR); + + console.log('Creating fonts.zip'); + const fontsCmd = `zip -r ${fontsZip} -j fonts.conf libs/noto/*.ttf libs/liberation/*.ttf libs/liberation/LICENSE libs/liberation/AUTHORS`; + await execAsync(fontsCmd); + + console.log('Fonts setup completed'); + } + + async buildLibrary(library) { + const libDir = path.join(LIBS_DIR, library.name); + const zipPath = path.join(PUBLIC_LIBS_DIR, `${library.name}.zip`); + + // Clone repository if not exists + if (!existsSync(libDir)) { + await this.cloneRepo(library.repo, libDir, library.branch); + } + + // Create zip + await this.createZip( + libDir, + zipPath, + library.zipIncludes || ['*.scad'], + library.zipExcludes || [], + library.workingDir || '.' + ); + + console.log(`Built ${library.name}`); + } + + async buildAllLibraries() { + await this.ensureDir(PUBLIC_LIBS_DIR); + + for (const library of this.config.libraries) { + await this.buildLibrary(library); + } + } + + async clean() { + console.log('Cleaning build artifacts...'); + + const cleanPaths = [ + LIBS_DIR, + 'build', + 'public/openscad.js', + 'public/openscad.wasm', + `${PUBLIC_LIBS_DIR}/*.zip`, + SRC_WASM_DIR + ]; + + for (const cleanPath of cleanPaths) { + try { + if (cleanPath.includes('*')) { + await execAsync(`rm -f ${cleanPath}`); + } else { + await fs.rm(cleanPath, { recursive: true, force: true }); + } + } catch { + // Ignore errors for files that don't exist + } + } + + console.log('Clean completed'); + } + + async build() { + console.log('Building all libraries...'); + + await this.buildWasm(); + await this.buildFonts(); + await this.buildAllLibraries(); + + console.log('Build completed successfully!'); + } +} + +async function main() { + const builder = new LibsBuilder(); + await builder.loadConfig(); + + const command = process.argv[2] || 'build'; + + switch (command) { + case 'build': + await builder.build(); + break; + case 'clean': + await builder.clean(); + break; + case 'wasm': + await builder.buildWasm(); + break; + case 'fonts': + await builder.buildFonts(); + break; + default: + console.log('Usage: node build-libs.js [build|clean|wasm|fonts]'); + process.exit(1); + } +} + +main().catch(error => { + console.error('Build failed:', error); + process.exit(1); +}); diff --git a/libs-config.json b/libs-config.json new file mode 100644 index 0000000..30a75a0 --- /dev/null +++ b/libs-config.json @@ -0,0 +1,248 @@ +{ + "wasmBuild": { + "url": "https://files.openscad.org/playground/OpenSCAD-2025.03.25.wasm24456-WebAssembly-web.zip", + "target": "libs/openscad-wasm" + }, + "libraries": [ + { + "name": "openscad", + "repo": "https://github.com/openscad/openscad.git", + "branch": "master", + "zipIncludes": [ + "examples/**/*.scad" + ], + "zipExcludes": [ + "**/tests/**" + ] + }, + { + "name": "BOSL2", + "repo": "https://github.com/BelfrySCAD/BOSL2.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE", + "examples" + ] + }, + { + "name": "BOSL", + "repo": "https://github.com/revarbat/BOSL.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "NopSCADlib", + "repo": "https://github.com/nophead/NopSCADlib.git", + "branch": "master", + "zipIncludes": [ + "**/*.scad", + "COPYING" + ] + }, + { + "name": "funcutils", + "repo": "https://github.com/thehans/funcutils.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "FunctionalOpenSCAD", + "repo": "https://github.com/thehans/FunctionalOpenSCAD.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "YAPP_Box", + "repo": "https://github.com/mrWheel/YAPP_Box.git", + "branch": "main", + "zipIncludes": [ + "**/*.scad", + "LICENSE" + ] + }, + { + "name": "MCAD", + "repo": "https://github.com/openscad/MCAD.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "bitmap/*.scad", + "LICENSE" + ] + }, + { + "name": "boltsparts", + "repo": "https://github.com/boltsparts/boltsparts.git", + "branch": "main", + "zipIncludes": [ + "**/*.scad", + "../LICENSE" + ], + "zipExcludes": [ + "**/tests/**" + ], + "workingDir": "openscad" + }, + { + "name": "OpenSCAD-Snippet", + "repo": "https://github.com/AngeloNicoli/OpenSCAD-Snippet.git", + "branch": "main", + "zipIncludes": [ + "**/*.scad", + "LICENSE" + ] + }, + { + "name": "Stemfie_OpenSCAD", + "repo": "https://github.com/Cantareus/Stemfie_OpenSCAD.git", + "branch": "main", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "pathbuilder", + "repo": "https://github.com/dinther/pathbuilder.git", + "branch": "main", + "zipIncludes": [ + "*.scad", + "demo/*.scad", + "LICENSE" + ] + }, + { + "name": "openscad_attachable_text3d", + "repo": "https://github.com/jon-gilbert/openscad_attachable_text3d.git", + "branch": "main", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "brailleSCAD", + "repo": "https://github.com/BelfrySCAD/brailleSCAD.git", + "branch": "main", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "smooth-prim", + "repo": "https://github.com/rcolyer/smooth-prim.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE.txt" + ] + }, + { + "name": "plot-function", + "repo": "https://github.com/rcolyer/plot-function.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE.txt" + ] + }, + { + "name": "closepoints", + "repo": "https://github.com/rcolyer/closepoints.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE.txt" + ] + }, + { + "name": "UB.scad", + "repo": "https://github.com/UBaer21/UB.scad.git", + "branch": "main", + "zipIncludes": [ + "libraries/*.scad", + "LICENSE", + "examples/UBexamples" + ] + }, + { + "name": "openscad-tray", + "repo": "https://github.com/sofian/openscad-tray.git", + "branch": "main", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + }, + { + "name": "lasercut", + "repo": "https://github.com/bmsleight/lasercut.git", + "branch": "master", + "zipIncludes": [ + "*.scad", + "LICENSE" + ] + } + ], + "fonts": { + "notoFonts": [ + "NotoNaskhArabic-Bold.ttf", + "NotoNaskhArabic-Regular.ttf", + "NotoSans-Bold.ttf", + "NotoSans-Italic.ttf", + "NotoSans-Regular.ttf", + "NotoSansArmenian-Bold.ttf", + "NotoSansArmenian-Regular.ttf", + "NotoSansBalinese-Regular.ttf", + "NotoSansBengali-Bold.ttf", + "NotoSansBengali-Regular.ttf", + "NotoSansDevanagari-Bold.ttf", + "NotoSansDevanagari-Regular.ttf", + "NotoSansEthiopic-Bold.ttf", + "NotoSansEthiopic-Regular.ttf", + "NotoSansGeorgian-Bold.ttf", + "NotoSansGeorgian-Regular.ttf", + "NotoSansGujarati-Bold.ttf", + "NotoSansGujarati-Regular.ttf", + "NotoSansGurmukhi-Bold.ttf", + "NotoSansGurmukhi-Regular.ttf", + "NotoSansHebrew-Bold.ttf", + "NotoSansHebrew-Regular.ttf", + "NotoSansJavanese-Regular.ttf", + "NotoSansKannada-Bold.ttf", + "NotoSansKannada-Regular.ttf", + "NotoSansKhmer-Bold.ttf", + "NotoSansKhmer-Regular.ttf", + "NotoSansLao-Bold.ttf", + "NotoSansLao-Regular.ttf", + "NotoSansMongolian-Regular.ttf", + "NotoSansMyanmar-Bold.ttf", + "NotoSansMyanmar-Regular.ttf", + "NotoSansOriya-Bold.ttf", + "NotoSansOriya-Regular.ttf", + "NotoSansSinhala-Bold.ttf", + "NotoSansSinhala-Regular.ttf", + "NotoSansTamil-Bold.ttf", + "NotoSansTamil-Regular.ttf", + "NotoSansThai-Bold.ttf", + "NotoSansThai-Regular.ttf", + "NotoSansTibetan-Bold.ttf", + "NotoSansTibetan-Regular.ttf", + "NotoSansTifinagh-Regular.ttf" + ], + "notoBaseUrl": "https://github.com/openmaptiles/fonts/raw/master/noto-sans/", + "liberationRepo": "https://github.com/shantigilbert/liberation-fonts-ttf.git", + "liberationBranch": "master" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 3f04aba..6f026e2 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,12 @@ "start:development": "npx webpack serve --mode=development", "start:production": "NODE_ENV=production PUBLIC_URL=http://localhost:3000/dist/ npm run build && npx serve", "start": "npm run start:development", - "build": "NODE_ENV=production webpack --mode=production" + "build": "NODE_ENV=production webpack --mode=production", + "build:libs": "node build-libs.js build", + "build:libs:clean": "node build-libs.js clean", + "build:libs:wasm": "node build-libs.js wasm", + "build:libs:fonts": "node build-libs.js fonts", + "build:all": "npm run build:libs && npm run build" }, "eslintConfig": { "extends": [ @@ -74,9 +79,10 @@ "style-loader": "^4.0.0", "ts-loader": "^9.5.1", "tslib": "^2.8.1", + "typescript": "^5.9.2", "webpack": "^5.97.1", "webpack-cli": "^6.0.1", "webpack-dev-server": "^5.2.0", "workbox-webpack-plugin": "^7.3.0" } -} +} \ No newline at end of file