release: WRNexusJS 0.8.0
This commit is contained in:
@@ -12,8 +12,10 @@ import type {
|
||||
PackageStyleDefinition,
|
||||
PackageRouteDefinition,
|
||||
PackageMigrationDefinition,
|
||||
PluginPermission,
|
||||
} from "./types.ts";
|
||||
import { definePlugin, flattenPlugins } from "./base.ts";
|
||||
import { testPluginCompatibility } from "./compatibility.ts";
|
||||
|
||||
interface PackageJson {
|
||||
name?: string;
|
||||
@@ -32,6 +34,70 @@ export interface DiscoverPluginOptions {
|
||||
strict?: boolean;
|
||||
includePeerDependencies?: boolean;
|
||||
warn?: (message: string) => void;
|
||||
runtime?: string;
|
||||
capabilities?: readonly string[];
|
||||
runtimeVersion?: string;
|
||||
os?: string;
|
||||
/** When true, every requested capability must be declared and granted by the application. */
|
||||
enforcePermissions?: boolean;
|
||||
grantedPermissions?: Readonly<Record<string, readonly PluginPermission[]>>;
|
||||
}
|
||||
|
||||
function manifestPermissions(manifest: WrnexusPackageManifest): PluginPermission[] {
|
||||
const permissions: PluginPermission[] = [];
|
||||
if (manifest.components?.length) permissions.push("components");
|
||||
if (manifest.clientRuntimes?.length) permissions.push("client-runtime");
|
||||
if (manifest.assets?.length) permissions.push("assets");
|
||||
if (manifest.styles?.length) permissions.push("styles");
|
||||
if (manifest.routes?.length) permissions.push("routes");
|
||||
if (manifest.middleware?.length) permissions.push("middleware");
|
||||
if (manifest.migrations?.length) permissions.push("migrations");
|
||||
return permissions;
|
||||
}
|
||||
|
||||
function modulePermissions(plugin: WrnexusPlugin): PluginPermission[] {
|
||||
const permissions: PluginPermission[] = [];
|
||||
if (plugin.componentDirs) permissions.push("components");
|
||||
if (plugin.clientRuntimes) permissions.push("client-runtime");
|
||||
if (plugin.assets) permissions.push("assets");
|
||||
if (plugin.styleSources) permissions.push("styles");
|
||||
if (plugin.routeEntries || plugin.routes) permissions.push("routes");
|
||||
if (plugin.middleware) permissions.push("middleware");
|
||||
if (plugin.migrations) permissions.push("migrations");
|
||||
if (plugin.configure || plugin.configResolved) permissions.push("config");
|
||||
if (plugin.transformAst || plugin.transformCode) permissions.push("transform");
|
||||
if (plugin.diagnostics || plugin.devToolbarPanels) permissions.push("diagnostics");
|
||||
if (plugin.configureServer || plugin.buildStart || plugin.buildEnd) permissions.push("server");
|
||||
if (plugin.cliCommands) permissions.push("cli");
|
||||
if (plugin.directives) permissions.push("directives");
|
||||
if (plugin.virtualModules) permissions.push("virtual-modules");
|
||||
if (plugin.deploymentAdapters || plugin.deploy) permissions.push("deployment");
|
||||
if (plugin.documentation) permissions.push("documentation");
|
||||
if (plugin.typeDefinitions) permissions.push("types");
|
||||
return [...new Set(permissions)];
|
||||
}
|
||||
|
||||
function enforcePermissions(
|
||||
packageName: string,
|
||||
manifest: WrnexusPackageManifest,
|
||||
required: readonly PluginPermission[],
|
||||
options: DiscoverPluginOptions,
|
||||
): void {
|
||||
if (!options.enforcePermissions) return;
|
||||
const declared = new Set(manifest.permissions ?? []);
|
||||
const undeclared = required.filter((permission) => !declared.has(permission));
|
||||
if (undeclared.length) {
|
||||
throw new Error(
|
||||
`WRN-PLUGIN-PERMISSION-UNDECLARED: ${packageName} uses ${undeclared.join(", ")} without declaring them.`,
|
||||
);
|
||||
}
|
||||
const grants = new Set(options.grantedPermissions?.[packageName] ?? []);
|
||||
const denied = required.filter((permission) => !grants.has(permission));
|
||||
if (denied.length) {
|
||||
throw new Error(
|
||||
`WRN-PLUGIN-PERMISSION-DENIED: ${packageName} is not granted ${denied.join(", ")}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function readJson(path: string): PackageJson | null {
|
||||
@@ -244,6 +310,40 @@ export async function discoverPlugins(
|
||||
const packageJson = readJson(join(packageRoot, "package.json"));
|
||||
const packageManifest = packageJson?.wrnexus;
|
||||
if (!packageManifest) continue;
|
||||
enforcePermissions(name, packageManifest, manifestPermissions(packageManifest), options);
|
||||
if (
|
||||
options.runtime &&
|
||||
packageManifest.runtimes?.length &&
|
||||
!packageManifest.runtimes.includes(options.runtime as never)
|
||||
) {
|
||||
throw new Error(`WRN-PLUGIN-RUNTIME: ${name} does not support ${options.runtime}.`);
|
||||
}
|
||||
const missingCapabilities = (packageManifest.requires ?? []).filter(
|
||||
(capability) => !options.capabilities?.includes(capability),
|
||||
);
|
||||
if (options.runtime && missingCapabilities.length) {
|
||||
throw new Error(
|
||||
`WRN-PLUGIN-CAPABILITY: ${name} requires unavailable capabilities: ${missingCapabilities.join(", ")}.`,
|
||||
);
|
||||
}
|
||||
if (options.runtime) {
|
||||
const matrix = testPluginCompatibility(packageManifest, [
|
||||
{
|
||||
runtime: options.runtime as
|
||||
"bun" | "node" | "edge" | "worker" | "service-worker" | "browser",
|
||||
version:
|
||||
options.runtimeVersion ??
|
||||
(options.runtime === "bun" && typeof Bun !== "undefined" ? Bun.version : undefined),
|
||||
os: options.os ?? process.platform,
|
||||
capabilities: options.capabilities,
|
||||
},
|
||||
])[0]!;
|
||||
const compatibilityIssue = matrix.issues.find((issue) =>
|
||||
["WRN-PLUGIN-MATRIX-VERSION", "WRN-PLUGIN-MATRIX-OS"].includes(issue.code),
|
||||
);
|
||||
if (compatibilityIssue)
|
||||
throw new Error(`${compatibilityIssue.code}: ${name}: ${compatibilityIssue.message}`);
|
||||
}
|
||||
const manifestPlugin = manifestContributionPlugin(
|
||||
packageRoot,
|
||||
packageJson?.name ?? name,
|
||||
@@ -258,6 +358,7 @@ export async function discoverPlugins(
|
||||
if (manifest.autoDiscover === false) continue;
|
||||
try {
|
||||
const plugin = await loadPlugin(packageRoot, manifest);
|
||||
enforcePermissions(name, packageManifest, modulePermissions(plugin), options);
|
||||
if (!explicitNames.has(plugin.name) && !automatic.some((item) => item.name === plugin.name)) {
|
||||
automatic.push(plugin);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user