mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-21 21:45:42 -05:00
perf(plugins): parallelize builds, skip when dist/ exists, fix double-build
- Use --ignore-scripts during npm install to prevent duplicate builds - Skip build entirely when dist/ directory already exists (pre-built plugins) - Add buildPluginAsync() and runParallel() for concurrent plugin builds - Convert all 5 build loops to parallel execution bounded by CPU count - Prune devDependencies after build to avoid singleton duplication
This commit is contained in:
parent
85680868b7
commit
341b8b3779
@ -1,7 +1,8 @@
|
|||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { execSync } from "child_process"
|
import os from "os"
|
||||||
import { styleText } from "util"
|
import { execSync, exec as execCb } from "child_process"
|
||||||
|
import { styleText, promisify } from "util"
|
||||||
import {
|
import {
|
||||||
readPluginsJson,
|
readPluginsJson,
|
||||||
writePluginsJson,
|
writePluginsJson,
|
||||||
@ -18,12 +19,17 @@ import {
|
|||||||
|
|
||||||
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
|
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
|
||||||
|
|
||||||
|
const execAsync = promisify(execCb)
|
||||||
|
|
||||||
function buildPlugin(pluginDir, name) {
|
function buildPlugin(pluginDir, name) {
|
||||||
try {
|
try {
|
||||||
|
const skipBuild = !needsBuild(pluginDir)
|
||||||
console.log(styleText("cyan", ` → ${name}: installing dependencies...`))
|
console.log(styleText("cyan", ` → ${name}: installing dependencies...`))
|
||||||
execSync("npm install", { cwd: pluginDir, stdio: "ignore" })
|
execSync("npm install --ignore-scripts", { cwd: pluginDir, stdio: "ignore" })
|
||||||
console.log(styleText("cyan", ` → ${name}: building...`))
|
if (!skipBuild) {
|
||||||
execSync("npm run build", { cwd: pluginDir, stdio: "ignore" })
|
console.log(styleText("cyan", ` → ${name}: building...`))
|
||||||
|
execSync("npm run build", { cwd: pluginDir, stdio: "ignore" })
|
||||||
|
}
|
||||||
// Remove devDependencies after build — they are no longer needed and their
|
// Remove devDependencies after build — they are no longer needed and their
|
||||||
// presence can cause duplicate-singleton issues when a plugin ships its own
|
// presence can cause duplicate-singleton issues when a plugin ships its own
|
||||||
// copy of a shared dependency (e.g. bases-page's ViewRegistry).
|
// copy of a shared dependency (e.g. bases-page's ViewRegistry).
|
||||||
@ -39,6 +45,47 @@ function buildPlugin(pluginDir, name) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function buildPluginAsync(pluginDir, name) {
|
||||||
|
try {
|
||||||
|
const skipBuild = !needsBuild(pluginDir)
|
||||||
|
console.log(styleText("cyan", ` → ${name}: installing dependencies...`))
|
||||||
|
await execAsync("npm install --ignore-scripts", { cwd: pluginDir })
|
||||||
|
if (!skipBuild) {
|
||||||
|
console.log(styleText("cyan", ` → ${name}: building...`))
|
||||||
|
await execAsync("npm run build", { cwd: pluginDir })
|
||||||
|
}
|
||||||
|
await execAsync("npm prune --omit=dev", { cwd: pluginDir })
|
||||||
|
linkPeerPlugins(pluginDir)
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.log(styleText("red", ` ✗ ${name}: build failed`))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run async tasks with bounded concurrency.
|
||||||
|
* @param {Array} items - Items to process
|
||||||
|
* @param {number} concurrency - Max parallel tasks
|
||||||
|
* @param {Function} fn - Async function to run per item
|
||||||
|
* @returns {Promise<Array>} Results in order
|
||||||
|
*/
|
||||||
|
async function runParallel(items, concurrency, fn) {
|
||||||
|
const results = new Array(items.length)
|
||||||
|
let nextIndex = 0
|
||||||
|
|
||||||
|
async function worker() {
|
||||||
|
while (nextIndex < items.length) {
|
||||||
|
const i = nextIndex++
|
||||||
|
results[i] = await fn(items[i], i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => worker())
|
||||||
|
await Promise.all(workers)
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
function needsBuild(pluginDir) {
|
function needsBuild(pluginDir) {
|
||||||
const distDir = path.join(pluginDir, "dist")
|
const distDir = path.join(pluginDir, "dist")
|
||||||
return !fs.existsSync(distDir)
|
return !fs.existsSync(distDir)
|
||||||
@ -289,13 +336,14 @@ export async function handlePluginInstall() {
|
|||||||
if (pluginsToBuild.length > 0) {
|
if (pluginsToBuild.length > 0) {
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("cyan", "→ Building plugins..."))
|
console.log(styleText("cyan", "→ Building plugins..."))
|
||||||
for (const { name, pluginDir } of pluginsToBuild) {
|
const concurrency = Math.max(1, os.cpus().length)
|
||||||
if (!buildPlugin(pluginDir, name)) {
|
const results = await runParallel(pluginsToBuild, concurrency, async ({ name, pluginDir }) => {
|
||||||
failed++
|
const ok = await buildPluginAsync(pluginDir, name)
|
||||||
installed--
|
if (ok) console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
} else {
|
return ok
|
||||||
console.log(styleText("green", ` ✓ ${name} built`))
|
})
|
||||||
}
|
for (const ok of results) {
|
||||||
|
if (!ok) { failed++; installed-- }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,11 +426,12 @@ export async function handlePluginAdd(sources) {
|
|||||||
if (addedPlugins.length > 0) {
|
if (addedPlugins.length > 0) {
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("cyan", "→ Building plugins..."))
|
console.log(styleText("cyan", "→ Building plugins..."))
|
||||||
for (const { name, pluginDir } of addedPlugins) {
|
const concurrency = Math.max(1, os.cpus().length)
|
||||||
if (buildPlugin(pluginDir, name)) {
|
await runParallel(addedPlugins, concurrency, async ({ name, pluginDir }) => {
|
||||||
console.log(styleText("green", ` ✓ ${name} built`))
|
const ok = await buildPluginAsync(pluginDir, name)
|
||||||
}
|
if (ok) console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
}
|
return ok
|
||||||
|
})
|
||||||
await regeneratePluginIndex()
|
await regeneratePluginIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,11 +742,12 @@ export async function handlePluginUpdate(names) {
|
|||||||
if (updatedPlugins.length > 0) {
|
if (updatedPlugins.length > 0) {
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("cyan", "→ Rebuilding updated plugins..."))
|
console.log(styleText("cyan", "→ Rebuilding updated plugins..."))
|
||||||
for (const { name, pluginDir } of updatedPlugins) {
|
const concurrency = Math.max(1, os.cpus().length)
|
||||||
if (buildPlugin(pluginDir, name)) {
|
await runParallel(updatedPlugins, concurrency, async ({ name, pluginDir }) => {
|
||||||
console.log(styleText("green", ` ✓ ${name} rebuilt`))
|
const ok = await buildPluginAsync(pluginDir, name)
|
||||||
}
|
if (ok) console.log(styleText("green", ` ✓ ${name} rebuilt`))
|
||||||
}
|
return ok
|
||||||
|
})
|
||||||
await regeneratePluginIndex()
|
await regeneratePluginIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -824,13 +874,14 @@ export async function handlePluginRestore() {
|
|||||||
if (restoredPlugins.length > 0) {
|
if (restoredPlugins.length > 0) {
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("cyan", "→ Building restored plugins..."))
|
console.log(styleText("cyan", "→ Building restored plugins..."))
|
||||||
for (const { name, pluginDir } of restoredPlugins) {
|
const concurrency = Math.max(1, os.cpus().length)
|
||||||
if (!buildPlugin(pluginDir, name)) {
|
const results = await runParallel(restoredPlugins, concurrency, async ({ name, pluginDir }) => {
|
||||||
failed++
|
const ok = await buildPluginAsync(pluginDir, name)
|
||||||
installed--
|
if (ok) console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
} else {
|
return ok
|
||||||
console.log(styleText("green", ` ✓ ${name} built`))
|
})
|
||||||
}
|
for (const ok of results) {
|
||||||
|
if (!ok) { failed++; installed-- }
|
||||||
}
|
}
|
||||||
await regeneratePluginIndex()
|
await regeneratePluginIndex()
|
||||||
}
|
}
|
||||||
@ -1029,12 +1080,14 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
if (installed.length > 0) {
|
if (installed.length > 0) {
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("cyan", "→ Building plugins..."))
|
console.log(styleText("cyan", "→ Building plugins..."))
|
||||||
for (const { name, pluginDir } of installed) {
|
const concurrency = Math.max(1, os.cpus().length)
|
||||||
if (!buildPlugin(pluginDir, name)) {
|
const results = await runParallel(installed, concurrency, async ({ name, pluginDir }) => {
|
||||||
failed++
|
const ok = await buildPluginAsync(pluginDir, name)
|
||||||
} else {
|
if (ok) console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
console.log(styleText("green", ` ✓ ${name} built`))
|
return ok
|
||||||
}
|
})
|
||||||
|
for (const ok of results) {
|
||||||
|
if (!ok) failed++
|
||||||
}
|
}
|
||||||
await regeneratePluginIndex()
|
await regeneratePluginIndex()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user