导出优化

This commit is contained in:
tianmo committed 2026-09-05 18:45:43 +08:00
1 parent 72d8daa283
commit 79ce3165c1
17 files changed
+1559 -1291

No files matched your search

+10 -6
View File
@@ -3,6 +3,7 @@ import { constants } from 'node:fs'
import { homedir, platform } from 'node:os'
import { join } from 'node:path'
import { execFile } from 'node:child_process'
import { createSpine38ImportOptions } from './spine38-project.mjs'
const SUPPORTED_VERSIONS = new Set(['3.8', '4.0', '4.1', '4.2', '4.3'])
@@ -42,9 +43,9 @@ export function normalizeSpineVersion(value) {
return match[1]
}
function run(executablePath, args, timeoutMs = 180000) {
function run(executablePath, args, { cwd, timeoutMs = 180000 } = {}) {
return new Promise((resolve, reject) => {
execFile(executablePath, args, { timeout: timeoutMs, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
execFile(executablePath, args, { cwd, timeout: timeoutMs, windowsHide: true, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
if (error) {
const details = [stdout, stderr, error.message]
.filter(Boolean)
@@ -59,15 +60,18 @@ function run(executablePath, args, timeoutMs = 180000) {
})
}
export async function importSpineProject({ executablePath, inputPath, outputPath, projectName, spineVersion }) {
export async function importSpineProject({ executablePath, inputPath, outputPath, projectName, spineVersion, workspaceRoot }) {
const version = normalizeSpineVersion(spineVersion)
const editorVersion = `${version}.xx`
const importOptions = version === '3.8'
? createSpine38ImportOptions({ workspaceRoot, inputPath, outputPath })
: { inputPath, outputPath }
const result = await run(executablePath, [
'--update', editorVersion,
'--input', inputPath,
'--output', outputPath,
'--input', importOptions.inputPath,
'--output', importOptions.outputPath,
'--import', projectName,
])
], { cwd: importOptions.cwd })
const output = await stat(outputPath).catch(() => null)
if (!output?.isFile() || output.size === 0) throw new Error('Spine 未生成有效的工程文件')
const detected = /Starting:\s+Spine\s+([0-9.]+)/i.exec(`${result.stdout}\n${result.stderr}`)?.[1] || editorVersion
+1
View File
@@ -105,6 +105,7 @@ const server = createServer(async (request, response) => {
outputPath: workspace.outputPath,
projectName: workspace.projectName,
spineVersion,
workspaceRoot: workspace.root,
})
const project = await workspace.readOutput()
const projectFileName = `${safeProjectName(workspace.projectName)}.spine`
+6 -1
View File
@@ -1,6 +1,7 @@
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join, normalize, sep } from 'node:path'
import { prepareSpine38SkeletonJson } from './spine38-project.mjs'
const MAX_JSON_BYTES = 64 * 1024 * 1024
const MAX_IMAGE_BYTES = 32 * 1024 * 1024
@@ -49,7 +50,11 @@ export async function createConversionWorkspace(payload) {
const projectName = safeProjectName(payload.projectName)
const inputPath = join(root, `${projectName}.json`)
const outputPath = join(root, `${projectName}.spine`)
await writeFile(inputPath, payload.skeletonJson, 'utf8')
const importJson = spineVersion === '3.8'
? prepareSpine38SkeletonJson(skeleton, imagesDirectory)
: payload.skeletonJson
await mkdir(join(root, imagesDirectory), { recursive: true })
await writeFile(inputPath, importJson, 'utf8')
let total = 0
const archiveImages = []
for (const image of Array.isArray(payload.images) ? payload.images : []) {
+34
View File
@@ -0,0 +1,34 @@
import { relative, sep } from 'node:path'
function relativeWorkspacePath(workspaceRoot, targetPath) {
const path = relative(workspaceRoot, targetPath)
if (!path || path === '..' || path.startsWith(`..${sep}`)) {
throw new Error('Spine 3.8 工程文件必须位于连接器工作目录内')
}
return path
}
export function normalizeSpine38ImagesPath(imagesDirectory) {
const path = String(imagesDirectory || 'images')
.replace(/\\/g, '/')
.replace(/^\.\/+/, '')
.replace(/\/+$/, '')
return path || 'images'
}
export function prepareSpine38SkeletonJson(skeleton, imagesDirectory) {
const prepared = JSON.parse(JSON.stringify(skeleton))
prepared.skeleton ||= {}
// Spine 3.8 的命令行导入器对 "./images/" 的处理并不稳定,某些工程会
// 将其固化为导入时的临时绝对路径。使用无前缀、无尾斜杠的工程内路径。
prepared.skeleton.images = normalizeSpine38ImagesPath(imagesDirectory)
return `${JSON.stringify(prepared, null, 2)}\n`
}
export function createSpine38ImportOptions({ workspaceRoot, inputPath, outputPath }) {
return {
cwd: workspaceRoot,
inputPath: relativeWorkspacePath(workspaceRoot, inputPath),
outputPath: relativeWorkspacePath(workspaceRoot, outputPath),
}
}