|
|
@@ -55,90 +55,99 @@ async function addApiExtension(
|
|
|
|
|
|
// Detect non-interactive mode
|
|
|
const isNonInteractive = options?.isNonInteractive === true;
|
|
|
-
|
|
|
+
|
|
|
let plugin: VendurePluginRef | undefined = providedVendurePlugin;
|
|
|
-
|
|
|
+
|
|
|
// If a plugin name was provided, try to find it
|
|
|
if (!plugin && options?.pluginName) {
|
|
|
const pluginClasses = getPluginClasses(project);
|
|
|
const foundPlugin = pluginClasses.find(p => p.getName() === options.pluginName);
|
|
|
-
|
|
|
+
|
|
|
if (!foundPlugin) {
|
|
|
// List available plugins if the specified one wasn't found
|
|
|
const availablePlugins = pluginClasses.map(p => p.getName()).filter(Boolean);
|
|
|
throw new Error(
|
|
|
`Plugin "${options.pluginName}" not found. Available plugins:\n` +
|
|
|
- availablePlugins.map(name => ` - ${name}`).join('\n')
|
|
|
+ availablePlugins.map(name => ` - ${name as string}`).join('\n')
|
|
|
);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
plugin = new VendurePluginRef(foundPlugin);
|
|
|
}
|
|
|
-
|
|
|
- // In non-interactive mode, we need all required values
|
|
|
+
|
|
|
+ // In non-interactive mode, we need all required values upfront
|
|
|
if (isNonInteractive) {
|
|
|
if (!plugin) {
|
|
|
throw new Error('Plugin must be specified when running in non-interactive mode');
|
|
|
}
|
|
|
- // Don't require query/mutation names - we'll use defaults
|
|
|
+ // Require names to be specified explicitly
|
|
|
+ if (!options?.queryName && !options?.mutationName) {
|
|
|
+ throw new Error(
|
|
|
+ 'At least one of queryName or mutationName must be specified in non-interactive mode.\n' +
|
|
|
+ 'Usage: npx vendure add -a <PluginName> --queryName <name> --mutationName <name>'
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- plugin = plugin ?? (await selectPlugin(project, cancelledMessage));
|
|
|
-
|
|
|
- // In non-interactive mode, we cannot prompt for service selection
|
|
|
+ // In non-interactive mode, we cannot prompt for plugin selection
|
|
|
if (isNonInteractive && !plugin) {
|
|
|
- throw new Error('Cannot select service in non-interactive mode - plugin must be specified');
|
|
|
+ throw new Error('Cannot select plugin in non-interactive mode - plugin must be specified');
|
|
|
}
|
|
|
-
|
|
|
- let serviceRef: ServiceRef | undefined;
|
|
|
-
|
|
|
+
|
|
|
+ plugin = plugin ?? (await selectPlugin(project, cancelledMessage));
|
|
|
+
|
|
|
if (isNonInteractive) {
|
|
|
- // In non-interactive mode, find existing services or create a new one
|
|
|
- const existingServices = getServices(project).filter(sr => {
|
|
|
- return sr.classDeclaration
|
|
|
- .getSourceFile()
|
|
|
- .getDirectoryPath()
|
|
|
- .includes(plugin.getSourceFile().getDirectoryPath());
|
|
|
+ // In non-interactive mode, require explicit service specification
|
|
|
+ throw new Error(
|
|
|
+ 'Service selection is not supported in non-interactive mode.\n' +
|
|
|
+ 'Please first create a service using: npx vendure add -s <ServiceName>\n' +
|
|
|
+ 'Then add the API extension interactively.'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ const services = getServices(project).filter(sr => {
|
|
|
+ return sr.classDeclaration
|
|
|
+ .getSourceFile()
|
|
|
+ .getDirectoryPath()
|
|
|
+ .includes(plugin.getSourceFile().getDirectoryPath());
|
|
|
+ });
|
|
|
+
|
|
|
+ let serviceRef: ServiceRef | undefined;
|
|
|
+ let serviceEntityRef: EntityRef | undefined;
|
|
|
+
|
|
|
+ const scaffoldSpinner = spinner();
|
|
|
+
|
|
|
+ if (services.length === 0) {
|
|
|
+ log.info('No services found in the selected plugin. Let\'s create one first!');
|
|
|
+ const result = await addServiceCommand.run({
|
|
|
+ plugin,
|
|
|
});
|
|
|
-
|
|
|
- if (existingServices.length > 0) {
|
|
|
- // Use the first available service
|
|
|
- serviceRef = existingServices[0];
|
|
|
- log.info(`Using existing service: ${serviceRef.name}`);
|
|
|
- } else {
|
|
|
- // Create a new service automatically
|
|
|
- log.info('No existing services found, creating a new service...');
|
|
|
- const result = await addServiceCommand.run({
|
|
|
- type: 'basic',
|
|
|
- plugin,
|
|
|
- serviceName: 'GeneratedService',
|
|
|
- isNonInteractive: true
|
|
|
- });
|
|
|
- serviceRef = result.serviceRef;
|
|
|
- }
|
|
|
+ serviceRef = result.serviceRef;
|
|
|
} else {
|
|
|
- // Interactive mode - let user choose
|
|
|
- serviceRef = await selectServiceRef(project, plugin, false);
|
|
|
+ serviceRef = await selectServiceRef(project, plugin);
|
|
|
}
|
|
|
|
|
|
if (!serviceRef) {
|
|
|
- throw new Error('Service is required for API extension');
|
|
|
+ cancel(cancelledMessage);
|
|
|
+ process.exit(0);
|
|
|
}
|
|
|
|
|
|
- const serviceEntityRef = serviceRef.crudEntityRef;
|
|
|
const modifiedSourceFiles: SourceFile[] = [];
|
|
|
- let resolver: ClassDeclaration | undefined;
|
|
|
- let apiExtensions: VariableDeclaration | undefined;
|
|
|
|
|
|
- const scaffoldSpinner = spinner();
|
|
|
+ if (serviceRef.crudEntityRef) {
|
|
|
+ serviceEntityRef = serviceRef.crudEntityRef;
|
|
|
+ }
|
|
|
+
|
|
|
+ let resolver: ClassDeclaration;
|
|
|
+ let apiExtensions: VariableDeclaration | undefined;
|
|
|
|
|
|
let queryName = '';
|
|
|
let mutationName = '';
|
|
|
if (!serviceEntityRef) {
|
|
|
if (isNonInteractive) {
|
|
|
- // Use defaults in non-interactive mode
|
|
|
- queryName = options?.queryName || 'customQuery';
|
|
|
- mutationName = options?.mutationName || 'customMutation';
|
|
|
+ // Use provided values - we already validated at least one exists
|
|
|
+ queryName = options?.queryName || '';
|
|
|
+ mutationName = options?.mutationName || '';
|
|
|
} else {
|
|
|
const queryNameResult = options?.queryName ?? await text({
|
|
|
message: 'Enter a name for the new query',
|