Skip to content

Commit 75041e7

Browse files
authored
Merge pull request #117 from muench-dev/feature/plugin-peer-dependency-check
feat(plugins): validate peerDependency version on plugin load
2 parents f2d5a17 + 4442692 commit 75041e7

8 files changed

Lines changed: 106 additions & 48 deletions

File tree

lib/plugin-loader.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { pathToFileURL } from 'url';
44
import globalDirs from 'global-dirs';
55
import chalk from 'chalk';
66
import { createRequire } from 'module';
7+
import semver from 'semver';
78
import { loadConfig } from './config.js';
89

910
const require = createRequire(import.meta.url);
11+
const TOOL_VERSION = require('../package.json').version;
1012

1113
function expandHomeDirectory(pluginRef) {
1214
if (pluginRef.startsWith('~/') || pluginRef.startsWith('~\\')) {
@@ -114,13 +116,17 @@ export class PluginLoader {
114116
const mageConfigPath = path.join(pluginRoot, 'mage-remote-run.json');
115117
const pkgPath = path.join(pluginRoot, 'package.json');
116118

119+
// Always read package.json to check peer dependencies and static config
120+
let pluginPkg = null;
121+
if (await fs.promises.access(pkgPath).then(() => true).catch(() => false)) {
122+
pluginPkg = JSON.parse(await fs.promises.readFile(pkgPath, 'utf8'));
123+
this._checkPeerDependency(pluginName, pluginPkg);
124+
}
125+
117126
if (await fs.promises.access(mageConfigPath).then(() => true).catch(() => false)) {
118127
staticConfig = JSON.parse(await fs.promises.readFile(mageConfigPath, 'utf8'));
119-
} else if (await fs.promises.access(pkgPath).then(() => true).catch(() => false)) {
120-
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, 'utf8'));
121-
if (pkg['mage-remote-run']) {
122-
staticConfig = pkg['mage-remote-run'];
123-
}
128+
} else if (pluginPkg?.['mage-remote-run']) {
129+
staticConfig = pluginPkg['mage-remote-run'];
124130
}
125131

126132
if (staticConfig) {
@@ -171,4 +177,16 @@ export class PluginLoader {
171177
console.warn(chalk.yellow(`Plugin ${pluginName} could not be loaded because it has no default export and no static config.`));
172178
}
173179
}
180+
181+
_checkPeerDependency(pluginName, pkg) {
182+
const requiredRange = pkg.peerDependencies?.['mage-remote-run'];
183+
if (!requiredRange) return;
184+
185+
if (!semver.satisfies(TOOL_VERSION, requiredRange)) {
186+
console.warn(chalk.yellow(
187+
`Plugin "${pluginName}" requires mage-remote-run "${requiredRange}" but the running version is ${TOOL_VERSION}. ` +
188+
`The plugin may not work correctly.`
189+
));
190+
}
191+
}
174192
}

package-lock.json

Lines changed: 27 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"mkdirp": "^3.0.1",
4848
"oauth-1.0a": "^2.2.6",
4949
"openapi-client-axios": "^7.8.0",
50+
"semver": "^7.8.4",
5051
"zod": "^4.2.1"
5152
},
5253
"devDependencies": {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default async function (appContext) {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "compatible-peer-plugin",
3+
"version": "1.0.0",
4+
"type": "module",
5+
"main": "index.js",
6+
"peerDependencies": {
7+
"mage-remote-run": "^1.0.0"
8+
}
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default async function (appContext) {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "incompatible-peer-plugin",
3+
"version": "1.0.0",
4+
"type": "module",
5+
"main": "index.js",
6+
"peerDependencies": {
7+
"mage-remote-run": "^99.0.0"
8+
}
9+
}

tests/plugin-loader.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const PKG_CONFIG_PLUGIN_PATH = path.join(__dirname, 'fixtures/pkg-config-plugin'
1111
const OBJECT_DEFAULT_WITH_CONFIG_PATH = path.join(__dirname, 'fixtures/object-default-with-config-plugin');
1212
const NESTED_SRC_PLUGIN_PATH = path.join(__dirname, 'fixtures/nested-src-plugin');
1313
const BAD_IMPORT_PLUGIN_PATH = path.join(__dirname, 'fixtures/bad-import-plugin');
14+
const COMPATIBLE_PEER_PLUGIN_PATH = path.join(__dirname, 'fixtures/compatible-peer-plugin');
15+
const INCOMPATIBLE_PEER_PLUGIN_PATH = path.join(__dirname, 'fixtures/incompatible-peer-plugin');
1416

1517
// Mock Config
1618
jest.unstable_mockModule('../lib/config.js', () => ({
@@ -340,4 +342,37 @@ describe('PluginLoader', () => {
340342
delete process.env.DEBUG;
341343
consoleSpy.mockRestore();
342344
});
345+
346+
it('should not warn when plugin peerDependency is satisfied', async () => {
347+
loadConfig.mockResolvedValue({
348+
plugins: [COMPATIBLE_PEER_PLUGIN_PATH]
349+
});
350+
351+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
352+
const loader = new PluginLoader(context);
353+
await loader.loadPlugins();
354+
355+
const peerWarnings = warnSpy.mock.calls.filter(args =>
356+
args.some(a => typeof a === 'string' && a.includes('requires mage-remote-run'))
357+
);
358+
expect(peerWarnings).toHaveLength(0);
359+
warnSpy.mockRestore();
360+
});
361+
362+
it('should warn when plugin peerDependency is not satisfied', async () => {
363+
loadConfig.mockResolvedValue({
364+
plugins: [INCOMPATIBLE_PEER_PLUGIN_PATH]
365+
});
366+
367+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
368+
const loader = new PluginLoader(context);
369+
await loader.loadPlugins();
370+
371+
const peerWarnings = warnSpy.mock.calls.filter(args =>
372+
args.some(a => typeof a === 'string' && a.includes('requires mage-remote-run'))
373+
);
374+
expect(peerWarnings).toHaveLength(1);
375+
expect(peerWarnings[0].join(' ')).toContain('^99.0.0');
376+
warnSpy.mockRestore();
377+
});
343378
});

0 commit comments

Comments
 (0)