فهرست منبع

fix(cli): Include plugin options in service constructor

Michael Bromley 1 سال پیش
والد
کامیت
a77251e441
2فایلهای تغییر یافته به همراه80 افزوده شده و 8 حذف شده
  1. 30 7
      packages/cli/src/commands/add/service/add-service.ts
  2. 50 1
      packages/cli/src/shared/vendure-plugin-ref.ts

+ 30 - 7
packages/cli/src/commands/add/service/add-service.ts

@@ -1,7 +1,7 @@
 import { cancel, isCancel, log, select, spinner, text } from '@clack/prompts';
 import { paramCase } from 'change-case';
 import path from 'path';
-import { ClassDeclaration, SourceFile } from 'ts-morph';
+import { ClassDeclaration, Scope, SourceFile } from 'ts-morph';
 
 import { Messages, pascalCaseRegex } from '../../../constants';
 import { CliCommand, CliCommandReturnVal } from '../../../shared/cli-command';
@@ -77,13 +77,8 @@ async function addService(
     }
 
     const serviceSpinner = spinner();
-    const serviceFileName = paramCase(options.serviceName).replace(/-service$/, '.service');
+
     let serviceSourceFile: SourceFile;
-    const serviceSourceFilePath = path.join(
-        vendurePlugin.getPluginDir().getPath(),
-        'services',
-        `${serviceFileName}.ts`,
-    );
     let serviceClassDeclaration: ClassDeclaration;
     if (options.type === 'basic') {
         const name = await text({
@@ -106,6 +101,7 @@ async function addService(
 
         options.serviceName = name;
         serviceSpinner.start(`Creating ${options.serviceName}...`);
+        const serviceSourceFilePath = getServiceFilePath(vendurePlugin, options.serviceName);
         await pauseForPromptDisplay();
         serviceSourceFile = createFile(
             project,
@@ -119,6 +115,7 @@ async function addService(
     } else {
         serviceSpinner.start(`Creating ${options.serviceName}...`);
         await pauseForPromptDisplay();
+        const serviceSourceFilePath = getServiceFilePath(vendurePlugin, options.serviceName);
         serviceSourceFile = createFile(
             project,
             path.join(__dirname, 'templates/entity-service.template.ts'),
@@ -163,6 +160,27 @@ async function addService(
         customizeUpdateMethod(serviceClassDeclaration, entityRef);
         removedUnusedConstructorArgs(serviceClassDeclaration, entityRef);
     }
+    const pluginOptions = vendurePlugin.getPluginOptions();
+    if (pluginOptions) {
+        addImportsToFile(serviceSourceFile, {
+            moduleSpecifier: pluginOptions.constantDeclaration.getSourceFile(),
+            namedImports: [pluginOptions.constantDeclaration.getName()],
+        });
+        addImportsToFile(serviceSourceFile, {
+            moduleSpecifier: pluginOptions.typeDeclaration.getSourceFile(),
+            namedImports: [pluginOptions.typeDeclaration.getName()],
+        });
+        addImportsToFile(serviceSourceFile, {
+            moduleSpecifier: '@nestjs/common',
+            namedImports: ['Inject'],
+        });
+        serviceClassDeclaration.getConstructors()[0]?.addParameter({
+            scope: Scope.Private,
+            name: 'options',
+            type: pluginOptions.typeDeclaration.getName(),
+            decorators: [{ name: 'Inject', arguments: [pluginOptions.constantDeclaration.getName()] }],
+        });
+    }
     modifiedSourceFiles.push(serviceSourceFile);
 
     serviceSpinner.message(`Registering service with plugin...`);
@@ -184,6 +202,11 @@ async function addService(
     };
 }
 
+function getServiceFilePath(plugin: VendurePluginRef, serviceName: string) {
+    const serviceFileName = paramCase(serviceName).replace(/-service$/, '.service');
+    return path.join(plugin.getPluginDir().getPath(), 'services', `${serviceFileName}.ts`);
+}
+
 function customizeFindOneMethod(serviceClassDeclaration: ClassDeclaration, entityRef: EntityRef) {
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     const findOneMethod = serviceClassDeclaration.getMethod('findOne')!;

+ 50 - 1
packages/cli/src/shared/vendure-plugin-ref.ts

@@ -1,4 +1,13 @@
-import { ClassDeclaration, Node, StructureKind, SyntaxKind, VariableDeclaration } from 'ts-morph';
+import {
+    ClassDeclaration,
+    InterfaceDeclaration,
+    Node,
+    PropertyAssignment,
+    StructureKind,
+    SyntaxKind,
+    Type,
+    VariableDeclaration,
+} from 'ts-morph';
 
 import { AdminUiExtensionTypeName } from '../constants';
 
@@ -31,6 +40,46 @@ export class VendurePluginRef {
         return pluginOptions;
     }
 
+    getPluginOptions():
+        | { typeDeclaration: InterfaceDeclaration; constantDeclaration: VariableDeclaration }
+        | undefined {
+        const metadataOptions = this.getMetadataOptions();
+        const staticOptions = this.classDeclaration.getStaticProperty('options');
+        const typeDeclaration = staticOptions
+            ?.getType()
+            .getSymbolOrThrow()
+            .getDeclarations()
+            .find(d => Node.isInterfaceDeclaration(d));
+        if (!typeDeclaration || !Node.isInterfaceDeclaration(typeDeclaration)) {
+            return;
+        }
+        const providersArray = metadataOptions
+            .getProperty('providers')
+            ?.getFirstChildByKind(SyntaxKind.ArrayLiteralExpression);
+        if (!providersArray) {
+            return;
+        }
+        const elements = providersArray.getElements();
+        const optionsProviders = elements
+            .filter(Node.isObjectLiteralExpression)
+            .filter(el => el.getProperty('useFactory')?.getText().includes(`${this.name}.options`));
+
+        if (!optionsProviders.length) {
+            return;
+        }
+        const optionsSymbol = optionsProviders[0].getProperty('provide') as PropertyAssignment;
+        const initializer = optionsSymbol?.getInitializer();
+        if (!initializer || !Node.isIdentifier(initializer)) {
+            return;
+        }
+        const constantDeclaration = initializer.getDefinitions()[0]?.getDeclarationNode();
+        if (!constantDeclaration || !Node.isVariableDeclaration(constantDeclaration)) {
+            return;
+        }
+
+        return { typeDeclaration, constantDeclaration };
+    }
+
     addEntity(entityClassName: string) {
         const pluginOptions = this.getMetadataOptions();
         const entityProperty = pluginOptions.getProperty('entities');