@object-ui/create-plugin 0.3.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ObjectQL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # @object-ui/create-plugin
2
+
3
+ CLI tool to quickly scaffold new ObjectUI plugins with best practices.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ # Using pnpm
9
+ pnpm create @object-ui/plugin my-plugin
10
+
11
+ # Using npm
12
+ npm create @object-ui/plugin my-plugin
13
+
14
+ # Using npx
15
+ npx @object-ui/create-plugin my-plugin
16
+ ```
17
+
18
+ ## What Gets Generated
19
+
20
+ The tool creates a complete plugin package structure:
21
+
22
+ ```
23
+ packages/plugin-my-plugin/
24
+ ├── src/
25
+ │ ├── index.tsx # Plugin export & registration
26
+ │ ├── MyPluginImpl.tsx # Component implementation
27
+ │ ├── MyPluginImpl.test.tsx # Tests
28
+ │ └── types.ts # Schema definitions
29
+ ├── package.json
30
+ ├── tsconfig.json
31
+ ├── vite.config.ts
32
+ └── README.md
33
+ ```
34
+
35
+ ## Features
36
+
37
+ - ✅ TypeScript support out of the box
38
+ - ✅ Vite build configuration
39
+ - ✅ Component registration with ComponentRegistry
40
+ - ✅ Test setup with Vitest
41
+ - ✅ Proper package.json with workspace dependencies
42
+ - ✅ README template
43
+ - ✅ Type definitions
44
+
45
+ ## Interactive Mode
46
+
47
+ Run without arguments for interactive prompts:
48
+
49
+ ```bash
50
+ pnpm create @object-ui/plugin
51
+ ```
52
+
53
+ You'll be prompted for:
54
+ - Plugin name
55
+ - Description
56
+ - Author name
57
+
58
+ ## Options
59
+
60
+ ```bash
61
+ pnpm create @object-ui/plugin my-plugin --description "My awesome plugin" --author "Your Name"
62
+ ```
63
+
64
+ ## After Creation
65
+
66
+ 1. Navigate to the plugin directory:
67
+ ```bash
68
+ cd packages/plugin-my-plugin
69
+ ```
70
+
71
+ 2. Install dependencies:
72
+ ```bash
73
+ pnpm install
74
+ ```
75
+
76
+ 3. Build the plugin:
77
+ ```bash
78
+ pnpm build
79
+ ```
80
+
81
+ 4. Run tests:
82
+ ```bash
83
+ pnpm test
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import chalk from "chalk";
6
+ import prompts from "prompts";
7
+ import * as path from "path";
8
+ import fs from "fs-extra";
9
+ import { fileURLToPath } from "url";
10
+ var __filename = fileURLToPath(import.meta.url);
11
+ var __dirname = path.dirname(__filename);
12
+ var program = new Command();
13
+ async function createPlugin(pluginName, options = {}) {
14
+ console.log(chalk.blue("\n\u{1F680} ObjectUI Plugin Generator\n"));
15
+ if (!pluginName) {
16
+ const response = await prompts({
17
+ type: "text",
18
+ name: "name",
19
+ message: "Plugin name (without prefix):",
20
+ validate: (value) => {
21
+ if (value.length === 0) return "Plugin name is required";
22
+ if (!/^[a-z0-9-]+$/.test(value)) {
23
+ return "Plugin name must contain only lowercase letters, numbers, and hyphens";
24
+ }
25
+ if (value.includes("..") || value.includes("/") || value.includes("\\")) {
26
+ return 'Plugin name cannot contain path separators or ".."';
27
+ }
28
+ return true;
29
+ }
30
+ });
31
+ pluginName = response.name;
32
+ if (!pluginName) {
33
+ console.log(chalk.red("\n\u274C Plugin name is required"));
34
+ process.exit(1);
35
+ }
36
+ }
37
+ if (!/^[a-z0-9-]+$/.test(pluginName)) {
38
+ console.log(chalk.red("\n\u274C Plugin name must contain only lowercase letters, numbers, and hyphens"));
39
+ process.exit(1);
40
+ }
41
+ if (pluginName.includes("..") || pluginName.includes("/") || pluginName.includes("\\") || path.isAbsolute(pluginName)) {
42
+ console.log(chalk.red("\n\u274C Invalid plugin name: path traversal detected"));
43
+ process.exit(1);
44
+ }
45
+ const cleanName = pluginName.replace(/^plugin-/, "").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
46
+ if (cleanName.length === 0) {
47
+ console.log(chalk.red("\n\u274C Plugin name cannot be empty after sanitization"));
48
+ process.exit(1);
49
+ }
50
+ const fullPackageName = `plugin-${cleanName}`;
51
+ const pascalCaseName = cleanName.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
52
+ const answers = await prompts([
53
+ {
54
+ type: "text",
55
+ name: "description",
56
+ message: "Plugin description:",
57
+ initial: options.description || `${pascalCaseName} plugin for ObjectUI`
58
+ },
59
+ {
60
+ type: "text",
61
+ name: "author",
62
+ message: "Author name:",
63
+ initial: options.author || ""
64
+ }
65
+ ]);
66
+ const targetDir = path.join(process.cwd(), "packages", fullPackageName);
67
+ if (fs.existsSync(targetDir)) {
68
+ console.log(chalk.red(`
69
+ \u274C Directory already exists: ${targetDir}`));
70
+ process.exit(1);
71
+ }
72
+ console.log(chalk.green(`
73
+ \u2728 Creating plugin: ${fullPackageName}...
74
+ `));
75
+ fs.mkdirpSync(targetDir);
76
+ fs.mkdirpSync(path.join(targetDir, "src"));
77
+ const templateDir = path.join(__dirname, "..", "templates", "plugin");
78
+ const vars = {
79
+ PACKAGE_NAME: `@object-ui/${fullPackageName}`,
80
+ PLUGIN_NAME: cleanName,
81
+ PASCAL_NAME: pascalCaseName,
82
+ DESCRIPTION: answers.description,
83
+ AUTHOR: answers.author,
84
+ VERSION: "0.1.0",
85
+ YEAR: (/* @__PURE__ */ new Date()).getFullYear()
86
+ };
87
+ const packageJson = {
88
+ name: vars.PACKAGE_NAME,
89
+ version: vars.VERSION,
90
+ type: "module",
91
+ license: "MIT",
92
+ description: vars.DESCRIPTION,
93
+ main: "dist/index.umd.cjs",
94
+ module: "dist/index.js",
95
+ types: "dist/index.d.ts",
96
+ exports: {
97
+ ".": {
98
+ types: "./dist/index.d.ts",
99
+ import: "./dist/index.js",
100
+ require: "./dist/index.umd.cjs"
101
+ }
102
+ },
103
+ scripts: {
104
+ build: "vite build",
105
+ test: "vitest run",
106
+ lint: "eslint ."
107
+ },
108
+ dependencies: {
109
+ "@object-ui/components": "workspace:*",
110
+ "@object-ui/core": "workspace:*",
111
+ "@object-ui/react": "workspace:*",
112
+ "@object-ui/types": "workspace:*",
113
+ "lucide-react": "^0.563.0"
114
+ },
115
+ peerDependencies: {
116
+ react: "^18.0.0 || ^19.0.0",
117
+ "react-dom": "^18.0.0 || ^19.0.0"
118
+ },
119
+ devDependencies: {
120
+ "@vitejs/plugin-react": "^4.2.1",
121
+ typescript: "^5.9.3",
122
+ vite: "^7.3.1",
123
+ "vite-plugin-dts": "^4.5.4",
124
+ vitest: "^4.0.18"
125
+ }
126
+ };
127
+ fs.writeFileSync(
128
+ path.join(targetDir, "package.json"),
129
+ JSON.stringify(packageJson, null, 2)
130
+ );
131
+ const tsconfig = {
132
+ extends: "../../tsconfig.json",
133
+ compilerOptions: {
134
+ outDir: "./dist",
135
+ rootDir: "./src",
136
+ declaration: true,
137
+ declarationMap: true
138
+ },
139
+ include: ["src/**/*"],
140
+ exclude: ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]
141
+ };
142
+ fs.writeFileSync(
143
+ path.join(targetDir, "tsconfig.json"),
144
+ JSON.stringify(tsconfig, null, 2)
145
+ );
146
+ const viteConfig = `import { defineConfig } from 'vite';
147
+ import react from '@vitejs/plugin-react';
148
+ import dts from 'vite-plugin-dts';
149
+ import * as path from 'path';
150
+
151
+ export default defineConfig({
152
+ plugins: [
153
+ react(),
154
+ dts({
155
+ insertTypesEntry: true,
156
+ }),
157
+ ],
158
+ build: {
159
+ lib: {
160
+ entry: path.resolve(__dirname, 'src/index.tsx'),
161
+ name: '${pascalCaseName}',
162
+ formats: ['es', 'umd'],
163
+ fileName: (format) => \`index.\${format === 'es' ? 'js' : 'umd.cjs'}\`,
164
+ },
165
+ rollupOptions: {
166
+ external: ['react', 'react-dom', 'react/jsx-runtime'],
167
+ output: {
168
+ globals: {
169
+ react: 'React',
170
+ 'react-dom': 'ReactDOM',
171
+ },
172
+ },
173
+ },
174
+ },
175
+ });
176
+ `;
177
+ fs.writeFileSync(path.join(targetDir, "vite.config.ts"), viteConfig);
178
+ const readme = `# ${vars.PACKAGE_NAME}
179
+
180
+ ${vars.DESCRIPTION}
181
+
182
+ ## Installation
183
+
184
+ \`\`\`bash
185
+ pnpm add ${vars.PACKAGE_NAME}
186
+ \`\`\`
187
+
188
+ ## Usage
189
+
190
+ \`\`\`tsx
191
+ import { ${pascalCaseName} } from '${vars.PACKAGE_NAME}';
192
+
193
+ // Use the component
194
+ <${pascalCaseName} />
195
+ \`\`\`
196
+
197
+ ## Development
198
+
199
+ \`\`\`bash
200
+ # Build the plugin
201
+ pnpm build
202
+
203
+ # Run tests
204
+ pnpm test
205
+
206
+ # Lint code
207
+ pnpm lint
208
+ \`\`\`
209
+
210
+ ## License
211
+
212
+ MIT \xA9 ${vars.AUTHOR}
213
+ `;
214
+ fs.writeFileSync(path.join(targetDir, "README.md"), readme);
215
+ const indexFile = `/**
216
+ * ObjectUI
217
+ * Copyright (c) ${vars.YEAR}-present ObjectStack Inc.
218
+ *
219
+ * This source code is licensed under the MIT license found in the
220
+ * LICENSE file in the root directory of this source tree.
221
+ */
222
+
223
+ import React from 'react';
224
+ import { ComponentRegistry } from '@object-ui/core';
225
+ import { ${pascalCaseName} } from './${pascalCaseName}Impl';
226
+
227
+ export { ${pascalCaseName} };
228
+ export type { ${pascalCaseName}Props } from './${pascalCaseName}Impl';
229
+
230
+ // Register component with ComponentRegistry
231
+ const ${pascalCaseName}Renderer: React.FC<{ schema: any }> = ({ schema }) => {
232
+ return <${pascalCaseName} {...schema} />;
233
+ };
234
+
235
+ ComponentRegistry.register('${cleanName}', ${pascalCaseName}Renderer, {
236
+ label: '${pascalCaseName}',
237
+ category: 'plugin',
238
+ inputs: [
239
+ // Define your component inputs here
240
+ ],
241
+ defaultProps: {
242
+ // Define default props here
243
+ }
244
+ });
245
+ `;
246
+ fs.writeFileSync(path.join(targetDir, "src", "index.tsx"), indexFile);
247
+ const implFile = `/**
248
+ * ObjectUI
249
+ * Copyright (c) ${vars.YEAR}-present ObjectStack Inc.
250
+ *
251
+ * This source code is licensed under the MIT license found in the
252
+ * LICENSE file in the root directory of this source tree.
253
+ */
254
+
255
+ import React from 'react';
256
+
257
+ export interface ${pascalCaseName}Props {
258
+ // Define your props here
259
+ className?: string;
260
+ }
261
+
262
+ /**
263
+ * ${pascalCaseName} component
264
+ */
265
+ export const ${pascalCaseName}: React.FC<${pascalCaseName}Props> = ({ className }) => {
266
+ return (
267
+ <div className={className}>
268
+ <h2>${pascalCaseName} Plugin</h2>
269
+ <p>Implement your plugin logic here.</p>
270
+ </div>
271
+ );
272
+ };
273
+ `;
274
+ fs.writeFileSync(
275
+ path.join(targetDir, "src", `${pascalCaseName}Impl.tsx`),
276
+ implFile
277
+ );
278
+ const typesFile = `/**
279
+ * ObjectUI
280
+ * Copyright (c) ${vars.YEAR}-present ObjectStack Inc.
281
+ *
282
+ * This source code is licensed under the MIT license found in the
283
+ * LICENSE file in the root directory of this source tree.
284
+ */
285
+
286
+ /**
287
+ * Schema definition for ${pascalCaseName}
288
+ */
289
+ export interface ${pascalCaseName}Schema {
290
+ type: '${cleanName}';
291
+ id?: string;
292
+ className?: string;
293
+ // Add schema properties here
294
+ }
295
+ `;
296
+ fs.writeFileSync(path.join(targetDir, "src", "types.ts"), typesFile);
297
+ const testFile = `/**
298
+ * ObjectUI
299
+ * Copyright (c) ${vars.YEAR}-present ObjectStack Inc.
300
+ *
301
+ * This source code is licensed under the MIT license found in the
302
+ * LICENSE file in the root directory of this source tree.
303
+ */
304
+
305
+ import { describe, it, expect } from 'vitest';
306
+ import { render, screen } from '@testing-library/react';
307
+ import React from 'react';
308
+ import { ${pascalCaseName} } from './${pascalCaseName}Impl';
309
+
310
+ describe('${pascalCaseName}', () => {
311
+ it('should render', () => {
312
+ render(<${pascalCaseName} />);
313
+ expect(screen.getByText('${pascalCaseName} Plugin')).toBeInTheDocument();
314
+ });
315
+ });
316
+ `;
317
+ fs.writeFileSync(
318
+ path.join(targetDir, "src", `${pascalCaseName}Impl.test.tsx`),
319
+ testFile
320
+ );
321
+ console.log(chalk.green("\u2705 Plugin created successfully!\n"));
322
+ console.log(chalk.blue("Next steps:\n"));
323
+ console.log(chalk.gray(` cd packages/${fullPackageName}`));
324
+ console.log(chalk.gray(" pnpm install"));
325
+ console.log(chalk.gray(" pnpm build\n"));
326
+ console.log(chalk.blue("To use the plugin:\n"));
327
+ console.log(chalk.gray(` import { ${pascalCaseName} } from '${vars.PACKAGE_NAME}';
328
+ `));
329
+ }
330
+ program.name("create-plugin").description("Create a new ObjectUI plugin").argument("[plugin-name]", "Name of the plugin (without plugin- prefix)").option("-d, --description <description>", "Plugin description").option("-a, --author <author>", "Author name").action(createPlugin);
331
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@object-ui/create-plugin",
3
+ "version": "0.3.1",
4
+ "description": "CLI tool to scaffold ObjectUI plugins",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "create-plugin": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "templates"
13
+ ],
14
+ "dependencies": {
15
+ "chalk": "^5.4.1",
16
+ "commander": "^14.0.2",
17
+ "fs-extra": "^11.2.0",
18
+ "prompts": "^2.4.2"
19
+ },
20
+ "devDependencies": {
21
+ "@types/fs-extra": "^11.0.4",
22
+ "@types/node": "^25.0.10",
23
+ "@types/prompts": "^2.4.9",
24
+ "tsup": "^8.0.0",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "^4.0.18"
27
+ },
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "test": "vitest run",
32
+ "lint": "eslint ."
33
+ }
34
+ }