UNPKG

@vendure/core

Version:

A modern, headless ecommerce framework

296 lines 13.2 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PluginCollector = void 0; exports.isVendurePluginPackage = isVendurePluginPackage; const common_1 = require("@nestjs/common"); const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const config_service_1 = require("../../config/config.service"); const plugin_metadata_1 = require("../../plugin/plugin-metadata"); /** * Known Vendure plugins mapped to their npm package names. * This is more reliable than require.cache inspection which fails with ESM/TypeScript. */ const KNOWN_VENDURE_PLUGINS = { // @vendure/core DefaultSearchPlugin: '@vendure/core', DefaultJobQueuePlugin: '@vendure/core', DefaultSchedulerPlugin: '@vendure/core', // @vendure/asset-server-plugin AssetServerPlugin: '@vendure/asset-server-plugin', // @vendure/email-plugin EmailPlugin: '@vendure/email-plugin', // @vendure/admin-ui-plugin AdminUiPlugin: '@vendure/admin-ui-plugin', // @vendure/dashboard DashboardPlugin: '@vendure/dashboard', // @vendure/job-queue-plugin BullMQJobQueuePlugin: '@vendure/job-queue-plugin', // @vendure/graphiql-plugin GraphiqlPlugin: '@vendure/graphiql-plugin', // @vendure/harden-plugin HardenPlugin: '@vendure/harden-plugin', // Community plugins (moved to @vendure-community/*) ElasticsearchPlugin: '@vendure-community/elasticsearch-plugin', SentryPlugin: '@vendure-community/sentry-plugin', StripePlugin: '@vendure-community/stripe-plugin', MolliePlugin: '@vendure-community/mollie-plugin', BraintreePlugin: '@vendure-community/braintree-plugin', }; /** * npm package names that identify official Vendure plugins, derived from the * known-plugin map above so there is a single source of truth. */ const KNOWN_VENDURE_PACKAGES = new Set(Object.values(KNOWN_VENDURE_PLUGINS)); /** * Determines whether an npm package name belongs to the Vendure plugin * ecosystem. Deliberately restricted to packages published on the public npm * registry under the official (`@vendure/*-plugin`, plus `@vendure/core`) and * community (`@vendure-community/*`) scopes. * * Arbitrary third-party or privately-named packages are intentionally NOT * matched here, so that scanning the host `package.json` can never transmit a * private or internal package name — preserving the guarantee that custom * plugin names are not collected. Such third-party plugins are still detected * by package name via require.cache when they are actually loaded under * CommonJS. */ function isVendurePluginPackage(name) { return (KNOWN_VENDURE_PACKAGES.has(name) || name.startsWith('@vendure-community/') || (name.startsWith('@vendure/') && name.endsWith('-plugin'))); } /** * Collects information about plugins used in the Vendure installation. * Detects npm packages by checking if the plugin originates from node_modules. * Custom plugin names are NOT collected for privacy. */ let PluginCollector = class PluginCollector { constructor(configService) { this.configService = configService; } collect() { try { const plugins = this.configService.plugins; const npmPlugins = new Set(); let customCount = 0; for (const plugin of plugins) { try { const npmPackage = this.findNpmPackage(plugin); if (npmPackage) { npmPlugins.add(npmPackage); } else { customCount++; } } catch (_a) { customCount++; } } // Also record Vendure ecosystem packages declared in the host // package.json. This filesystem-based detection is ESM-safe and // catches official/community plugin packages (and @vendure/core) // that require.cache inspection misses when modules are loaded as // native ESM. Only public ecosystem packages are matched (see // isVendurePluginPackage), so no private package name is ever sent. for (const pkg of this.getDeclaredVendurePackages()) { npmPlugins.add(pkg); } return { npm: Array.from(npmPlugins).sort((a, b) => a.localeCompare(b)), customCount, }; } catch (_b) { return { npm: [], customCount: 0 }; } } /** * Reads every `package.json` found by walking up from each search directory * and returns the names of declared Vendure plugin packages. Relies only on * the filesystem, so it works regardless of whether plugins were loaded via * CommonJS or native ESM. * * Monorepo-aware: it merges manifests up the tree (stopping at a project * boundary) and searches from both the current working directory and the * application entry point. This covers workspace layouts where plugin * dependencies live in a sub-package and/or the repository root, and where * the process is started from a different directory than the app package. * * Only runtime dependency sections are scanned (`dependencies` and * `optionalDependencies`); `devDependencies` are excluded since they are * not runtime plugins. Returns an empty array on any failure. */ getDeclaredVendurePackages(searchDirs = this.getManifestSearchDirs()) { const found = new Set(); const visited = new Set(); for (const startDir of searchDirs) { for (const pkgPath of this.findPackageJsonPaths(startDir)) { if (visited.has(pkgPath)) { continue; } visited.add(pkgPath); for (const name of this.readVendurePackagesFromManifest(pkgPath)) { found.add(name); } } } return Array.from(found); } /** * Parses a single `package.json` and returns the Vendure ecosystem package * names declared in its runtime dependency sections. Returns an empty array * if the manifest cannot be read or parsed. */ readVendurePackagesFromManifest(pkgPath) { var _a, _b; try { const pkg = JSON.parse(node_fs_1.default.readFileSync(pkgPath, 'utf-8')); const depNames = [ ...Object.keys((_a = pkg.dependencies) !== null && _a !== void 0 ? _a : {}), ...Object.keys((_b = pkg.optionalDependencies) !== null && _b !== void 0 ? _b : {}), ]; return depNames.filter(isVendurePluginPackage); } catch (_c) { return []; } } /** * The directories from which to search for package.json manifests: the * current working directory (the primary signal) and, when resolvable, the * directory of the application entry point — which in a monorepo may sit in * a different workspace package than the cwd. Deduplicated. */ getManifestSearchDirs() { var _a; const dirs = new Set([process.cwd()]); const mainFile = typeof require === 'undefined' ? undefined : (_a = require.main) === null || _a === void 0 ? void 0 : _a.filename; const entryFile = mainFile !== null && mainFile !== void 0 ? mainFile : process.argv[1]; if (entryFile) { dirs.add(node_path_1.default.dirname(entryFile)); } return Array.from(dirs); } /** * Returns the paths of all `package.json` files found by walking up from * `startDir`, stopping at a project boundary — a directory containing a * `.git` entry (repo root) or a `node_modules` directory (install / * workspace root). Both markers exist in real deployments, so the walk * stays inside the project rather than reading unrelated ancestor * manifests. Bounded to a fixed depth as a final safety net. */ findPackageJsonPaths(startDir) { const paths = []; let dir = startDir; for (let i = 0; i < 15; i++) { const candidate = node_path_1.default.join(dir, 'package.json'); if (node_fs_1.default.existsSync(candidate)) { paths.push(candidate); } if (node_fs_1.default.existsSync(node_path_1.default.join(dir, '.git')) || node_fs_1.default.existsSync(node_path_1.default.join(dir, 'node_modules'))) { break; } const parent = node_path_1.default.dirname(dir); if (parent === dir) { break; } dir = parent; } return paths; } /** * Finds the npm package name for a plugin. * First checks against known Vendure plugins, then falls back to require.cache inspection. */ findNpmPackage(plugin) { var _a; const pluginClass = (0, plugin_metadata_1.isDynamicModule)(plugin) ? plugin.module : plugin; if (!pluginClass) { return undefined; } const pluginName = (_a = pluginClass.name) !== null && _a !== void 0 ? _a : 'unknown'; // First, check against known Vendure plugins (most reliable) const knownPackage = KNOWN_VENDURE_PLUGINS[pluginName]; if (knownPackage) { return knownPackage; } // Fall back to require.cache inspection for third-party npm plugins return this.findInRequireCache(pluginClass); } /** * Searches the require cache for a plugin class. * This is a fallback for third-party npm plugins not in our known list. */ findInRequireCache(pluginClass) { // Check if require.cache is available (may not be in ESM-only environments) if (typeof require === 'undefined' || !require.cache) { return undefined; } try { for (const [modulePath, moduleObj] of Object.entries(require.cache)) { if (!(moduleObj === null || moduleObj === void 0 ? void 0 : moduleObj.exports) || !modulePath.includes('node_modules')) { continue; } try { const exports = moduleObj.exports; // Direct match or default export match if (exports === pluginClass || (exports === null || exports === void 0 ? void 0 : exports.default) === pluginClass) { return this.extractPackageName(modulePath); } // Check named exports if (typeof exports === 'object' && exports !== null) { const exportValues = Object.values(exports); if (exportValues.includes(pluginClass)) { return this.extractPackageName(modulePath); } } } catch (_a) { // Skip modules with problematic exports continue; } } } catch (_b) { // Ignore errors accessing require.cache } return undefined; } /** * Extracts the npm package name from a node_modules path. * Handles both scoped (@scope/package) and unscoped packages. */ extractPackageName(modulePath) { const nodeModulesIndex = modulePath.lastIndexOf('node_modules'); if (nodeModulesIndex === -1) { return undefined; } const pathAfterNodeModules = modulePath.slice(nodeModulesIndex + 'node_modules/'.length); const parts = pathAfterNodeModules.split(/[/\\]/); if (parts[0].startsWith('@')) { // Scoped package: @scope/package return `${parts[0]}/${parts[1]}`; } return parts[0]; } }; exports.PluginCollector = PluginCollector; exports.PluginCollector = PluginCollector = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [config_service_1.ConfigService]) ], PluginCollector); //# sourceMappingURL=plugin.collector.js.map