create-brika 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.
- package/README.md +81 -0
- package/package.json +29 -0
- package/src/index.ts +84 -0
- package/src/prompts.ts +120 -0
- package/src/scaffold.ts +188 -0
- package/src/utils.ts +60 -0
- package/template/README.md +48 -0
- package/template/_gitignore +4 -0
- package/template/_package.json +40 -0
- package/template/locales/en/plugin.json +16 -0
- package/template/src/index.ts.template +57 -0
- package/template/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# create-brika
|
|
2
|
+
|
|
3
|
+
Scaffold a new BRIKA plugin with a single command.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun create brika my-plugin
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This launches an interactive wizard that:
|
|
12
|
+
|
|
13
|
+
1. Asks for plugin details (name, description, category, author)
|
|
14
|
+
2. Fetches the latest SDK version from npm
|
|
15
|
+
3. Creates the complete plugin structure
|
|
16
|
+
4. Installs dependencies
|
|
17
|
+
5. Initializes a git repository
|
|
18
|
+
|
|
19
|
+
## Options
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Interactive mode (prompts for all options)
|
|
23
|
+
bun create brika
|
|
24
|
+
|
|
25
|
+
# With plugin name
|
|
26
|
+
bun create brika my-plugin
|
|
27
|
+
|
|
28
|
+
# Skip git initialization
|
|
29
|
+
bun create brika my-plugin --no-git
|
|
30
|
+
|
|
31
|
+
# Skip dependency installation
|
|
32
|
+
bun create brika my-plugin --no-install
|
|
33
|
+
|
|
34
|
+
# Show help
|
|
35
|
+
bun create brika --help
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Generated Structure
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
my-plugin/
|
|
42
|
+
├── package.json # Plugin manifest with blocks
|
|
43
|
+
├── tsconfig.json # TypeScript configuration
|
|
44
|
+
├── README.md # Documentation
|
|
45
|
+
├── .gitignore
|
|
46
|
+
├── src/
|
|
47
|
+
│ └── index.ts # Block definitions
|
|
48
|
+
└── locales/
|
|
49
|
+
└── en/
|
|
50
|
+
└── plugin.json # i18n translations
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Categories
|
|
54
|
+
|
|
55
|
+
When prompted for category, choose based on your plugin's purpose:
|
|
56
|
+
|
|
57
|
+
| Category | Description | Examples |
|
|
58
|
+
|----------|-------------|----------|
|
|
59
|
+
| `trigger` | Starts workflows | Timers, sensors, webhooks |
|
|
60
|
+
| `action` | Performs operations | Send notification, control device |
|
|
61
|
+
| `transform` | Processes data | Map, filter, format |
|
|
62
|
+
| `flow` | Controls execution | Condition, delay, split |
|
|
63
|
+
|
|
64
|
+
## After Creating
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd my-plugin
|
|
68
|
+
bun link # Link for local development
|
|
69
|
+
bun run tsc # Type check
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Add to your `brika.yml`:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
plugins:
|
|
76
|
+
- path: ./my-plugin
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-brika",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a new BRIKA plugin with a single command",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "BRIKA Team",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/maxscharwath/brika.git",
|
|
11
|
+
"directory": "packages/create-brika"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["brika", "plugin", "scaffold", "cli", "create"],
|
|
14
|
+
"bin": {
|
|
15
|
+
"create-brika": "./src/index.ts"
|
|
16
|
+
},
|
|
17
|
+
"files": ["src", "template"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "bun run src/index.ts",
|
|
20
|
+
"tsc": "bunx --bun tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@clack/prompts": "^0.11.0",
|
|
24
|
+
"picocolors": "^1.1.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/bun": "^1.3.5"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create-brika CLI
|
|
5
|
+
*
|
|
6
|
+
* Scaffold a new BRIKA plugin with a single command.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun create brika my-plugin
|
|
10
|
+
* bunx create-brika my-plugin
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as p from '@clack/prompts';
|
|
14
|
+
import pc from 'picocolors';
|
|
15
|
+
import { parseArgs } from 'util';
|
|
16
|
+
import { promptForConfig } from './prompts';
|
|
17
|
+
import { scaffold } from './scaffold';
|
|
18
|
+
|
|
19
|
+
const HELP = `
|
|
20
|
+
${pc.bold('create-brika')} - Create a new BRIKA plugin
|
|
21
|
+
|
|
22
|
+
${pc.bold('Usage:')}
|
|
23
|
+
${pc.cyan('bun create brika')} ${pc.dim('[plugin-name]')} ${pc.dim('[options]')}
|
|
24
|
+
|
|
25
|
+
${pc.bold('Options:')}
|
|
26
|
+
${pc.cyan('-h, --help')} Show this help message
|
|
27
|
+
${pc.cyan('--no-git')} Skip git initialization
|
|
28
|
+
${pc.cyan('--no-install')} Skip dependency installation
|
|
29
|
+
|
|
30
|
+
${pc.bold('Examples:')}
|
|
31
|
+
${pc.dim('# Interactive mode')}
|
|
32
|
+
bun create brika
|
|
33
|
+
|
|
34
|
+
${pc.dim('# With plugin name')}
|
|
35
|
+
bun create brika my-plugin
|
|
36
|
+
|
|
37
|
+
${pc.dim('# Skip git and install')}
|
|
38
|
+
bun create brika my-plugin --no-git --no-install
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
async function main(): Promise<void> {
|
|
42
|
+
const { positionals, values } = parseArgs({
|
|
43
|
+
args: Bun.argv.slice(2),
|
|
44
|
+
allowPositionals: true,
|
|
45
|
+
strict: false,
|
|
46
|
+
options: {
|
|
47
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
48
|
+
git: { type: 'boolean', default: true },
|
|
49
|
+
install: { type: 'boolean', default: true },
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (values.help) {
|
|
54
|
+
console.log(HELP);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const config = await promptForConfig(positionals[0]);
|
|
60
|
+
|
|
61
|
+
await scaffold({
|
|
62
|
+
...config,
|
|
63
|
+
git: values.git !== false,
|
|
64
|
+
install: values.install !== false,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
p.outro(`${pc.green('Success!')} Your plugin is ready at ${pc.cyan(`./${config.name}`)}`);
|
|
68
|
+
|
|
69
|
+
console.log();
|
|
70
|
+
console.log(pc.bold('Next steps:'));
|
|
71
|
+
console.log(` ${pc.cyan('cd')} ${config.name}`);
|
|
72
|
+
console.log(` ${pc.cyan('bun')} link`);
|
|
73
|
+
console.log();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof Error && error.message === 'cancelled') {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
p.cancel('An error occurred');
|
|
79
|
+
console.error(error);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
main();
|
package/src/prompts.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts for plugin configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import pc from 'picocolors';
|
|
7
|
+
import { getGitUser } from './utils';
|
|
8
|
+
|
|
9
|
+
export interface PluginConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
category: 'trigger' | 'action' | 'transform' | 'flow';
|
|
13
|
+
author: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const CATEGORIES = [
|
|
17
|
+
{
|
|
18
|
+
value: 'trigger',
|
|
19
|
+
label: 'Trigger',
|
|
20
|
+
hint: 'Starts workflows (sensors, timers, webhooks)',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
value: 'action',
|
|
24
|
+
label: 'Action',
|
|
25
|
+
hint: 'Performs operations (send, control, notify)',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: 'transform',
|
|
29
|
+
label: 'Transform',
|
|
30
|
+
hint: 'Processes data (map, filter, format)',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: 'flow',
|
|
34
|
+
label: 'Flow',
|
|
35
|
+
hint: 'Controls flow (condition, delay, split)',
|
|
36
|
+
},
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validate plugin name (kebab-case)
|
|
41
|
+
*/
|
|
42
|
+
function validatePluginName(name: string): string | undefined {
|
|
43
|
+
if (!name) return 'Plugin name is required';
|
|
44
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
45
|
+
return 'Plugin name must be kebab-case (e.g., my-plugin)';
|
|
46
|
+
}
|
|
47
|
+
if (name.startsWith('-') || name.endsWith('-')) {
|
|
48
|
+
return 'Plugin name cannot start or end with a hyphen';
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Prompt for plugin configuration
|
|
55
|
+
*/
|
|
56
|
+
export async function promptForConfig(pluginName?: string): Promise<PluginConfig> {
|
|
57
|
+
p.intro(pc.bgCyan(pc.black(' create-brika ')));
|
|
58
|
+
|
|
59
|
+
// Validate provided name
|
|
60
|
+
if (pluginName) {
|
|
61
|
+
const error = validatePluginName(pluginName);
|
|
62
|
+
if (error) {
|
|
63
|
+
p.cancel(error);
|
|
64
|
+
throw new Error('cancelled');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const gitUser = await getGitUser();
|
|
69
|
+
|
|
70
|
+
const answers = await p.group(
|
|
71
|
+
{
|
|
72
|
+
name: () => {
|
|
73
|
+
if (pluginName) {
|
|
74
|
+
p.log.info(`Plugin name: ${pc.cyan(pluginName)}`);
|
|
75
|
+
return Promise.resolve(pluginName);
|
|
76
|
+
}
|
|
77
|
+
return p.text({
|
|
78
|
+
message: 'What is your plugin name?',
|
|
79
|
+
placeholder: 'my-plugin',
|
|
80
|
+
validate: validatePluginName,
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
description: ({ results }) =>
|
|
84
|
+
p.text({
|
|
85
|
+
message: 'Description',
|
|
86
|
+
placeholder: `A BRIKA plugin for ${results.name}`,
|
|
87
|
+
defaultValue: `A BRIKA plugin for ${results.name}`,
|
|
88
|
+
}),
|
|
89
|
+
category: () =>
|
|
90
|
+
p.select({
|
|
91
|
+
message: 'What type of plugin is this?',
|
|
92
|
+
options: CATEGORIES.map((c) => ({
|
|
93
|
+
value: c.value,
|
|
94
|
+
label: c.label,
|
|
95
|
+
hint: c.hint,
|
|
96
|
+
})),
|
|
97
|
+
initialValue: 'action',
|
|
98
|
+
}),
|
|
99
|
+
author: () =>
|
|
100
|
+
p.text({
|
|
101
|
+
message: 'Author',
|
|
102
|
+
placeholder: gitUser || 'Your Name',
|
|
103
|
+
defaultValue: gitUser,
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
onCancel: () => {
|
|
108
|
+
p.cancel('Operation cancelled.');
|
|
109
|
+
throw new Error('cancelled');
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
name: answers.name as string,
|
|
116
|
+
description: answers.description as string,
|
|
117
|
+
category: answers.category as PluginConfig['category'],
|
|
118
|
+
author: answers.author as string,
|
|
119
|
+
};
|
|
120
|
+
}
|
package/src/scaffold.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scaffold a new BRIKA plugin from file-based templates
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import pc from 'picocolors';
|
|
9
|
+
import type { PluginConfig } from './prompts';
|
|
10
|
+
import { renderTemplate, runCommand, toCamelCase, toPascalCase } from './utils';
|
|
11
|
+
|
|
12
|
+
export interface ScaffoldOptions extends PluginConfig {
|
|
13
|
+
git: boolean;
|
|
14
|
+
install: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Template variables available in all template files
|
|
19
|
+
*/
|
|
20
|
+
interface TemplateVars {
|
|
21
|
+
name: string;
|
|
22
|
+
packageName: string;
|
|
23
|
+
description: string;
|
|
24
|
+
category: string;
|
|
25
|
+
author: string;
|
|
26
|
+
blockId: string;
|
|
27
|
+
blockNamePascal: string;
|
|
28
|
+
blockNameCamel: string;
|
|
29
|
+
sdkVersion: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Fetch the latest version of a package from npm
|
|
34
|
+
*/
|
|
35
|
+
async function fetchLatestVersion(packageName: string): Promise<string> {
|
|
36
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw new Error(`Failed to fetch ${packageName} version: ${response.status}`);
|
|
39
|
+
}
|
|
40
|
+
const data = (await response.json()) as { version: string };
|
|
41
|
+
return data.version;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* File rename mappings (template name -> output name)
|
|
46
|
+
*/
|
|
47
|
+
const FILE_RENAMES: Record<string, string> = {
|
|
48
|
+
_gitignore: '.gitignore',
|
|
49
|
+
'_package.json': 'package.json',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get output filename, handling .template extension
|
|
54
|
+
*/
|
|
55
|
+
function getOutputName(filename: string): string {
|
|
56
|
+
// Remove .template extension if present
|
|
57
|
+
if (filename.endsWith('.template')) {
|
|
58
|
+
filename = filename.slice(0, -'.template'.length);
|
|
59
|
+
}
|
|
60
|
+
// Apply rename mappings
|
|
61
|
+
return FILE_RENAMES[filename] ?? filename;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create template variables from config
|
|
66
|
+
*/
|
|
67
|
+
function createTemplateVars(config: PluginConfig, sdkVersion: string): TemplateVars {
|
|
68
|
+
return {
|
|
69
|
+
name: config.name,
|
|
70
|
+
packageName: `@brika/plugin-${config.name}`,
|
|
71
|
+
description: config.description,
|
|
72
|
+
category: config.category,
|
|
73
|
+
author: config.author,
|
|
74
|
+
blockId: config.name,
|
|
75
|
+
blockNamePascal: toPascalCase(config.name),
|
|
76
|
+
blockNameCamel: toCamelCase(config.name),
|
|
77
|
+
sdkVersion,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the template directory path
|
|
83
|
+
*/
|
|
84
|
+
function getTemplateDir(): string {
|
|
85
|
+
return path.resolve(import.meta.dir, '..', 'template');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Process a single file: read, render, and write
|
|
90
|
+
*/
|
|
91
|
+
async function processFile(
|
|
92
|
+
srcPath: string,
|
|
93
|
+
destPath: string,
|
|
94
|
+
vars: Record<string, string>
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const content = await fs.readFile(srcPath, 'utf-8');
|
|
97
|
+
const rendered = renderTemplate(content, vars);
|
|
98
|
+
await fs.writeFile(destPath, rendered, 'utf-8');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Recursively copy and render template files
|
|
103
|
+
*/
|
|
104
|
+
async function copyTemplateDir(
|
|
105
|
+
srcDir: string,
|
|
106
|
+
destDir: string,
|
|
107
|
+
vars: Record<string, string>
|
|
108
|
+
): Promise<void> {
|
|
109
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
110
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
111
|
+
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
114
|
+
const destName = getOutputName(entry.name);
|
|
115
|
+
const destPath = path.join(destDir, destName);
|
|
116
|
+
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
await copyTemplateDir(srcPath, destPath, vars);
|
|
119
|
+
} else {
|
|
120
|
+
await processFile(srcPath, destPath, vars);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Scaffold a new plugin
|
|
127
|
+
*/
|
|
128
|
+
export async function scaffold(options: ScaffoldOptions): Promise<void> {
|
|
129
|
+
const { git, install, ...config } = options;
|
|
130
|
+
const targetDir = path.resolve(process.cwd(), config.name);
|
|
131
|
+
|
|
132
|
+
// Check if directory exists
|
|
133
|
+
try {
|
|
134
|
+
await fs.access(targetDir);
|
|
135
|
+
p.cancel(`Directory ${pc.cyan(config.name)} already exists`);
|
|
136
|
+
throw new Error('cancelled');
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const templateDir = getTemplateDir();
|
|
144
|
+
const spinner = p.spinner();
|
|
145
|
+
|
|
146
|
+
// Fetch latest SDK version
|
|
147
|
+
spinner.start('Fetching latest SDK version');
|
|
148
|
+
const sdkVersion = await fetchLatestVersion('@brika/sdk');
|
|
149
|
+
spinner.stop(`Using SDK version ${pc.cyan(sdkVersion)}`);
|
|
150
|
+
|
|
151
|
+
const vars = createTemplateVars(config, sdkVersion) as unknown as Record<string, string>;
|
|
152
|
+
|
|
153
|
+
// Create files from template
|
|
154
|
+
spinner.start('Creating plugin files');
|
|
155
|
+
await copyTemplateDir(templateDir, targetDir, vars);
|
|
156
|
+
spinner.stop('Created plugin files');
|
|
157
|
+
|
|
158
|
+
// Initialize git repository
|
|
159
|
+
if (git) {
|
|
160
|
+
spinner.start('Initializing git repository');
|
|
161
|
+
const success = await runCommand(['git', 'init'], targetDir);
|
|
162
|
+
spinner.stop(success ? 'Initialized git repository' : 'Skipped git init');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Install dependencies
|
|
166
|
+
if (install) {
|
|
167
|
+
spinner.start('Installing dependencies');
|
|
168
|
+
const success = await runCommand(['bun', 'install'], targetDir);
|
|
169
|
+
if (success) {
|
|
170
|
+
spinner.stop('Installed dependencies');
|
|
171
|
+
} else {
|
|
172
|
+
spinner.stop('Failed to install dependencies');
|
|
173
|
+
p.log.warn('Run `bun install` manually to install dependencies');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Show summary
|
|
178
|
+
p.note(
|
|
179
|
+
[
|
|
180
|
+
`${pc.cyan('package.json')} Plugin manifest with blocks`,
|
|
181
|
+
`${pc.cyan('src/index.ts')} Block definitions`,
|
|
182
|
+
`${pc.cyan('tsconfig.json')} TypeScript config`,
|
|
183
|
+
`${pc.cyan('README.md')} Documentation`,
|
|
184
|
+
`${pc.cyan('locales/')} i18n translations`,
|
|
185
|
+
].join('\n'),
|
|
186
|
+
'Created files'
|
|
187
|
+
);
|
|
188
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for create-brika CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert kebab-case to PascalCase
|
|
7
|
+
*/
|
|
8
|
+
export function toPascalCase(str: string): string {
|
|
9
|
+
return str
|
|
10
|
+
.split('-')
|
|
11
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
12
|
+
.join('');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Convert kebab-case to camelCase
|
|
17
|
+
*/
|
|
18
|
+
export function toCamelCase(str: string): string {
|
|
19
|
+
const pascal = toPascalCase(str);
|
|
20
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Render template string with variables using {{variable}} syntax
|
|
25
|
+
*/
|
|
26
|
+
export function renderTemplate(content: string, vars: Record<string, string>): string {
|
|
27
|
+
return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? '');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get git user name
|
|
32
|
+
*/
|
|
33
|
+
export async function getGitUser(): Promise<string> {
|
|
34
|
+
try {
|
|
35
|
+
const proc = Bun.spawn(['git', 'config', 'user.name'], {
|
|
36
|
+
stdout: 'pipe',
|
|
37
|
+
});
|
|
38
|
+
const text = await new Response(proc.stdout).text();
|
|
39
|
+
return text.trim();
|
|
40
|
+
} catch {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Run a command in a directory
|
|
47
|
+
*/
|
|
48
|
+
export async function runCommand(cmd: string[], cwd: string): Promise<boolean> {
|
|
49
|
+
try {
|
|
50
|
+
const proc = Bun.spawn(cmd, {
|
|
51
|
+
cwd,
|
|
52
|
+
stdout: 'ignore',
|
|
53
|
+
stderr: 'ignore',
|
|
54
|
+
});
|
|
55
|
+
await proc.exited;
|
|
56
|
+
return proc.exitCode === 0;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# {{packageName}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add {{packageName}}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Add the plugin to your `brika.yml`:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
plugins:
|
|
17
|
+
"{{packageName}}":
|
|
18
|
+
enabled: true
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Blocks
|
|
22
|
+
|
|
23
|
+
### {{blockNamePascal}}
|
|
24
|
+
|
|
25
|
+
{{description}}
|
|
26
|
+
|
|
27
|
+
**Inputs:**
|
|
28
|
+
- `in` - Input data to process
|
|
29
|
+
|
|
30
|
+
**Outputs:**
|
|
31
|
+
- `out` - Processed output
|
|
32
|
+
|
|
33
|
+
**Configuration:**
|
|
34
|
+
- `enabled` (boolean) - Enable processing (default: true)
|
|
35
|
+
|
|
36
|
+
## Development
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Link for local development
|
|
40
|
+
bun link
|
|
41
|
+
|
|
42
|
+
# Type check
|
|
43
|
+
bun run tsc
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://schema.brika.dev/plugin.schema.json",
|
|
3
|
+
"name": "{{packageName}}",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "{{description}}",
|
|
6
|
+
"author": "{{author}}",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"brika",
|
|
9
|
+
"plugin",
|
|
10
|
+
"{{name}}"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"brika": "^{{sdkVersion}}"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./src/index.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./src/index.ts"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"link": "bun link",
|
|
22
|
+
"tsc": "bunx --bun tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"blocks": [
|
|
25
|
+
{
|
|
26
|
+
"id": "{{blockId}}",
|
|
27
|
+
"name": "{{blockNamePascal}}",
|
|
28
|
+
"description": "{{description}}",
|
|
29
|
+
"category": "{{category}}",
|
|
30
|
+
"icon": "box",
|
|
31
|
+
"color": "#3b82f6"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@brika/sdk": "^{{sdkVersion}}"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"bun-types": "^1.3.5"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{blockNamePascal}}",
|
|
3
|
+
"description": "{{description}}",
|
|
4
|
+
"blocks": {
|
|
5
|
+
"{{blockId}}": {
|
|
6
|
+
"name": "{{blockNamePascal}}",
|
|
7
|
+
"description": "{{description}}"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"fields": {
|
|
11
|
+
"enabled": {
|
|
12
|
+
"label": "Enabled",
|
|
13
|
+
"description": "Enable processing"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{blockNamePascal}} Plugin for BRIKA
|
|
3
|
+
*
|
|
4
|
+
* {{description}}
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
defineReactiveBlock,
|
|
9
|
+
input,
|
|
10
|
+
log,
|
|
11
|
+
onStop,
|
|
12
|
+
output,
|
|
13
|
+
z,
|
|
14
|
+
} from "@brika/sdk";
|
|
15
|
+
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// {{blockNamePascal}} Block
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export const {{blockNameCamel}} = defineReactiveBlock(
|
|
21
|
+
{
|
|
22
|
+
id: "{{blockId}}",
|
|
23
|
+
inputs: {
|
|
24
|
+
in: input(z.generic(), { name: "Input" }),
|
|
25
|
+
},
|
|
26
|
+
outputs: {
|
|
27
|
+
out: output(z.passthrough("in"), { name: "Output" }),
|
|
28
|
+
},
|
|
29
|
+
config: z.object({
|
|
30
|
+
// Add your configuration options here
|
|
31
|
+
enabled: z.boolean().default(true).describe("Enable processing"),
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
({ inputs, outputs, config, log }) => {
|
|
35
|
+
inputs.in.on((data) => {
|
|
36
|
+
if (!config.enabled) {
|
|
37
|
+
log("debug", "Processing disabled, skipping");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
log("info", "Processing data", { data });
|
|
42
|
+
|
|
43
|
+
// TODO: Add your block logic here
|
|
44
|
+
outputs.out.emit(data);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
// Lifecycle
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
onStop(() => {
|
|
54
|
+
log.info("{{blockNamePascal}} plugin stopping");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
log.info("{{blockNamePascal}} plugin loaded");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"composite": true,
|
|
11
|
+
"types": ["bun-types"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|