vnite-plugin-sdk
Version:
Vnite Plugin Development SDK
317 lines • 11.4 kB
JavaScript
;
/**
* 插件开发工具函数
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginPackager = exports.VersionUtils = void 0;
exports.validateManifest = validateManifest;
exports.createManifestTemplate = createManifestTemplate;
exports.generatePluginId = generatePluginId;
const types_1 = require("./types");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
// 读取 package.json 版本 - 使用安全的路径解析
const getPackageJson = () => {
try {
const packagePath = path.resolve(__dirname, "../package.json");
if (fs.existsSync(packagePath)) {
return JSON.parse(fs.readFileSync(packagePath, "utf8"));
}
// 备用路径(从根目录查找)
const rootPackagePath = path.resolve(process.cwd(), "package.json");
if (fs.existsSync(rootPackagePath)) {
return JSON.parse(fs.readFileSync(rootPackagePath, "utf8"));
}
// 默认值
return { version: "1.0.0" };
}
catch (error) {
return { version: "1.0.0" };
}
};
const packageJson = getPackageJson();
/**
* 验证插件清单文件
*/
function validateManifest(manifest) {
return !!(manifest.id &&
manifest.name &&
manifest.version &&
manifest.main &&
manifest.vniteVersion);
}
/**
* 创建插件清单模板
*/
function createManifestTemplate(pluginId, pluginName) {
return {
id: pluginId,
name: pluginName,
version: "1.0.0",
description: "A Vnite plugin",
author: "Plugin Author",
vniteVersion: "^1.0.0",
main: "dist/index.js",
category: types_1.PluginCategory.UTILITY,
};
}
/**
* 生成插件ID(基于名称)
*/
function generatePluginId(name) {
return name
.toLowerCase()
.replace(/[^a-z0-9]/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}
/**
* 版本比较工具
*/
class VersionUtils {
/**
* 比较两个版本号
* @returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal
*/
static compare(v1, v2) {
const normalize = (version) => {
const parts = version.replace(/[^0-9.]/g, "").split(".");
return parts.map((part) => parseInt(part, 10) || 0);
};
const parts1 = normalize(v1);
const parts2 = normalize(v2);
const maxLength = Math.max(parts1.length, parts2.length);
for (let i = 0; i < maxLength; i++) {
const part1 = parts1[i] || 0;
const part2 = parts2[i] || 0;
if (part1 > part2)
return 1;
if (part1 < part2)
return -1;
}
return 0;
}
/**
* 检查版本是否满足要求
*/
static satisfies(version, requirement) {
// 简单的版本检查,支持 ^1.0.0 格式
if (requirement.startsWith("^")) {
const requiredVersion = requirement.slice(1);
const majorVersion = requiredVersion.split(".")[0];
const actualMajorVersion = version.split(".")[0];
return (actualMajorVersion === majorVersion &&
this.compare(version, requiredVersion) >= 0);
}
// 精确匹配
return this.compare(version, requirement) === 0;
}
}
exports.VersionUtils = VersionUtils;
/**
* 插件打包工具
*/
class PluginPackager {
/**
* 打包插件为 .vnpkg 文件
*/
static async packagePlugin(options) {
const { sourceDir, outputDir = sourceDir, manifestPath, excludePatterns = [], } = options;
// 默认排除模式
const defaultExcludes = [
"node_modules/**",
"src/**",
"*.ts",
"*.map",
".git/**",
".vscode/**",
"tsconfig.json",
".gitignore",
"README.md",
];
const allExcludes = [...defaultExcludes, ...excludePatterns];
// 读取清单文件
const manifest = this.loadManifest(sourceDir, manifestPath);
// 验证清单
if (!validateManifest(manifest)) {
throw new Error("Invalid plugin manifest");
}
// 检查必需文件
const mainFile = path.join(sourceDir, manifest.main);
if (!fs.existsSync(mainFile)) {
throw new Error(`Main file not found: ${manifest.main}`);
}
// 创建打包文件夹
const tempDir = path.join(outputDir, ".temp-package");
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true });
}
fs.mkdirSync(tempDir, { recursive: true });
try {
// 复制文件
await this.copyFiles(sourceDir, tempDir, allExcludes); // 生成最终的 package.json(确保主应用能识别)
const finalManifest = {
...manifest,
packagedAt: new Date().toISOString(),
sdkVersion: packageJson.version,
};
fs.writeFileSync(path.join(tempDir, "package.json"), JSON.stringify(finalManifest, null, 2));
// 创建 .vnpkg 文件(实际上是zip格式)
const outputFile = path.join(outputDir, `${manifest.id}-${manifest.version}.vnpkg`);
await this.createArchive(tempDir, outputFile);
return outputFile;
}
finally {
// 清理临时文件
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true });
}
}
}
/**
* 加载插件清单
*/
static loadManifest(sourceDir, manifestPath) {
const manifestFile = manifestPath || path.join(sourceDir, "package.json");
if (!fs.existsSync(manifestFile)) {
throw new Error(`Manifest file not found: ${manifestFile}`);
}
const content = fs.readFileSync(manifestFile, "utf8");
return JSON.parse(content);
}
/**
* 复制文件(排除指定模式)
*/
static async copyFiles(sourceDir, targetDir, excludePatterns) {
const copyRecursive = (src, dest) => {
const stat = fs.statSync(src);
if (stat.isDirectory()) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}
const files = fs.readdirSync(src);
for (const file of files) {
const srcPath = path.join(src, file);
const destPath = path.join(dest, file);
const relativePath = path
.relative(sourceDir, srcPath)
.replace(/\\/g, "/");
// 检查是否应该排除
const shouldExclude = excludePatterns.some((pattern) => {
if (pattern.endsWith("/**")) {
const dir = pattern.slice(0, -3);
return relativePath.startsWith(dir + "/") || relativePath === dir;
}
if (pattern.includes("*")) {
return this.matchPattern(relativePath, pattern);
}
return (relativePath === pattern || relativePath.endsWith("/" + pattern));
});
if (!shouldExclude) {
copyRecursive(srcPath, destPath);
}
}
}
else {
fs.copyFileSync(src, dest);
}
};
copyRecursive(sourceDir, targetDir);
}
/**
* 简单的通配符匹配
*/
static matchPattern(text, pattern) {
const regexPattern = pattern
.replace(/\./g, "\\.")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
return new RegExp(`^${regexPattern}$`).test(text);
}
/**
* 创建压缩包
*/
static async createArchive(sourceDir, outputFile) {
// 使用简单的方式创建压缩包
// 在实际项目中可能需要使用更专业的压缩库如 archiver
try {
// 尝试使用系统的压缩工具
if (process.platform === "win32") {
// Windows 使用 PowerShell
const command = `Compress-Archive -Path "${sourceDir}\\*" -DestinationPath "${outputFile}" -Force`;
(0, child_process_1.execSync)(`powershell -Command "${command}"`, { stdio: "inherit" });
}
else {
// Unix 系统使用 zip
(0, child_process_1.execSync)(`cd "${sourceDir}" && zip -r "${outputFile}" .`, {
stdio: "inherit",
});
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to create archive: ${errorMessage}`);
}
}
/**
* 验证打包的插件
*/
static validatePackage(packagePath) {
return new Promise((resolve) => {
const errors = [];
try {
if (!fs.existsSync(packagePath)) {
errors.push("Package file does not exist");
return resolve({ valid: false, errors });
}
// TODO: 这里应该解压并验证包内容
// 目前简化处理
resolve({
valid: errors.length === 0,
errors,
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push(`Validation error: ${errorMessage}`);
resolve({ valid: false, errors });
}
});
}
}
exports.PluginPackager = PluginPackager;
//# sourceMappingURL=utils.js.map