@mlightcad/cad-simple-viewer-cli 1.7.4 → 1.7.6-preview.0
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 +5 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/headlessGpu.js +129 -0
- package/dist/headlessGpu.js.map +1 -0
- package/dist/runHeadless.js +248 -135
- package/dist/runHeadless.js.map +1 -1
- package/dist-runner/assets/cad-html-plugin-BubTPQfK.js +1564 -0
- package/dist-runner/assets/{cad-pdf-plugin-CJkwEiNn.js → cad-pdf-plugin-DZizxUR8.js} +69 -69
- package/dist-runner/assets/cad-svg-plugin-B8nYzClc.js +16 -0
- package/dist-runner/assets/{index-C09XPdM4.js → index-zOqzz2XY.js} +306 -270
- package/dist-runner/index.html +1 -1
- package/dist-runner/viewer-runtime.iife.js +219 -204
- package/dist-runner/workers/libredwg-parser-worker.js +20 -24
- package/dist-runner/workers/libredwg-web.wasm +0 -0
- package/dist-runner/workers/mtext-renderer-worker.js +2987 -2815
- package/examples/batch-export-html-multi-preview.mjs +117 -47
- package/examples/open-quit.scr +2 -0
- package/package.json +10 -10
- package/dist-runner/assets/cad-html-plugin-dtjd-0PT.js +0 -1564
- package/dist-runner/assets/cad-svg-plugin-DxTNeZjG.js +0 -16
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
* extension, with path separators turned into underscores. Same-stem
|
|
15
15
|
* `.dwg`/`.dxf` pairs get a `_dwg` / `_dxf` suffix so they do not collide.
|
|
16
16
|
*
|
|
17
|
+
* Reuses one Chromium session across all drawings (WASM / fonts stay warm).
|
|
18
|
+
*
|
|
17
19
|
* Usage (from packages/cad-simple-viewer-cli after build):
|
|
18
20
|
* node examples/batch-export-html-multi-preview.mjs <inputDir> <outputDir>
|
|
19
21
|
*/
|
|
20
|
-
import {
|
|
22
|
+
import { spawnSync } from 'node:child_process'
|
|
21
23
|
import { existsSync } from 'node:fs'
|
|
22
24
|
import {
|
|
23
25
|
copyFile,
|
|
26
|
+
cp,
|
|
24
27
|
mkdir,
|
|
25
28
|
mkdtemp,
|
|
26
29
|
readdir,
|
|
@@ -29,11 +32,11 @@ import {
|
|
|
29
32
|
} from 'node:fs/promises'
|
|
30
33
|
import os from 'node:os'
|
|
31
34
|
import path from 'node:path'
|
|
32
|
-
import { fileURLToPath } from 'node:url'
|
|
35
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
33
36
|
|
|
34
37
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
35
38
|
const packageRoot = path.resolve(__dirname, '..')
|
|
36
|
-
const
|
|
39
|
+
const runHeadlessJs = path.join(packageRoot, 'dist', 'runHeadless.js')
|
|
37
40
|
const scriptPath = path.join(__dirname, 'export-html-multi-preview.scr')
|
|
38
41
|
|
|
39
42
|
const DRAWING_EXT = new Set(['.dwg', '.dxf'])
|
|
@@ -89,34 +92,86 @@ function drawingFolderName(inputDir, drawingPath, used) {
|
|
|
89
92
|
return name
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
function
|
|
93
|
-
return new Promise((resolve,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
95
|
+
function delay(ms) {
|
|
96
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const REPLACE_DIR_RETRY = new Set([
|
|
100
|
+
'EPERM',
|
|
101
|
+
'EACCES',
|
|
102
|
+
'EBUSY',
|
|
103
|
+
'ENOTEMPTY',
|
|
104
|
+
'EMFILE',
|
|
105
|
+
'ENFILE'
|
|
106
|
+
])
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Move `src` onto `dest`. Windows often returns EPERM from rename while
|
|
110
|
+
* Defender or a file watcher still has a file inside a freshly unpacked
|
|
111
|
+
* directory open (sharing without delete). Retry, then copy into place.
|
|
112
|
+
*/
|
|
113
|
+
async function replaceDir(src, dest) {
|
|
114
|
+
const waits = [0, 50, 100, 200, 400, 800, 1600]
|
|
115
|
+
let lastError
|
|
116
|
+
for (const wait of waits) {
|
|
117
|
+
if (wait) await delay(wait)
|
|
118
|
+
try {
|
|
119
|
+
await rm(dest, {
|
|
120
|
+
recursive: true,
|
|
121
|
+
force: true,
|
|
122
|
+
maxRetries: 5,
|
|
123
|
+
retryDelay: 100
|
|
124
|
+
})
|
|
125
|
+
await rename(src, dest)
|
|
126
|
+
return
|
|
127
|
+
} catch (error) {
|
|
128
|
+
lastError = error
|
|
129
|
+
const code = error && error.code
|
|
130
|
+
if (code === 'EXDEV') break
|
|
131
|
+
if (!REPLACE_DIR_RETRY.has(code)) throw error
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let copyError
|
|
136
|
+
for (const wait of [0, 200, 500, 1000, 2000]) {
|
|
137
|
+
if (wait) await delay(wait)
|
|
138
|
+
try {
|
|
139
|
+
await rm(dest, {
|
|
140
|
+
recursive: true,
|
|
141
|
+
force: true,
|
|
142
|
+
maxRetries: 5,
|
|
143
|
+
retryDelay: 100
|
|
144
|
+
})
|
|
145
|
+
await cp(src, dest, { recursive: true })
|
|
146
|
+
copyError = undefined
|
|
147
|
+
break
|
|
148
|
+
} catch (error) {
|
|
149
|
+
copyError = error
|
|
150
|
+
const code = error && error.code
|
|
151
|
+
if (code !== 'EXDEV' && !REPLACE_DIR_RETRY.has(code)) break
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (copyError) {
|
|
155
|
+
await rm(dest, { recursive: true, force: true }).catch(() => {})
|
|
156
|
+
const renameMsg =
|
|
157
|
+
lastError instanceof Error ? lastError.message : String(lastError)
|
|
158
|
+
const copyMsg =
|
|
159
|
+
copyError instanceof Error ? copyError.message : String(copyError)
|
|
160
|
+
throw new Error(
|
|
161
|
+
`Failed to move ${src} to ${dest} (${renameMsg}); copy fallback failed: ${copyMsg}`
|
|
108
162
|
)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
117
|
-
)
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
await rm(src, {
|
|
166
|
+
recursive: true,
|
|
167
|
+
force: true,
|
|
168
|
+
maxRetries: 8,
|
|
169
|
+
retryDelay: 200
|
|
118
170
|
})
|
|
119
|
-
})
|
|
171
|
+
} catch (error) {
|
|
172
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
173
|
+
console.warn(`Could not remove temporary directory ${src}: ${message}`)
|
|
174
|
+
}
|
|
120
175
|
}
|
|
121
176
|
|
|
122
177
|
function unzipWithTar(zipPath, destDir) {
|
|
@@ -162,10 +217,14 @@ async function packageDrawing(workDir, targetDir, sourceDrawing) {
|
|
|
162
217
|
const destName = ext === '.dxf' ? 'drawing.dxf' : 'drawing.dwg'
|
|
163
218
|
await copyFile(sourceDrawing, path.join(stageDir, destName))
|
|
164
219
|
|
|
165
|
-
await
|
|
166
|
-
await rename(stageDir, targetDir)
|
|
220
|
+
await replaceDir(stageDir, targetDir)
|
|
167
221
|
} catch (error) {
|
|
168
|
-
await rm(stageDir, {
|
|
222
|
+
await rm(stageDir, {
|
|
223
|
+
recursive: true,
|
|
224
|
+
force: true,
|
|
225
|
+
maxRetries: 5,
|
|
226
|
+
retryDelay: 100
|
|
227
|
+
}).catch(() => {})
|
|
169
228
|
throw error
|
|
170
229
|
}
|
|
171
230
|
}
|
|
@@ -181,7 +240,7 @@ async function main() {
|
|
|
181
240
|
process.exitCode = 1
|
|
182
241
|
return
|
|
183
242
|
}
|
|
184
|
-
if (!existsSync(
|
|
243
|
+
if (!existsSync(runHeadlessJs)) {
|
|
185
244
|
console.error(
|
|
186
245
|
'CLI not built. Run: pnpm --filter @mlightcad/cad-simple-viewer-cli build'
|
|
187
246
|
)
|
|
@@ -208,25 +267,36 @@ async function main() {
|
|
|
208
267
|
`Found ${drawings.length} drawing(s). Output (demo-drawings layout): ${outputDir}`
|
|
209
268
|
)
|
|
210
269
|
|
|
270
|
+
const { HeadlessCadSession } = await import(pathToFileURL(runHeadlessJs).href)
|
|
271
|
+
const session = await HeadlessCadSession.create()
|
|
211
272
|
const usedFolders = new Set()
|
|
212
273
|
let failed = 0
|
|
213
274
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
275
|
+
try {
|
|
276
|
+
for (const drawing of drawings) {
|
|
277
|
+
const folder = drawingFolderName(inputDir, drawing, usedFolders)
|
|
278
|
+
const targetDir = path.join(outputDir, folder)
|
|
279
|
+
console.log(`\n=== ${drawing} → ${folder}/ ===`)
|
|
218
280
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
281
|
+
const workDir = await mkdtemp(path.join(os.tmpdir(), 'cad-cli-demo-'))
|
|
282
|
+
try {
|
|
283
|
+
await session.run({
|
|
284
|
+
inputPath: drawing,
|
|
285
|
+
scriptPath,
|
|
286
|
+
outputDir: workDir,
|
|
287
|
+
mode: 'read'
|
|
288
|
+
})
|
|
289
|
+
await packageDrawing(workDir, targetDir, drawing)
|
|
290
|
+
console.log(`Packaged ${targetDir}`)
|
|
291
|
+
} catch (error) {
|
|
292
|
+
failed++
|
|
293
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
294
|
+
} finally {
|
|
295
|
+
await rm(workDir, { recursive: true, force: true })
|
|
296
|
+
}
|
|
229
297
|
}
|
|
298
|
+
} finally {
|
|
299
|
+
await session.close()
|
|
230
300
|
}
|
|
231
301
|
|
|
232
302
|
console.log(`\nDone. success=${drawings.length - failed} failed=${failed}`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mlightcad/cad-simple-viewer-cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.6-preview.0",
|
|
4
4
|
"description": "AcCoreConsole-style headless CLI: run .scr command scripts against DXF/DWG drawings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"script"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@mlightcad/data-model": "^1.15.
|
|
46
|
-
"@mlightcad/libredwg-converter": "^3.15.
|
|
47
|
-
"@mlightcad/mtext-parser": "^1.5.
|
|
48
|
-
"@mlightcad/mtext-renderer": "^0.13.
|
|
45
|
+
"@mlightcad/data-model": "^1.15.7",
|
|
46
|
+
"@mlightcad/libredwg-converter": "^3.15.7",
|
|
47
|
+
"@mlightcad/mtext-parser": "^1.5.5",
|
|
48
|
+
"@mlightcad/mtext-renderer": "^0.13.4",
|
|
49
49
|
"commander": "^12.1.0",
|
|
50
50
|
"playwright": "^1.49.1",
|
|
51
|
-
"@mlightcad/cad-html-plugin": "1.7.
|
|
52
|
-
"@mlightcad/pdf-
|
|
53
|
-
"@mlightcad/cad-simple-viewer": "1.7.
|
|
54
|
-
"@mlightcad/
|
|
55
|
-
"@mlightcad/cad-svg-plugin": "1.7.
|
|
51
|
+
"@mlightcad/cad-html-plugin": "1.7.6-preview.0",
|
|
52
|
+
"@mlightcad/cad-pdf-plugin": "1.7.6-preview.0",
|
|
53
|
+
"@mlightcad/cad-simple-viewer": "1.7.6-preview.0",
|
|
54
|
+
"@mlightcad/pdf-renderer": "1.7.6-preview.0",
|
|
55
|
+
"@mlightcad/cad-svg-plugin": "1.7.6-preview.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@types/node": "^20.14.9",
|