@fireberry/cli 0.4.0 ā 0.4.2
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/.github/workflows/main.yml +88 -20
- package/CLAUDE.md +32 -1
- package/dist/bin/fireberry.js +10 -5
- package/dist/commands/create-component.js +14 -8
- package/dist/commands/create.js +31 -21
- package/package.json +4 -2
- package/src/bin/fireberry.ts +11 -6
- package/src/commands/create-component.ts +20 -8
- package/src/commands/create.ts +37 -28
- package/src/templates/index.html +0 -15
- package/src/templates/manifest.yml +0 -14
|
@@ -3,19 +3,11 @@ name: Publish Fireberry CLI to npm
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
5
|
inputs:
|
|
6
|
-
type:
|
|
7
|
-
description: "Publish type"
|
|
8
|
-
required: true
|
|
9
|
-
type: choice
|
|
10
|
-
options:
|
|
11
|
-
- beta
|
|
12
|
-
- production
|
|
13
6
|
version:
|
|
14
|
-
description: "Version bump (for
|
|
7
|
+
description: "Version bump type (ignored for beta/dev branch)"
|
|
15
8
|
required: false
|
|
16
9
|
type: choice
|
|
17
10
|
options:
|
|
18
|
-
- prerelease
|
|
19
11
|
- patch
|
|
20
12
|
- minor
|
|
21
13
|
- major
|
|
@@ -25,11 +17,10 @@ jobs:
|
|
|
25
17
|
publish:
|
|
26
18
|
runs-on: ubuntu-latest
|
|
27
19
|
permissions:
|
|
28
|
-
contents:
|
|
20
|
+
contents: write # Enable pushing commits and tags
|
|
29
21
|
id-token: write
|
|
30
22
|
environment:
|
|
31
23
|
name: npm
|
|
32
|
-
url: https://www.npmjs.com/package/@fireberry/cli/v/${{ github.event.inputs.version }}
|
|
33
24
|
|
|
34
25
|
steps:
|
|
35
26
|
- name: Checkout repository
|
|
@@ -46,19 +37,77 @@ jobs:
|
|
|
46
37
|
- name: Install dependencies
|
|
47
38
|
run: npm ci
|
|
48
39
|
|
|
49
|
-
- name:
|
|
50
|
-
|
|
40
|
+
- name: Detect release type from branch
|
|
41
|
+
id: detect-type
|
|
42
|
+
run: |
|
|
43
|
+
BRANCH_NAME="${GITHUB_REF#refs/heads/}"
|
|
44
|
+
echo "Branch: $BRANCH_NAME"
|
|
45
|
+
if [[ "$BRANCH_NAME" == "dev" ]]; then
|
|
46
|
+
echo "type=beta" >> $GITHUB_OUTPUT
|
|
47
|
+
echo "Detected release type: beta"
|
|
48
|
+
elif [[ "$BRANCH_NAME" == "main" ]]; then
|
|
49
|
+
echo "type=production" >> $GITHUB_OUTPUT
|
|
50
|
+
echo "Detected release type: production"
|
|
51
|
+
else
|
|
52
|
+
echo "Error: Workflow must be run from 'dev' or 'main' branch"
|
|
53
|
+
echo "Current branch: $BRANCH_NAME"
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
- name: Bump version (Beta)
|
|
58
|
+
if: steps.detect-type.outputs.type == 'beta'
|
|
59
|
+
id: version-beta
|
|
51
60
|
run: |
|
|
52
61
|
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
62
|
+
echo "Current version: $CURRENT_VERSION"
|
|
63
|
+
|
|
53
64
|
if [[ $CURRENT_VERSION == *"-beta."* ]]; then
|
|
65
|
+
echo "Incrementing beta prerelease version"
|
|
54
66
|
npm version prerelease --preid=beta --no-git-tag-version
|
|
55
67
|
else
|
|
68
|
+
echo "Creating new beta version from production"
|
|
56
69
|
npm version prepatch --preid=beta --no-git-tag-version
|
|
57
70
|
fi
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
73
|
+
echo "New version: $NEW_VERSION"
|
|
74
|
+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
75
|
+
|
|
76
|
+
- name: Bump version (Production)
|
|
77
|
+
if: steps.detect-type.outputs.type == 'production'
|
|
78
|
+
id: version-prod
|
|
79
|
+
run: |
|
|
80
|
+
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
81
|
+
echo "Current version: $CURRENT_VERSION"
|
|
82
|
+
|
|
83
|
+
# Remove -beta suffix first if present
|
|
84
|
+
if [[ $CURRENT_VERSION == *"-beta."* ]]; then
|
|
85
|
+
echo "Removing beta suffix"
|
|
86
|
+
BASE_VERSION=$(echo $CURRENT_VERSION | sed 's/-beta\.[0-9]*$//')
|
|
87
|
+
echo "Base version: $BASE_VERSION"
|
|
88
|
+
npm version $BASE_VERSION --no-git-tag-version
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Then apply the version bump
|
|
92
|
+
echo "Applying ${{ github.event.inputs.version }} bump"
|
|
93
|
+
npm version ${{ github.event.inputs.version }} --no-git-tag-version
|
|
94
|
+
|
|
95
|
+
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
96
|
+
echo "New version: $NEW_VERSION"
|
|
97
|
+
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
98
|
+
|
|
99
|
+
- name: Commit version bump
|
|
100
|
+
run: |
|
|
101
|
+
VERSION=${{ steps.version-beta.outputs.version || steps.version-prod.outputs.version }}
|
|
102
|
+
echo "Committing version: $VERSION"
|
|
103
|
+
|
|
104
|
+
git config user.name "github-actions[bot]"
|
|
105
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
106
|
+
git add package.json package-lock.json
|
|
107
|
+
git commit -m "chore: bump version to v${VERSION} [skip ci]"
|
|
108
|
+
git tag "v${VERSION}"
|
|
109
|
+
|
|
110
|
+
echo "Created commit and tag v${VERSION}"
|
|
62
111
|
|
|
63
112
|
- name: Build package
|
|
64
113
|
run: npm run build --if-present
|
|
@@ -69,10 +118,29 @@ jobs:
|
|
|
69
118
|
- name: Update npm to latest version
|
|
70
119
|
run: npm install -g npm@latest
|
|
71
120
|
|
|
121
|
+
- name: Push changes
|
|
122
|
+
run: |
|
|
123
|
+
echo "Pushing to branch: ${{ github.ref_name }}"
|
|
124
|
+
git push origin ${{ github.ref_name }}
|
|
125
|
+
git push origin --tags
|
|
126
|
+
echo "Successfully pushed commit and tags"
|
|
127
|
+
|
|
72
128
|
- name: Publish to npm (Beta)
|
|
73
|
-
if:
|
|
74
|
-
run:
|
|
129
|
+
if: steps.detect-type.outputs.type == 'beta'
|
|
130
|
+
run: |
|
|
131
|
+
VERSION=${{ steps.version-beta.outputs.version }}
|
|
132
|
+
echo "Publishing beta version $VERSION to npm"
|
|
133
|
+
npm publish --tag beta --provenance --access public
|
|
134
|
+
echo "Successfully published @fireberry/cli@$VERSION (beta tag)"
|
|
135
|
+
env:
|
|
136
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
75
137
|
|
|
76
138
|
- name: Publish to npm (Production)
|
|
77
|
-
if:
|
|
78
|
-
run:
|
|
139
|
+
if: steps.detect-type.outputs.type == 'production'
|
|
140
|
+
run: |
|
|
141
|
+
VERSION=${{ steps.version-prod.outputs.version }}
|
|
142
|
+
echo "Publishing production version $VERSION to npm"
|
|
143
|
+
npm publish --tag latest --provenance --access public
|
|
144
|
+
echo "Successfully published @fireberry/cli@$VERSION (latest tag)"
|
|
145
|
+
env:
|
|
146
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/CLAUDE.md
CHANGED
|
@@ -32,11 +32,42 @@ fireberry --help # Test the linked CLI
|
|
|
32
32
|
|
|
33
33
|
### Publishing
|
|
34
34
|
|
|
35
|
+
Releases are managed through GitHub Actions workflow with automatic version management:
|
|
36
|
+
|
|
37
|
+
**Beta Releases (from `dev` branch):**
|
|
38
|
+
1. Go to GitHub Actions ā "Publish Fireberry CLI to npm"
|
|
39
|
+
2. Click "Run workflow" and select `dev` branch
|
|
40
|
+
3. Workflow automatically:
|
|
41
|
+
- Increments beta version (e.g., 0.4.1-beta.0 ā 0.4.1-beta.1)
|
|
42
|
+
- Commits version change to git
|
|
43
|
+
- Creates git tag (v0.4.1-beta.1)
|
|
44
|
+
- Builds and tests
|
|
45
|
+
- Publishes to npm with `beta` tag
|
|
46
|
+
|
|
47
|
+
**Production Releases (from `main` branch):**
|
|
48
|
+
1. Merge `dev` ā `main` after QA passes
|
|
49
|
+
2. Go to GitHub Actions ā "Publish Fireberry CLI to npm"
|
|
50
|
+
3. Click "Run workflow" and select `main` branch
|
|
51
|
+
4. Choose version bump type (patch/minor/major)
|
|
52
|
+
5. Workflow automatically:
|
|
53
|
+
- Removes `-beta` suffix and bumps version
|
|
54
|
+
- Commits version change to git
|
|
55
|
+
- Creates git tag (v0.4.1)
|
|
56
|
+
- Builds and tests
|
|
57
|
+
- Publishes to npm with `latest` tag
|
|
58
|
+
|
|
59
|
+
**Local Publishing (Emergency Hotfixes):**
|
|
35
60
|
```bash
|
|
36
61
|
npm run publish:beta # Version bump (beta) and publish to npm with beta tag
|
|
37
|
-
npm run publish:prod #
|
|
62
|
+
npm run publish:prod # Version bump (patch) and publish to npm with latest tag
|
|
38
63
|
```
|
|
39
64
|
|
|
65
|
+
**Key Features:**
|
|
66
|
+
- No manual version editing required
|
|
67
|
+
- Git tags automatically created for each release
|
|
68
|
+
- Version consistency maintained between git and npm
|
|
69
|
+
- `[skip ci]` in commit messages prevents workflow loops
|
|
70
|
+
|
|
40
71
|
## Architecture
|
|
41
72
|
|
|
42
73
|
### Module System
|
package/dist/bin/fireberry.js
CHANGED
|
@@ -13,7 +13,13 @@ const program = new Command();
|
|
|
13
13
|
program
|
|
14
14
|
.name("fireberry")
|
|
15
15
|
.description("Fireberry developer CLI")
|
|
16
|
-
.version(packageJson.version)
|
|
16
|
+
.version(packageJson.version)
|
|
17
|
+
.configureOutput({
|
|
18
|
+
outputError: (str, write) => {
|
|
19
|
+
const formatted = str.replace(/^error:/i, 'Error:');
|
|
20
|
+
write(chalk.red(formatted));
|
|
21
|
+
}
|
|
22
|
+
});
|
|
17
23
|
program
|
|
18
24
|
.command("init")
|
|
19
25
|
.argument("[tokenid]", "Fireberry token id")
|
|
@@ -23,10 +29,9 @@ program
|
|
|
23
29
|
});
|
|
24
30
|
program
|
|
25
31
|
.command("create")
|
|
26
|
-
.argument("[name
|
|
27
|
-
.description("Create a new Fireberry app")
|
|
28
|
-
.action(async (
|
|
29
|
-
const name = nameArgs ? nameArgs.join("-") : undefined;
|
|
32
|
+
.argument("[name]", "App name")
|
|
33
|
+
.description("Create a new Fireberry app with a component")
|
|
34
|
+
.action(async (name) => {
|
|
30
35
|
await runCreate({ name });
|
|
31
36
|
});
|
|
32
37
|
program
|
|
@@ -13,6 +13,12 @@ import { HEIGHT_OPTIONS } from "../constants/height-options.js";
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
14
|
const __dirname = path.dirname(__filename);
|
|
15
15
|
const VALID_COMPONENT_TYPES = Object.values(COMPONENT_TYPE);
|
|
16
|
+
function sanitizeComponentName(name) {
|
|
17
|
+
return name
|
|
18
|
+
.toLowerCase()
|
|
19
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
20
|
+
.replace(/^-+|-+$/g, "");
|
|
21
|
+
}
|
|
16
22
|
function validateComponentType(type) {
|
|
17
23
|
if (!VALID_COMPONENT_TYPES.includes(type)) {
|
|
18
24
|
throw new Error(`Invalid component type: "${type}". Valid types are: ${VALID_COMPONENT_TYPES.join(", ")}`);
|
|
@@ -118,17 +124,17 @@ export async function runCreateComponent({ type, name, }) {
|
|
|
118
124
|
throw new Error(`Component with name "${componentName}" already exists in manifest.yml`);
|
|
119
125
|
}
|
|
120
126
|
const validatedType = validateComponentType(componentType);
|
|
127
|
+
const sanitizedName = sanitizeComponentName(componentName);
|
|
121
128
|
const spinner = ora();
|
|
122
129
|
try {
|
|
123
130
|
const componentSettings = await promptForSettings(validatedType);
|
|
124
131
|
spinner.text = chalk.cyan(`Creating Vite React app for "${chalk.cyan(componentName)}"...`);
|
|
125
132
|
spinner.start();
|
|
126
|
-
const componentDir = path.join(process.cwd(),
|
|
133
|
+
const componentDir = path.join(process.cwd(), componentName);
|
|
127
134
|
await fs.ensureDir(componentDir);
|
|
128
|
-
// Create Vite app with React template
|
|
129
135
|
spinner.text = `Running npm create vite@latest...`;
|
|
130
|
-
const viteResult = spawnSync(`npm create vite@latest ${
|
|
131
|
-
cwd:
|
|
136
|
+
const viteResult = spawnSync(`npm create vite@latest ${sanitizedName} -- --template react --no-interactive`, {
|
|
137
|
+
cwd: process.cwd(),
|
|
132
138
|
stdio: "inherit",
|
|
133
139
|
shell: true,
|
|
134
140
|
});
|
|
@@ -177,7 +183,7 @@ export async function runCreateComponent({ type, name, }) {
|
|
|
177
183
|
type: validatedType,
|
|
178
184
|
title: componentName,
|
|
179
185
|
id: componentId,
|
|
180
|
-
path:
|
|
186
|
+
path: `${componentName}/dist`,
|
|
181
187
|
settings: componentSettings,
|
|
182
188
|
};
|
|
183
189
|
if (!manifest.components) {
|
|
@@ -193,9 +199,9 @@ export async function runCreateComponent({ type, name, }) {
|
|
|
193
199
|
spinner.succeed(`Successfully created component "${chalk.cyan(componentName)}"!`);
|
|
194
200
|
console.log(chalk.gray(`Component ID: ${componentId}`));
|
|
195
201
|
console.log(chalk.gray(`Type: ${validatedType}`));
|
|
196
|
-
console.log(chalk.gray(`Path:
|
|
197
|
-
console.log(chalk.green(
|
|
198
|
-
console.log(chalk.white(` cd
|
|
202
|
+
console.log(chalk.gray(`Path: ${sanitizedName}/dist`));
|
|
203
|
+
console.log(chalk.green(`\nYour component "${chalk.cyan(componentName)}" is ready!`));
|
|
204
|
+
console.log(chalk.white(` cd ${sanitizedName}`));
|
|
199
205
|
console.log(chalk.white(` npm run dev # Start development server`));
|
|
200
206
|
console.log(chalk.white(` npm run build # Build for production`));
|
|
201
207
|
}
|
package/dist/commands/create.js
CHANGED
|
@@ -2,13 +2,12 @@ import inquirer from "inquirer";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "fs-extra";
|
|
4
4
|
import { v4 as uuidv4 } from "uuid";
|
|
5
|
-
import
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { createApp } from "../api/requests.js";
|
|
9
9
|
import { getManifest } from "../utils/components.utils.js";
|
|
10
|
-
|
|
11
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
+
import { runCreateComponent } from "./create-component.js";
|
|
12
11
|
function slugifyName(name) {
|
|
13
12
|
const validPattern = /^[a-zA-Z0-9_-]+$/;
|
|
14
13
|
if (!validPattern.test(name)) {
|
|
@@ -20,44 +19,55 @@ export async function runCreate({ name }) {
|
|
|
20
19
|
let appName = name;
|
|
21
20
|
if (!appName) {
|
|
22
21
|
const answers = await inquirer.prompt([
|
|
23
|
-
{
|
|
22
|
+
{
|
|
23
|
+
type: "input",
|
|
24
|
+
name: "name",
|
|
25
|
+
message: "App name:",
|
|
26
|
+
},
|
|
24
27
|
]);
|
|
25
28
|
appName = (answers.name || "").trim();
|
|
26
29
|
}
|
|
27
30
|
if (!appName) {
|
|
28
|
-
throw new Error("Missing name.");
|
|
31
|
+
throw new Error("Missing app name.");
|
|
29
32
|
}
|
|
30
33
|
const slug = slugifyName(appName);
|
|
31
34
|
const appId = uuidv4();
|
|
32
|
-
const componentId = uuidv4();
|
|
33
35
|
const appDir = path.resolve(process.cwd(), slug);
|
|
36
|
+
const componentName = `${slug}-component`;
|
|
34
37
|
if (await fs.pathExists(appDir)) {
|
|
35
38
|
throw new Error(`Already exists. ${chalk.yellow(slug)}`);
|
|
36
39
|
}
|
|
37
40
|
const spinner = ora(`Creating app "${chalk.cyan(appName)}"...`).start();
|
|
41
|
+
const originalCwd = process.cwd();
|
|
38
42
|
try {
|
|
39
43
|
await fs.ensureDir(appDir);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
await fs.writeFile(path.join(appDir, "manifest.yml"),
|
|
49
|
-
|
|
50
|
-
const manifest = await getManifest(appDir);
|
|
51
|
-
await createApp(manifest);
|
|
52
|
-
spinner.succeed(`Successfully created "${chalk.cyan(appName)}" app!`);
|
|
44
|
+
const initialManifest = {
|
|
45
|
+
app: {
|
|
46
|
+
id: appId,
|
|
47
|
+
name: appName,
|
|
48
|
+
description: "",
|
|
49
|
+
},
|
|
50
|
+
components: [],
|
|
51
|
+
};
|
|
52
|
+
await fs.writeFile(path.join(appDir, "manifest.yml"), yaml.dump(initialManifest, { indent: 2, lineWidth: -1, noRefs: true }), "utf-8");
|
|
53
|
+
spinner.succeed(`App directory "${chalk.cyan(appName)}" created!`);
|
|
53
54
|
console.log(chalk.gray(`š Location: ${appDir}`));
|
|
54
55
|
console.log(chalk.gray(`App ID: ${appId}`));
|
|
55
|
-
|
|
56
|
+
process.chdir(appDir);
|
|
57
|
+
console.log(chalk.cyan(`\nAdding component "${componentName}"...`));
|
|
58
|
+
await runCreateComponent({ name: componentName });
|
|
59
|
+
spinner.start();
|
|
60
|
+
spinner.text = `Registering app with Fireberry...`;
|
|
61
|
+
await createApp(await getManifest());
|
|
62
|
+
spinner.succeed(`App registered with Fireberry!`);
|
|
63
|
+
console.log(chalk.green(`\nš Your app is ready!`));
|
|
64
|
+
console.log(chalk.white(`\nNext steps:`));
|
|
56
65
|
console.log(chalk.white(` cd ${slug}`));
|
|
57
|
-
console.log(chalk.white(
|
|
66
|
+
console.log(chalk.white(` fireberry push # Push to Fireberry`));
|
|
58
67
|
}
|
|
59
68
|
catch (error) {
|
|
60
69
|
spinner.fail(`Failed to create app "${chalk.cyan(appName)}"`);
|
|
70
|
+
process.chdir(originalCwd);
|
|
61
71
|
throw error;
|
|
62
72
|
}
|
|
63
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fireberry/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Fireberry CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test": "node test/smoke.test.js",
|
|
23
23
|
"version:beta": "npm version prerelease --preid=beta",
|
|
24
24
|
"publish:beta": "npm run version:beta && npm publish --tag beta",
|
|
25
|
-
"publish:prod": "npm publish --tag latest"
|
|
25
|
+
"publish:prod": "npm version patch && npm publish --tag latest"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"cli",
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
+
"@fireberry/ds": "^0.0.151",
|
|
45
|
+
"@fireberry/sdk": "^0.0.5",
|
|
44
46
|
"axios": "^1.12.2",
|
|
45
47
|
"chalk": "^5.3.0",
|
|
46
48
|
"commander": "^12.1.0",
|
package/src/bin/fireberry.ts
CHANGED
|
@@ -15,7 +15,13 @@ const program = new Command();
|
|
|
15
15
|
program
|
|
16
16
|
.name("fireberry")
|
|
17
17
|
.description("Fireberry developer CLI")
|
|
18
|
-
.version(packageJson.version)
|
|
18
|
+
.version(packageJson.version)
|
|
19
|
+
.configureOutput({
|
|
20
|
+
outputError: (str, write) => {
|
|
21
|
+
const formatted = str.replace(/^error:/i, 'Error:');
|
|
22
|
+
write(chalk.red(formatted));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
19
25
|
|
|
20
26
|
program
|
|
21
27
|
.command("init")
|
|
@@ -27,11 +33,10 @@ program
|
|
|
27
33
|
|
|
28
34
|
program
|
|
29
35
|
.command("create")
|
|
30
|
-
.argument("[name
|
|
31
|
-
.description("Create a new Fireberry app")
|
|
32
|
-
.action(async (
|
|
33
|
-
|
|
34
|
-
await runCreate({ name });
|
|
36
|
+
.argument("[name]", "App name")
|
|
37
|
+
.description("Create a new Fireberry app with a component")
|
|
38
|
+
.action(async (name?: string) => {
|
|
39
|
+
await runCreate({ name });
|
|
35
40
|
});
|
|
36
41
|
|
|
37
42
|
program
|
|
@@ -22,6 +22,13 @@ interface CreateComponentOptions {
|
|
|
22
22
|
|
|
23
23
|
const VALID_COMPONENT_TYPES = Object.values(COMPONENT_TYPE);
|
|
24
24
|
|
|
25
|
+
function sanitizeComponentName(name: string): string {
|
|
26
|
+
return name
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
29
|
+
.replace(/^-+|-+$/g, "");
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function validateComponentType(type: string): ComponentType {
|
|
26
33
|
if (!VALID_COMPONENT_TYPES.includes(type as ComponentType)) {
|
|
27
34
|
throw new Error(
|
|
@@ -156,6 +163,8 @@ export async function runCreateComponent({
|
|
|
156
163
|
|
|
157
164
|
const validatedType = validateComponentType(componentType);
|
|
158
165
|
|
|
166
|
+
const sanitizedName = sanitizeComponentName(componentName);
|
|
167
|
+
|
|
159
168
|
const spinner = ora();
|
|
160
169
|
|
|
161
170
|
try {
|
|
@@ -166,15 +175,14 @@ export async function runCreateComponent({
|
|
|
166
175
|
);
|
|
167
176
|
spinner.start();
|
|
168
177
|
|
|
169
|
-
const componentDir = path.join(process.cwd(),
|
|
178
|
+
const componentDir = path.join(process.cwd(), componentName);
|
|
170
179
|
await fs.ensureDir(componentDir);
|
|
171
180
|
|
|
172
|
-
// Create Vite app with React template
|
|
173
181
|
spinner.text = `Running npm create vite@latest...`;
|
|
174
182
|
const viteResult = spawnSync(
|
|
175
|
-
`npm create vite@latest ${
|
|
183
|
+
`npm create vite@latest ${sanitizedName} -- --template react --no-interactive`,
|
|
176
184
|
{
|
|
177
|
-
cwd:
|
|
185
|
+
cwd: process.cwd(),
|
|
178
186
|
stdio: "inherit",
|
|
179
187
|
shell: true,
|
|
180
188
|
}
|
|
@@ -247,7 +255,7 @@ export async function runCreateComponent({
|
|
|
247
255
|
type: validatedType,
|
|
248
256
|
title: componentName,
|
|
249
257
|
id: componentId,
|
|
250
|
-
path:
|
|
258
|
+
path: `${componentName}/dist`,
|
|
251
259
|
settings: componentSettings,
|
|
252
260
|
};
|
|
253
261
|
|
|
@@ -270,11 +278,15 @@ export async function runCreateComponent({
|
|
|
270
278
|
spinner.succeed(
|
|
271
279
|
`Successfully created component "${chalk.cyan(componentName)}"!`
|
|
272
280
|
);
|
|
281
|
+
|
|
273
282
|
console.log(chalk.gray(`Component ID: ${componentId}`));
|
|
274
283
|
console.log(chalk.gray(`Type: ${validatedType}`));
|
|
275
|
-
console.log(chalk.gray(`Path:
|
|
276
|
-
|
|
277
|
-
console.log(
|
|
284
|
+
console.log(chalk.gray(`Path: ${sanitizedName}/dist`));
|
|
285
|
+
|
|
286
|
+
console.log(
|
|
287
|
+
chalk.green(`\nYour component "${chalk.cyan(componentName)}" is ready!`)
|
|
288
|
+
);
|
|
289
|
+
console.log(chalk.white(` cd ${sanitizedName}`));
|
|
278
290
|
console.log(chalk.white(` npm run dev # Start development server`));
|
|
279
291
|
console.log(chalk.white(` npm run build # Build for production`));
|
|
280
292
|
} catch (error) {
|
package/src/commands/create.ts
CHANGED
|
@@ -2,14 +2,12 @@ import inquirer from "inquirer";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "fs-extra";
|
|
4
4
|
import { v4 as uuidv4 } from "uuid";
|
|
5
|
-
import
|
|
5
|
+
import yaml from "js-yaml";
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { createApp } from "../api/requests.js";
|
|
9
9
|
import { getManifest } from "../utils/components.utils.js";
|
|
10
|
-
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
+
import { runCreateComponent } from "./create-component.js";
|
|
13
11
|
|
|
14
12
|
interface CreateOptions {
|
|
15
13
|
name?: string;
|
|
@@ -31,59 +29,70 @@ export async function runCreate({ name }: CreateOptions): Promise<void> {
|
|
|
31
29
|
|
|
32
30
|
if (!appName) {
|
|
33
31
|
const answers = await inquirer.prompt([
|
|
34
|
-
{
|
|
32
|
+
{
|
|
33
|
+
type: "input",
|
|
34
|
+
name: "name",
|
|
35
|
+
message: "App name:",
|
|
36
|
+
},
|
|
35
37
|
]);
|
|
36
38
|
appName = (answers.name || "").trim();
|
|
37
39
|
}
|
|
40
|
+
|
|
38
41
|
if (!appName) {
|
|
39
|
-
throw new Error("Missing name.");
|
|
42
|
+
throw new Error("Missing app name.");
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
const slug = slugifyName(appName);
|
|
43
46
|
const appId = uuidv4();
|
|
44
|
-
const componentId = uuidv4();
|
|
45
47
|
const appDir = path.resolve(process.cwd(), slug);
|
|
48
|
+
const componentName = `${slug}-component`;
|
|
46
49
|
|
|
47
50
|
if (await fs.pathExists(appDir)) {
|
|
48
51
|
throw new Error(`Already exists. ${chalk.yellow(slug)}`);
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
const spinner = ora(`Creating app "${chalk.cyan(appName)}"...`).start();
|
|
55
|
+
const originalCwd = process.cwd();
|
|
52
56
|
|
|
53
57
|
try {
|
|
54
58
|
await fs.ensureDir(appDir);
|
|
55
59
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
const initialManifest = {
|
|
61
|
+
app: {
|
|
62
|
+
id: appId,
|
|
63
|
+
name: appName,
|
|
64
|
+
description: "",
|
|
65
|
+
},
|
|
66
|
+
components: [],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
await fs.writeFile(
|
|
70
|
+
path.join(appDir, "manifest.yml"),
|
|
71
|
+
yaml.dump(initialManifest, { indent: 2, lineWidth: -1, noRefs: true }),
|
|
63
72
|
"utf-8"
|
|
64
73
|
);
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.replace(/{{componentId}}/g, componentId);
|
|
75
|
+
spinner.succeed(`App directory "${chalk.cyan(appName)}" created!`);
|
|
76
|
+
console.log(chalk.gray(`š Location: ${appDir}`));
|
|
77
|
+
console.log(chalk.gray(`App ID: ${appId}`));
|
|
70
78
|
|
|
71
|
-
|
|
79
|
+
process.chdir(appDir);
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
await fs.writeFile(path.join(appDir, "index.html"), htmlContent);
|
|
75
|
-
const manifest = await getManifest(appDir);
|
|
81
|
+
console.log(chalk.cyan(`\nAdding component "${componentName}"...`));
|
|
76
82
|
|
|
77
|
-
await
|
|
83
|
+
await runCreateComponent({ name: componentName });
|
|
84
|
+
spinner.start();
|
|
85
|
+
spinner.text = `Registering app with Fireberry...`;
|
|
86
|
+
await createApp(await getManifest());
|
|
87
|
+
spinner.succeed(`App registered with Fireberry!`);
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
console.log(chalk.
|
|
81
|
-
console.log(chalk.gray(`App ID: ${appId}`));
|
|
82
|
-
console.log(chalk.green("\nš Your app is ready! Next steps:"));
|
|
89
|
+
console.log(chalk.green(`\nš Your app is ready!`));
|
|
90
|
+
console.log(chalk.white(`\nNext steps:`));
|
|
83
91
|
console.log(chalk.white(` cd ${slug}`));
|
|
84
|
-
console.log(chalk.white(
|
|
92
|
+
console.log(chalk.white(` fireberry push # Push to Fireberry`));
|
|
85
93
|
} catch (error) {
|
|
86
94
|
spinner.fail(`Failed to create app "${chalk.cyan(appName)}"`);
|
|
95
|
+
process.chdir(originalCwd);
|
|
87
96
|
throw error;
|
|
88
97
|
}
|
|
89
98
|
}
|
package/src/templates/index.html
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>{{appName}}</title>
|
|
8
|
-
</head>
|
|
9
|
-
|
|
10
|
-
<body>
|
|
11
|
-
<h1>Hello World</h1>
|
|
12
|
-
<p>Welcome to {{appName}}!</p>
|
|
13
|
-
</body>
|
|
14
|
-
|
|
15
|
-
</html>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
app:
|
|
2
|
-
id: "{{appId}}"
|
|
3
|
-
name: "{{appName}}"
|
|
4
|
-
description: ""
|
|
5
|
-
components:
|
|
6
|
-
- type: record
|
|
7
|
-
title: my-first-component
|
|
8
|
-
id: "{{componentId}}"
|
|
9
|
-
path: static/comp/build
|
|
10
|
-
settings:
|
|
11
|
-
iconName: "related-single"
|
|
12
|
-
iconColor: "#7aae7f"
|
|
13
|
-
objectType: 0
|
|
14
|
-
height: "M"
|