mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-21 21:45:42 -05:00
feat: build installed plugins
This commit is contained in:
parent
34bbee4089
commit
dbb3aac1c0
@ -4,26 +4,26 @@
|
|||||||
"explorer": {
|
"explorer": {
|
||||||
"source": "github:quartz-community/explorer",
|
"source": "github:quartz-community/explorer",
|
||||||
"resolved": "https://github.com/quartz-community/explorer.git",
|
"resolved": "https://github.com/quartz-community/explorer.git",
|
||||||
"commit": "05687798c9eaecb4d4158d00c649c5a5f57a0494",
|
"commit": "c8766ee830ad014f9aa42783f24a6d813e223dbd",
|
||||||
"installedAt": "2026-02-09T21:40:20.825Z"
|
"installedAt": "2026-02-09T22:56:25.140Z"
|
||||||
},
|
},
|
||||||
"graph": {
|
"graph": {
|
||||||
"source": "github:quartz-community/graph",
|
"source": "github:quartz-community/graph",
|
||||||
"resolved": "https://github.com/quartz-community/graph.git",
|
"resolved": "https://github.com/quartz-community/graph.git",
|
||||||
"commit": "ec2917b5ea34e0ba36ebe96b8311368224e31b6e",
|
"commit": "1dcf09268575bc3073f98d9bf98793cbb9f4edd1",
|
||||||
"installedAt": "2026-02-09T21:40:21.267Z"
|
"installedAt": "2026-02-09T22:56:25.708Z"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"source": "github:quartz-community/search",
|
"source": "github:quartz-community/search",
|
||||||
"resolved": "https://github.com/quartz-community/search.git",
|
"resolved": "https://github.com/quartz-community/search.git",
|
||||||
"commit": "54b33d6fb493fe83c48f2a461217fd3fa962eedd",
|
"commit": "1d7a55c77157abc78fbd4f6185a769f149e6f97a",
|
||||||
"installedAt": "2026-02-09T21:40:21.714Z"
|
"installedAt": "2026-02-09T22:56:26.300Z"
|
||||||
},
|
},
|
||||||
"backlinks": {
|
"backlinks": {
|
||||||
"source": "github:quartz-community/backlinks",
|
"source": "github:quartz-community/backlinks",
|
||||||
"resolved": "https://github.com/quartz-community/backlinks.git",
|
"resolved": "https://github.com/quartz-community/backlinks.git",
|
||||||
"commit": "8590be93126db3045ad46bd482437f0d30401f00",
|
"commit": "f28678cb3196483599d3b50a44ac18598d7c7dea",
|
||||||
"installedAt": "2026-02-09T22:21:12.217Z"
|
"installedAt": "2026-02-09T22:56:26.761Z"
|
||||||
},
|
},
|
||||||
"table-of-contents": {
|
"table-of-contents": {
|
||||||
"source": "github:quartz-community/table-of-contents",
|
"source": "github:quartz-community/table-of-contents",
|
||||||
@ -34,8 +34,8 @@
|
|||||||
"comments": {
|
"comments": {
|
||||||
"source": "github:quartz-community/comments",
|
"source": "github:quartz-community/comments",
|
||||||
"resolved": "https://github.com/quartz-community/comments.git",
|
"resolved": "https://github.com/quartz-community/comments.git",
|
||||||
"commit": "265c635da1422da52dc2fa87bd4725b1ca0dc7ea",
|
"commit": "e3140c7cd3fe48dee2a890eded9bc9ca80d62909",
|
||||||
"installedAt": "2026-02-09T22:21:30.065Z"
|
"installedAt": "2026-02-09T22:56:27.743Z"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,88 @@ import { styleText } from "util"
|
|||||||
|
|
||||||
const LOCKFILE_PATH = path.join(process.cwd(), "quartz.lock.json")
|
const LOCKFILE_PATH = path.join(process.cwd(), "quartz.lock.json")
|
||||||
const PLUGINS_DIR = path.join(process.cwd(), ".quartz", "plugins")
|
const PLUGINS_DIR = path.join(process.cwd(), ".quartz", "plugins")
|
||||||
|
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
|
||||||
|
|
||||||
|
function buildPlugin(pluginDir, name) {
|
||||||
|
try {
|
||||||
|
console.log(styleText("cyan", ` → ${name}: installing dependencies...`))
|
||||||
|
execSync("npm install", { cwd: pluginDir, stdio: "ignore" })
|
||||||
|
console.log(styleText("cyan", ` → ${name}: building...`))
|
||||||
|
execSync("npm run build", { cwd: pluginDir, stdio: "ignore" })
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.log(styleText("red", ` ✗ ${name}: build failed`))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function needsBuild(pluginDir) {
|
||||||
|
const distDir = path.join(pluginDir, "dist")
|
||||||
|
return !fs.existsSync(distDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseExportsFromDts(content) {
|
||||||
|
const exports = []
|
||||||
|
const exportMatches = content.matchAll(/export\s*{\s*([^}]+)\s*}(?:\s*from\s*['"]([^'"]+)['"])?/g)
|
||||||
|
for (const match of exportMatches) {
|
||||||
|
const fromModule = match[2]
|
||||||
|
if (fromModule?.startsWith("@")) continue
|
||||||
|
|
||||||
|
const names = match[1]
|
||||||
|
.split(",")
|
||||||
|
.map((n) => n.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
for (const name of names) {
|
||||||
|
const cleanName = name.split(" as ").pop()?.trim() || name.trim()
|
||||||
|
if (cleanName && !cleanName.startsWith("_") && !INTERNAL_EXPORTS.has(cleanName)) {
|
||||||
|
const finalName = cleanName.replace(/^type\s+/, "")
|
||||||
|
if (name.includes("type ")) {
|
||||||
|
exports.push(`type ${finalName}`)
|
||||||
|
} else {
|
||||||
|
exports.push(finalName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exports
|
||||||
|
}
|
||||||
|
|
||||||
|
async function regeneratePluginIndex() {
|
||||||
|
if (!fs.existsSync(PLUGINS_DIR)) return
|
||||||
|
|
||||||
|
const plugins = fs.readdirSync(PLUGINS_DIR).filter((name) => {
|
||||||
|
const pluginPath = path.join(PLUGINS_DIR, name)
|
||||||
|
return fs.statSync(pluginPath).isDirectory()
|
||||||
|
})
|
||||||
|
|
||||||
|
const exports = []
|
||||||
|
|
||||||
|
for (const pluginName of plugins) {
|
||||||
|
const pluginDir = path.join(PLUGINS_DIR, pluginName)
|
||||||
|
const distIndex = path.join(pluginDir, "dist", "index.d.ts")
|
||||||
|
|
||||||
|
if (!fs.existsSync(distIndex)) continue
|
||||||
|
|
||||||
|
const dtsContent = fs.readFileSync(distIndex, "utf-8")
|
||||||
|
const exportedNames = parseExportsFromDts(dtsContent)
|
||||||
|
|
||||||
|
if (exportedNames.length > 0) {
|
||||||
|
const namedExports = exportedNames.filter((e) => !e.startsWith("type "))
|
||||||
|
const typeExports = exportedNames.filter((e) => e.startsWith("type ")).map((e) => e.slice(5))
|
||||||
|
|
||||||
|
if (namedExports.length > 0) {
|
||||||
|
exports.push(`export { ${namedExports.join(", ")} } from "./${pluginName}"`)
|
||||||
|
}
|
||||||
|
if (typeExports.length > 0) {
|
||||||
|
exports.push(`export type { ${typeExports.join(", ")} } from "./${pluginName}"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexContent = exports.join("\n") + "\n"
|
||||||
|
const indexPath = path.join(PLUGINS_DIR, "index.ts")
|
||||||
|
fs.writeFileSync(indexPath, indexContent)
|
||||||
|
}
|
||||||
|
|
||||||
function readLockfile() {
|
function readLockfile() {
|
||||||
if (!fs.existsSync(LOCKFILE_PATH)) {
|
if (!fs.existsSync(LOCKFILE_PATH)) {
|
||||||
@ -65,6 +147,7 @@ export async function handlePluginInstall() {
|
|||||||
console.log(styleText("cyan", "→ Installing plugins from lockfile..."))
|
console.log(styleText("cyan", "→ Installing plugins from lockfile..."))
|
||||||
let installed = 0
|
let installed = 0
|
||||||
let failed = 0
|
let failed = 0
|
||||||
|
const pluginsToBuild = []
|
||||||
|
|
||||||
for (const [name, entry] of Object.entries(lockfile.plugins)) {
|
for (const [name, entry] of Object.entries(lockfile.plugins)) {
|
||||||
const pluginDir = path.join(PLUGINS_DIR, name)
|
const pluginDir = path.join(PLUGINS_DIR, name)
|
||||||
@ -72,16 +155,19 @@ export async function handlePluginInstall() {
|
|||||||
if (fs.existsSync(pluginDir)) {
|
if (fs.existsSync(pluginDir)) {
|
||||||
try {
|
try {
|
||||||
const currentCommit = getGitCommit(pluginDir)
|
const currentCommit = getGitCommit(pluginDir)
|
||||||
if (currentCommit === entry.commit) {
|
if (currentCommit === entry.commit && !needsBuild(pluginDir)) {
|
||||||
console.log(
|
console.log(
|
||||||
styleText("gray", ` ✓ ${name}@${entry.commit.slice(0, 7)} already installed`),
|
styleText("gray", ` ✓ ${name}@${entry.commit.slice(0, 7)} already installed`),
|
||||||
)
|
)
|
||||||
installed++
|
installed++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
console.log(styleText("cyan", ` → ${name}: updating to ${entry.commit.slice(0, 7)}...`))
|
if (currentCommit !== entry.commit) {
|
||||||
execSync("git fetch --depth 1 origin", { cwd: pluginDir, stdio: "ignore" })
|
console.log(styleText("cyan", ` → ${name}: updating to ${entry.commit.slice(0, 7)}...`))
|
||||||
execSync(`git reset --hard ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
execSync("git fetch --depth 1 origin", { cwd: pluginDir, stdio: "ignore" })
|
||||||
|
execSync(`git reset --hard ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
||||||
|
}
|
||||||
|
pluginsToBuild.push({ name, pluginDir })
|
||||||
installed++
|
installed++
|
||||||
} catch {
|
} catch {
|
||||||
console.log(styleText("red", ` ✗ ${name}: failed to update`))
|
console.log(styleText("red", ` ✗ ${name}: failed to update`))
|
||||||
@ -99,6 +185,7 @@ export async function handlePluginInstall() {
|
|||||||
execSync(`git checkout ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
execSync(`git checkout ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
||||||
}
|
}
|
||||||
console.log(styleText("green", ` ✓ ${name}@${entry.commit.slice(0, 7)}`))
|
console.log(styleText("green", ` ✓ ${name}@${entry.commit.slice(0, 7)}`))
|
||||||
|
pluginsToBuild.push({ name, pluginDir })
|
||||||
installed++
|
installed++
|
||||||
} catch {
|
} catch {
|
||||||
console.log(styleText("red", ` ✗ ${name}: failed to clone`))
|
console.log(styleText("red", ` ✗ ${name}: failed to clone`))
|
||||||
@ -107,6 +194,21 @@ export async function handlePluginInstall() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pluginsToBuild.length > 0) {
|
||||||
|
console.log()
|
||||||
|
console.log(styleText("cyan", "→ Building plugins..."))
|
||||||
|
for (const { name, pluginDir } of pluginsToBuild) {
|
||||||
|
if (!buildPlugin(pluginDir, name)) {
|
||||||
|
failed++
|
||||||
|
installed--
|
||||||
|
} else {
|
||||||
|
console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await regeneratePluginIndex()
|
||||||
|
|
||||||
console.log()
|
console.log()
|
||||||
if (failed === 0) {
|
if (failed === 0) {
|
||||||
console.log(styleText("green", `✓ Installed ${installed} plugin(s)`))
|
console.log(styleText("green", `✓ Installed ${installed} plugin(s)`))
|
||||||
@ -125,6 +227,8 @@ export async function handlePluginAdd(sources) {
|
|||||||
fs.mkdirSync(PLUGINS_DIR, { recursive: true })
|
fs.mkdirSync(PLUGINS_DIR, { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addedPlugins = []
|
||||||
|
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
try {
|
try {
|
||||||
const { name, url, ref } = parseGitSource(source)
|
const { name, url, ref } = parseGitSource(source)
|
||||||
@ -151,12 +255,24 @@ export async function handlePluginAdd(sources) {
|
|||||||
installedAt: new Date().toISOString(),
|
installedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addedPlugins.push({ name, pluginDir })
|
||||||
console.log(styleText("green", `✓ Added ${name}@${commit.slice(0, 7)}`))
|
console.log(styleText("green", `✓ Added ${name}@${commit.slice(0, 7)}`))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(styleText("red", `✗ Failed to add ${source}: ${error}`))
|
console.log(styleText("red", `✗ Failed to add ${source}: ${error}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addedPlugins.length > 0) {
|
||||||
|
console.log()
|
||||||
|
console.log(styleText("cyan", "→ Building plugins..."))
|
||||||
|
for (const { name, pluginDir } of addedPlugins) {
|
||||||
|
if (buildPlugin(pluginDir, name)) {
|
||||||
|
console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await regeneratePluginIndex()
|
||||||
|
}
|
||||||
|
|
||||||
writeLockfile(lockfile)
|
writeLockfile(lockfile)
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("gray", "Updated quartz.lock.json"))
|
console.log(styleText("gray", "Updated quartz.lock.json"))
|
||||||
@ -169,6 +285,7 @@ export async function handlePluginRemove(names) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let removed = false
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const pluginDir = path.join(PLUGINS_DIR, name)
|
const pluginDir = path.join(PLUGINS_DIR, name)
|
||||||
|
|
||||||
@ -185,6 +302,11 @@ export async function handlePluginRemove(names) {
|
|||||||
|
|
||||||
delete lockfile.plugins[name]
|
delete lockfile.plugins[name]
|
||||||
console.log(styleText("green", `✓ Removed ${name}`))
|
console.log(styleText("green", `✓ Removed ${name}`))
|
||||||
|
removed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed) {
|
||||||
|
await regeneratePluginIndex()
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLockfile(lockfile)
|
writeLockfile(lockfile)
|
||||||
@ -200,6 +322,7 @@ export async function handlePluginUpdate(names) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pluginsToUpdate = names || Object.keys(lockfile.plugins)
|
const pluginsToUpdate = names || Object.keys(lockfile.plugins)
|
||||||
|
const updatedPlugins = []
|
||||||
|
|
||||||
for (const name of pluginsToUpdate) {
|
for (const name of pluginsToUpdate) {
|
||||||
const entry = lockfile.plugins[name]
|
const entry = lockfile.plugins[name]
|
||||||
@ -225,6 +348,7 @@ export async function handlePluginUpdate(names) {
|
|||||||
if (newCommit !== entry.commit) {
|
if (newCommit !== entry.commit) {
|
||||||
entry.commit = newCommit
|
entry.commit = newCommit
|
||||||
entry.installedAt = new Date().toISOString()
|
entry.installedAt = new Date().toISOString()
|
||||||
|
updatedPlugins.push({ name, pluginDir })
|
||||||
console.log(styleText("green", `✓ Updated ${name} to ${newCommit.slice(0, 7)}`))
|
console.log(styleText("green", `✓ Updated ${name} to ${newCommit.slice(0, 7)}`))
|
||||||
} else {
|
} else {
|
||||||
console.log(styleText("gray", `✓ ${name} already up to date`))
|
console.log(styleText("gray", `✓ ${name} already up to date`))
|
||||||
@ -234,6 +358,17 @@ export async function handlePluginUpdate(names) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updatedPlugins.length > 0) {
|
||||||
|
console.log()
|
||||||
|
console.log(styleText("cyan", "→ Rebuilding updated plugins..."))
|
||||||
|
for (const { name, pluginDir } of updatedPlugins) {
|
||||||
|
if (buildPlugin(pluginDir, name)) {
|
||||||
|
console.log(styleText("green", ` ✓ ${name} rebuilt`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await regeneratePluginIndex()
|
||||||
|
}
|
||||||
|
|
||||||
writeLockfile(lockfile)
|
writeLockfile(lockfile)
|
||||||
console.log()
|
console.log()
|
||||||
console.log(styleText("gray", "Updated quartz.lock.json"))
|
console.log(styleText("gray", "Updated quartz.lock.json"))
|
||||||
@ -294,6 +429,7 @@ export async function handlePluginRestore() {
|
|||||||
|
|
||||||
let installed = 0
|
let installed = 0
|
||||||
let failed = 0
|
let failed = 0
|
||||||
|
const restoredPlugins = []
|
||||||
|
|
||||||
for (const [name, entry] of Object.entries(lockfile.plugins)) {
|
for (const [name, entry] of Object.entries(lockfile.plugins)) {
|
||||||
const pluginDir = path.join(pluginsDir, name)
|
const pluginDir = path.join(pluginsDir, name)
|
||||||
@ -310,6 +446,7 @@ export async function handlePluginRestore() {
|
|||||||
execSync(`git clone ${entry.resolved} ${pluginDir}`, { stdio: "ignore" })
|
execSync(`git clone ${entry.resolved} ${pluginDir}`, { stdio: "ignore" })
|
||||||
execSync(`git checkout ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
execSync(`git checkout ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
||||||
console.log(styleText("green", `✓ ${name} restored`))
|
console.log(styleText("green", `✓ ${name} restored`))
|
||||||
|
restoredPlugins.push({ name, pluginDir })
|
||||||
installed++
|
installed++
|
||||||
} catch {
|
} catch {
|
||||||
console.log(styleText("red", `✗ ${name}: failed to restore`))
|
console.log(styleText("red", `✗ ${name}: failed to restore`))
|
||||||
@ -317,6 +454,20 @@ export async function handlePluginRestore() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (restoredPlugins.length > 0) {
|
||||||
|
console.log()
|
||||||
|
console.log(styleText("cyan", "→ Building restored plugins..."))
|
||||||
|
for (const { name, pluginDir } of restoredPlugins) {
|
||||||
|
if (!buildPlugin(pluginDir, name)) {
|
||||||
|
failed++
|
||||||
|
installed--
|
||||||
|
} else {
|
||||||
|
console.log(styleText("green", ` ✓ ${name} built`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await regeneratePluginIndex()
|
||||||
|
}
|
||||||
|
|
||||||
console.log()
|
console.log()
|
||||||
if (failed === 0) {
|
if (failed === 0) {
|
||||||
console.log(styleText("green", `✓ Restored ${installed} plugin(s)`))
|
console.log(styleText("green", `✓ Restored ${installed} plugin(s)`))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user