mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-21 21:45:42 -05:00
fix(cli): extended options for plugin sources
This commit is contained in:
parent
b2ae694b9a
commit
7b85aa3464
@ -83,32 +83,73 @@ export function writeLockfile(lockfile) {
|
|||||||
fs.writeFileSync(LOCKFILE_PATH, JSON.stringify(lockfile, null, 2) + "\n")
|
fs.writeFileSync(LOCKFILE_PATH, JSON.stringify(lockfile, null, 2) + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a source value to a URL string.
|
||||||
|
* Source can be a plain string (e.g. "github:owner/repo") or an object
|
||||||
|
* with { name?, repo, subdir? } for installing from a subdirectory of a repo.
|
||||||
|
*/
|
||||||
|
export function getSourceUrl(source) {
|
||||||
|
if (typeof source === "string") return source
|
||||||
|
if (typeof source === "object" && source !== null && typeof source.repo === "string") {
|
||||||
|
return source.repo
|
||||||
|
}
|
||||||
|
throw new Error(`Invalid plugin source: ${JSON.stringify(source)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the subdir from an object source, or undefined for string sources.
|
||||||
|
*/
|
||||||
|
export function getSourceSubdir(source) {
|
||||||
|
if (typeof source === "object" && source !== null && typeof source.subdir === "string") {
|
||||||
|
return source.subdir
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a display-friendly string for a source value.
|
||||||
|
*/
|
||||||
|
export function formatSource(source) {
|
||||||
|
if (typeof source === "string") return source
|
||||||
|
if (typeof source === "object" && source !== null) {
|
||||||
|
const parts = [source.repo]
|
||||||
|
if (source.subdir) parts.push(`(subdir: ${source.subdir})`)
|
||||||
|
return parts.join(" ")
|
||||||
|
}
|
||||||
|
return String(source)
|
||||||
|
}
|
||||||
|
|
||||||
export function isLocalSource(source) {
|
export function isLocalSource(source) {
|
||||||
if (source.startsWith("./") || source.startsWith("../") || source.startsWith("/")) {
|
const url = getSourceUrl(source)
|
||||||
|
if (url.startsWith("./") || url.startsWith("../") || url.startsWith("/")) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Windows absolute paths (e.g. C:\ or D:/)
|
// Windows absolute paths (e.g. C:\ or D:/)
|
||||||
if (/^[A-Za-z]:[\\/]/.test(source)) {
|
if (/^[A-Za-z]:[\\/]/.test(url)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
export function extractPluginName(source) {
|
export function extractPluginName(source) {
|
||||||
if (isLocalSource(source)) {
|
if (typeof source === "object" && source !== null && typeof source.name === "string") {
|
||||||
return path.basename(source.replace(/[\/]+$/, ""))
|
return source.name
|
||||||
}
|
}
|
||||||
if (source.startsWith("github:")) {
|
const url = getSourceUrl(source)
|
||||||
const withoutPrefix = source.replace("github:", "")
|
if (isLocalSource(url)) {
|
||||||
|
return path.basename(url.replace(/[\/]+$/, ""))
|
||||||
|
}
|
||||||
|
if (url.startsWith("github:")) {
|
||||||
|
const withoutPrefix = url.replace("github:", "")
|
||||||
const [repoPath] = withoutPrefix.split("#")
|
const [repoPath] = withoutPrefix.split("#")
|
||||||
const parts = repoPath.split("/")
|
const parts = repoPath.split("/")
|
||||||
return parts[parts.length - 1]
|
return parts[parts.length - 1]
|
||||||
}
|
}
|
||||||
if (source.startsWith("git+") || source.startsWith("https://")) {
|
if (url.startsWith("git+") || url.startsWith("https://")) {
|
||||||
const url = source.replace("git+", "")
|
const cleaned = url.replace("git+", "")
|
||||||
const match = url.match(/\/([^/]+?)(?:\.git)?(?:#|$)/)
|
const match = cleaned.match(/\/([^/]+?)(?:\.git)?(?:#|$)/)
|
||||||
return match?.[1] ?? source
|
return match?.[1] ?? url
|
||||||
}
|
}
|
||||||
return source
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readManifestFromPackageJson(pluginDir) {
|
export function readManifestFromPackageJson(pluginDir) {
|
||||||
@ -123,28 +164,33 @@ export function readManifestFromPackageJson(pluginDir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseGitSource(source) {
|
export function parseGitSource(source) {
|
||||||
if (isLocalSource(source)) {
|
const url = getSourceUrl(source)
|
||||||
const resolved = path.resolve(source)
|
const subdir = getSourceSubdir(source)
|
||||||
const name = path.basename(resolved)
|
if (isLocalSource(url)) {
|
||||||
return { name, url: resolved, ref: undefined, local: true }
|
const resolved = path.resolve(url)
|
||||||
|
const name = typeof source === "object" && source.name ? source.name : path.basename(resolved)
|
||||||
|
return { name, url: resolved, ref: undefined, local: true, subdir }
|
||||||
}
|
}
|
||||||
if (source.startsWith("github:")) {
|
if (url.startsWith("github:")) {
|
||||||
const [repoPath, ref] = source.replace("github:", "").split("#")
|
const [repoPath, ref] = url.replace("github:", "").split("#")
|
||||||
const [owner, repo] = repoPath.split("/")
|
const [owner, repo] = repoPath.split("/")
|
||||||
return { name: repo, url: `https://github.com/${owner}/${repo}.git`, ref }
|
const name = typeof source === "object" && source.name ? source.name : repo
|
||||||
|
return { name, url: `https://github.com/${owner}/${repo}.git`, ref, subdir }
|
||||||
}
|
}
|
||||||
if (source.startsWith("git+")) {
|
if (url.startsWith("git+")) {
|
||||||
const raw = source.replace("git+", "")
|
const raw = url.replace("git+", "")
|
||||||
const [url, ref] = raw.split("#")
|
const [parsed, ref] = raw.split("#")
|
||||||
const name = path.basename(url, ".git")
|
const name =
|
||||||
return { name, url, ref }
|
typeof source === "object" && source.name ? source.name : path.basename(parsed, ".git")
|
||||||
|
return { name, url: parsed, ref, subdir }
|
||||||
}
|
}
|
||||||
if (source.startsWith("https://")) {
|
if (url.startsWith("https://")) {
|
||||||
const [url, ref] = source.split("#")
|
const [parsed, ref] = url.split("#")
|
||||||
const name = path.basename(url, ".git")
|
const name =
|
||||||
return { name, url, ref }
|
typeof source === "object" && source.name ? source.name : path.basename(parsed, ".git")
|
||||||
|
return { name, url: parsed, ref, subdir }
|
||||||
}
|
}
|
||||||
throw new Error(`Cannot parse plugin source: ${source}`)
|
throw new Error(`Cannot parse plugin source: ${formatSource(source)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGitCommit(pluginDir) {
|
export function getGitCommit(pluginDir) {
|
||||||
@ -195,6 +241,8 @@ export function getEnrichedPlugins() {
|
|||||||
name,
|
name,
|
||||||
displayName: manifest?.displayName ?? name,
|
displayName: manifest?.displayName ?? name,
|
||||||
source: entry.source,
|
source: entry.source,
|
||||||
|
sourceDisplay: formatSource(entry.source),
|
||||||
|
subdir: getSourceSubdir(entry.source) ?? locked?.subdir ?? undefined,
|
||||||
enabled: entry.enabled ?? true,
|
enabled: entry.enabled ?? true,
|
||||||
options: entry.options ?? {},
|
options: entry.options ?? {},
|
||||||
order: entry.order ?? 50,
|
order: entry.order ?? 50,
|
||||||
|
|||||||
@ -15,12 +15,33 @@ import {
|
|||||||
PLUGINS_DIR,
|
PLUGINS_DIR,
|
||||||
LOCKFILE_PATH,
|
LOCKFILE_PATH,
|
||||||
isLocalSource,
|
isLocalSource,
|
||||||
|
getSourceUrl,
|
||||||
|
formatSource,
|
||||||
} from "./plugin-data.js"
|
} from "./plugin-data.js"
|
||||||
|
|
||||||
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
|
const INTERNAL_EXPORTS = new Set(["manifest", "default"])
|
||||||
|
|
||||||
const execAsync = promisify(execCb)
|
const execAsync = promisify(execCb)
|
||||||
|
|
||||||
|
function cloneWithSubdir({ url, ref, subdir, pluginDir }) {
|
||||||
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "quartz-plugin-"))
|
||||||
|
try {
|
||||||
|
if (ref) {
|
||||||
|
execSync(`git clone --depth 1 --branch ${ref} "${url}" "${tmpDir}"`, { stdio: "ignore" })
|
||||||
|
} else {
|
||||||
|
execSync(`git clone --depth 1 "${url}" "${tmpDir}"`, { stdio: "ignore" })
|
||||||
|
}
|
||||||
|
const subdirPath = path.join(tmpDir, subdir)
|
||||||
|
if (!fs.existsSync(subdirPath)) {
|
||||||
|
throw new Error(`Subdirectory "${subdir}" not found in cloned repository`)
|
||||||
|
}
|
||||||
|
fs.cpSync(subdirPath, pluginDir, { recursive: true })
|
||||||
|
return getGitCommit(tmpDir)
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function buildPlugin(pluginDir, name) {
|
function buildPlugin(pluginDir, name) {
|
||||||
try {
|
try {
|
||||||
const skipBuild = !needsBuild(pluginDir)
|
const skipBuild = !needsBuild(pluginDir)
|
||||||
@ -306,6 +327,18 @@ export async function handlePluginInstall() {
|
|||||||
|
|
||||||
if (fs.existsSync(pluginDir)) {
|
if (fs.existsSync(pluginDir)) {
|
||||||
try {
|
try {
|
||||||
|
if (entry.subdir) {
|
||||||
|
if (!needsBuild(pluginDir)) {
|
||||||
|
console.log(
|
||||||
|
styleText(
|
||||||
|
"gray",
|
||||||
|
` ✓ ${name}@${entry.commit.slice(0, 7)} already installed (subdir)`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
installed++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const currentCommit = getGitCommit(pluginDir)
|
const currentCommit = getGitCommit(pluginDir)
|
||||||
if (currentCommit === entry.commit && !needsBuild(pluginDir)) {
|
if (currentCommit === entry.commit && !needsBuild(pluginDir)) {
|
||||||
console.log(
|
console.log(
|
||||||
@ -315,11 +348,14 @@ export async function handlePluginInstall() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (currentCommit !== entry.commit) {
|
if (currentCommit !== entry.commit) {
|
||||||
console.log(styleText("cyan", ` → ${name}: updating to ${entry.commit.slice(0, 7)}...`))
|
console.log(
|
||||||
|
styleText("cyan", ` → ${name}: updating to ${entry.commit.slice(0, 7)}...`),
|
||||||
|
)
|
||||||
const fetchRef = entry.ref ? ` ${entry.ref}` : ""
|
const fetchRef = entry.ref ? ` ${entry.ref}` : ""
|
||||||
execSync(`git fetch --depth 1 origin${fetchRef}`, { cwd: pluginDir, stdio: "ignore" })
|
execSync(`git fetch --depth 1 origin${fetchRef}`, { cwd: pluginDir, stdio: "ignore" })
|
||||||
execSync(`git reset --hard ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
execSync(`git reset --hard ${entry.commit}`, { cwd: pluginDir, stdio: "ignore" })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pluginsToBuild.push({ name, pluginDir })
|
pluginsToBuild.push({ name, pluginDir })
|
||||||
installed++
|
installed++
|
||||||
} catch {
|
} catch {
|
||||||
@ -328,6 +364,11 @@ export async function handlePluginInstall() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
if (entry.subdir) {
|
||||||
|
console.log(styleText("cyan", ` → ${name}: cloning (subdir: ${entry.subdir})...`))
|
||||||
|
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||||
|
cloneWithSubdir({ url: entry.resolved, ref: entry.ref, subdir: entry.subdir, pluginDir })
|
||||||
|
} else {
|
||||||
console.log(styleText("cyan", ` → ${name}: cloning...`))
|
console.log(styleText("cyan", ` → ${name}: cloning...`))
|
||||||
const branchArg = entry.ref ? ` --branch ${entry.ref}` : ""
|
const branchArg = entry.ref ? ` --branch ${entry.ref}` : ""
|
||||||
execSync(`git clone --depth 1${branchArg} "${entry.resolved}" "${pluginDir}"`, {
|
execSync(`git clone --depth 1${branchArg} "${entry.resolved}" "${pluginDir}"`, {
|
||||||
@ -340,6 +381,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 })
|
pluginsToBuild.push({ name, pluginDir })
|
||||||
installed++
|
installed++
|
||||||
@ -391,7 +433,7 @@ export async function handlePluginAdd(sources) {
|
|||||||
|
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
try {
|
try {
|
||||||
const { name, url, ref, local } = parseGitSource(source)
|
const { name, url, ref, local, subdir } = parseGitSource(source)
|
||||||
const pluginDir = path.join(PLUGINS_DIR, name)
|
const pluginDir = path.join(PLUGINS_DIR, name)
|
||||||
|
|
||||||
if (fs.existsSync(pluginDir)) {
|
if (fs.existsSync(pluginDir)) {
|
||||||
@ -400,8 +442,8 @@ export async function handlePluginAdd(sources) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (local) {
|
if (local) {
|
||||||
// Local path: create symlink instead of git clone
|
let resolvedPath = path.resolve(url)
|
||||||
const resolvedPath = path.resolve(url)
|
if (subdir) resolvedPath = path.join(resolvedPath, subdir)
|
||||||
if (!fs.existsSync(resolvedPath)) {
|
if (!fs.existsSync(resolvedPath)) {
|
||||||
console.log(styleText("red", `✗ Local path does not exist: ${resolvedPath}`))
|
console.log(styleText("red", `✗ Local path does not exist: ${resolvedPath}`))
|
||||||
continue
|
continue
|
||||||
@ -413,10 +455,25 @@ export async function handlePluginAdd(sources) {
|
|||||||
source,
|
source,
|
||||||
resolved: resolvedPath,
|
resolved: resolvedPath,
|
||||||
commit: "local",
|
commit: "local",
|
||||||
|
...(subdir && { subdir }),
|
||||||
installedAt: new Date().toISOString(),
|
installedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
addedPlugins.push({ name, pluginDir, source })
|
addedPlugins.push({ name, pluginDir, source })
|
||||||
console.log(styleText("green", `✓ Added ${name} (local symlink)`))
|
console.log(styleText("green", `✓ Added ${name} (local symlink)`))
|
||||||
|
} else if (subdir) {
|
||||||
|
console.log(styleText("cyan", `→ Adding ${name} from ${url} (subdir: ${subdir})...`))
|
||||||
|
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||||
|
const commit = cloneWithSubdir({ url, ref, subdir, pluginDir })
|
||||||
|
lockfile.plugins[name] = {
|
||||||
|
source,
|
||||||
|
resolved: url,
|
||||||
|
commit,
|
||||||
|
...(ref && { ref }),
|
||||||
|
subdir,
|
||||||
|
installedAt: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
addedPlugins.push({ name, pluginDir, source })
|
||||||
|
console.log(styleText("green", `✓ Added ${name}@${commit.slice(0, 7)} (subdir: ${subdir})`))
|
||||||
} else {
|
} else {
|
||||||
console.log(styleText("cyan", `→ Adding ${name} from ${url}...`))
|
console.log(styleText("cyan", `→ Adding ${name} from ${url}...`))
|
||||||
|
|
||||||
@ -441,7 +498,7 @@ export async function handlePluginAdd(sources) {
|
|||||||
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 ${formatSource(source)}: ${error}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +582,8 @@ export async function handlePluginRemove(names) {
|
|||||||
if (pluginsJson?.plugins) {
|
if (pluginsJson?.plugins) {
|
||||||
pluginsJson.plugins = pluginsJson.plugins.filter(
|
pluginsJson.plugins = pluginsJson.plugins.filter(
|
||||||
(plugin) =>
|
(plugin) =>
|
||||||
!names.includes(extractPluginName(plugin.source)) && !names.includes(plugin.source),
|
!names.includes(extractPluginName(plugin.source)) &&
|
||||||
|
!names.includes(formatSource(plugin.source)),
|
||||||
)
|
)
|
||||||
writePluginsJson(pluginsJson)
|
writePluginsJson(pluginsJson)
|
||||||
}
|
}
|
||||||
@ -542,7 +600,7 @@ export async function handlePluginEnable(names) {
|
|||||||
|
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const entry = json.plugins.find(
|
const entry = json.plugins.find(
|
||||||
(e) => extractPluginName(e.source) === name || e.source === name,
|
(e) => extractPluginName(e.source) === name || formatSource(e.source) === name,
|
||||||
)
|
)
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
console.log(styleText("yellow", `⚠ Plugin "${name}" not found in quartz.config.yaml`))
|
console.log(styleText("yellow", `⚠ Plugin "${name}" not found in quartz.config.yaml`))
|
||||||
@ -568,7 +626,7 @@ export async function handlePluginDisable(names) {
|
|||||||
|
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
const entry = json.plugins.find(
|
const entry = json.plugins.find(
|
||||||
(e) => extractPluginName(e.source) === name || e.source === name,
|
(e) => extractPluginName(e.source) === name || formatSource(e.source) === name,
|
||||||
)
|
)
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
console.log(styleText("yellow", `⚠ Plugin "${name}" not found in quartz.config.yaml`))
|
console.log(styleText("yellow", `⚠ Plugin "${name}" not found in quartz.config.yaml`))
|
||||||
@ -592,7 +650,9 @@ export async function handlePluginConfig(name, options = {}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = json.plugins.find((e) => extractPluginName(e.source) === name || e.source === name)
|
const entry = json.plugins.find(
|
||||||
|
(e) => extractPluginName(e.source) === name || formatSource(e.source) === name,
|
||||||
|
)
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
console.log(styleText("red", `✗ Plugin "${name}" not found in quartz.config.yaml`))
|
console.log(styleText("red", `✗ Plugin "${name}" not found in quartz.config.yaml`))
|
||||||
return
|
return
|
||||||
@ -617,7 +677,7 @@ export async function handlePluginConfig(name, options = {}) {
|
|||||||
console.log(styleText("green", `✓ Set ${name}.${key} = ${JSON.stringify(value)}`))
|
console.log(styleText("green", `✓ Set ${name}.${key} = ${JSON.stringify(value)}`))
|
||||||
} else {
|
} else {
|
||||||
console.log(styleText("bold", `Plugin: ${name}`))
|
console.log(styleText("bold", `Plugin: ${name}`))
|
||||||
console.log(` Source: ${entry.source}`)
|
console.log(` Source: ${formatSource(entry.source)}`)
|
||||||
console.log(` Enabled: ${entry.enabled}`)
|
console.log(` Enabled: ${entry.enabled}`)
|
||||||
console.log(` Order: ${entry.order ?? 50}`)
|
console.log(` Order: ${entry.order ?? 50}`)
|
||||||
if (entry.options && Object.keys(entry.options).length > 0) {
|
if (entry.options && Object.keys(entry.options).length > 0) {
|
||||||
@ -739,6 +799,30 @@ export async function handlePluginUpdate(names) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(styleText("cyan", `→ Updating ${name}...`))
|
console.log(styleText("cyan", `→ Updating ${name}...`))
|
||||||
|
|
||||||
|
if (entry.subdir) {
|
||||||
|
fs.rmSync(pluginDir, { recursive: true })
|
||||||
|
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||||
|
const newCommit = cloneWithSubdir({
|
||||||
|
url: entry.resolved,
|
||||||
|
ref: entry.ref,
|
||||||
|
subdir: entry.subdir,
|
||||||
|
pluginDir,
|
||||||
|
})
|
||||||
|
if (newCommit !== entry.commit) {
|
||||||
|
entry.commit = newCommit
|
||||||
|
entry.installedAt = new Date().toISOString()
|
||||||
|
updatedPlugins.push({ name, pluginDir })
|
||||||
|
console.log(
|
||||||
|
styleText(
|
||||||
|
"green",
|
||||||
|
`✓ Updated ${name} to ${newCommit.slice(0, 7)} (subdir: ${entry.subdir})`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
console.log(styleText("gray", `✓ ${name} already up to date`))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const fetchRef = entry.ref || ""
|
const fetchRef = entry.ref || ""
|
||||||
const resetTarget = entry.ref ? `origin/${entry.ref}` : "origin/HEAD"
|
const resetTarget = entry.ref ? `origin/${entry.ref}` : "origin/HEAD"
|
||||||
execSync(`git fetch --depth 1 origin${fetchRef ? " " + fetchRef : ""}`, {
|
execSync(`git fetch --depth 1 origin${fetchRef ? " " + fetchRef : ""}`, {
|
||||||
@ -756,6 +840,7 @@ export async function handlePluginUpdate(names) {
|
|||||||
} else {
|
} else {
|
||||||
console.log(styleText("gray", `✓ ${name} already up to date`))
|
console.log(styleText("gray", `✓ ${name} already up to date`))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(styleText("red", `✗ Failed to update ${name}: ${error}`))
|
console.log(styleText("red", `✗ Failed to update ${name}: ${error}`))
|
||||||
}
|
}
|
||||||
@ -797,7 +882,7 @@ export async function handlePluginList() {
|
|||||||
const isLinked = exists && fs.lstatSync(pluginDir).isSymbolicLink()
|
const isLinked = exists && fs.lstatSync(pluginDir).isSymbolicLink()
|
||||||
const status = isLinked ? styleText("green", "✓") : styleText("red", "✗")
|
const status = isLinked ? styleText("green", "✓") : styleText("red", "✗")
|
||||||
console.log(` ${status} ${styleText("bold", name)}`)
|
console.log(` ${status} ${styleText("bold", name)}`)
|
||||||
console.log(` Source: ${entry.source}`)
|
console.log(` Source: ${formatSource(entry.source)}`)
|
||||||
console.log(` Type: local symlink`)
|
console.log(` Type: local symlink`)
|
||||||
console.log(` Target: ${entry.resolved}`)
|
console.log(` Target: ${entry.resolved}`)
|
||||||
console.log(` Installed: ${new Date(entry.installedAt).toLocaleDateString()}`)
|
console.log(` Installed: ${new Date(entry.installedAt).toLocaleDateString()}`)
|
||||||
@ -818,7 +903,7 @@ export async function handlePluginList() {
|
|||||||
: styleText("red", "✗")
|
: styleText("red", "✗")
|
||||||
|
|
||||||
console.log(` ${status} ${styleText("bold", name)}`)
|
console.log(` ${status} ${styleText("bold", name)}`)
|
||||||
console.log(` Source: ${entry.source}`)
|
console.log(` Source: ${formatSource(entry.source)}`)
|
||||||
console.log(` Commit: ${entry.commit.slice(0, 7)}`)
|
console.log(` Commit: ${entry.commit.slice(0, 7)}`)
|
||||||
if (currentCommit !== entry.commit && exists) {
|
if (currentCommit !== entry.commit && exists) {
|
||||||
console.log(` Current: ${currentCommit.slice(0, 7)} (modified)`)
|
console.log(` Current: ${currentCommit.slice(0, 7)} (modified)`)
|
||||||
@ -878,6 +963,16 @@ export async function handlePluginRestore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (entry.subdir) {
|
||||||
|
console.log(
|
||||||
|
styleText(
|
||||||
|
"cyan",
|
||||||
|
`→ ${name}: cloning ${entry.resolved}@${entry.commit.slice(0, 7)} (subdir: ${entry.subdir})...`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||||
|
cloneWithSubdir({ url: entry.resolved, ref: entry.ref, subdir: entry.subdir, pluginDir })
|
||||||
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
styleText("cyan", `→ ${name}: cloning ${entry.resolved}@${entry.commit.slice(0, 7)}...`),
|
styleText("cyan", `→ ${name}: cloning ${entry.resolved}@${entry.commit.slice(0, 7)}...`),
|
||||||
)
|
)
|
||||||
@ -886,6 +981,7 @@ export async function handlePluginRestore() {
|
|||||||
stdio: "ignore",
|
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 })
|
restoredPlugins.push({ name, pluginDir })
|
||||||
installed++
|
installed++
|
||||||
@ -997,8 +1093,7 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
const name = extractPluginName(entry.source)
|
const name = extractPluginName(entry.source)
|
||||||
const pluginDir = path.join(PLUGINS_DIR, name)
|
const pluginDir = path.join(PLUGINS_DIR, name)
|
||||||
if (lockfile.plugins[name] && fs.existsSync(pluginDir)) return false
|
if (lockfile.plugins[name] && fs.existsSync(pluginDir)) return false
|
||||||
// Only attempt sources that parseGitSource can handle (git URLs + local paths)
|
const src = getSourceUrl(entry.source)
|
||||||
const src = entry.source
|
|
||||||
return (
|
return (
|
||||||
src.startsWith("github:") ||
|
src.startsWith("github:") ||
|
||||||
src.startsWith("git+") ||
|
src.startsWith("git+") ||
|
||||||
@ -1015,7 +1110,7 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
console.log(`Found ${missing.length} uninstalled plugin(s) in config:\n`)
|
console.log(`Found ${missing.length} uninstalled plugin(s) in config:\n`)
|
||||||
for (const entry of missing) {
|
for (const entry of missing) {
|
||||||
const name = extractPluginName(entry.source)
|
const name = extractPluginName(entry.source)
|
||||||
console.log(` ${styleText("yellow", name)} — ${entry.source}`)
|
console.log(` ${styleText("yellow", name)} — ${formatSource(entry.source)}`)
|
||||||
}
|
}
|
||||||
console.log()
|
console.log()
|
||||||
|
|
||||||
@ -1031,7 +1126,7 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
|
|
||||||
for (const entry of missing) {
|
for (const entry of missing) {
|
||||||
try {
|
try {
|
||||||
const { name, url, ref, local } = parseGitSource(entry.source)
|
const { name, url, ref, local, subdir } = parseGitSource(entry.source)
|
||||||
const pluginDir = path.join(PLUGINS_DIR, name)
|
const pluginDir = path.join(PLUGINS_DIR, name)
|
||||||
|
|
||||||
if (fs.existsSync(pluginDir)) {
|
if (fs.existsSync(pluginDir)) {
|
||||||
@ -1041,6 +1136,7 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
source: entry.source,
|
source: entry.source,
|
||||||
resolved: url,
|
resolved: url,
|
||||||
commit: "local",
|
commit: "local",
|
||||||
|
...(subdir && { subdir }),
|
||||||
installedAt: new Date().toISOString(),
|
installedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
installed.push({ name, pluginDir })
|
installed.push({ name, pluginDir })
|
||||||
@ -1053,6 +1149,7 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
resolved: url,
|
resolved: url,
|
||||||
commit,
|
commit,
|
||||||
...(ref && { ref }),
|
...(ref && { ref }),
|
||||||
|
...(subdir && { subdir }),
|
||||||
installedAt: new Date().toISOString(),
|
installedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
installed.push({ name, pluginDir })
|
installed.push({ name, pluginDir })
|
||||||
@ -1061,7 +1158,8 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
|
|
||||||
if (local) {
|
if (local) {
|
||||||
// Local path: symlink
|
// Local path: symlink
|
||||||
const resolvedPath = path.resolve(url)
|
let resolvedPath = path.resolve(url)
|
||||||
|
if (subdir) resolvedPath = path.join(resolvedPath, subdir)
|
||||||
if (!fs.existsSync(resolvedPath)) {
|
if (!fs.existsSync(resolvedPath)) {
|
||||||
console.log(styleText("red", `✗ Local path does not exist: ${resolvedPath}`))
|
console.log(styleText("red", `✗ Local path does not exist: ${resolvedPath}`))
|
||||||
failed++
|
failed++
|
||||||
@ -1074,10 +1172,27 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
source: entry.source,
|
source: entry.source,
|
||||||
resolved: resolvedPath,
|
resolved: resolvedPath,
|
||||||
commit: "local",
|
commit: "local",
|
||||||
|
...(subdir && { subdir }),
|
||||||
installedAt: new Date().toISOString(),
|
installedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
installed.push({ name, pluginDir })
|
installed.push({ name, pluginDir })
|
||||||
console.log(styleText("green", `✓ Linked ${name} (local)`))
|
console.log(styleText("green", `✓ Linked ${name} (local)`))
|
||||||
|
} else if (subdir) {
|
||||||
|
console.log(styleText("cyan", `→ Cloning ${name} from ${url} (subdir: ${subdir})...`))
|
||||||
|
fs.mkdirSync(path.dirname(pluginDir), { recursive: true })
|
||||||
|
const commit = cloneWithSubdir({ url, ref, subdir, pluginDir })
|
||||||
|
lockfile.plugins[name] = {
|
||||||
|
source: entry.source,
|
||||||
|
resolved: url,
|
||||||
|
commit,
|
||||||
|
...(ref && { ref }),
|
||||||
|
subdir,
|
||||||
|
installedAt: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
installed.push({ name, pluginDir })
|
||||||
|
console.log(
|
||||||
|
styleText("green", `✓ Cloned ${name}@${commit.slice(0, 7)} (subdir: ${subdir})`),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
console.log(styleText("cyan", `→ Cloning ${name} from ${url}...`))
|
console.log(styleText("cyan", `→ Cloning ${name} from ${url}...`))
|
||||||
|
|
||||||
@ -1102,7 +1217,7 @@ export async function handlePluginResolve({ dryRun = false } = {}) {
|
|||||||
console.log(styleText("green", `✓ Cloned ${name}@${commit.slice(0, 7)}`))
|
console.log(styleText("green", `✓ Cloned ${name}@${commit.slice(0, 7)}`))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(styleText("red", `✗ Failed to resolve ${entry.source}: ${error}`))
|
console.log(styleText("red", `✗ Failed to resolve ${formatSource(entry.source)}: ${error}`))
|
||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -246,7 +246,10 @@ plugins:
|
|||||||
position: beforeBody
|
position: beforeBody
|
||||||
priority: 15
|
priority: 15
|
||||||
display: all
|
display: all
|
||||||
- source: github:saberzero1/quartz-themes
|
- source:
|
||||||
|
name: quartz-themes
|
||||||
|
repo: github:saberzero1/quartz-themes
|
||||||
|
subdir: plugin
|
||||||
enabled: true
|
enabled: true
|
||||||
options:
|
options:
|
||||||
theme: default
|
theme: default
|
||||||
|
|||||||
@ -249,7 +249,10 @@ plugins:
|
|||||||
# TTRPG-specific plugins
|
# TTRPG-specific plugins
|
||||||
- source: github:Requiae/quartz-leaflet-map-plugin
|
- source: github:Requiae/quartz-leaflet-map-plugin
|
||||||
enabled: true
|
enabled: true
|
||||||
- source: github:saberzero1/quartz-themes
|
- source:
|
||||||
|
name: quartz-themes
|
||||||
|
repo: github:saberzero1/quartz-themes
|
||||||
|
subdir: plugin
|
||||||
enabled: true
|
enabled: true
|
||||||
options:
|
options:
|
||||||
theme: its-theme
|
theme: its-theme
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user