فهرست منبع

chore(cli): Scaffold CLI package

Michael Bromley 2 سال پیش
والد
کامیت
aad6979d7c

+ 49 - 0
packages/cli/package.json

@@ -0,0 +1,49 @@
+{
+    "name": "@vendure/cli",
+    "version": "2.1.0-next.4",
+    "description": "A modern, headless ecommerce framework",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/vendure-ecommerce/vendure/"
+    },
+    "keywords": [
+        "vendure",
+        "ecommerce",
+        "headless",
+        "graphql",
+        "typescript"
+    ],
+    "homepage": "https://www.vendure.io/",
+    "funding": "https://github.com/sponsors/michaelbromley",
+    "private": false,
+    "license": "MIT",
+    "type": "commonjs",
+    "scripts": {
+        "build": "rimraf dist && tsc -p ./tsconfig.cli.json",
+        "watch": "tsc -p ./tsconfig.cli.json --watch",
+        "ci": "yarn build"
+    },
+    "publishConfig": {
+        "access": "public"
+    },
+    "main": "dist/index.js",
+    "types": "dist/index.d.ts",
+    "bin": {
+        "vendure": "dist/cli.js"
+    },
+    "files": [
+        "dist/**/*",
+        "cli/**/*"
+    ],
+    "dependencies": {
+        "@vendure/common": "2.1.0-next.4",
+        "commander": "^11.0.0",
+        "@clack/prompts": "^0.7.0",
+        "fs-extra": "^11.1.1",
+        "picocolors": "^1.0.0",
+        "change-case": "^4.1.2"
+    },
+    "devDependencies": {
+        "typescript": "4.9.5"
+    }
+}

+ 15 - 0
packages/cli/src/cli.ts

@@ -0,0 +1,15 @@
+#! /usr/bin/env node
+
+import { Command } from 'commander';
+
+import { registerCommand as registerPluginCommand } from './commands/plugin/index';
+
+const program = new Command();
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const version = require('../package.json').version;
+
+program.version(version).description('The Vendure CLI');
+registerPluginCommand(program);
+
+program.parse(process.argv);

+ 52 - 0
packages/cli/src/commands/plugin/index.ts

@@ -0,0 +1,52 @@
+import { constantCase, paramCase, pascalCase } from 'change-case';
+import { Command } from 'commander';
+import * as fs from 'fs-extra';
+import path from 'path';
+
+import { render as renderConstants } from './scaffold/constants';
+import { render as renderPlugin } from './scaffold/plugin';
+import { render as renderTypes } from './scaffold/types';
+import { GeneratePluginOptions, TemplateContext } from './types';
+
+export function registerCommand(program: Command) {
+    program
+        .command('plugin')
+        .description('Scaffold a new Vendure plugin')
+        .argument('-n, --name <name>', 'The name of the plugin')
+        .alias('p')
+        .action((name: string) => {
+            generatePlugin({ name });
+        });
+}
+
+export function generatePlugin(options: GeneratePluginOptions) {
+    const nameWithoutPlugin = options.name.replace(/-?plugin$/i, '');
+    const normalizedName = nameWithoutPlugin + '-plugin';
+    const templateContext: TemplateContext = {
+        pluginName: pascalCase(normalizedName),
+        pluginInitOptionsName: constantCase(normalizedName) + '_OPTIONS',
+    };
+
+    const files: Array<{ render: (context: TemplateContext) => string; path: string }> = [
+        {
+            render: renderPlugin,
+            path: paramCase(nameWithoutPlugin) + '.plugin.ts',
+        },
+        {
+            render: renderTypes,
+            path: 'types.ts',
+        },
+        {
+            render: renderConstants,
+            path: 'constants.ts',
+        },
+    ];
+
+    const pluginDir = path.join(process.cwd(), paramCase(nameWithoutPlugin));
+    fs.ensureDirSync(pluginDir);
+    files.forEach(file => {
+        const filePath = path.join(pluginDir, file.path);
+        const rendered = file.render(templateContext);
+        fs.writeFileSync(filePath, rendered);
+    });
+}

+ 10 - 0
packages/cli/src/commands/plugin/scaffold/constants.ts

@@ -0,0 +1,10 @@
+import { constantCase, pascalCase } from 'change-case';
+
+import { TemplateContext } from '../types';
+
+export function render(context: TemplateContext): string {
+    return /* language=TypeScript */ `
+export const ${context.pluginInitOptionsName} = Symbol('${context.pluginInitOptionsName}');
+export const loggerCtx = '${context.pluginName}';
+`;
+}

+ 45 - 0
packages/cli/src/commands/plugin/scaffold/plugin.ts

@@ -0,0 +1,45 @@
+import { constantCase, pascalCase } from 'change-case';
+
+import { TemplateContext } from '../types';
+
+export function render(context: TemplateContext) {
+    return /* language=TypeScript */ `
+import { PluginCommonModule, Type, VendurePlugin } from '@vendure/core';
+import { AdminUiExtension } from '@vendure/ui-devkit/compiler';
+import path from 'path';
+
+import { ${context.pluginInitOptionsName} } from './constants';
+import { PluginInitOptions } from './types';
+
+/**
+ * An example Vendure plugin.
+ */
+@VendurePlugin({
+    // Importing the PluginCommonModule gives all of our plugin's injectables (services, resolvers)
+    // access to the Vendure core providers. See https://www.vendure.io/docs/typescript-api/plugin/plugin-common-module/
+    imports: [PluginCommonModule],
+    entities: [],
+    providers: [
+        // By definiting the \`PLUGIN_INIT_OPTIONS\` symbol as a provider, we can then inject the
+        // user-defined options into other classes, such as the {@link ExampleService}.
+        { provide: ${context.pluginInitOptionsName}, useFactory: () => ${context.pluginName}.options },
+    ],
+})
+export class ${context.pluginName} {
+    static options: PluginInitOptions;
+
+    /**
+     * The static \`init()\` method is a convention used by Vendure plugins which allows options
+     * to be configured by the user.
+     */
+    static init(options: PluginInitOptions): Type<${context.pluginName}> {
+        this.options = options;
+        return ${context.pluginName};
+    }
+
+    static uiExtensions: AdminUiExtension = {
+        extensionPath: path.join(__dirname, 'ui'),
+    };
+}
+`;
+}

+ 12 - 0
packages/cli/src/commands/plugin/scaffold/types.ts

@@ -0,0 +1,12 @@
+import { TemplateContext } from '../types';
+
+export function render(options: TemplateContext): string {
+    return /* language=TypeScript */ `
+/**
+ * The plugin can be configured using the following options:
+ */
+export interface PluginInitOptions {
+    exampleOption: string;
+}
+`;
+}

+ 8 - 0
packages/cli/src/commands/plugin/types.ts

@@ -0,0 +1,8 @@
+export interface TemplateContext {
+    pluginName: string;
+    pluginInitOptionsName: string;
+}
+
+export interface GeneratePluginOptions {
+    name: string;
+}

+ 9 - 0
packages/cli/tsconfig.cli.json

@@ -0,0 +1,9 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "./dist"
+  },
+  "files": [
+    "./src/cli.ts",
+  ]
+}

+ 11 - 0
packages/cli/tsconfig.json

@@ -0,0 +1,11 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "moduleResolution": "NodeNext",
+    "declaration": true,
+    "removeComments": true,
+    "strictPropertyInitialization": false,
+    "sourceMap": true,
+    "allowJs": true
+  }
+}

+ 14 - 0
packages/cli/typings.d.ts

@@ -0,0 +1,14 @@
+// This file is for any 3rd party JS libs which don't have a corresponding @types/ package.
+
+declare module 'opn' {
+    declare const opn: (path: string) => Promise<any>;
+    export default opn;
+}
+
+declare module 'i18next-icu' {
+    // default
+}
+
+declare module 'i18next-fs-backend' {
+    // default
+}