mirror of
https://github.com/alex8088/electron-vite.git
synced 2026-07-13 01:51:07 +08:00
Compare commits
6 Commits
397b02e384
...
3e9ded666c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e9ded666c | ||
|
|
1bba6766e8 | ||
|
|
4edffe3b9a | ||
|
|
cfd9812a91 | ||
|
|
7c7f31b2a3 | ||
|
|
ae57b2489a |
@ -1,3 +1,11 @@
|
||||
### v5.0.0-beta.2 (_2025-10-30_)
|
||||
|
||||
- feat: reporter plugin for isolated builds
|
||||
- perf(isolateEntries): transform log
|
||||
- perf(isolateEntries): optimize entries transformation
|
||||
- fix(asset): normalize imported public asset chunk path
|
||||
- fix: avoid duplicate chunk emission
|
||||
|
||||
### v5.0.0-beta.1 (_2025-10-29_)
|
||||
|
||||
- feat: enhanced string protection
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron-vite",
|
||||
"version": "5.0.0-beta.1",
|
||||
"version": "5.0.0-beta.2",
|
||||
"description": "Electron build tooling based on Vite",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs/promises'
|
||||
import type { SourceMapInput } from 'rollup'
|
||||
import { type Plugin } from 'vite'
|
||||
import { type Plugin, normalizePath } from 'vite'
|
||||
import MagicString from 'magic-string'
|
||||
import { cleanUrl, getHash, toRelativePath } from '../utils'
|
||||
import { supportImportMetaPaths } from '../electron'
|
||||
@ -28,7 +28,6 @@ export default async function loadWasm(file, importObject = {}) {
|
||||
|
||||
export default function assetPlugin(): Plugin {
|
||||
let publicDir = ''
|
||||
let outDir = ''
|
||||
const publicAssetPathCache = new Map<string, string>()
|
||||
const assetCache = new Map<string, string>()
|
||||
const isImportMetaPathSupported = supportImportMetaPaths()
|
||||
@ -42,7 +41,6 @@ export default function assetPlugin(): Plugin {
|
||||
},
|
||||
configResolved(config): void {
|
||||
publicDir = config.publicDir
|
||||
outDir = config.build.outDir
|
||||
},
|
||||
resolveId(id): string | void {
|
||||
if (id === wasmHelperId) {
|
||||
@ -105,7 +103,7 @@ export default function assetPlugin(): Plugin {
|
||||
export default importObject => loadWasm(${referenceId}, importObject)`
|
||||
}
|
||||
},
|
||||
renderChunk(code, chunk, { sourcemap }): { code: string; map: SourceMapInput } | null {
|
||||
renderChunk(code, chunk, { sourcemap, dir }): { code: string; map: SourceMapInput } | null {
|
||||
let match: RegExpExecArray | null
|
||||
let s: MagicString | undefined
|
||||
|
||||
@ -126,7 +124,7 @@ export default function assetPlugin(): Plugin {
|
||||
s ||= new MagicString(code)
|
||||
const [full, hash] = match
|
||||
const filename = publicAssetPathCache.get(hash)!
|
||||
const outputFilepath = toRelativePath(filename, path.join(outDir, chunk.fileName))
|
||||
const outputFilepath = toRelativePath(filename, normalizePath(path.join(dir!, chunk.fileName)))
|
||||
const replacement = JSON.stringify(outputFilepath)
|
||||
s.overwrite(match.index, match.index + full.length, replacement, {
|
||||
contentOnly: true
|
||||
|
||||
27
src/plugins/buildReporter.ts
Normal file
27
src/plugins/buildReporter.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { type Plugin } from 'vite'
|
||||
|
||||
type BuildReporterApi = {
|
||||
getWatchFiles: () => string[]
|
||||
}
|
||||
|
||||
export default function buildReporterPlugin(): Plugin<BuildReporterApi> {
|
||||
const moduleIds: string[] = []
|
||||
return {
|
||||
name: 'vite:build-reporter',
|
||||
|
||||
buildEnd() {
|
||||
const allModuleIds = Array.from(this.getModuleIds())
|
||||
const sourceFiles = allModuleIds.filter(id => {
|
||||
const info = this.getModuleInfo(id)
|
||||
return info && !info.isExternal
|
||||
})
|
||||
moduleIds.push(...sourceFiles)
|
||||
},
|
||||
|
||||
api: {
|
||||
getWatchFiles() {
|
||||
return moduleIds
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,24 @@
|
||||
import { type InlineConfig, type Plugin, type Logger, build as viteBuild, mergeConfig } from 'vite'
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-function-type */
|
||||
import path from 'node:path'
|
||||
import { type InlineConfig, type Plugin, type Logger, type LogLevel, build as viteBuild, mergeConfig } from 'vite'
|
||||
import type { InputOptions, RollupOutput } from 'rollup'
|
||||
import colors from 'picocolors'
|
||||
import buildReporterPlugin from './buildReporter'
|
||||
|
||||
const VIRTUAL_ENTRY_ID = '\0virtual:isolate-entries'
|
||||
|
||||
const LogLevels: Record<LogLevel, number> = {
|
||||
silent: 0,
|
||||
error: 1,
|
||||
warn: 2,
|
||||
info: 3
|
||||
}
|
||||
|
||||
export default function isolateEntriesPlugin(userConfig: InlineConfig): Plugin {
|
||||
let logger: Logger
|
||||
|
||||
let entries: string[] | Record<string, string>
|
||||
let entries: string[] | { [x: string]: string }[]
|
||||
|
||||
let transformedCount = 0
|
||||
|
||||
const assetCache = new Set<string>()
|
||||
@ -15,40 +26,47 @@ export default function isolateEntriesPlugin(userConfig: InlineConfig): Plugin {
|
||||
return {
|
||||
name: 'vite:isolate-entries',
|
||||
apply: 'build',
|
||||
|
||||
configResolved(config): void {
|
||||
logger = config.logger
|
||||
},
|
||||
|
||||
options(opts): InputOptions | void {
|
||||
const { input } = opts
|
||||
if (input && typeof input === 'object') {
|
||||
if ((Array.isArray(input) && input.length > 0) || Object.keys(input).length > 1) {
|
||||
opts.input = VIRTUAL_ENTRY_ID
|
||||
entries = input
|
||||
entries = Array.isArray(input) ? input : Object.entries(input).map(([key, value]) => ({ [key]: value }))
|
||||
return opts
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
buildStart(): void {
|
||||
transformedCount = 0
|
||||
assetCache.clear()
|
||||
},
|
||||
|
||||
resolveId(id): string | null {
|
||||
if (id === VIRTUAL_ENTRY_ID) {
|
||||
return id
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
async load(id): Promise<string | void> {
|
||||
if (id === VIRTUAL_ENTRY_ID) {
|
||||
const _entries = Array.isArray(entries)
|
||||
? entries
|
||||
: Object.entries(entries).map(([key, value]) => ({ [key]: value }))
|
||||
const shouldLog = LogLevels[userConfig.logLevel || 'info'] >= LogLevels.info
|
||||
const shouldWatch = this.meta.watchMode
|
||||
|
||||
const watchFiles = new Set<string>()
|
||||
for (const entry of _entries) {
|
||||
const re = await bundleEntryFile(entry, userConfig, this.meta.watchMode)
|
||||
|
||||
for (const entry of entries) {
|
||||
const re = await bundleEntryFile(entry, userConfig, shouldWatch, shouldLog, transformedCount)
|
||||
|
||||
const outputChunks = re.bundles.output
|
||||
for (const chunk of outputChunks) {
|
||||
if (chunk.type === 'asset' && assetCache.has(chunk.fileName)) {
|
||||
if (assetCache.has(chunk.fileName)) {
|
||||
continue
|
||||
}
|
||||
this.emitFile({
|
||||
@ -56,27 +74,31 @@ export default function isolateEntriesPlugin(userConfig: InlineConfig): Plugin {
|
||||
fileName: chunk.fileName,
|
||||
source: chunk.type === 'chunk' ? chunk.code : chunk.source
|
||||
})
|
||||
if (chunk.type === 'asset') {
|
||||
assetCache.add(chunk.fileName)
|
||||
}
|
||||
assetCache.add(chunk.fileName)
|
||||
}
|
||||
|
||||
for (const id of re.watchFiles) {
|
||||
watchFiles.add(id)
|
||||
}
|
||||
|
||||
transformedCount += re.transformedCount
|
||||
}
|
||||
|
||||
for (const id of watchFiles) {
|
||||
this.addWatchFile(id)
|
||||
}
|
||||
|
||||
return `
|
||||
// This is the virtual entry file
|
||||
console.log(1)`
|
||||
}
|
||||
},
|
||||
|
||||
renderStart(): void {
|
||||
clearLine()
|
||||
clearLine(-1)
|
||||
logger.info(`${colors.green(`✓`)} ${transformedCount} modules transformed.`)
|
||||
},
|
||||
|
||||
generateBundle(_, bundle): void {
|
||||
for (const chunkName in bundle) {
|
||||
if (chunkName.includes('virtual_isolate-entries')) {
|
||||
@ -90,41 +112,19 @@ export default function isolateEntriesPlugin(userConfig: InlineConfig): Plugin {
|
||||
async function bundleEntryFile(
|
||||
input: string | Record<string, string>,
|
||||
config: InlineConfig,
|
||||
watch: boolean
|
||||
watch: boolean,
|
||||
shouldLog: boolean,
|
||||
preTransformedCount: number
|
||||
): Promise<{ bundles: RollupOutput; watchFiles: string[]; transformedCount: number }> {
|
||||
const moduleIds: string[] = []
|
||||
let transformedCount = 0
|
||||
const transformReporter = transformReporterPlugin(preTransformedCount, shouldLog)
|
||||
const buildReporter = watch ? buildReporterPlugin() : undefined
|
||||
|
||||
const viteConfig = mergeConfig(config, {
|
||||
build: {
|
||||
write: false,
|
||||
watch: false
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: 'vite:transform-counter',
|
||||
transform(): void {
|
||||
transformedCount++
|
||||
}
|
||||
} as Plugin,
|
||||
...(watch
|
||||
? [
|
||||
{
|
||||
name: 'vite:get-watch-files',
|
||||
buildEnd(): void {
|
||||
const allModuleIds = Array.from(this.getModuleIds())
|
||||
|
||||
const sourceFiles = allModuleIds.filter(id => {
|
||||
const info = this.getModuleInfo(id)
|
||||
return info && !info.isExternal
|
||||
})
|
||||
|
||||
moduleIds.push(...sourceFiles)
|
||||
}
|
||||
} as Plugin
|
||||
]
|
||||
: [])
|
||||
],
|
||||
plugins: [transformReporter, buildReporter],
|
||||
logLevel: 'warn',
|
||||
configFile: false
|
||||
}) as InlineConfig
|
||||
@ -136,13 +136,63 @@ async function bundleEntryFile(
|
||||
|
||||
return {
|
||||
bundles: bundles as RollupOutput,
|
||||
watchFiles: moduleIds,
|
||||
transformedCount
|
||||
watchFiles: buildReporter?.api?.getWatchFiles() || [],
|
||||
transformedCount: transformReporter?.api?.getTransformedCount() || 0
|
||||
}
|
||||
}
|
||||
|
||||
function clearLine(): void {
|
||||
process.stdout.moveCursor(0, -1)
|
||||
function transformReporterPlugin(
|
||||
preTransformedCount = 0,
|
||||
shouldLog = true
|
||||
): Plugin<{ getTransformedCount: () => number }> {
|
||||
let transformedCount = 0
|
||||
let root
|
||||
const log = throttle(id => {
|
||||
writeLine(`transforming (${preTransformedCount + transformedCount}) ${colors.dim(path.relative(root, id))}`)
|
||||
})
|
||||
return {
|
||||
name: 'vite:transform-reporter',
|
||||
configResolved(config) {
|
||||
root = config.root
|
||||
},
|
||||
transform(_, id) {
|
||||
transformedCount++
|
||||
if (!shouldLog) return
|
||||
if (id.includes('?')) return
|
||||
log(id)
|
||||
},
|
||||
api: {
|
||||
getTransformedCount() {
|
||||
return transformedCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeLine(output: string): void {
|
||||
clearLine()
|
||||
if (output.length < process.stdout.columns) {
|
||||
process.stdout.write(output)
|
||||
} else {
|
||||
process.stdout.write(output.substring(0, process.stdout.columns - 1))
|
||||
}
|
||||
}
|
||||
|
||||
function clearLine(move: number = 0): void {
|
||||
if (move < 0) {
|
||||
process.stdout.moveCursor(0, move)
|
||||
}
|
||||
process.stdout.clearLine(0)
|
||||
process.stdout.cursorTo(0)
|
||||
}
|
||||
|
||||
function throttle(fn: Function) {
|
||||
let timerHandle: NodeJS.Timeout | null = null
|
||||
return (...args: any[]) => {
|
||||
if (timerHandle) return
|
||||
fn(...args)
|
||||
timerHandle = setTimeout(() => {
|
||||
timerHandle = null
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import path from 'node:path'
|
||||
import { type Plugin, type InlineConfig, build as viteBuild, mergeConfig } from 'vite'
|
||||
import type { SourceMapInput, RollupOutput, OutputOptions } from 'rollup'
|
||||
import MagicString from 'magic-string'
|
||||
import buildReporterPlugin from './buildReporter'
|
||||
import { cleanUrl, toRelativePath } from '../utils'
|
||||
import { supportImportMetaPaths } from '../electron'
|
||||
|
||||
@ -31,7 +32,7 @@ export default function modulePathPlugin(config: InlineConfig): Plugin {
|
||||
source: outputChunk.code
|
||||
})
|
||||
for (const chunk of outputChunks) {
|
||||
if (chunk.type === 'asset' && assetCache.has(chunk.fileName)) {
|
||||
if (assetCache.has(chunk.fileName)) {
|
||||
continue
|
||||
}
|
||||
this.emitFile({
|
||||
@ -39,9 +40,7 @@ export default function modulePathPlugin(config: InlineConfig): Plugin {
|
||||
fileName: chunk.fileName,
|
||||
source: chunk.type === 'chunk' ? chunk.code : chunk.source
|
||||
})
|
||||
if (chunk.type === 'asset') {
|
||||
assetCache.add(chunk.fileName)
|
||||
}
|
||||
assetCache.add(chunk.fileName)
|
||||
}
|
||||
for (const id of re.watchFiles) {
|
||||
this.addWatchFile(id)
|
||||
@ -86,7 +85,7 @@ async function bundleEntryFile(
|
||||
config: InlineConfig,
|
||||
watch: boolean
|
||||
): Promise<{ bundles: RollupOutput; watchFiles: string[] }> {
|
||||
const moduleIds: string[] = []
|
||||
const reporter = watch ? buildReporterPlugin() : undefined
|
||||
const viteConfig = mergeConfig(config, {
|
||||
build: {
|
||||
rollupOptions: { input },
|
||||
@ -103,23 +102,7 @@ async function bundleEntryFile(
|
||||
return output
|
||||
}
|
||||
},
|
||||
...(watch
|
||||
? [
|
||||
{
|
||||
name: 'vite:get-watch-files',
|
||||
buildEnd(): void {
|
||||
const allModuleIds = Array.from(this.getModuleIds())
|
||||
|
||||
const sourceFiles = allModuleIds.filter(id => {
|
||||
const info = this.getModuleInfo(id)
|
||||
return info && !info.isExternal
|
||||
})
|
||||
|
||||
moduleIds.push(...sourceFiles)
|
||||
}
|
||||
} as Plugin
|
||||
]
|
||||
: [])
|
||||
reporter
|
||||
],
|
||||
logLevel: 'warn',
|
||||
configFile: false
|
||||
@ -128,6 +111,6 @@ async function bundleEntryFile(
|
||||
|
||||
return {
|
||||
bundles: bundles as RollupOutput,
|
||||
watchFiles: moduleIds
|
||||
watchFiles: reporter?.api?.getWatchFiles() || []
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user