@launch77/plugin-runtime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,57 @@
1
+ interface GeneratorContext {
2
+ appPath: string;
3
+ appName: string;
4
+ workspaceName: string;
5
+ pluginPath: string;
6
+ }
7
+ interface PluginMetadata {
8
+ name: string;
9
+ version: string;
10
+ pluginDependencies?: Record<string, string>;
11
+ libraryDependencies?: Record<string, string>;
12
+ }
13
+
14
+ /**
15
+ * Base abstract class for all plugin generators.
16
+ *
17
+ * The only requirement for a generator is to implement the run() method.
18
+ * This method is called by the CLI when a plugin is installed.
19
+ *
20
+ * Use this base class when you need full control over the installation process.
21
+ * For convention-based installation, extend StandardGenerator instead.
22
+ */
23
+ declare abstract class Generator {
24
+ protected context: GeneratorContext;
25
+ constructor(context: GeneratorContext);
26
+ /**
27
+ * Main entry point for plugin installation.
28
+ * This method is called by the CLI and must be implemented by all generators.
29
+ */
30
+ abstract run(): Promise<void>;
31
+ }
32
+
33
+ /**
34
+ * Standard generator with convention-over-configuration approach.
35
+ *
36
+ * Provides a structured lifecycle with smart defaults:
37
+ * 1. updateDependencies() - Reads plugin.json, merges into package.json
38
+ * 2. installDependencies() - Runs npm install
39
+ * 3. copyTemplates() - Copies templates/ folder to app
40
+ * 4. injectCode() - Override this for surgical code edits
41
+ *
42
+ * Most plugins only need to implement injectCode().
43
+ * For full control, extend Generator instead.
44
+ */
45
+ declare abstract class StandardGenerator extends Generator {
46
+ run(): Promise<void>;
47
+ protected updateDependencies(): Promise<void>;
48
+ protected installDependencies(): Promise<void>;
49
+ protected copyTemplates(): Promise<void>;
50
+ protected injectCode(): Promise<void>;
51
+ protected showNextSteps(): void;
52
+ }
53
+
54
+ declare function copyRecursive(src: string, dest: string): Promise<void>;
55
+ declare function pathExists(filePath: string): Promise<boolean>;
56
+
57
+ export { Generator, type GeneratorContext, type PluginMetadata, StandardGenerator, copyRecursive, pathExists };
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ // src/generator.ts
2
+ var Generator = class {
3
+ constructor(context) {
4
+ this.context = context;
5
+ }
6
+ };
7
+
8
+ // src/standard-generator.ts
9
+ import * as path2 from "path";
10
+ import * as fs2 from "fs/promises";
11
+ import chalk from "chalk";
12
+ import { execa } from "execa";
13
+
14
+ // src/utils/file-operations.ts
15
+ import * as fs from "fs/promises";
16
+ import * as path from "path";
17
+ async function copyRecursive(src, dest) {
18
+ const stat2 = await fs.stat(src);
19
+ if (stat2.isDirectory()) {
20
+ await fs.mkdir(dest, { recursive: true });
21
+ const entries = await fs.readdir(src);
22
+ for (const entry of entries) {
23
+ await copyRecursive(path.join(src, entry), path.join(dest, entry));
24
+ }
25
+ } else {
26
+ await fs.copyFile(src, dest);
27
+ }
28
+ }
29
+ async function pathExists(filePath) {
30
+ try {
31
+ await fs.access(filePath);
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ // src/standard-generator.ts
39
+ var StandardGenerator = class extends Generator {
40
+ async run() {
41
+ console.log(chalk.green(`
42
+ \u2705 Installing plugin...
43
+ `));
44
+ await this.updateDependencies();
45
+ await this.installDependencies();
46
+ await this.copyTemplates();
47
+ await this.injectCode();
48
+ console.log(chalk.green(`
49
+ \u2705 Plugin installed successfully!
50
+ `));
51
+ this.showNextSteps();
52
+ }
53
+ async updateDependencies() {
54
+ const pluginJsonPath = path2.join(this.context.pluginPath, "plugin.json");
55
+ if (!await pathExists(pluginJsonPath)) return;
56
+ try {
57
+ const pluginJsonContent = await fs2.readFile(pluginJsonPath, "utf-8");
58
+ const pluginMetadata = JSON.parse(pluginJsonContent);
59
+ if (!pluginMetadata.libraryDependencies || Object.keys(pluginMetadata.libraryDependencies).length === 0) {
60
+ return;
61
+ }
62
+ const packageJsonPath = path2.join(this.context.appPath, "package.json");
63
+ const packageJsonContent = await fs2.readFile(packageJsonPath, "utf-8");
64
+ const packageJson = JSON.parse(packageJsonContent);
65
+ packageJson.dependencies = {
66
+ ...packageJson.dependencies,
67
+ ...pluginMetadata.libraryDependencies
68
+ };
69
+ await fs2.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf-8");
70
+ console.log(chalk.green(" \u2713 Updated package.json with dependencies"));
71
+ } catch (error) {
72
+ console.log(chalk.yellow(` \u26A0\uFE0F Could not update dependencies: ${error}`));
73
+ }
74
+ }
75
+ async installDependencies() {
76
+ try {
77
+ console.log(chalk.cyan(" Installing dependencies..."));
78
+ const workspaceRoot = path2.dirname(path2.dirname(this.context.appPath));
79
+ await execa("npm", ["install"], {
80
+ cwd: workspaceRoot,
81
+ stdio: "pipe"
82
+ });
83
+ console.log(chalk.green(" \u2713 Dependencies installed"));
84
+ } catch (error) {
85
+ console.log(chalk.yellow(` \u26A0\uFE0F Could not install dependencies: ${error}`));
86
+ }
87
+ }
88
+ async copyTemplates() {
89
+ const templatesDir = path2.join(this.context.pluginPath, "templates");
90
+ if (!await pathExists(templatesDir)) return;
91
+ try {
92
+ await copyRecursive(templatesDir, this.context.appPath);
93
+ console.log(chalk.green(" \u2713 Copied template files"));
94
+ } catch (error) {
95
+ console.log(chalk.yellow(` \u26A0\uFE0F Could not copy template files: ${error}`));
96
+ }
97
+ }
98
+ async injectCode() {
99
+ }
100
+ showNextSteps() {
101
+ }
102
+ };
103
+ export {
104
+ Generator,
105
+ StandardGenerator,
106
+ copyRecursive,
107
+ pathExists
108
+ };
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generator.ts","../src/standard-generator.ts","../src/utils/file-operations.ts"],"sourcesContent":["import type { GeneratorContext } from './types.js'\n\n/**\n * Base abstract class for all plugin generators.\n *\n * The only requirement for a generator is to implement the run() method.\n * This method is called by the CLI when a plugin is installed.\n *\n * Use this base class when you need full control over the installation process.\n * For convention-based installation, extend StandardGenerator instead.\n */\nexport abstract class Generator {\n constructor(protected context: GeneratorContext) {}\n\n /**\n * Main entry point for plugin installation.\n * This method is called by the CLI and must be implemented by all generators.\n */\n abstract run(): Promise<void>\n}\n","import * as path from 'path'\nimport * as fs from 'fs/promises'\nimport chalk from 'chalk'\nimport { execa } from 'execa'\nimport { Generator } from './generator.js'\nimport { copyRecursive, pathExists } from './utils/file-operations.js'\nimport type { PluginMetadata } from './types.js'\n\n/**\n * Standard generator with convention-over-configuration approach.\n *\n * Provides a structured lifecycle with smart defaults:\n * 1. updateDependencies() - Reads plugin.json, merges into package.json\n * 2. installDependencies() - Runs npm install\n * 3. copyTemplates() - Copies templates/ folder to app\n * 4. injectCode() - Override this for surgical code edits\n *\n * Most plugins only need to implement injectCode().\n * For full control, extend Generator instead.\n */\nexport abstract class StandardGenerator extends Generator {\n async run(): Promise<void> {\n console.log(chalk.green(`\\n✅ Installing plugin...\\n`))\n\n await this.updateDependencies()\n await this.installDependencies()\n await this.copyTemplates()\n await this.injectCode()\n\n console.log(chalk.green(`\\n✅ Plugin installed successfully!\\n`))\n this.showNextSteps()\n }\n\n protected async updateDependencies(): Promise<void> {\n const pluginJsonPath = path.join(this.context.pluginPath, 'plugin.json')\n\n if (!(await pathExists(pluginJsonPath))) return\n\n try {\n const pluginJsonContent = await fs.readFile(pluginJsonPath, 'utf-8')\n const pluginMetadata: PluginMetadata = JSON.parse(pluginJsonContent)\n\n if (!pluginMetadata.libraryDependencies || Object.keys(pluginMetadata.libraryDependencies).length === 0) {\n return\n }\n\n const packageJsonPath = path.join(this.context.appPath, 'package.json')\n const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8')\n const packageJson = JSON.parse(packageJsonContent)\n\n packageJson.dependencies = {\n ...packageJson.dependencies,\n ...pluginMetadata.libraryDependencies,\n }\n\n await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\\n', 'utf-8')\n console.log(chalk.green(' ✓ Updated package.json with dependencies'))\n } catch (error) {\n console.log(chalk.yellow(` ⚠️ Could not update dependencies: ${error}`))\n }\n }\n\n protected async installDependencies(): Promise<void> {\n try {\n console.log(chalk.cyan(' Installing dependencies...'))\n const workspaceRoot = path.dirname(path.dirname(this.context.appPath))\n\n await execa('npm', ['install'], {\n cwd: workspaceRoot,\n stdio: 'pipe',\n })\n\n console.log(chalk.green(' ✓ Dependencies installed'))\n } catch (error) {\n console.log(chalk.yellow(` ⚠️ Could not install dependencies: ${error}`))\n }\n }\n\n protected async copyTemplates(): Promise<void> {\n const templatesDir = path.join(this.context.pluginPath, 'templates')\n\n if (!(await pathExists(templatesDir))) return\n\n try {\n await copyRecursive(templatesDir, this.context.appPath)\n console.log(chalk.green(' ✓ Copied template files'))\n } catch (error) {\n console.log(chalk.yellow(` ⚠️ Could not copy template files: ${error}`))\n }\n }\n\n protected async injectCode(): Promise<void> {\n // No-op by default - plugins override this for custom code injection\n }\n\n protected showNextSteps(): void {\n // No-op by default - plugins can override to show custom next steps\n }\n}\n","import * as fs from 'fs/promises'\nimport * as path from 'path'\n\nexport async function copyRecursive(src: string, dest: string): Promise<void> {\n const stat = await fs.stat(src)\n\n if (stat.isDirectory()) {\n await fs.mkdir(dest, { recursive: true })\n const entries = await fs.readdir(src)\n\n for (const entry of entries) {\n await copyRecursive(path.join(src, entry), path.join(dest, entry))\n }\n } else {\n await fs.copyFile(src, dest)\n }\n}\n\nexport async function pathExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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","names":["path","fs","stat"]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@launch77/plugin-runtime",
3
+ "version": "0.1.0",
4
+ "license": "UNLICENSED",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "eslint src/**/*.ts"
24
+ },
25
+ "dependencies": {
26
+ "chalk": "^5.3.0",
27
+ "execa": "^9.5.2",
28
+ "fs-extra": "^11.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/fs-extra": "^11.0.4",
32
+ "@types/node": "^20.12.7",
33
+ "tsup": "^8.0.2",
34
+ "typescript": "^5.4.5"
35
+ }
36
+ }