Skip to content

Commit f5e4c50

Browse files
authored
feat: Integrate Node.js runtime for code highlighting (#5)
1 parent 87eee46 commit f5e4c50

57 files changed

Lines changed: 4529 additions & 216 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/workflow.yaml

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,56 @@ jobs:
4848
if [ -n "$version" ]; then
4949
sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties
5050
fi
51-
./gradlew clean build -x test
51+
./gradlew clean buildAll
5252
- name: Archive extra-api jar
5353
uses: actions/upload-artifact@v4
5454
with:
55-
name: extra-api
55+
name: extra-api-archive
5656
path: |
5757
build/libs/*.jar
5858
retention-days: 1
59+
- name: Upload extra-api-full-all-platforms
60+
uses: actions/upload-artifact@v4
61+
with:
62+
name: extra-api-full-all-platforms
63+
path: build/libs/extra-api-full-all-platforms-*.jar
64+
retention-days: 1
65+
- name: Upload extra-api-full-linux-arm64
66+
uses: actions/upload-artifact@v4
67+
with:
68+
name: extra-api-full-linux-arm64
69+
path: build/libs/extra-api-full-linux-arm64-*.jar
70+
retention-days: 1
71+
- name: Upload extra-api-full-linux-x86_64
72+
uses: actions/upload-artifact@v4
73+
with:
74+
name: extra-api-full-linux-x86_64
75+
path: build/libs/extra-api-full-linux-x86_64-*.jar
76+
retention-days: 1
77+
- name: Upload extra-api-full-macos-arm64
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: extra-api-full-macos-arm64
81+
path: build/libs/extra-api-full-macos-arm64-*.jar
82+
retention-days: 1
83+
- name: Upload extra-api-full-macos-x86_64
84+
uses: actions/upload-artifact@v4
85+
with:
86+
name: extra-api-full-macos-x86_64
87+
path: build/libs/extra-api-full-macos-x86_64-*.jar
88+
retention-days: 1
89+
- name: Upload extra-api-full-windows-x86_64
90+
uses: actions/upload-artifact@v4
91+
with:
92+
name: extra-api-full-windows-x86_64
93+
path: build/libs/extra-api-full-windows-x86_64-*.jar
94+
retention-days: 1
95+
- name: Upload extra-api-lite
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: extra-api-lite
99+
path: build/libs/extra-api-lite-*.jar
100+
retention-days: 1
59101

60102
github-release:
61103
runs-on: ubuntu-latest
@@ -67,17 +109,14 @@ jobs:
67109
with:
68110
name: extra-api
69111
path: build/libs
70-
- name: Get Name of Artifact
71-
id: get_artifact
112+
- name: Get Name of Artifacts
113+
id: get_artifacts
72114
run: |
73-
ARTIFACT_PATHNAME=$(ls build/libs/*.jar | head -n 1)
74-
ARTIFACT_NAME=$(basename ${ARTIFACT_PATHNAME})
75-
echo "Artifact pathname: ${ARTIFACT_PATHNAME}"
76-
echo "Artifact name: ${ARTIFACT_NAME}"
77-
echo "ARTIFACT_PATHNAME=${ARTIFACT_PATHNAME}" >> $GITHUB_ENV
78-
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
115+
ARTIFACT_PATHNAMES=$(ls build/libs/*.jar)
116+
echo "Artifact pathnames: ${ARTIFACT_PATHNAMES}"
117+
echo "ARTIFACT_PATHNAMES=${ARTIFACT_PATHNAMES}" >> $GITHUB_ENV
79118
echo "RELEASE_ID=${{ github.event.release.id }}" >> $GITHUB_ENV
80-
- name: Upload a Release Asset
119+
- name: Upload Release Assets
81120
uses: actions/github-script@v2
82121
if: github.event_name == 'release'
83122
with:
@@ -90,14 +129,17 @@ jobs:
90129
const { repo: { owner, repo }, sha } = context;
91130
console.log({ owner, repo, sha });
92131
93-
const releaseId = process.env.RELEASE_ID
94-
const artifactPathName = process.env.ARTIFACT_PATHNAME
95-
const artifactName = process.env.ARTIFACT_NAME
96-
console.log('Releasing', releaseId, artifactPathName, artifactName)
132+
const releaseId = process.env.RELEASE_ID;
133+
const artifactPathNames = process.env.ARTIFACT_PATHNAMES.split(' ');
134+
console.log('Releasing', releaseId, artifactPathNames);
97135
98-
await github.repos.uploadReleaseAsset({
99-
owner, repo,
100-
release_id: releaseId,
101-
name: artifactName,
102-
data: await fs.readFile(artifactPathName)
103-
});
136+
for (const artifactPathName of artifactPathNames) {
137+
const artifactName = artifactPathName.split('/').pop();
138+
console.log('Uploading', artifactPathName, artifactName);
139+
await github.repos.uploadReleaseAsset({
140+
owner, repo,
141+
release_id: releaseId,
142+
name: artifactName,
143+
data: await fs.readFile(artifactPathName)
144+
});
145+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/src/main/resources/js/**
2+
13
### Maven
24
target/
35
logs/

CONTRIBUTING.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# 贡献指南
2+
3+
## 开发环境
4+
5+
- Java 21+
6+
- Node.js 18+
7+
- pnpm
8+
9+
## 开发
10+
11+
```bash
12+
# 构建插件
13+
./gradlew build
14+
15+
# 开发前端
16+
cd ui
17+
pnpm install
18+
pnpm dev
19+
```
20+
21+
## 构建
22+
23+
```bash
24+
./gradlew build
25+
```
26+
27+
### 资源处理任务
28+
29+
- __processUiResources__ - 处理UI前端资源,将ui子项目的构建输出复制到console目录
30+
- __processShikiResources__ - 处理Shiki代码高亮的JS资源,仅完整版需要
31+
- __processLiteResources__ - 专门为轻量版处理资源,排除JS相关内容
32+
33+
### 📦 核心构建任务
34+
35+
- __jarLite__ - 构建轻量版,完全排除JS功能和Javet依赖
36+
- __jarFullAllPlatforms__ - 构建包含所有平台支持的完整版
37+
- __jarFull{Platform}__ - 构建特定平台的完整版(如jarFullLinux-x86_64)
38+
39+
### 🚀 便捷任务
40+
41+
- __buildAll__ - 构建所有版本
42+
- __buildLite__ - 仅构建轻量版
43+
- __build__/__jar__ - 默认构建包含所有平台支持的完整版
44+
45+
构建完成后,可以在 `build/libs` 目录找到插件 jar 文件。
46+
47+
## 如何添加新的嵌入式 JS 模块
48+
49+
本项目将 JavaScript 工具嵌入到 Java 运行时中,并将其预加载到 Javet V8 运行时中。按照以下步骤添加对新 JS 模块的支持。
50+
51+
### `JsModule` 枚举中添加条目
52+
53+
- 文件:`src/main/java/top/howiehz/halo/plugin/extra/api/service/js/module/JsModule.java`
54+
- 为模块添加一个枚举常量。UMD 模块 `marked` 的示例:
55+
56+
```java
57+
MARKED("marked", "marked.umd.js", JsModuleType.UMD),
58+
```
59+
60+
- 该枚举将 `module.getModuleName()` 映射到 `js/<name>``getSourceCode()` 将加载 `resources/js/<fileName>`
61+
62+
### 将 JS 文件放在 resources 目录下
63+
64+
- 路径:`src/main/resources/js/`
65+
- 名称必须与您添加的 `fileName` 匹配。示例:`marked.umd.js`
66+
- 该文件可以是真实的库构建文件或精简的 UMD 文件。对于 ESM 或其他模块类型,请相应调整 `JsModuleType`
67+
68+
### 预加载模块(可选但推荐)
69+
70+
- 文件:`src/main/java/top/howiehz/halo/plugin/extra/api/service/js/CustomJavetEngine.java`
71+
- 引擎当前在 `preloadModules()` 中预加载 `Shiki`
72+
- 使用 `JsModule.MARKED.getSourceCode()` 读取资源,使用 `v8Runtime.getExecutor(code).executeVoid()` 执行。
73+
- 加载后,验证预期的函数是否暴露在 `globalThis`(或其他入口点)上。保持预加载对错误的容忍性,避免引擎创建失败。
74+
75+
### 暴露 Java 服务来调用模块
76+
77+
-`service/js/<module>` 下创建服务接口(示例 `service/js/marked/MarkedService.java`),定义您需要的操作。
78+
- 使用 Spring `@Service` 类实现接口,该类使用现有的 `V8EnginePoolService` 对运行时执行调用,类似于 `ShikiHighlightServiceImpl`
79+
- 优先读取 `globalThis` 函数(例如 `parseMarkdown`)或 `globalThis.<lib>.parse`
80+
81+
### 验证
82+
83+
- 使用 `V8EnginePoolService.executeScript``withEngine` 调用函数并验证结果。
84+
-`CustomJavetEngine.preloadModules()` 中添加快速布尔检查,如 `typeof parseMarkdown === 'function'` 并记录结果。
85+
86+
### 模块类型和自定义解析器
87+
88+
- JsModuleType.UMD:直接执行脚本(UMD 通常附加到 globalThis)。
89+
- JsModuleType.ESM:`CustomV8ModuleResolver` 编译并为 ESM 模块返回 IV8Module。
90+
- JsModuleType.CJS:CommonJS 模块使用模拟的 `module.exports` 对象执行,导出作为模块对象返回。
91+
92+
### 构建和测试
93+
94+
- 从项目根目录运行:
95+
96+
```cmd
97+
gradlew.bat clean assemble -x test
98+
```
99+
100+
- 如果编译成功,通过启动主机应用程序或执行新服务的单元测试来在运行时测试功能。
101+
102+
### 注意事项和技巧
103+
104+
- 保持嵌入的 JS 文件相对较小,以保持 jar 大小可管理。
105+
- 优先选择精简或压缩的 UMD 构建版本进行嵌入。
106+
- 如果库暴露异步 API(promises),Java 实现应该使用 Javet Promise 助手或 V8ValuePromise 轮询来等待 promise 结果。
107+
- 添加单元测试,使用模拟或引擎池来验证解析/高亮行为。

0 commit comments

Comments
 (0)