@lvce-editor/shared-process 0.125.15 → 0.125.16
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/index.js +3 -2
- package/package.json +2 -1
- package/src/parts/BuiltinExtensionsPath/BuiltinExtensionsPath.js +1 -1
- package/src/parts/BundleStaticWorkers/BundleStaticWorkers.js +88 -0
- package/src/parts/BundledWorkerRuntime/BundledWorkerRuntime.js +183 -0
- package/src/parts/ExportStatic/ExportStatic.js +7 -1
- package/src/parts/Platform/Platform.js +3 -3
- package/src/parts/PreloadUrl/PreloadUrl.js +1 -1
package/index.js
CHANGED
|
@@ -22,10 +22,10 @@ const toAbsoluteExtensionPath = (root, extensionPath) => {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* @param {{extensionPath?: string, extensionPaths?: string[] | string, onLoadCommands?: readonly unknown[], cspOptions?: {allowExternalConnections?: boolean}, testPath?: string, root?: string}} [options]
|
|
25
|
+
* @param {{bundleMode?: boolean, extensionPath?: string, extensionPaths?: string[] | string, onLoadCommands?: readonly unknown[], cspOptions?: {allowExternalConnections?: boolean}, testPath?: string, root?: string}} [options]
|
|
26
26
|
*/
|
|
27
27
|
export const exportStatic = async (options = {}) => {
|
|
28
|
-
const { cspOptions, extensionPath, extensionPaths = [], onLoadCommands = [], root = '', testPath = '' } = options
|
|
28
|
+
const { bundleMode = false, cspOptions, extensionPath, extensionPaths = [], onLoadCommands = [], root = '', testPath = '' } = options
|
|
29
29
|
if (!root) {
|
|
30
30
|
throw new Error(`root argument is required`)
|
|
31
31
|
}
|
|
@@ -36,6 +36,7 @@ export const exportStatic = async (options = {}) => {
|
|
|
36
36
|
const absoluteExtensionPaths = extensionPathList.map((extensionPath) => toAbsoluteExtensionPath(root, extensionPath))
|
|
37
37
|
const pathPrefix = process.env.PATH_PREFIX || ''
|
|
38
38
|
return fn.exportStatic({
|
|
39
|
+
bundleMode,
|
|
39
40
|
cspOptions,
|
|
40
41
|
extensionPath: absoluteExtensionPath,
|
|
41
42
|
extensionPaths: absoluteExtensionPaths,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/shared-process",
|
|
3
|
-
"version": "0.125.
|
|
3
|
+
"version": "0.125.16",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@lvce-editor/rpc-registry": "9.66.2",
|
|
28
28
|
"@lvce-editor/verror": "1.7.0",
|
|
29
29
|
"is-object": "^1.0.2",
|
|
30
|
+
"rollup": "^4.63.6",
|
|
30
31
|
"xdg-basedir": "^5.1.0"
|
|
31
32
|
},
|
|
32
33
|
"optionalDependencies": {
|
|
@@ -3,6 +3,6 @@ import { fileURLToPath } from 'url'
|
|
|
3
3
|
|
|
4
4
|
export const getBuiltinExtensionsPath = () => {
|
|
5
5
|
const staticServerPath = fileURLToPath(import.meta.resolve('@lvce-editor/static-server'))
|
|
6
|
-
const builtinExtensionsPath = join(staticServerPath, '..', '..', 'static', '
|
|
6
|
+
const builtinExtensionsPath = join(staticServerPath, '..', '..', 'static', 'b9e03b1', 'extensions')
|
|
7
7
|
return builtinExtensionsPath
|
|
8
8
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { rollup } from 'rollup'
|
|
4
|
+
import { createBundledWorkerConstructor } from '../BundledWorkerRuntime/BundledWorkerRuntime.js'
|
|
5
|
+
|
|
6
|
+
const mainCall = /^(?:await\s+)?main\(\);?$/m
|
|
7
|
+
const awaitedListen = /^await listen\(\);?$/m
|
|
8
|
+
|
|
9
|
+
const workerIds = ['editor', 'extensionManagementWorker', 'iconThemeWorker', 'cacheWorker', 'explorer']
|
|
10
|
+
const bindings = [
|
|
11
|
+
'globalThis',
|
|
12
|
+
'self',
|
|
13
|
+
'Worker',
|
|
14
|
+
'WorkerGlobalScope',
|
|
15
|
+
'MessageChannel',
|
|
16
|
+
'location',
|
|
17
|
+
'name',
|
|
18
|
+
'postMessage',
|
|
19
|
+
'addEventListener',
|
|
20
|
+
'removeEventListener',
|
|
21
|
+
'close',
|
|
22
|
+
'setTimeout',
|
|
23
|
+
'clearTimeout',
|
|
24
|
+
'setInterval',
|
|
25
|
+
'clearInterval',
|
|
26
|
+
'requestAnimationFrame',
|
|
27
|
+
'cancelAnimationFrame',
|
|
28
|
+
'fetch',
|
|
29
|
+
'window',
|
|
30
|
+
'document',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
export const createWorkerFactory = async (input, publicPath) => {
|
|
34
|
+
const bundle = await rollup({
|
|
35
|
+
input,
|
|
36
|
+
plugins: [
|
|
37
|
+
{
|
|
38
|
+
name: 'bundled-worker-scope',
|
|
39
|
+
resolveImportMeta(property) {
|
|
40
|
+
if (property === 'url') return 'globalThis.location.href'
|
|
41
|
+
throw new Error(`Unsupported worker import.meta property: ${property}`)
|
|
42
|
+
},
|
|
43
|
+
transform(code, id) {
|
|
44
|
+
if (id.replaceAll('\\', '/') === input.replaceAll('\\', '/')) {
|
|
45
|
+
// Capture rejected startup rather than leaving the parent's ready wait hanging.
|
|
46
|
+
if (awaitedListen.test(code)) return null
|
|
47
|
+
if (!mainCall.test(code)) throw new Error(`Missing worker startup call: ${input}`)
|
|
48
|
+
return code.replace(mainCall, 'const bundledStartupPromise = main();') + '\nawait bundledStartupPromise;\n'
|
|
49
|
+
}
|
|
50
|
+
return null
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
preserveEntrySignatures: false,
|
|
55
|
+
})
|
|
56
|
+
try {
|
|
57
|
+
const { output } = await bundle.generate({ format: 'es', inlineDynamicImports: true })
|
|
58
|
+
const chunk = output[0]
|
|
59
|
+
if (chunk.type !== 'chunk' || chunk.imports.length || chunk.exports.length) throw new Error(`Worker must be self-contained: ${input}`)
|
|
60
|
+
return `[new URL(${JSON.stringify(publicPath)}, location.origin).href]: async (scope) => {\nconst { ${bindings.join(', ')} } = scope;\nawait (async () => {\n${chunk.code}\n})();\n}`
|
|
61
|
+
} finally {
|
|
62
|
+
await bundle.close()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const bundleStaticWorkers = async ({ commitHash, pathPrefix, root, workers } ) => {
|
|
67
|
+
const paths = [
|
|
68
|
+
'/packages/renderer-worker/dist/rendererWorkerMain.js',
|
|
69
|
+
...workerIds.map((id) => {
|
|
70
|
+
const worker = workers.find((item) => item.id === id)
|
|
71
|
+
if (!worker) throw new Error(`Missing bundled worker: ${id}`)
|
|
72
|
+
return worker.productionPath
|
|
73
|
+
}),
|
|
74
|
+
]
|
|
75
|
+
const factories = []
|
|
76
|
+
for (const path of paths) {
|
|
77
|
+
const publicPath = `${pathPrefix}/${commitHash}${path}`
|
|
78
|
+
factories.push(await createWorkerFactory(join(root, 'dist', commitHash, path), publicPath))
|
|
79
|
+
}
|
|
80
|
+
const rendererDirectory = join(root, 'dist', commitHash, 'packages/renderer-process/dist')
|
|
81
|
+
const renderer = await readFile(join(rendererDirectory, 'rendererProcessMain.js'), 'utf8')
|
|
82
|
+
const runtime = `const Worker = (${createBundledWorkerConstructor.toString()})({${factories.join(',\n')}}, globalThis);\n`
|
|
83
|
+
await writeFile(join(rendererDirectory, 'renderer-process.bundled.js'), runtime + renderer)
|
|
84
|
+
const htmlPath = join(root, 'dist', 'index.html')
|
|
85
|
+
const html = await readFile(htmlPath, 'utf8')
|
|
86
|
+
if (!html.includes('/rendererProcessMain.js')) throw new Error('Missing renderer script in static export')
|
|
87
|
+
await writeFile(htmlPath, html.replaceAll('/rendererProcessMain.js', '/renderer-process.bundled.js'))
|
|
88
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// This function is serialized into the static export. Keep it self-contained.
|
|
2
|
+
export const createBundledWorkerConstructor = (factories, host) => {
|
|
3
|
+
const NativeWorker = host.Worker
|
|
4
|
+
class BundledWorker extends host.EventTarget {
|
|
5
|
+
static [Symbol.hasInstance](value) {
|
|
6
|
+
return value instanceof NativeWorker || Object.prototype.isPrototypeOf.call(this.prototype, value)
|
|
7
|
+
}
|
|
8
|
+
onmessage = null
|
|
9
|
+
onmessageerror = null
|
|
10
|
+
onerror = null
|
|
11
|
+
stopped = false
|
|
12
|
+
ports = new Set ()
|
|
13
|
+
timers = new Map ()
|
|
14
|
+
frames = new Map ()
|
|
15
|
+
children = new Set ()
|
|
16
|
+
abortController = new host.AbortController()
|
|
17
|
+
endpoint
|
|
18
|
+
|
|
19
|
+
constructor(url, options = {}) {
|
|
20
|
+
super()
|
|
21
|
+
const resolved = new URL(url, host.location.href)
|
|
22
|
+
const factoryUrl = new URL(resolved)
|
|
23
|
+
factoryUrl.search = ''
|
|
24
|
+
factoryUrl.hash = ''
|
|
25
|
+
const factory = factories[resolved.href] || factories[factoryUrl.href]
|
|
26
|
+
if (!factory || options.type !== 'module') {
|
|
27
|
+
return new NativeWorker(url, options)
|
|
28
|
+
}
|
|
29
|
+
const channel = new host.MessageChannel()
|
|
30
|
+
this.endpoint = channel.port1
|
|
31
|
+
this.ports.add(channel.port1)
|
|
32
|
+
this.ports.add(channel.port2)
|
|
33
|
+
const dispatch = (event) => {
|
|
34
|
+
if (this.stopped) return
|
|
35
|
+
const copy = new host.MessageEvent(event.type, { data: event.data, ports: event.ports })
|
|
36
|
+
this.dispatchEvent(copy)
|
|
37
|
+
const listener = event.type === 'message' ? this.onmessage : this.onmessageerror
|
|
38
|
+
listener?.call(this, copy)
|
|
39
|
+
}
|
|
40
|
+
channel.port1.addEventListener('message', dispatch)
|
|
41
|
+
channel.port1.addEventListener('messageerror', dispatch)
|
|
42
|
+
channel.port1.start()
|
|
43
|
+
// The nested Worker/MessageChannel constructors have their own this.
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
45
|
+
const owner = this
|
|
46
|
+
const schedule =
|
|
47
|
+
(start, stop, repeat, registry = owner.timers) =>
|
|
48
|
+
(callback, delay, ...args) => {
|
|
49
|
+
if (owner.stopped) return 0
|
|
50
|
+
const id = start((...callbackArgs) => {
|
|
51
|
+
if (!repeat) registry.delete(id)
|
|
52
|
+
if (!owner.stopped) callback(...callbackArgs, ...args)
|
|
53
|
+
}, delay)
|
|
54
|
+
registry.set(id, stop)
|
|
55
|
+
return id
|
|
56
|
+
}
|
|
57
|
+
const clear = (id) => {
|
|
58
|
+
owner.timers.get(id)?.(id)
|
|
59
|
+
owner.timers.delete(id)
|
|
60
|
+
}
|
|
61
|
+
const trackedPorts = new WeakSet ()
|
|
62
|
+
const trackPort = (port) => {
|
|
63
|
+
if (owner.stopped) {
|
|
64
|
+
port.close()
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
if (trackedPorts.has(port)) return
|
|
68
|
+
trackedPorts.add(port)
|
|
69
|
+
owner.ports.add(port)
|
|
70
|
+
const close = port.close.bind(port)
|
|
71
|
+
const postMessage = port.postMessage.bind(port)
|
|
72
|
+
port.close = () => {
|
|
73
|
+
owner.ports.delete(port)
|
|
74
|
+
close()
|
|
75
|
+
}
|
|
76
|
+
port.postMessage = (message, options) => {
|
|
77
|
+
postMessage(message, options)
|
|
78
|
+
const transfer = Array.isArray(options) ? options : options?.transfer || []
|
|
79
|
+
for (const item of transfer) owner.ports.delete(item)
|
|
80
|
+
}
|
|
81
|
+
port.addEventListener('message', (event) => {
|
|
82
|
+
for (const transferred of event.ports) trackPort(transferred)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
trackPort(channel.port2)
|
|
86
|
+
const scope = {
|
|
87
|
+
addEventListener: channel.port2.addEventListener.bind(channel.port2),
|
|
88
|
+
cancelAnimationFrame: (id) => {
|
|
89
|
+
owner.frames.get(id)?.(id)
|
|
90
|
+
owner.frames.delete(id)
|
|
91
|
+
},
|
|
92
|
+
clearInterval: clear,
|
|
93
|
+
clearTimeout: clear,
|
|
94
|
+
close: () => owner.terminate(),
|
|
95
|
+
document: undefined,
|
|
96
|
+
fetch: (input, init) => {
|
|
97
|
+
const signal = init?.signal || input?.signal
|
|
98
|
+
return host.fetch(typeof input === 'string' ? new URL(input, resolved) : input, {
|
|
99
|
+
...init,
|
|
100
|
+
signal: signal ? host.AbortSignal.any([signal, owner.abortController.signal]) : owner.abortController.signal,
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
location: resolved,
|
|
104
|
+
MessageChannel: class extends host.MessageChannel {
|
|
105
|
+
constructor() {
|
|
106
|
+
super()
|
|
107
|
+
trackPort(this.port1)
|
|
108
|
+
trackPort(this.port2)
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
name: options.name || '',
|
|
112
|
+
postMessage: channel.port2.postMessage.bind(channel.port2),
|
|
113
|
+
removeEventListener: channel.port2.removeEventListener.bind(channel.port2),
|
|
114
|
+
requestAnimationFrame: schedule(host.requestAnimationFrame.bind(host), host.cancelAnimationFrame.bind(host), false, owner.frames),
|
|
115
|
+
setInterval: schedule(host.setInterval.bind(host), host.clearInterval.bind(host), true),
|
|
116
|
+
setTimeout: schedule(host.setTimeout.bind(host), host.clearTimeout.bind(host), false),
|
|
117
|
+
window: undefined,
|
|
118
|
+
Worker: class extends BundledWorker {
|
|
119
|
+
constructor(childUrl, childOptions) {
|
|
120
|
+
super(new URL(childUrl, resolved), childOptions)
|
|
121
|
+
if (owner.stopped) this.terminate()
|
|
122
|
+
else owner.children.add(this)
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
WorkerGlobalScope: class WorkerGlobalScope {},
|
|
126
|
+
}
|
|
127
|
+
Object.defineProperty(scope, 'onmessage', {
|
|
128
|
+
get: () => channel.port2.onmessage,
|
|
129
|
+
set: (listener) => {
|
|
130
|
+
channel.port2.onmessage = listener
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
const global = new Proxy(scope, {
|
|
134
|
+
get(target, key) {
|
|
135
|
+
if (key in target) return target[key]
|
|
136
|
+
return host[key]
|
|
137
|
+
},
|
|
138
|
+
has: (target, key) => key in target || key in host,
|
|
139
|
+
})
|
|
140
|
+
scope.globalThis = global
|
|
141
|
+
scope.self = global
|
|
142
|
+
channel.port2.start()
|
|
143
|
+
// Start after callers have installed readiness/error listeners.
|
|
144
|
+
queueMicrotask(() => {
|
|
145
|
+
if (owner.stopped) return
|
|
146
|
+
Promise.resolve()
|
|
147
|
+
.then(() => factory(global))
|
|
148
|
+
.catch((error) => {
|
|
149
|
+
if (owner.stopped) return
|
|
150
|
+
const event = new host.ErrorEvent('error', { error, filename: resolved.href, message: String(error) })
|
|
151
|
+
try {
|
|
152
|
+
owner.dispatchEvent(event)
|
|
153
|
+
owner.onerror?.call(owner, event)
|
|
154
|
+
} finally {
|
|
155
|
+
owner.terminate()
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
postMessage(message, transfer) {
|
|
162
|
+
if (!this.stopped) this.endpoint.postMessage(message, transfer)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
terminate() {
|
|
166
|
+
if (this.stopped) return
|
|
167
|
+
this.stopped = true
|
|
168
|
+
this.abortController.abort()
|
|
169
|
+
for (const child of this.children) child.terminate()
|
|
170
|
+
for (const [id, cancel] of this.timers) cancel(id)
|
|
171
|
+
for (const [id, cancel] of this.frames) cancel(id)
|
|
172
|
+
for (const port of this.ports) port.close()
|
|
173
|
+
this.children.clear()
|
|
174
|
+
this.timers.clear()
|
|
175
|
+
this.frames.clear()
|
|
176
|
+
this.ports.clear()
|
|
177
|
+
this.onmessage = null
|
|
178
|
+
this.onmessageerror = null
|
|
179
|
+
this.onerror = null
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return BundledWorker
|
|
183
|
+
}
|
|
@@ -756,9 +756,10 @@ const getExtensionPaths = (extensionPath, extensionPaths) => {
|
|
|
756
756
|
|
|
757
757
|
/**
|
|
758
758
|
*
|
|
759
|
-
* @param {{root:string, pathPrefix:string, extensionPath:string, extensionPaths?:string[], onLoadCommands?:readonly unknown[], cspOptions?: {allowExternalConnections?: boolean}, testPath:string, useSimpleWebExtensionFile?:boolean, serverStaticPath?:string }} param0
|
|
759
|
+
* @param {{root:string, pathPrefix:string, extensionPath:string, extensionPaths?:string[], onLoadCommands?:readonly unknown[], cspOptions?: {allowExternalConnections?: boolean}, testPath:string, useSimpleWebExtensionFile?:boolean, serverStaticPath?:string, bundleMode?:boolean }} param0
|
|
760
760
|
*/
|
|
761
761
|
export const exportStatic = async ({
|
|
762
|
+
bundleMode = false,
|
|
762
763
|
cspOptions = {},
|
|
763
764
|
extensionPath,
|
|
764
765
|
extensionPaths,
|
|
@@ -814,6 +815,11 @@ export const exportStatic = async ({
|
|
|
814
815
|
console.timeEnd('applyOverrides')
|
|
815
816
|
|
|
816
817
|
await copyBuiltinWebExtensions({ commitHash, pathPrefix, root })
|
|
818
|
+
|
|
819
|
+
if (bundleMode) {
|
|
820
|
+
const BundleStaticWorkers = await import('../BundleStaticWorkers/BundleStaticWorkers.js')
|
|
821
|
+
await BundleStaticWorkers.bundleStaticWorkers({ commitHash, pathPrefix, root, workers })
|
|
822
|
+
}
|
|
817
823
|
await updateExtensionsJson({ commitHash, extraExtensions: [], pathPrefix, root })
|
|
818
824
|
await JsonFile.writeJson(Path.join(root, 'dist', commitHash, 'config', 'onLoadCommands.json'), onLoadCommands)
|
|
819
825
|
|
|
@@ -56,11 +56,11 @@ export const getSetupName = () => {
|
|
|
56
56
|
return 'Lvce-Setup'
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export const version = '0.125.
|
|
59
|
+
export const version = '0.125.16'
|
|
60
60
|
|
|
61
|
-
export const commit = '
|
|
61
|
+
export const commit = 'b9e03b1'
|
|
62
62
|
|
|
63
|
-
export const date = '2026-10-
|
|
63
|
+
export const date = '2026-10-24T21:14:34.000Z'
|
|
64
64
|
|
|
65
65
|
export const getVersion = () => {
|
|
66
66
|
return version
|
|
@@ -2,5 +2,5 @@ import { join } from 'node:path'
|
|
|
2
2
|
import * as Root from '../Root/Root.js'
|
|
3
3
|
|
|
4
4
|
export const getPreloadUrl = () => {
|
|
5
|
-
return join(Root.root, 'static', '
|
|
5
|
+
return join(Root.root, 'static', 'b9e03b1', 'packages', 'preload', 'dist', 'index.js')
|
|
6
6
|
}
|