node-plantuml-2 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # node-plantuml-2
2
2
 
3
- > **Pure Node.js PlantUML Renderer - No Java Required!**
3
+ > **Node.js PlantUML Renderer with Java Backend**
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/node-plantuml-2)](https://www.npmjs.com/package/node-plantuml-2)
6
6
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
7
  [![Node.js](https://img.shields.io/badge/node-%3E%3D12-green.svg)](https://nodejs.org/)
8
8
 
9
- A powerful Node.js module and CLI for running [PlantUML](http://plantuml.sourceforge.net/) with **pure Node.js support**. This project is a fork and enhancement of [node-plantuml](https://github.com/markushedvall/node-plantuml), featuring WebAssembly-based execution that eliminates the need for Java runtime.
9
+ A powerful Node.js module and CLI for running [PlantUML](http://plantuml.sourceforge.net/). This project is a fork and enhancement of [node-plantuml](https://github.com/markushedvall/node-plantuml), providing improved performance with Nailgun optimization for faster Java startup.
10
10
 
11
11
  <div align="center">
12
12
 
@@ -20,12 +20,12 @@ A powerful Node.js module and CLI for running [PlantUML](http://plantuml.sourcef
20
20
 
21
21
  ## ✨ Key Features
22
22
 
23
- - 🚀 **Pure Node.js Environment** - No Java installation required! Uses pre-compiled WebAssembly module
24
- - 📦 **Zero Configuration** - Just `npm install` and start using
23
+ - 🎯 **Zero Java Installation Required** - **Java is automatically bundled!** Just `npm install` and it works - no manual Java setup needed
24
+ - 📦 **Auto-Integrated JRE** - Automatically installs a lightweight JRE (40-60MB) for your platform during `npm install`
25
+ - 🚀 **Optimized Java Execution** - Uses Nailgun for faster Java startup, keeping JVM resident in memory
25
26
  - 🎨 **Multiple Output Formats** - Support for PNG, SVG, EPS, ASCII, and Unicode text
26
27
  - 🌏 **Multi-language Support** - Perfect rendering for Chinese, Japanese, Korean, and other CJK characters with automatic font detection
27
- - ⚡ **Fast Startup** - WebAssembly execution is faster than JVM
28
- - 🔄 **Automatic Fallback** - Falls back to Java executor if Wasm is unavailable
28
+ - ⚡ **Fast Performance** - Nailgun optimization reduces Java startup overhead
29
29
  - 📝 **CLI & API** - Both command-line interface and programmatic API
30
30
  - 🎯 **Based on PlantUML** - Full compatibility with PlantUML syntax
31
31
 
@@ -33,11 +33,24 @@ A powerful Node.js module and CLI for running [PlantUML](http://plantuml.sourcef
33
33
 
34
34
  ## 📦 Installation
35
35
 
36
+ ### Quick Install (Recommended)
37
+
36
38
  ```bash
37
39
  npm install node-plantuml-2
38
40
  ```
39
41
 
40
- **That's it!** No Java, no configuration, no build steps required.
42
+ **That's it! 🎉 No Java installation needed!**
43
+
44
+ The package **automatically detects your platform** and installs a lightweight, bundled JRE (40-60MB) during `npm install`. You don't need to install Java separately - it's handled automatically!
45
+
46
+ **How it works:**
47
+ - During `npm install`, the package detects your operating system and architecture
48
+ - Automatically downloads and installs the matching JRE runtime package for your platform:
49
+ - **Windows x64** → `@node-plantuml-2/jre-win32-x64`
50
+ - **macOS ARM64** → `@node-plantuml-2/jre-darwin-arm64`
51
+ - **Linux x64** → `@node-plantuml-2/jre-linux-x64`
52
+ - Only the JRE for your platform is installed (others are skipped automatically)
53
+ - Works out of the box - no configuration needed!
41
54
 
42
55
  For global CLI installation:
43
56
 
@@ -45,6 +58,47 @@ For global CLI installation:
45
58
  npm install node-plantuml-2 -g
46
59
  ```
47
60
 
61
+ **⚠️ Important:** You do NOT need to install Java on your system. The bundled JRE is automatically integrated when you install this npm package.
62
+
63
+ ### Java Requirements
64
+
65
+ **🎯 Zero Java Installation Required!**
66
+
67
+ This package **automatically bundles a minimal JRE** during installation. You do **NOT** need to install Java on your system!
68
+
69
+ **Automatic JRE Integration:**
70
+
71
+ When you run `npm install node-plantuml-2`, the package:
72
+ 1. **Detects your platform** (Windows/macOS/Linux and architecture)
73
+ 2. **Automatically downloads** the matching lightweight JRE (40-60MB)
74
+ 3. **Integrates it seamlessly** - no manual setup needed
75
+ 4. **Works immediately** after installation completes
76
+
77
+ **Java Resolution Priority:**
78
+
79
+ The package uses the following priority to find Java:
80
+
81
+ 1. **Bundled JRE** (Primary) ⭐ - Automatically installed during `npm install`
82
+ - Platform-specific: only your platform's JRE is downloaded
83
+ - Lightweight: ~40-60MB, built with `jlink` for optimal size
84
+ - Zero configuration: works out of the box
85
+
86
+ 2. **System Java** (Fallback) - If bundled JRE is unavailable, uses system Java if present
87
+ - Checks `JAVA_HOME` environment variable
88
+ - Checks system PATH for `java` command
89
+
90
+ 3. **Custom Java** (Optional) - Override with custom Java path:
91
+ ```javascript
92
+ plantuml.generate(code, { javaPath: '/custom/path/to/java' })
93
+ ```
94
+
95
+ **Supported Platforms (with automatic JRE):**
96
+ - ✅ Windows x64 - JRE automatically bundled
97
+ - ✅ macOS ARM64 (Apple Silicon) - JRE automatically bundled
98
+ - ✅ Linux x64 - JRE automatically bundled
99
+
100
+ **Summary: Install the npm package, Java is included automatically! No separate Java installation needed.**
101
+
48
102
  ---
49
103
 
50
104
  ## 🚀 Quick Start
@@ -427,29 +481,27 @@ app.get('/svg/:uml', (req, res) => {
427
481
  app.listen(8080)
428
482
  ```
429
483
 
430
- ### Force Java Executor (Optional)
431
-
432
- If you prefer to use Java executor (requires Java installed):
433
-
434
- ```bash
435
- PLANTUML_USE_JAVA=true node your-script.js
436
- ```
437
-
438
484
  ---
439
485
 
440
486
  ## 🏗️ Architecture
441
487
 
442
- This project uses a **hybrid execution model**:
488
+ This project uses **Java execution** with automatic JRE bundling and optimization:
489
+
490
+ 1. **Bundled JRE** (Automatic)
491
+ - Lightweight minimal JRE (~40-60MB) installed automatically via `optionalDependencies`
492
+ - Built with `jlink` for optimal size
493
+ - Platform-specific packages ensure only relevant JRE is installed
494
+ - **No manual Java installation required!**
443
495
 
444
- 1. **Primary: WebAssembly Executor** (Pure Node.js)
445
- - Pre-compiled Wasm module included in npm package
446
- - Fast startup, low memory footprint
447
- - No Java required
496
+ 2. **Java Executor** (Primary)
497
+ - Uses bundled JRE or system Java to execute `java -jar plantuml.jar`
498
+ - Full compatibility with PlantUML features
499
+ - Automatic Java path resolution with fallback strategy
448
500
 
449
- 2. **Fallback: Java Executor** (Optional)
450
- - Automatic fallback if Wasm unavailable
451
- - Requires Java runtime
452
- - Full compatibility with original node-plantuml
501
+ 3. **Nailgun Optimization** (Optional, for performance)
502
+ - Keeps JVM resident in memory for faster startup
503
+ - Use `plantumlExecutor.useNailgun()` to enable
504
+ - Reduces startup overhead significantly
453
505
 
454
506
  ### Execution Flow
455
507
 
@@ -458,9 +510,19 @@ User Code
458
510
 
459
511
  plantuml.generate()
460
512
 
461
- Check Wasm Availability
462
- ├─ Available → Use Wasm Executor ✅ (Pure Node)
463
- └─ Unavailable Use Java Executor (Fallback)
513
+ plantumlExecutor.exec()
514
+
515
+ Java Path Resolution (Priority Order)
516
+ ├─ options.javaPath (User specified)
517
+ ├─ Bundled JRE (Auto-installed)
518
+ ├─ JAVA_HOME (System env var)
519
+ └─ System PATH java
520
+
521
+ Check if Nailgun is running
522
+ ├─ Running → Use Nailgun (faster)
523
+ └─ Not running → Use spawn('java', ...)
524
+
525
+ Execute PlantUML JAR
464
526
 
465
527
  Generate Diagram
466
528
 
@@ -471,10 +533,19 @@ Return Stream
471
533
 
472
534
  ## 📋 System Requirements
473
535
 
474
- - **Node.js 12+** (recommended 20+ for stable WASI support)
475
- - **No Java required** (Wasm executor works out of the box)
536
+ - **Node.js 12+** - That's it!
537
+ - ~~**Java Runtime Environment (JRE)**~~ - **NOT REQUIRED!** Automatically bundled during `npm install`
476
538
  - **Graphviz** (optional, for advanced diagram types)
477
539
 
540
+ **✅ Supported Platforms (with automatic JRE integration):**
541
+ - Windows x64 - JRE automatically installed during `npm install`
542
+ - macOS ARM64 (Apple Silicon) - JRE automatically installed during `npm install`
543
+ - Linux x64 - JRE automatically installed during `npm install`
544
+
545
+ **🎯 Key Point:** You only need Node.js installed. The Java runtime is automatically downloaded and integrated when you install this package. **No manual Java installation or configuration needed!**
546
+
547
+ The bundled JRE is lightweight (~40-60MB) and specifically built for your platform.
548
+
478
549
  ---
479
550
 
480
551
  ## 🧪 Testing
@@ -495,13 +566,13 @@ npm run test:batch:png
495
566
 
496
567
  ## 📝 Changelog
497
568
 
498
- ### v0.9.0
569
+ ### v1.0.2
499
570
 
500
- - ✨ **Pure Node.js Support** - WebAssembly-based execution, no Java required
501
571
  - 🌏 **Multi-language Support** - Perfect rendering for Chinese, Japanese, Korean with automatic font detection
502
572
  - 📦 **Auto-update** - Automatic PlantUML JAR updates from GitHub Releases
503
573
  - 🎨 **Multiple Formats** - PNG, SVG, EPS, ASCII, Unicode support
504
- - 🔄 **Smart Fallback** - Automatic fallback to Java if Wasm unavailable
574
+ - **Performance Optimization** - Nailgun support for faster Java startup
575
+ - 🧹 **Code Cleanup** - Removed non-functional Wasm implementation (see docs/WASM_BUILD_LIMITATIONS.md)
505
576
 
506
577
  ---
507
578
 
@@ -523,7 +594,6 @@ This project is based on:
523
594
 
524
595
  - **[PlantUML](http://plantuml.sourceforge.net/)** - The powerful diagramming tool
525
596
  - **[node-plantuml](https://github.com/markushedvall/node-plantuml)** - Original Node.js wrapper by Markus Hedvall
526
- - **[Bytecoder](https://github.com/mirkosertic/Bytecoder)** - Java to WebAssembly compiler
527
597
 
528
598
  Special thanks to the PlantUML community and all contributors!
529
599
 
@@ -545,34 +615,47 @@ Special thanks to the PlantUML community and all contributors!
545
615
 
546
616
  # node-plantuml-2
547
617
 
548
- > **纯 Node.js PlantUML 渲染器 - 无需 Java!**
618
+ > **Node.js PlantUML 渲染器 - 基于 Java 后端**
549
619
 
550
620
  [![npm version](https://img.shields.io/npm/v/node-plantuml-2)](https://www.npmjs.com/package/node-plantuml-2)
551
621
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
552
622
  [![Node.js](https://img.shields.io/badge/node-%3E%3D12-green.svg)](https://nodejs.org/)
553
623
 
554
- 一个强大的 Node.js 模块和 CLI,用于运行 [PlantUML](http://plantuml.sourceforge.net/),支持**纯 Node.js 环境**。本项目基于 [node-plantuml](https://github.com/markushedvall/node-plantuml) Fork 并增强,采用 WebAssembly 执行,无需 Java 运行时。
624
+ 一个强大的 Node.js 模块和 CLI,用于运行 [PlantUML](http://plantuml.sourceforge.net/)。本项目基于 [node-plantuml](https://github.com/markushedvall/node-plantuml) Fork 并增强,通过 Nailgun 优化提供更快的 Java 启动性能。
555
625
 
556
626
  ## ✨ 核心特性
557
627
 
558
- - 🚀 **纯 Node.js 环境** - 无需安装 Java!使用预编译的 WebAssembly 模块
559
- - 📦 **零配置** - 只需 `npm install` 即可使用
628
+ - 🎯 **零 Java 安装要求** - **Java 自动集成!** 只需 `npm install` 即可使用 - 无需手动安装 Java
629
+ - 📦 **自动集成 JRE** - 安装 npm 包时自动为您的平台下载并安装轻量级 JRE(40-60MB)
630
+ - 🚀 **优化的 Java 执行** - 使用 Nailgun 加速 Java 启动,保持 JVM 常驻内存
560
631
  - 🎨 **多种输出格式** - 支持 PNG、SVG、EPS、ASCII 和 Unicode 文本
561
632
  - 🌏 **多语言支持** - 完美支持中文、日文、韩文等多种 CJK 字符渲染,自动字体检测和配置
562
- - ⚡ **快速启动** - WebAssembly 执行比 JVM 更快
563
- - 🔄 **自动降级** - Wasm 不可用时自动降级到 Java 执行器
633
+ - ⚡ **高性能** - Nailgun 优化减少 Java 启动开销
564
634
  - 📝 **CLI 和 API** - 同时提供命令行界面和编程 API
565
- - 🎯 **基于 PlantUML** - 完全兼容 PlantUML 语法
635
+ - 🏗️ **基于 PlantUML** - 完全兼容 PlantUML 语法
566
636
 
567
637
  ---
568
638
 
569
639
  ## 📦 安装
570
640
 
641
+ ### 快速安装(推荐)
642
+
571
643
  ```bash
572
644
  npm install node-plantuml-2
573
645
  ```
574
646
 
575
- **就这么简单!** 无需 Java,无需配置,无需构建步骤。
647
+ **就这么简单!🎉 无需安装 Java!**
648
+
649
+ 该包会在安装时**自动检测您的平台**并安装轻量级 JRE(40-60MB)。您无需单独安装 Java - 一切自动处理!
650
+
651
+ **工作原理:**
652
+ - 运行 `npm install` 时,包会自动检测您的操作系统和架构
653
+ - 自动下载并安装匹配平台的 JRE runtime 包:
654
+ - **Windows x64** → `@node-plantuml-2/jre-win32-x64`
655
+ - **macOS ARM64** → `@node-plantuml-2/jre-darwin-arm64`
656
+ - **Linux x64** → `@node-plantuml-2/jre-linux-x64`
657
+ - 只安装您平台的 JRE(其他平台自动跳过)
658
+ - 开箱即用 - 无需配置!
576
659
 
577
660
  全局安装 CLI:
578
661
 
@@ -580,6 +663,47 @@ npm install node-plantuml-2
580
663
  npm install node-plantuml-2 -g
581
664
  ```
582
665
 
666
+ **⚠️ 重要提示:** 您**无需**在系统上安装 Java。JRE 会在安装此 npm 包时自动集成。
667
+
668
+ ### Java 要求
669
+
670
+ **🎯 零 Java 安装要求!**
671
+
672
+ 此包在安装时**自动集成最小 JRE**。您**无需**在系统上安装 Java!
673
+
674
+ **自动 JRE 集成:**
675
+
676
+ 当您运行 `npm install node-plantuml-2` 时,包会:
677
+ 1. **检测您的平台**(Windows/macOS/Linux 和架构)
678
+ 2. **自动下载**匹配的轻量级 JRE(40-60MB)
679
+ 3. **无缝集成** - 无需手动设置
680
+ 4. **立即可用** - 安装完成后即可使用
681
+
682
+ **Java 解析优先级:**
683
+
684
+ 包使用以下优先级查找 Java:
685
+
686
+ 1. **捆绑的 JRE**(主要方式)⭐ - 在 `npm install` 时自动安装
687
+ - 平台特定:只下载您平台的 JRE
688
+ - 轻量级:约 40-60MB,使用 `jlink` 构建以获得最佳体积
689
+ - 零配置:开箱即用
690
+
691
+ 2. **系统 Java**(后备方案)- 如果捆绑的 JRE 不可用,会使用系统 Java(如果存在)
692
+ - 检查 `JAVA_HOME` 环境变量
693
+ - 检查系统 PATH 中的 `java` 命令
694
+
695
+ 3. **自定义 Java**(可选)- 使用自定义 Java 路径覆盖:
696
+ ```javascript
697
+ plantuml.generate(code, { javaPath: '/custom/path/to/java' })
698
+ ```
699
+
700
+ **支持的平台(带自动 JRE):**
701
+ - ✅ Windows x64 - 自动集成 JRE
702
+ - ✅ macOS ARM64 (Apple Silicon) - 自动集成 JRE
703
+ - ✅ Linux x64 - 自动集成 JRE
704
+
705
+ **总结:安装 npm 包,Java 自动包含在内!无需单独安装 Java。**
706
+
583
707
  ---
584
708
 
585
709
  ## 🚀 快速开始
@@ -960,17 +1084,23 @@ PLANTUML_USE_JAVA=true node your-script.js
960
1084
 
961
1085
  ## 🏗️ 架构
962
1086
 
963
- 本项目采用**混合执行模型**:
1087
+ 本项目使用**Java 执行**,并自动捆绑 JRE 和进行优化:
1088
+
1089
+ 1. **捆绑的 JRE**(自动)
1090
+ - 轻量级最小 JRE(约 40-60MB)通过 `optionalDependencies` 自动安装
1091
+ - 使用 `jlink` 构建以获得最佳体积
1092
+ - 特定平台的包确保只安装相关的 JRE
1093
+ - **无需手动安装 Java!**
964
1094
 
965
- 1. **主要:WebAssembly 执行器**(纯 Node.js)
966
- - npm 包中包含预编译的 Wasm 模块
967
- - 快速启动,低内存占用
968
- - 无需 Java
1095
+ 2. **Java 执行器**(主要)
1096
+ - 使用捆绑的 JRE 或系统 Java 执行 `java -jar plantuml.jar`
1097
+ - 完全支持 PlantUML 的所有功能
1098
+ - 自动 Java 路径解析,带后备策略
969
1099
 
970
- 2. **降级:Java 执行器**(可选)
971
- - Wasm 不可用时自动降级
972
- - 需要 Java 运行时
973
- - 与原始 node-plantuml 完全兼容
1100
+ 3. **Nailgun 优化**(可选,用于性能提升)
1101
+ - 保持 JVM 常驻内存以加速启动
1102
+ - 使用 `plantumlExecutor.useNailgun()` 启用
1103
+ - 显著减少启动开销
974
1104
 
975
1105
  ### 执行流程
976
1106
 
@@ -979,9 +1109,19 @@ PLANTUML_USE_JAVA=true node your-script.js
979
1109
 
980
1110
  plantuml.generate()
981
1111
 
982
- 检查 Wasm 可用性
983
- ├─ 可用 → 使用 Wasm 执行器 ✅ (纯 Node)
984
- └─ 不可用 → 使用 Java 执行器 (降级)
1112
+ plantumlExecutor.exec()
1113
+
1114
+ Java 路径解析(优先级顺序)
1115
+ ├─ options.javaPath(用户指定)
1116
+ ├─ 捆绑的 JRE(自动安装)
1117
+ ├─ JAVA_HOME(系统环境变量)
1118
+ └─ 系统 PATH 中的 java
1119
+
1120
+ 检查 Nailgun 是否运行
1121
+ ├─ 运行中 → 使用 Nailgun(更快)
1122
+ └─ 未运行 → 使用 spawn('java', ...)
1123
+
1124
+ 执行 PlantUML JAR
985
1125
 
986
1126
  生成图表
987
1127
 
@@ -992,10 +1132,19 @@ plantuml.generate()
992
1132
 
993
1133
  ## 📋 系统要求
994
1134
 
995
- - **Node.js 12+**(推荐 20+ 以获得稳定的 WASI 支持)
996
- - **无需 Java** ✅(Wasm 执行器开箱即用)
1135
+ - **Node.js 12+** - 仅此而已!
1136
+ - ~~**Java Runtime Environment (JRE)**~~ - **不需要!** 在 `npm install` 时自动集成
997
1137
  - **Graphviz**(可选,用于高级图表类型)
998
1138
 
1139
+ **✅ 支持的平台(带自动 JRE 集成):**
1140
+ - Windows x64 - 在 `npm install` 时自动安装 JRE
1141
+ - macOS ARM64 (Apple Silicon) - 在 `npm install` 时自动安装 JRE
1142
+ - Linux x64 - 在 `npm install` 时自动安装 JRE
1143
+
1144
+ **🎯 关键点:** 您只需要安装 Node.js。Java 运行时会在您安装此包时自动下载并集成。**无需手动安装或配置 Java!**
1145
+
1146
+ 捆绑的 JRE 轻量级(约 40-60MB),专门为您的平台构建。
1147
+
999
1148
  ---
1000
1149
 
1001
1150
  ## 🧪 测试
@@ -1016,13 +1165,13 @@ npm run test:batch:png
1016
1165
 
1017
1166
  ## 📝 更新日志
1018
1167
 
1019
- ### v0.9.0
1168
+ ### v1.0.2
1020
1169
 
1021
- - ✨ **纯 Node.js 支持** - 基于 WebAssembly 的执行,无需 Java
1022
1170
  - 🌏 **多语言支持** - 完美支持中文、日文、韩文等多种语言,自动字体检测
1023
1171
  - 📦 **自动更新** - 从 GitHub Releases 自动更新 PlantUML JAR
1024
1172
  - 🎨 **多种格式** - PNG、SVG、EPS、ASCII、Unicode 支持
1025
- - 🔄 **智能降级** - Wasm 不可用时自动降级到 Java
1173
+ - **性能优化** - Nailgun 支持以加速 Java 启动
1174
+ - 🧹 **代码清理** - 移除了不可用的 Wasm 实现(参见 docs/WASM_BUILD_LIMITATIONS.md)
1026
1175
 
1027
1176
  ---
1028
1177
 
@@ -1044,7 +1193,6 @@ MIT License
1044
1193
 
1045
1194
  - **[PlantUML](http://plantuml.sourceforge.net/)** - 强大的图表工具
1046
1195
  - **[node-plantuml](https://github.com/markushedvall/node-plantuml)** - Markus Hedvall 的原始 Node.js 包装器
1047
- - **[Bytecoder](https://github.com/mirkosertic/Bytecoder)** - Java 到 WebAssembly 编译器
1048
1196
 
1049
1197
  特别感谢 PlantUML 社区和所有贡献者!
1050
1198
 
@@ -0,0 +1,232 @@
1
+ 'use strict'
2
+
3
+ var fs = require('fs')
4
+ var path = require('path')
5
+ var os = require('os')
6
+ var childProcess = require('child_process')
7
+
8
+ /**
9
+ * Resolve Java executable path with fallback strategy
10
+ *
11
+ * Priority order:
12
+ * 1. options.javaPath (user-specified)
13
+ * 2. Bundled JRE (from optional dependencies)
14
+ * 3. JAVA_HOME environment variable
15
+ * 4. System 'java' in PATH (which java)
16
+ *
17
+ * @param {Object} options - Options object
18
+ * @param {string} options.javaPath - User-specified Java path (highest priority)
19
+ * @returns {string|null} - Path to Java executable, or null if not found
20
+ */
21
+ function resolveJavaExecutable (options) {
22
+ options = options || {}
23
+
24
+ // Priority 1: User-specified Java path
25
+ if (options.javaPath) {
26
+ var javaPath = path.resolve(options.javaPath)
27
+ if (fs.existsSync(javaPath)) {
28
+ try {
29
+ // Make executable on Unix if needed
30
+ if (process.platform !== 'win32') {
31
+ fs.chmodSync(javaPath, 0o755)
32
+ }
33
+ return javaPath
34
+ } catch (e) {
35
+ // If can't make executable, still try to use it
36
+ return javaPath
37
+ }
38
+ }
39
+ }
40
+
41
+ // Priority 2: Bundled JRE from optional dependencies
42
+ var bundledJava = resolveBundledJava()
43
+ if (bundledJava) {
44
+ return bundledJava
45
+ }
46
+
47
+ // Priority 3: JAVA_HOME environment variable
48
+ var javaHome = process.env.JAVA_HOME
49
+ if (javaHome) {
50
+ var javaHomeJava = getJavaFromHome(javaHome)
51
+ if (javaHomeJava && fs.existsSync(javaHomeJava)) {
52
+ return javaHomeJava
53
+ }
54
+ }
55
+
56
+ // Priority 4: System 'java' in PATH
57
+ try {
58
+ var systemJava = findJavaInPath()
59
+ if (systemJava) {
60
+ return systemJava
61
+ }
62
+ } catch (e) {
63
+ // Ignore errors when checking PATH
64
+ }
65
+
66
+ return null
67
+ }
68
+
69
+ /**
70
+ * Resolve bundled JRE from optional dependencies
71
+ * Based on platform and architecture
72
+ *
73
+ * @returns {string|null} - Path to bundled Java executable
74
+ */
75
+ function resolveBundledJava () {
76
+ var platform = os.platform()
77
+ var arch = os.arch()
78
+
79
+ // Map platform and arch to package name
80
+ var pkgName = getRuntimePackageName(platform, arch)
81
+ if (!pkgName) {
82
+ return null
83
+ }
84
+
85
+ try {
86
+ // Try to resolve the runtime package
87
+ var pkgPath
88
+ try {
89
+ // Try to require.resolve the package
90
+ var pkgJsonPath = require.resolve(pkgName + '/package.json')
91
+ pkgPath = path.dirname(pkgJsonPath)
92
+ } catch (e) {
93
+ // Package might not be installed (optional dependency)
94
+ // Try to find it in node_modules relative to this package
95
+ var thisPkgPath = path.join(__dirname, '..')
96
+ var possiblePath = path.join(thisPkgPath, 'node_modules', pkgName)
97
+ if (fs.existsSync(path.join(possiblePath, 'package.json'))) {
98
+ pkgPath = possiblePath
99
+ } else {
100
+ return null
101
+ }
102
+ }
103
+
104
+ // Construct Java executable path
105
+ var javaExe = platform === 'win32' ? 'java.exe' : 'java'
106
+ var javaPath = path.join(pkgPath, 'jre', 'bin', javaExe)
107
+
108
+ if (fs.existsSync(javaPath)) {
109
+ try {
110
+ // Make executable on Unix
111
+ if (platform !== 'win32') {
112
+ fs.chmodSync(javaPath, 0o755)
113
+ }
114
+ return javaPath
115
+ } catch (e) {
116
+ // If chmod fails, still return the path (it might already be executable)
117
+ return javaPath
118
+ }
119
+ }
120
+ } catch (e) {
121
+ // Silently fail - bundled JRE not available
122
+ return null
123
+ }
124
+
125
+ return null
126
+ }
127
+
128
+ /**
129
+ * Get runtime package name based on platform and architecture
130
+ *
131
+ * @param {string} platform - OS platform (win32, darwin, linux)
132
+ * @param {string} arch - Architecture (x64, arm64)
133
+ * @returns {string|null} - Package name or null if unsupported
134
+ */
135
+ function getRuntimePackageName (platform, arch) {
136
+ if (platform === 'win32' && arch === 'x64') {
137
+ return '@node-plantuml-2/jre-win32-x64'
138
+ } else if (platform === 'darwin' && arch === 'arm64') {
139
+ return '@node-plantuml-2/jre-darwin-arm64'
140
+ } else if (platform === 'darwin' && arch === 'x64') {
141
+ return '@node-plantuml-2/jre-darwin-x64'
142
+ } else if (platform === 'linux' && arch === 'x64') {
143
+ return '@node-plantuml-2/jre-linux-x64'
144
+ }
145
+
146
+ // Unsupported platform/arch combination
147
+ return null
148
+ }
149
+
150
+ /**
151
+ * Get Java executable from JAVA_HOME
152
+ *
153
+ * @param {string} javaHome - JAVA_HOME path
154
+ * @returns {string|null} - Path to java executable
155
+ */
156
+ function getJavaFromHome (javaHome) {
157
+ var javaExe = process.platform === 'win32' ? 'java.exe' : 'java'
158
+ var javaPath = path.join(javaHome, 'bin', javaExe)
159
+ return javaPath
160
+ }
161
+
162
+ /**
163
+ * Find Java executable in system PATH
164
+ *
165
+ * @returns {string|null} - Path to java executable
166
+ */
167
+ function findJavaInPath () {
168
+ try {
169
+ // Use 'which' on Unix, 'where' on Windows
170
+ var command = process.platform === 'win32' ? 'where' : 'which'
171
+ var result = childProcess.execSync(command + ' java', {
172
+ encoding: 'utf-8',
173
+ stdio: ['ignore', 'pipe', 'ignore']
174
+ })
175
+
176
+ if (result) {
177
+ var javaPath = result.trim().split('\n')[0]
178
+ if (javaPath && fs.existsSync(javaPath)) {
179
+ return javaPath
180
+ }
181
+ }
182
+ } catch (e) {
183
+ // Java not found in PATH
184
+ }
185
+
186
+ return null
187
+ }
188
+
189
+ /**
190
+ * Verify Java executable works
191
+ *
192
+ * @param {string} javaPath - Path to Java executable
193
+ * @returns {Promise<boolean>} - True if Java works
194
+ */
195
+ function verifyJava (javaPath) {
196
+ return new Promise(function (resolve) {
197
+ try {
198
+ var child = childProcess.spawn(javaPath, ['-version'], {
199
+ stdio: 'pipe'
200
+ })
201
+
202
+ var hasOutput = false
203
+ child.on('data', function () {
204
+ hasOutput = true
205
+ })
206
+
207
+ child.on('close', function (code) {
208
+ resolve(code === 0)
209
+ })
210
+
211
+ child.on('error', function () {
212
+ resolve(false)
213
+ })
214
+
215
+ // Timeout after 5 seconds
216
+ setTimeout(function () {
217
+ child.kill()
218
+ resolve(false)
219
+ }, 5000)
220
+ } catch (e) {
221
+ resolve(false)
222
+ }
223
+ })
224
+ }
225
+
226
+ module.exports = {
227
+ resolveJavaExecutable: resolveJavaExecutable,
228
+ resolveBundledJava: resolveBundledJava,
229
+ getRuntimePackageName: getRuntimePackageName,
230
+ verifyJava: verifyJava
231
+ }
232
+
@@ -265,7 +265,11 @@ module.exports.generate = function (input, options, callback) {
265
265
  callback = args.callback
266
266
 
267
267
  var o = joinOptions([PIPE], options)
268
- var child = plantumlExecutor.exec(o, options.include, callback)
268
+ // Pass options (including javaPath) to executor
269
+ var executorOptions = {
270
+ javaPath: options.javaPath
271
+ }
272
+ var child = plantumlExecutor.exec(o, options.include, executorOptions, callback)
269
273
 
270
274
  if (!input) {
271
275
  // For stdin, we need to handle font config in a transform stream
@@ -355,7 +359,7 @@ module.exports.encode = function (input, options, callback) {
355
359
  }
356
360
 
357
361
  module.exports.decode = function (encoded, callback) {
358
- var child = plantumlExecutor.exec([DECODE, encoded], callback)
362
+ var child = plantumlExecutor.exec([DECODE, encoded], undefined, {}, callback)
359
363
 
360
364
  return {
361
365
  out: child.stdout
@@ -363,7 +367,7 @@ module.exports.decode = function (encoded, callback) {
363
367
  }
364
368
 
365
369
  module.exports.testdot = function (callback) {
366
- var child = plantumlExecutor.exec([TESTDOT])
370
+ var child = plantumlExecutor.exec([TESTDOT], undefined, {}, callback)
367
371
 
368
372
  var chunks = []
369
373
  child.stdout.on('data', function (chunk) { chunks.push(chunk) })
@@ -4,6 +4,7 @@ var childProcess = require('child_process')
4
4
  var path = require('path')
5
5
  var nailgun = require('node-nailgun-server')
6
6
  var ngClient = require('node-nailgun-client')
7
+ var javaResolver = require('./java-resolver')
7
8
 
8
9
  var INCLUDED_PLANTUML_JAR = path.join(__dirname, '../vendor/plantuml.jar')
9
10
  var PLANTUML_JAR = process.env.PLANTUML_HOME || INCLUDED_PLANTUML_JAR
@@ -47,9 +48,33 @@ function execWithNailgun (argv, cwd, cb) {
47
48
  return ngClient.exec(PLANTUML_NAIL_CLASS, argv, clientOptions)
48
49
  }
49
50
 
51
+ /**
52
+ * Find Java executable with fallback strategy
53
+ * @param {Object} options - Options with optional javaPath
54
+ * @returns {string} - Java executable path
55
+ */
56
+ function findJavaExecutable (options) {
57
+ options = options || {}
58
+
59
+ // Try to resolve Java executable
60
+ var javaPath = javaResolver.resolveJavaExecutable(options)
61
+
62
+ if (!javaPath) {
63
+ // Fallback to 'java' in PATH if resolver fails
64
+ // This maintains backward compatibility
65
+ return 'java'
66
+ }
67
+
68
+ return javaPath
69
+ }
70
+
50
71
  // TODO: proper error handling
51
- function execWithSpawn (argv, cwd, cb) {
72
+ function execWithSpawn (argv, cwd, options, cb) {
52
73
  cwd = cwd || process.cwd()
74
+ options = options || {}
75
+
76
+ var javaExe = findJavaExecutable(options)
77
+
53
78
  var opts = [
54
79
  '-Dplantuml.include.path=' + cwd,
55
80
  '-Djava.awt.headless=true',
@@ -58,95 +83,47 @@ function execWithSpawn (argv, cwd, cb) {
58
83
  '-Duser.country=US',
59
84
  '-jar', PLANTUML_JAR
60
85
  ].concat(argv)
61
- return childProcess.spawn('java', opts)
62
- }
63
-
64
- module.exports.useWasm = function (callback) {
65
- var wasmExecutor = require('./plantuml-executor-wasm')
66
- if (wasmExecutor.isAvailable()) {
67
- return wasmExecutor.initWasm(callback)
68
- } else {
69
- console.warn('Wasm executor not available, falling back to Java executor')
70
- if (typeof callback === 'function') {
71
- callback(new Error('Wasm executor not available'))
72
- }
73
- }
86
+
87
+ return childProcess.spawn(javaExe, opts)
74
88
  }
75
89
 
76
- module.exports.exec = function (argv, cwd, callback) {
90
+ module.exports.exec = function (argv, cwd, callbackOrOptions, callback) {
91
+ var options = {}
92
+ var actualCallback
93
+
94
+ // Handle flexible argument signature
77
95
  if (typeof argv === 'function') {
78
- callback = argv
96
+ actualCallback = argv
79
97
  argv = undefined
80
98
  cwd = undefined
81
99
  } else if (typeof cwd === 'function') {
82
- callback = cwd
100
+ actualCallback = cwd
83
101
  cwd = undefined
102
+ } else if (typeof callbackOrOptions === 'function') {
103
+ actualCallback = callbackOrOptions
104
+ } else if (callbackOrOptions && typeof callbackOrOptions === 'object') {
105
+ options = callbackOrOptions
106
+ actualCallback = callback
107
+ } else if (typeof callback === 'function') {
108
+ actualCallback = callback
84
109
  }
85
110
 
86
- // Priority 1: Try Wasm executor first (pure Node, no Java needed)
87
- var wasmExecutor = require('./plantuml-executor-wasm')
88
- var useJava = process.env.PLANTUML_USE_JAVA === 'true' || process.env.PLANTUML_USE_JAVA === '1'
89
-
90
- var task
91
- // Use Wasm by default, unless explicitly requested to use Java
92
- if (!useJava && wasmExecutor.isAvailable()) {
93
- try {
94
- // Try to initialize Wasm synchronously first
95
- if (!wasmExecutor.isReady()) {
96
- if (!wasmExecutor.initWasmSync()) {
97
- // Sync init failed, try async (non-blocking)
98
- wasmExecutor.initWasm(function (err) {
99
- if (err) {
100
- console.warn('Wasm initialization failed, falling back to Java:', err.message)
101
- }
102
- })
103
- }
104
- }
105
-
106
- // Try to use Wasm executor
107
- if (wasmExecutor.isReady()) {
108
- task = wasmExecutor.exec(argv, cwd, callback)
109
- // Wasm executor handles its own callback setup
110
- if (task && typeof callback === 'function') {
111
- // Setup callback for Wasm executor if needed
112
- var chunks = []
113
- if (task.stdout) {
114
- task.stdout.on('data', function (chunk) { chunks.push(chunk) })
115
- task.stdout.on('end', function () {
116
- var data = Buffer.concat(chunks)
117
- callback(null, data)
118
- })
119
- task.stdout.on('error', function () {
120
- callback(new Error('error while reading plantuml output'), null)
121
- })
122
- }
123
- }
124
- return task
125
- } else {
126
- // Wasm not ready yet, fallback to Java
127
- task = getJavaTask(argv, cwd, callback)
128
- }
129
- } catch (e) {
130
- console.warn('Wasm executor failed, falling back to Java:', e.message)
131
- task = getJavaTask(argv, cwd, callback)
132
- }
133
- } else {
134
- // Use Java executor (fallback or explicitly requested)
135
- task = getJavaTask(argv, cwd, callback)
136
- }
137
-
138
- return task
111
+ // Use Java executor (Wasm executor is not available due to Bytecoder limitations)
112
+ // See docs/WASM_BUILD_LIMITATIONS.md for details
113
+ return getJavaTask(argv, cwd, options, actualCallback)
139
114
  }
140
115
 
141
116
  /**
142
117
  * Get Java executor task
143
118
  */
144
- function getJavaTask (argv, cwd, callback) {
119
+ function getJavaTask (argv, cwd, options, callback) {
120
+ options = options || {}
145
121
  var task
122
+
146
123
  if (nailgunRunning) {
147
124
  task = execWithNailgun(argv, cwd, callback)
148
125
  } else {
149
- task = execWithSpawn(argv, cwd, callback)
126
+ task = execWithSpawn(argv, cwd, options, callback)
150
127
  }
151
128
 
152
129
  if (typeof callback === 'function') {
@@ -436,7 +436,7 @@ function checkSyntaxError (code, callback) {
436
436
  var SVG = '-tsvg'
437
437
  var PIPE = '-pipe'
438
438
 
439
- var child = plantumlExecutor.exec([PIPE, SVG])
439
+ var child = plantumlExecutor.exec([PIPE, SVG], undefined, {})
440
440
  var svgChunks = []
441
441
  var errorChunks = []
442
442
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "node-plantuml-2",
3
- "version": "1.0.2",
4
- "plantumlVersion": "1.2026.0",
5
- "description": "Pure Node.js PlantUML renderer - No Java required! Multiple output formats (PNG, SVG, EPS, ASCII, Unicode) with full UTF-8 support",
3
+ "version": "1.0.4",
4
+ "plantumlVersion": "1.2026.1",
5
+ "description": "Node.js PlantUML renderer with Java backend. Multiple output formats (PNG, SVG, EPS, ASCII, Unicode) with full UTF-8 support",
6
6
  "main": "index.js",
7
7
  "author": "Markus Hedvall <mackanhedvall@gmail.com>",
8
8
  "repository": "https://github.com/JaredYe04/node-plantuml-2",
@@ -14,10 +14,6 @@
14
14
  "plantuml",
15
15
  "uml",
16
16
  "diagram",
17
- "wasm",
18
- "webassembly",
19
- "pure-node",
20
- "no-java",
21
17
  "svg",
22
18
  "png",
23
19
  "markdown",
@@ -29,6 +25,11 @@
29
25
  "node-nailgun-server": "^0.1.4",
30
26
  "plantuml-encoder": "^1.2.5"
31
27
  },
28
+ "optionalDependencies": {
29
+ "@node-plantuml-2/jre-win32-x64": "^1.0.4",
30
+ "@node-plantuml-2/jre-darwin-arm64": "^1.0.4",
31
+ "@node-plantuml-2/jre-linux-x64": "^1.0.4"
32
+ },
32
33
  "devDependencies": {
33
34
  "chai": "^4.x",
34
35
  "mocha": "^5.x",
@@ -39,27 +40,21 @@
39
40
  "index.js",
40
41
  "lib/",
41
42
  "vendor/plantuml.jar",
42
- "vendor/wasm/plantuml.wasm",
43
43
  "resources/",
44
44
  "nail/plantumlnail.jar",
45
45
  "scripts/download.js",
46
- "scripts/get-vizjs.js"
46
+ "scripts/get-vizjs.js",
47
+ "README.md"
47
48
  ],
48
49
  "scripts": {
49
50
  "prepublish": "node scripts/get-plantuml-jar.js --latest || echo 'JAR download failed, continuing...'",
50
- "build:wasm:publish": "node scripts/get-plantuml-jar.js --latest && node scripts/build-plantuml-wasm.js",
51
51
  "postinstall": "node scripts/get-vizjs.js",
52
52
  "test": "standard && node test/fixtures/prepare.js && mocha",
53
53
  "test:batch": "node test/batch-convert-test.js",
54
54
  "test:batch:svg": "node test/batch-convert-test.js --svg",
55
55
  "test:batch:png": "node test/batch-convert-test.js --png",
56
56
  "build": "node nail/build.js",
57
- "build:wasm": "node scripts/build-plantuml-wasm.js --method cheerpj",
58
- "build:wasm:cheerpj": "node scripts/build-plantuml-wasm.js --method cheerpj",
59
- "build:wasm:bytecoder": "node scripts/build-plantuml-wasm.js --method bytecoder",
60
- "build:all": "node scripts/build-all.js",
61
- "build:all:jar-only": "node scripts/build-all.js --skip-wasm",
62
- "build:all:wasm-only": "node scripts/build-all.js --skip-jar"
57
+ "build:all": "node scripts/build-all.js"
63
58
  },
64
59
  "bin": {
65
60
  "puml": "index.js"
@@ -30,44 +30,15 @@ plantuml.testdot(function (isOk) {
30
30
  if (fs.existsSync(VIZJS_JAR)) {
31
31
  console.info('vizjs.jar already exists. Skipping download.')
32
32
  } else {
33
- console.info('graphviz was not found on the system. Downloading vizjs instead. See http://plantuml.com/vizjs. This may take a few minutes.')
34
-
35
- // download additional libraries for working without dot installed.
36
- download(VIZJS_URL, VIZJS_JAR, false, function (err) {
37
- if (err) {
38
- console.warn('Warning: Failed to download vizjs.jar:', err)
39
- console.warn('The package can still be used if graphviz is installed on the system, or if vizjs.jar is provided manually.')
40
- console.warn('You can retry the download later or install graphviz to avoid this dependency.')
41
- // Don't exit with error code - this is a non-critical dependency
42
- } else {
43
- // also install the V8 engine just in case the currently installed Java does not have Nashorn
44
- switch (process.platform) {
45
- case 'win32':
46
- download(J2V8_WIN_URL, J2V8_WIN_JAR, false, function (err) {
47
- if (err) {
48
- console.warn('Warning: Failed to download j2v8 Windows jar:', err)
49
- }
50
- })
51
- break
52
- case 'linux':
53
- download(J2V8_LINUX_URL, J2V8_LINUX_JAR, false, function (err) {
54
- if (err) {
55
- console.warn('Warning: Failed to download j2v8 Linux jar:', err)
56
- }
57
- })
58
- break
59
- case 'darwin':
60
- download(J2V8_MAC_URL, J2V8_MAC_JAR, false, function (err) {
61
- if (err) {
62
- console.warn('Warning: Failed to download j2v8 macOS jar:', err)
63
- }
64
- })
65
- break
66
- default:
67
- console.error('Unsupported operating system for V8 jars.')
68
- }
69
- }
70
- })
33
+ console.info('graphviz was not found on the system.')
34
+ console.info('Note: vizjs and j2v8 downloads are disabled to avoid rate limiting.')
35
+ console.info('The package can still be used if:')
36
+ console.info(' 1. Graphviz is installed on the system (recommended)')
37
+ console.info(' 2. vizjs.jar is provided manually in vendor/ directory')
38
+ console.info(' 3. Only non-Graphviz diagram types are used')
39
+ console.info('See http://plantuml.com/vizjs for more information.')
40
+ // Don't download to avoid rate limiting (429 errors)
41
+ // Users should install graphviz or provide vizjs.jar manually if needed
71
42
  }
72
43
  }
73
44
  })
Binary file
@@ -1,328 +0,0 @@
1
- 'use strict'
2
-
3
- /**
4
- * PlantUML Wasm Executor
5
- *
6
- * This module provides Wasm-based execution of PlantUML using TeaVM/Bytecoder.
7
- *
8
- * Implementation Strategy:
9
- * 1. Use TeaVM or Bytecoder to convert PlantUML JAR to WebAssembly
10
- * 2. Run Wasm module in Node.js using WASI or WebAssembly API
11
- * 3. Provide file system access through WASI or Node.js FS API
12
- */
13
-
14
- var fs = require('fs')
15
- var path = require('path')
16
- var stream = require('stream')
17
-
18
- // WASI is available in Node.js 12+ as experimental, stable in 20+
19
- var WASI
20
- try {
21
- // Try Node.js 20+ stable API
22
- WASI = require('wasi').WASI
23
- } catch (e) {
24
- try {
25
- // Try experimental API (Node.js 12-19)
26
- WASI = require('wasi').WASI
27
- } catch (e2) {
28
- // WASI not available
29
- WASI = null
30
- }
31
- }
32
-
33
- var WASM_DIR = path.join(__dirname, '../vendor/wasm')
34
- var PLANTUML_WASM = path.join(WASM_DIR, 'plantuml.wasm')
35
- // CheerpJ files (official PlantUML WASM solution)
36
- var PLANTUML_CORE_JS = path.join(WASM_DIR, 'plantuml-core.js')
37
- var PLANTUML_CORE_WASM = path.join(WASM_DIR, 'plantuml-core.wasm')
38
- var wasmInstance = null
39
- var wasmMemory = null
40
- var wasi = null
41
- var wasmReady = false
42
-
43
- /**
44
- * Initialize Wasm module
45
- * @param {Function} callback - Callback when ready
46
- */
47
- function initWasm (callback) {
48
- if (wasmReady && wasmInstance) {
49
- if (typeof callback === 'function') {
50
- callback(null)
51
- }
52
- return
53
- }
54
-
55
- if (!fs.existsSync(PLANTUML_WASM)) {
56
- var err = new Error('Wasm module not found: ' + PLANTUML_WASM + '\nPlease run: node scripts/build-plantuml-wasm.js')
57
- if (typeof callback === 'function') {
58
- callback(err)
59
- } else {
60
- throw err
61
- }
62
- return
63
- }
64
-
65
- try {
66
- // Check Node.js version for WASI support (Node.js 12+)
67
- var nodeVersion = process.version
68
- var majorVersion = parseInt(nodeVersion.split('.')[0].substring(1))
69
- if (majorVersion < 12) {
70
- throw new Error('WASI requires Node.js 12+. Current version: ' + nodeVersion)
71
- }
72
-
73
- // Initialize WASI
74
- var cwd = process.cwd()
75
- wasi = new WASI({
76
- version: 'preview1',
77
- env: process.env,
78
- preopens: {
79
- '/': cwd,
80
- '/tmp': require('os').tmpdir()
81
- },
82
- args: []
83
- })
84
-
85
- // Load Wasm module
86
- var wasmBuffer = fs.readFileSync(PLANTUML_WASM)
87
-
88
- // Create import object for WASI
89
- var importObject = {
90
- wasi_snapshot_preview1: wasi.wasiImport
91
- }
92
-
93
- // Instantiate Wasm module
94
- /* global WebAssembly */
95
- WebAssembly.instantiate(wasmBuffer, importObject)
96
- .then(function (result) {
97
- wasmInstance = result.instance
98
- wasmMemory = wasmInstance.exports.memory
99
-
100
- // Initialize WASI
101
- wasi.initialize(wasmInstance)
102
-
103
- wasmReady = true
104
- console.log('✓ Wasm module loaded successfully')
105
-
106
- if (typeof callback === 'function') {
107
- callback(null)
108
- }
109
- })
110
- .catch(function (err) {
111
- console.error('Failed to load Wasm module:', err)
112
- if (typeof callback === 'function') {
113
- callback(err)
114
- } else {
115
- throw err
116
- }
117
- })
118
- } catch (err) {
119
- if (typeof callback === 'function') {
120
- callback(err)
121
- } else {
122
- throw err
123
- }
124
- }
125
- }
126
-
127
- /**
128
- * Create a process-like object for Wasm execution
129
- * @param {Array} argv - Command line arguments
130
- * @param {string} cwd - Working directory
131
- * @param {Function} callback - Callback function
132
- * @returns {Object} Child process-like object with stdin/stdout/stderr
133
- */
134
- function execWithWasm (argv, cwd, callback) {
135
- if (!wasmReady || !wasmInstance) {
136
- throw new Error('Wasm module not initialized. Call initWasm() first.')
137
- }
138
-
139
- // Create streams for stdin/stdout/stderr
140
- var stdinStream = new stream.PassThrough()
141
- var stdoutStream = new stream.PassThrough()
142
- var stderrStream = new stream.PassThrough()
143
-
144
- // Collect stdin data
145
- var stdinData = []
146
- stdinStream.on('data', function (chunk) {
147
- stdinData.push(chunk)
148
- })
149
-
150
- stdinStream.on('end', function () {
151
- // Process input when stdin ends
152
- var inputBuffer = Buffer.concat(stdinData)
153
- processWasmExecution(argv, inputBuffer, stdoutStream, stderrStream, cwd, callback)
154
- })
155
-
156
- // If no stdin data expected, process immediately
157
- setTimeout(function () {
158
- if (stdinData.length === 0 && stdinStream.readableEnded) {
159
- processWasmExecution(argv, null, stdoutStream, stderrStream, cwd, callback)
160
- }
161
- }, 100)
162
-
163
- // Return process-like object
164
- return {
165
- stdin: stdinStream,
166
- stdout: stdoutStream,
167
- stderr: stderrStream
168
- }
169
- }
170
-
171
- /**
172
- * Process Wasm execution
173
- * @private
174
- */
175
- function processWasmExecution (argv, stdinData, stdoutStream, stderrStream, cwd, callback) {
176
- try {
177
- // Convert argv to string array for Wasm
178
- var args = argv || []
179
- var argsString = args.join(' ')
180
-
181
- // Prepare input data
182
- var inputText = stdinData ? stdinData.toString('utf-8') : ''
183
-
184
- // Call Wasm main function
185
- // Note: This is a simplified version. Actual PlantUML Wasm module
186
- // may have different function signatures
187
- if (wasmInstance.exports.main) {
188
- // If main function exists, call it with arguments
189
- var result = wasmInstance.exports.main(args.length, argsString, inputText)
190
-
191
- // Read output from memory
192
- if (wasmMemory && result !== undefined) {
193
- // Parse result and write to stdout
194
- // This is simplified - actual implementation depends on Wasm module API
195
- stdoutStream.end(Buffer.from(result))
196
- } else {
197
- stdoutStream.end()
198
- }
199
- } else if (wasmInstance.exports._start) {
200
- // WASI entry point
201
- wasi.start(wasmInstance)
202
- stdoutStream.end()
203
- } else {
204
- // Fallback: try to find PlantUML-specific export
205
- console.warn('Wasm module does not export expected functions. PlantUML Wasm module may need custom integration.')
206
- stdoutStream.end()
207
- }
208
-
209
- if (typeof callback === 'function') {
210
- var chunks = []
211
- stdoutStream.on('data', function (chunk) {
212
- chunks.push(chunk)
213
- })
214
- stdoutStream.on('end', function () {
215
- var data = Buffer.concat(chunks)
216
- callback(null, data)
217
- })
218
- }
219
- } catch (err) {
220
- stderrStream.write('Error executing Wasm module: ' + err.message + '\n')
221
- stderrStream.end()
222
- if (typeof callback === 'function') {
223
- callback(err)
224
- }
225
- }
226
- }
227
-
228
- /**
229
- * Initialize CheerpJ runtime (official PlantUML WASM solution)
230
- */
231
- function initCheerpJ (callback) {
232
- try {
233
- // CheerpJ uses a different loading mechanism
234
- // The JS file contains the runtime and can be loaded as a module
235
- console.log('Loading CheerpJ PlantUML-core...')
236
-
237
- // For now, we'll use a simple approach - load the JS file
238
- // In a real implementation, you'd need to handle CheerpJ's runtime API
239
- var cheerpjCode = fs.readFileSync(PLANTUML_CORE_JS, 'utf8')
240
-
241
- // Note: CheerpJ requires a browser-like environment or special runtime
242
- // This is a placeholder - actual implementation would need CheerpJ runtime
243
- console.log('✓ CheerpJ PlantUML-core loaded (runtime initialization needed)')
244
-
245
- wasmReady = true
246
- if (typeof callback === 'function') {
247
- callback(null)
248
- }
249
- } catch (err) {
250
- if (typeof callback === 'function') {
251
- callback(err)
252
- } else {
253
- throw err
254
- }
255
- }
256
- }
257
-
258
- /**
259
- * Check if Wasm executor is available
260
- * @returns {boolean}
261
- */
262
- function isWasmAvailable () {
263
- return fs.existsSync(PLANTUML_WASM) || fs.existsSync(PLANTUML_CORE_JS)
264
- }
265
-
266
- /**
267
- * Check if Wasm executor is ready (initialized)
268
- * @returns {boolean}
269
- */
270
- function isReady () {
271
- return wasmReady && wasmInstance !== null
272
- }
273
-
274
- /**
275
- * Initialize Wasm synchronously (for immediate use)
276
- */
277
- function initWasmSync () {
278
- if (wasmReady && wasmInstance) {
279
- return true
280
- }
281
-
282
- if (!fs.existsSync(PLANTUML_WASM)) {
283
- return false
284
- }
285
-
286
- try {
287
- var nodeVersion = process.version
288
- var majorVersion = parseInt(nodeVersion.split('.')[0].substring(1))
289
- if (majorVersion < 12) {
290
- return false
291
- }
292
-
293
- var cwd = process.cwd()
294
- wasi = new WASI({
295
- version: 'preview1',
296
- env: process.env,
297
- preopens: {
298
- '/': cwd,
299
- '/tmp': require('os').tmpdir()
300
- },
301
- args: []
302
- })
303
-
304
- var wasmBuffer = fs.readFileSync(PLANTUML_WASM)
305
- var importObject = {
306
- wasi_snapshot_preview1: wasi.wasiImport
307
- }
308
-
309
- /* global WebAssembly */
310
- var result = WebAssembly.instantiateSync(wasmBuffer, importObject)
311
- wasmInstance = result.instance
312
- wasmMemory = wasmInstance.exports.memory
313
- wasi.initialize(wasmInstance)
314
- wasmReady = true
315
- return true
316
- } catch (e) {
317
- console.warn('Failed to initialize Wasm synchronously:', e.message)
318
- return false
319
- }
320
- }
321
-
322
- module.exports = {
323
- initWasm: initWasm,
324
- exec: execWithWasm,
325
- isAvailable: isWasmAvailable,
326
- isReady: isReady,
327
- initWasmSync: initWasmSync
328
- }