Skip to content

Commit d680c5b

Browse files
committed
fix: resolve plugin paths against the schema file where the plugin is declared
1 parent 2845e47 commit d680c5b

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

packages/cli/src/actions/check.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ async function checkPluginResolution(schemaFile: string, model: Model) {
2929
for (const plugin of plugins) {
3030
const provider = getPluginProvider(plugin);
3131
if (!provider.startsWith('@core/')) {
32-
await loadPluginModule(provider, path.dirname(schemaFile));
32+
const pluginSourcePath =
33+
plugin.$cstNode?.parent?.element.$document?.uri?.fsPath ?? schemaFile;
34+
await loadPluginModule(provider, path.dirname(pluginSourcePath));
3335
}
3436
}
3537
}

packages/cli/src/actions/generate.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,11 @@ async function runPlugins(schemaFile: string, model: Model, outputPath: string,
186186
throw new CliError(`Unknown core plugin: ${provider}`);
187187
}
188188
} else {
189-
cliPlugin = await loadPluginModule(provider, path.dirname(schemaFile));
189+
// resolve relative plugin paths against the schema file where the plugin is declared,
190+
// not the entry schema file
191+
const pluginSourcePath =
192+
plugin.$cstNode?.parent?.element.$document?.uri?.fsPath ?? schemaFile;
193+
cliPlugin = await loadPluginModule(provider, path.dirname(pluginSourcePath));
190194
}
191195

192196
if (cliPlugin) {

packages/cli/test/generate.test.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { formatDocument } from '@zenstackhq/language';
12
import fs from 'node:fs';
23
import path from 'node:path';
34
import { describe, expect, it } from 'vitest';
4-
import { createProject, runCli } from './utils';
5+
import { createProject, getDefaultPrelude, runCli } from './utils';
56

67
const model = `
78
model User {
@@ -306,6 +307,50 @@ export default plugin;
306307
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
307308
});
308309

310+
it('should resolve plugin paths relative to the schema file where the plugin is declared', async () => {
311+
// Entry schema imports a sub-schema that declares a plugin with a relative path.
312+
// The plugin path should resolve relative to the sub-schema, not the entry schema.
313+
const { workDir } = await createProject(
314+
`import './core/core'
315+
316+
${getDefaultPrelude()}
317+
318+
model User {
319+
id String @id @default(cuid())
320+
}
321+
`,
322+
{ customPrelude: true },
323+
);
324+
325+
// Create core/ subdirectory with its own schema and plugin
326+
const coreDir = path.join(workDir, 'zenstack/core');
327+
fs.mkdirSync(coreDir, { recursive: true });
328+
329+
const coreSchema = await formatDocument(`
330+
plugin foo {
331+
provider = './my-core-plugin.ts'
332+
}
333+
`);
334+
fs.writeFileSync(path.join(coreDir, 'core.zmodel'), coreSchema);
335+
336+
// Plugin lives next to the core schema, NOT next to the entry schema
337+
fs.writeFileSync(
338+
path.join(coreDir, 'my-core-plugin.ts'),
339+
`
340+
const plugin = {
341+
name: 'core-plugin',
342+
statusText: 'Testing core plugin',
343+
async generate() {},
344+
};
345+
export default plugin;
346+
`,
347+
);
348+
349+
// This would fail if the plugin path was resolved relative to the entry schema
350+
runCli('generate', workDir);
351+
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
352+
});
353+
309354
it('should prefer CLI options over @core/typescript plugin settings for generateModels and generateInput', async () => {
310355
const modelWithPlugin = `
311356
plugin typescript {

0 commit comments

Comments
 (0)