|
#!/usr/bin/env node |
|
|
|
const fsPromises = require("fs").promises; |
|
const fs = require("fs"); |
|
const util = require("util"); |
|
const path = require("path"); |
|
|
|
const exec = util.promisify(require("child_process").exec); |
|
|
|
const powercordPath = process.env.POWERCORD_PATH; |
|
|
|
if (!powercordPath) { |
|
console.error("POWERCORD_PATH environment variable not set"); |
|
process.exit(1); |
|
} |
|
|
|
const plugins = path.join(powercordPath, "src", "Powercord", "plugins"); |
|
const themes = path.join(powercordPath, "src", "Powercord", "themes"); |
|
|
|
const args = process.argv.slice(2); |
|
|
|
const getPackages = async () => { |
|
const pluginNames = ( |
|
await fsPromises.readdir(plugins, { withFileTypes: true }) |
|
) |
|
.filter((f) => f.isDirectory()) |
|
.map((dir) => dir.name) |
|
.filter((name) => !name.startsWith("pc-")); |
|
|
|
const themeNames = (await fsPromises.readdir(themes, { withFileTypes: true })) |
|
.filter((f) => f.isDirectory()) |
|
.map((dir) => dir.name); |
|
|
|
const info = await Promise.all( |
|
[ |
|
...pluginNames.map((n) => ({ name: n, type: "plugin" })), |
|
...themeNames.map((n) => ({ name: n, type: "theme" })), |
|
].map(async ({ name, type }) => { |
|
const packagePath = path.join(type === "plugin" ? plugins : themes, name); |
|
|
|
const { stdout } = await exec("git config --get remote.origin.url", { |
|
cwd: packagePath, |
|
}); |
|
|
|
return { |
|
name, |
|
url: stdout.trim(), |
|
type: type, |
|
path: packagePath, |
|
}; |
|
}) |
|
); |
|
|
|
return info; |
|
}; |
|
|
|
const getPackageName = (url) => { |
|
return path.basename(url, ".git"); |
|
}; |
|
|
|
const packageInstalled = (url, packageList) => { |
|
return !!packageList.find( |
|
(i) => i.url === url || i.name === getPackageName(url) |
|
); |
|
}; |
|
|
|
(async () => { |
|
switch (args[0]) { |
|
case "install": { |
|
const [, ...packages] = args; |
|
|
|
if (packages.length === 0) { |
|
console.error( |
|
"Usage: ppm install <theme|plugin:git_url> [theme|plugin:git_url...]" |
|
); |
|
process.exit(1); |
|
} |
|
|
|
const packageList = await getPackages(); |
|
|
|
const targets = packages.map((p) => { |
|
if (p.startsWith("theme:")) { |
|
const url = p.slice(6); |
|
|
|
if (packageInstalled(url, packageList)) { |
|
console.error(`Package ${getPackageName(p)} already installed`); |
|
process.exit(1); |
|
} |
|
|
|
return { |
|
type: "theme", |
|
url, |
|
}; |
|
} else if (p.startsWith("plugin:")) { |
|
const url = p.slice(7); |
|
|
|
if (packageInstalled(url, packageList)) { |
|
console.error(`Package ${getPackageName(p)} already installed`); |
|
process.exit(1); |
|
} |
|
|
|
return { |
|
type: "plugin", |
|
url, |
|
}; |
|
} else { |
|
console.error( |
|
`Package ${p} is not a valid package target, must be either "theme:<git_url>" or "plugin:<git_url>"` |
|
); |
|
process.exit(1); |
|
} |
|
}); |
|
|
|
await Promise.all( |
|
targets.map(async ({ url, type }) => { |
|
await exec(`git clone ${url}`, { |
|
cwd: type === "theme" ? themes : plugins, |
|
}); |
|
}) |
|
); |
|
|
|
console.log(`Installed ${targets.length} packages successfully!`); |
|
|
|
return; |
|
} |
|
|
|
case "remove": { |
|
const [, ...packages] = args; |
|
|
|
if (packages.length === 0) { |
|
console.error("Usage: ppm remove <package_name> [package_name...]"); |
|
process.exit(1); |
|
} |
|
|
|
const packageList = await getPackages(); |
|
|
|
// Initial check to make sure the packages exist |
|
const resolved = packages.map((package) => { |
|
const found = packageList.find((p) => p.name === package); |
|
|
|
if (!found) { |
|
return { name: package, found: false }; |
|
} |
|
|
|
return { path: found.path, found: true }; |
|
}); |
|
|
|
const notFound = resolved |
|
.filter((package) => !package.found) |
|
.map((p) => p.name); |
|
|
|
if (notFound.length > 0) { |
|
console.error( |
|
`The following packages are not installed: ${notFound.join(", ")}` |
|
); |
|
process.exit(1); |
|
} |
|
|
|
await Promise.all( |
|
resolved.map(async ({ path }) => |
|
fsPromises.rm(path, { |
|
recursive: true, |
|
force: true, |
|
}) |
|
) |
|
); |
|
|
|
console.log(`Removed ${resolved.length} packages successfully!`); |
|
|
|
return; |
|
} |
|
|
|
case "upgrade": { |
|
console.log( |
|
"NOTE: This command isn't necessary for most users. Powercord will automatically upgrade plugins through its own mechanism. Use this only if Powercord fails." |
|
); |
|
|
|
const packageList = await getPackages(); |
|
const paths = packageList.map((p) => p.path); |
|
|
|
await Promise.all( |
|
paths.map(async (p) => { |
|
await exec(`git pull`, { cwd: p }); |
|
}) |
|
); |
|
|
|
console.log(`Upgraded ${paths.length} packages successfully!`); |
|
|
|
return; |
|
} |
|
|
|
case "list": { |
|
const packages = await getPackages(); |
|
|
|
console.log( |
|
`${packages.length} packages installed:\n${packages |
|
.map((i) => `${i.type === "theme" ? "T" : "P"} ${i.name} - ${i.url}`) |
|
.join("\n")}` |
|
); |
|
|
|
return; |
|
} |
|
|
|
case "export": { |
|
const [, out] = args; |
|
|
|
if (!out) { |
|
console.error("Usage: ppm export <out_file>"); |
|
process.exit(1); |
|
} |
|
|
|
const packages = (await getPackages()).map((p) => ({ |
|
type: p.type, |
|
url: p.url, |
|
})); |
|
|
|
await fsPromises.writeFile(out, JSON.stringify(packages, null, 2)); |
|
|
|
console.log(`Exported ${packages.length} packages successfully!`); |
|
|
|
return; |
|
} |
|
|
|
case "import": { |
|
const [, in_file] = args; |
|
|
|
if (!in_file) { |
|
console.error("Usage: ppm import <in_file>"); |
|
process.exit(1); |
|
} |
|
|
|
const packageList = await getPackages(); |
|
const packages = JSON.parse(await fsPromises.readFile(in_file)); |
|
|
|
const result = ( |
|
await Promise.all( |
|
packages.map(async ({ url, type }) => { |
|
if (packageInstalled(url, packageList)) { |
|
console.error( |
|
`Package ${getPackageName( |
|
url |
|
)} already installed, not installing` |
|
); |
|
return null; |
|
} |
|
|
|
try { |
|
await exec(`git clone ${url}`, { |
|
cwd: type === "theme" ? themes : plugins, |
|
}); |
|
|
|
return { status: true, url: url }; |
|
} catch (e) { |
|
console.error(`Failed to import ${url} because:`); |
|
console.error(e); |
|
console.error("Continuing import..."); |
|
|
|
return { status: false, url: url }; |
|
} |
|
}) |
|
) |
|
).filter((i) => i !== null); |
|
|
|
console.log( |
|
`Imported and installed ${ |
|
result.filter((p) => p.status).length |
|
} packages successfully!` |
|
); |
|
|
|
const failed = result.filter((p) => !p.status); |
|
|
|
if (failed.length > 0) { |
|
console.error(`Failed to import ${failed.length} packages:`); |
|
console.error(failed.map((p) => p.url).join("\n")); |
|
} |
|
|
|
return; |
|
} |
|
|
|
default: { |
|
console.error( |
|
[ |
|
"A super simple ~~bodgy~~ package manager for Powercord", |
|
"By @lleyton (Leа#7962 on Discord, the a is a special character, so copy it)", |
|
"", |
|
"Usage: ppm <command> [args...]", |
|
"Commands:", |
|
" install <theme|plugin:git_url> [theme|plugin:git_url...]", |
|
" remove <package_name> [package_name...]", |
|
" upgrade", |
|
" list", |
|
" export <out_file>", |
|
" import <in_file>", |
|
].join("\n") |
|
); |
|
|
|
return; |
|
} |
|
} |
|
})(); |