feat: support for node worker (#51)
This commit is contained in:
parent
bf0c83835a
commit
28d069c453
5
node.d.ts
vendored
Normal file
5
node.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// node worker
|
||||||
|
declare module '*?nodeWorker' {
|
||||||
|
import { Worker, WorkerOptions } from 'node:worker_threads'
|
||||||
|
export default function (options: WorkerOptions): Worker
|
||||||
|
}
|
|
@ -9,7 +9,8 @@
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"bin",
|
"bin",
|
||||||
"dist"
|
"dist",
|
||||||
|
"node.d.ts"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.2.0"
|
"node": ">=12.2.0"
|
||||||
|
@ -83,6 +84,7 @@
|
||||||
"@babel/plugin-transform-arrow-functions": "^7.18.6",
|
"@babel/plugin-transform-arrow-functions": "^7.18.6",
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"esbuild": "^0.15.12",
|
"esbuild": "^0.15.12",
|
||||||
|
"magic-string": "^0.26.7",
|
||||||
"picocolors": "^1.0.0"
|
"picocolors": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ specifiers:
|
||||||
eslint-plugin-prettier: ^4.2.1
|
eslint-plugin-prettier: ^4.2.1
|
||||||
fs-extra: ^10.1.0
|
fs-extra: ^10.1.0
|
||||||
lint-staged: ^13.0.3
|
lint-staged: ^13.0.3
|
||||||
|
magic-string: ^0.26.7
|
||||||
picocolors: ^1.0.0
|
picocolors: ^1.0.0
|
||||||
prettier: ^2.7.1
|
prettier: ^2.7.1
|
||||||
rollup: ^2.79.1
|
rollup: ^2.79.1
|
||||||
|
@ -30,6 +31,7 @@ dependencies:
|
||||||
'@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.19.6
|
'@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.19.6
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
esbuild: 0.15.12
|
esbuild: 0.15.12
|
||||||
|
magic-string: 0.26.7
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
@ -1916,6 +1918,13 @@ packages:
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/magic-string/0.26.7:
|
||||||
|
resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
sourcemap-codec: 1.4.8
|
||||||
|
dev: false
|
||||||
|
|
||||||
/merge-stream/2.0.0:
|
/merge-stream/2.0.0:
|
||||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2277,6 +2286,10 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/sourcemap-codec/1.4.8:
|
||||||
|
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/sprintf-js/1.0.3:
|
/sprintf-js/1.0.3:
|
||||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
import { build } from 'esbuild'
|
import { build } from 'esbuild'
|
||||||
|
|
||||||
import { electronMainVitePlugin, electronPreloadVitePlugin, electronRendererVitePlugin } from './plugin'
|
import { electronMainVitePlugin, electronPreloadVitePlugin, electronRendererVitePlugin } from './plugin'
|
||||||
|
import workerPlugin from './plugins/worker'
|
||||||
import { isObject, dynamicImport } from './utils'
|
import { isObject, dynamicImport } from './utils'
|
||||||
|
|
||||||
export { defineConfig as defineViteConfig } from 'vite'
|
export { defineConfig as defineViteConfig } from 'vite'
|
||||||
|
@ -129,7 +130,7 @@ export async function resolveConfig(
|
||||||
resetOutDir(mainViteConfig, outDir, 'main')
|
resetOutDir(mainViteConfig, outDir, 'main')
|
||||||
}
|
}
|
||||||
|
|
||||||
mergePlugins(mainViteConfig, electronMainVitePlugin({ root }))
|
mergePlugins(mainViteConfig, [...electronMainVitePlugin({ root }), workerPlugin()])
|
||||||
|
|
||||||
loadResult.config.main = mainViteConfig
|
loadResult.config.main = mainViteConfig
|
||||||
loadResult.config.main.configFile = false
|
loadResult.config.main.configFile = false
|
||||||
|
|
69
src/plugins/worker.ts
Normal file
69
src/plugins/worker.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import path from 'node:path'
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
import type { SourceMapInput } from 'rollup'
|
||||||
|
import MagicString from 'magic-string'
|
||||||
|
import { cleanUrl, parseRequest } from '../utils'
|
||||||
|
|
||||||
|
const nodeWorkerAssetUrlRE = /__VITE_NODE_WORKER_ASSET__([a-z\d]{8})__/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve `?nodeWorker` import and automatically generate `Worker` wrapper.
|
||||||
|
*/
|
||||||
|
export default function workerPlugin(): Plugin {
|
||||||
|
let sourcemap: boolean | 'inline' | 'hidden' = false
|
||||||
|
return {
|
||||||
|
name: 'vite:node-worker',
|
||||||
|
apply: 'build',
|
||||||
|
enforce: 'pre',
|
||||||
|
configResolved(config): void {
|
||||||
|
sourcemap = config.build.sourcemap
|
||||||
|
},
|
||||||
|
resolveId(id, importer): string | void {
|
||||||
|
const query = parseRequest(id)
|
||||||
|
if (query && typeof query.nodeWorker === 'string') {
|
||||||
|
return id + `&importer=${importer}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
load(id): string | void {
|
||||||
|
const query = parseRequest(id)
|
||||||
|
if (query && typeof query.nodeWorker === 'string' && typeof query.importer === 'string') {
|
||||||
|
const cleanPath = cleanUrl(id)
|
||||||
|
const hash = this.emitFile({
|
||||||
|
type: 'chunk',
|
||||||
|
id: cleanPath,
|
||||||
|
importer: query.importer
|
||||||
|
})
|
||||||
|
const assetRefId = `__VITE_NODE_WORKER_ASSET__${hash}__`
|
||||||
|
return `
|
||||||
|
import { Worker } from 'node:worker_threads';
|
||||||
|
export default function (options) { return new Worker(require.resolve(${assetRefId}), options); }`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderChunk(code, chunk): { code: string; map: SourceMapInput } | null {
|
||||||
|
if (code.match(nodeWorkerAssetUrlRE)) {
|
||||||
|
let match: RegExpExecArray | null
|
||||||
|
const s = new MagicString(code)
|
||||||
|
|
||||||
|
while ((match = nodeWorkerAssetUrlRE.exec(code))) {
|
||||||
|
const [full, hash] = match
|
||||||
|
const filename = this.getFileName(hash)
|
||||||
|
let outputFilepath = path.posix.relative(path.dirname(chunk.fileName), filename)
|
||||||
|
if (!outputFilepath.startsWith('.')) {
|
||||||
|
outputFilepath = './' + outputFilepath
|
||||||
|
}
|
||||||
|
const replacement = JSON.stringify(outputFilepath)
|
||||||
|
s.overwrite(match.index, match.index + full.length, replacement, {
|
||||||
|
contentOnly: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: s.toString(),
|
||||||
|
map: sourcemap ? s.generateMap({ hires: true }) : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/utils.ts
15
src/utils.ts
|
@ -1,3 +1,5 @@
|
||||||
|
import { URL, URLSearchParams } from 'node:url'
|
||||||
|
|
||||||
export function isObject(value: unknown): value is Record<string, unknown> {
|
export function isObject(value: unknown): value is Record<string, unknown> {
|
||||||
return Object.prototype.toString.call(value) === '[object Object]'
|
return Object.prototype.toString.call(value) === '[object Object]'
|
||||||
}
|
}
|
||||||
|
@ -9,3 +11,16 @@ export const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000
|
||||||
export function resolveHostname(optionsHost: string | boolean | undefined): string {
|
export function resolveHostname(optionsHost: string | boolean | undefined): string {
|
||||||
return typeof optionsHost === 'string' && !wildcardHosts.has(optionsHost) ? optionsHost : 'localhost'
|
return typeof optionsHost === 'string' && !wildcardHosts.has(optionsHost) ? optionsHost : 'localhost'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const queryRE = /\?.*$/s
|
||||||
|
export const hashRE = /#.*$/s
|
||||||
|
|
||||||
|
export const cleanUrl = (url: string): string => url.replace(hashRE, '').replace(queryRE, '')
|
||||||
|
|
||||||
|
export function parseRequest(id: string): Record<string, string> | null {
|
||||||
|
const { search } = new URL(id, 'file:')
|
||||||
|
if (!search) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return Object.fromEntries(new URLSearchParams(search))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue