evophp 1.1.2 → 1.2.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 +28 -4
- package/bin/evophp.js +1 -0
- package/commands/entry.js +41 -0
- package/package.json +1 -1
- package/utils/entry.js +170 -0
- package/utils/helpers.js +21 -55
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@ EvoPHP CLI is a command-line interface designed to simplify the development and
|
|
|
10
10
|
|
|
11
11
|
* [Installation](#installation)
|
|
12
12
|
* [All Commands](#all-commands)
|
|
13
|
-
|
|
14
13
|
* [Initialization](#initialization)
|
|
15
14
|
* [Plugin/Theme Creation](#plugintheme-creation)
|
|
16
15
|
* [Plugin/Theme Installation](#plugintheme-installation)
|
|
17
16
|
* [Plugin/Theme Uninstallation](#plugintheme-uninstallation)
|
|
18
17
|
* [Dependency Management](#dependency-management)
|
|
18
|
+
* [Entry Management](#entry-management)
|
|
19
19
|
* [Authentication & PAT Storage](#authentication--pat-storage)
|
|
20
20
|
* [Further Documentation](#further-documentation)
|
|
21
21
|
|
|
@@ -70,7 +70,6 @@ evophp n theme <name>
|
|
|
70
70
|
* Creates a new plugin or theme from a template in **your own GitHub account**.
|
|
71
71
|
|
|
72
72
|
* Supports:
|
|
73
|
-
|
|
74
73
|
* Just `<name>` → Uses your GitHub account (token required)
|
|
75
74
|
* ⚠️ You can only create repositories under your GitHub account.
|
|
76
75
|
|
|
@@ -95,7 +94,6 @@ evophp i theme <name>
|
|
|
95
94
|
* Installs a plugin or theme from GitHub into your project.
|
|
96
95
|
|
|
97
96
|
* Supports:
|
|
98
|
-
|
|
99
97
|
* Just `<name>` → Clones from your GitHub account (token required)
|
|
100
98
|
* `<author>/<name>` → Clones from public GitHub repo (no token required)
|
|
101
99
|
|
|
@@ -157,6 +155,32 @@ evophp d MyPlugin --composer
|
|
|
157
155
|
|
|
158
156
|
---
|
|
159
157
|
|
|
158
|
+
### Entry Management
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
evophp entry <plugin> <entry>
|
|
162
|
+
evophp e <plugin> <entry>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
* Adds a new entry point to a plugin.
|
|
166
|
+
* Prompts for:
|
|
167
|
+
* Landing page name (default: `index`)
|
|
168
|
+
* Route path
|
|
169
|
+
* Role access permissions (IDs from config.json)
|
|
170
|
+
* Page title
|
|
171
|
+
* Creates:
|
|
172
|
+
* Blade view file
|
|
173
|
+
* JavaScript entry and router files
|
|
174
|
+
* Route entry in `Routes.php`
|
|
175
|
+
|
|
176
|
+
* Example:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
evophp entry MyPlugin Dashboard
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
160
184
|
## Authentication & PAT Storage
|
|
161
185
|
|
|
162
186
|
When using a command with just `<name>` (e.g. `evophp new plugin MyPlugin`), EvoPHP CLI will:
|
|
@@ -173,5 +197,5 @@ When using a command with just `<name>` (e.g. `evophp new plugin MyPlugin`), Evo
|
|
|
173
197
|
|
|
174
198
|
## Further Documentation
|
|
175
199
|
|
|
176
|
-
More advanced use cases, templates, and configuration details coming soon at:
|
|
200
|
+
More advanced use cases, templates, and configuration details coming soon at:
|
|
177
201
|
📘 [Official EvoPHP Documentation](#)
|
package/bin/evophp.js
CHANGED
|
@@ -9,6 +9,7 @@ yargs(hideBin(process.argv))
|
|
|
9
9
|
.command(require("../commands/install"))
|
|
10
10
|
.command(require("../commands/uninstall"))
|
|
11
11
|
.command(require("../commands/dependencies"))
|
|
12
|
+
.command(require("../commands/entry"))
|
|
12
13
|
.command(require("../commands/help"))
|
|
13
14
|
.demandCommand(1, "Please specify a valid command. Use --help to see available commands.")
|
|
14
15
|
.help()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { readLineAsync } = require("../utils/helpers");
|
|
4
|
+
const { addPluginEntry } = require("../utils/entry");
|
|
5
|
+
const log = require("../utils/logger");
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
command: "entry <plugin> <entry>",
|
|
9
|
+
aliases: ["e"],
|
|
10
|
+
describe: "Add a new entry point to a plugin",
|
|
11
|
+
|
|
12
|
+
builder: yargs => yargs
|
|
13
|
+
.positional("plugin", { describe: "Plugin name", type: "string" })
|
|
14
|
+
.positional("entry", { describe: "Entry folder name", type: "string" })
|
|
15
|
+
.option("landing", {
|
|
16
|
+
alias: "l",
|
|
17
|
+
type: "string",
|
|
18
|
+
describe: "Landing file name (default: index)"
|
|
19
|
+
}),
|
|
20
|
+
|
|
21
|
+
handler: async ({ plugin, entry, landing }) => {
|
|
22
|
+
try {
|
|
23
|
+
// 🛡️ Check if plugin exists
|
|
24
|
+
const pluginPath = path.join("Public", "Modules", plugin);
|
|
25
|
+
if (!fs.existsSync(pluginPath)) {
|
|
26
|
+
log.error(`Plugin '${plugin}' does not exist at ${pluginPath}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!landing) {
|
|
31
|
+
landing = await readLineAsync("Landing page filename? (default: index): ") || "index";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await addPluginEntry(plugin, entry, landing);
|
|
35
|
+
log.success(`Entry '${entry}/${landing}' successfully added to plugin '${plugin}'`);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
log.error(`Failed to add entry: ${err.message}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
package/package.json
CHANGED
package/utils/entry.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const editJsonFile = require("edit-json-file");
|
|
4
|
+
const log = require("./logger");
|
|
5
|
+
const readline = require("readline");
|
|
6
|
+
const { mergeConfig } = require("./helpers");
|
|
7
|
+
|
|
8
|
+
const readLineAsync = (msg) => {
|
|
9
|
+
const rl = readline.createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stdout,
|
|
12
|
+
});
|
|
13
|
+
return new Promise((resolve) =>
|
|
14
|
+
rl.question(msg, (ans) => {
|
|
15
|
+
rl.close();
|
|
16
|
+
resolve(ans);
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Add a new entry point to a plugin with optional custom landing page
|
|
23
|
+
* Creates both server-side view, JavaScript entry files, and route
|
|
24
|
+
*
|
|
25
|
+
* @param {string} pluginName - The plugin name
|
|
26
|
+
* @param {string} entryName - The entry folder name
|
|
27
|
+
* @param {string} landing - The landing filename (e.g. index)
|
|
28
|
+
*/
|
|
29
|
+
async function addPluginEntry(pluginName, entryName, landing = "index") {
|
|
30
|
+
const configPath = path.resolve("config.json");
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(configPath)) {
|
|
33
|
+
log.error(`Config file not found: ${configPath}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const configJson = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
38
|
+
|
|
39
|
+
// 1. Prompt for route and auth roles
|
|
40
|
+
const route = await readLineAsync("Enter route path (e.g. dashboard): ");
|
|
41
|
+
|
|
42
|
+
// Collect unique role IDs from Auth.roles
|
|
43
|
+
const rawRoles = Object.values(configJson.Auth?.roles || {}).flatMap((role) =>
|
|
44
|
+
Array.isArray(role.capacity) ? role.capacity : []
|
|
45
|
+
);
|
|
46
|
+
const uniqueRoles = [...new Set(rawRoles.map(String))];
|
|
47
|
+
|
|
48
|
+
console.log("Available Role IDs: ", uniqueRoles.join(", "));
|
|
49
|
+
const auth = await readLineAsync(
|
|
50
|
+
"Enter role IDs for access (comma separated): "
|
|
51
|
+
);
|
|
52
|
+
const authList = auth
|
|
53
|
+
.split(",")
|
|
54
|
+
.map((x) => x.trim())
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
|
|
57
|
+
const pageTitle = await readLineAsync("Enter page title: ");
|
|
58
|
+
|
|
59
|
+
// 2. Derive pluginPrefix from Controller filename
|
|
60
|
+
const controllerDir = path.join("Public", "Modules", pluginName);
|
|
61
|
+
const controllerFile = fs
|
|
62
|
+
.readdirSync(controllerDir)
|
|
63
|
+
.find((f) => f.endsWith("Controller.php"));
|
|
64
|
+
const pluginPrefix = controllerFile
|
|
65
|
+
? controllerFile.replace("Controller.php", "")
|
|
66
|
+
: pluginName.slice(0, 3).toUpperCase();
|
|
67
|
+
|
|
68
|
+
// 3. Normalize entryName with prefix if missing
|
|
69
|
+
if (!entryName.startsWith(pluginPrefix)) {
|
|
70
|
+
entryName = pluginPrefix + entryName;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const entryPath = `${entryName}/${landing}`;
|
|
74
|
+
|
|
75
|
+
// 4. Update root config.json entry
|
|
76
|
+
const rootConfig = editJsonFile(configPath);
|
|
77
|
+
const current = rootConfig.get(`modules.${pluginName}`) || {
|
|
78
|
+
entry: [],
|
|
79
|
+
active: true,
|
|
80
|
+
order: 0,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (!current.entry.includes(entryPath)) {
|
|
84
|
+
current.entry.push(entryPath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
rootConfig.set(`modules.${pluginName}`, current);
|
|
88
|
+
rootConfig.save();
|
|
89
|
+
log.success(`Added entry "${entryPath}" to ${pluginName} config.`);
|
|
90
|
+
|
|
91
|
+
// 5. Update EvoJsDev config.json entry
|
|
92
|
+
const pluginConfigPath = path.join(
|
|
93
|
+
"EvoJsDev",
|
|
94
|
+
"Modules",
|
|
95
|
+
pluginName,
|
|
96
|
+
"config.json"
|
|
97
|
+
);
|
|
98
|
+
const pluginConfig = editJsonFile(pluginConfigPath);
|
|
99
|
+
const currentPluginConfig = pluginConfig.get(`modules.${pluginName}`) || {
|
|
100
|
+
entry: [],
|
|
101
|
+
active: true,
|
|
102
|
+
order: 0,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (!currentPluginConfig.entry.includes(entryPath)) {
|
|
106
|
+
currentPluginConfig.entry.push(entryPath);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pluginConfig.set(`modules.${pluginName}`, currentPluginConfig);
|
|
110
|
+
pluginConfig.save();
|
|
111
|
+
log.success(`Updated EvoJsDev config with entry "${entryPath}".`);
|
|
112
|
+
|
|
113
|
+
// Load templates
|
|
114
|
+
const bladeContent = fs.readFileSync(
|
|
115
|
+
path.join(__dirname, "../templates/blade.landing.php"),
|
|
116
|
+
"utf8"
|
|
117
|
+
);
|
|
118
|
+
const jsContent = fs.readFileSync(
|
|
119
|
+
path.join(__dirname, "../templates/js.entry.js"),
|
|
120
|
+
"utf8"
|
|
121
|
+
);
|
|
122
|
+
const routerContent = fs.readFileSync(
|
|
123
|
+
path.join(__dirname, "../templates/js.router.js"),
|
|
124
|
+
"utf8"
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// 6. Create server-side landing blade file
|
|
128
|
+
const bladeDir = path.join(
|
|
129
|
+
"Public",
|
|
130
|
+
"Modules",
|
|
131
|
+
pluginName,
|
|
132
|
+
"Views",
|
|
133
|
+
entryName
|
|
134
|
+
);
|
|
135
|
+
const bladeFile = path.join(bladeDir, `${landing}.blade.php`);
|
|
136
|
+
|
|
137
|
+
if (!fs.existsSync(bladeDir)) fs.mkdirSync(bladeDir, { recursive: true });
|
|
138
|
+
fs.writeFileSync(bladeFile, bladeContent, "utf8");
|
|
139
|
+
log.success(`Created blade view: ${bladeFile}`);
|
|
140
|
+
|
|
141
|
+
// 7. Create JavaScript entry files
|
|
142
|
+
const jsEntryDir = path.join("EvoJsDev", "Modules", pluginName, entryName);
|
|
143
|
+
const jsRouterDir = path.join(jsEntryDir, "router");
|
|
144
|
+
|
|
145
|
+
if (!fs.existsSync(jsEntryDir)) fs.mkdirSync(jsEntryDir, { recursive: true });
|
|
146
|
+
if (!fs.existsSync(jsRouterDir)) fs.mkdirSync(jsRouterDir);
|
|
147
|
+
|
|
148
|
+
const jsFile = path.join(jsEntryDir, `${landing}.js`);
|
|
149
|
+
fs.writeFileSync(jsFile, jsContent, "utf8");
|
|
150
|
+
log.success(`Created JS entry: ${jsFile}`);
|
|
151
|
+
|
|
152
|
+
const routerIndex = path.join(jsRouterDir, "index.js");
|
|
153
|
+
fs.writeFileSync(routerIndex, routerContent, "utf8");
|
|
154
|
+
log.success(`Created JS router: ${routerIndex}`);
|
|
155
|
+
|
|
156
|
+
// 8. Add route to Public/Modules/plugin/Routes.php
|
|
157
|
+
const routesPath = path.join("Public", "Modules", pluginName, "Routes.php");
|
|
158
|
+
const routeLine =
|
|
159
|
+
`$router->get('/${route}', function($params){\n` +
|
|
160
|
+
` $controller = new ${pluginPrefix}Controller;\n` +
|
|
161
|
+
` $controller->{'${entryPath}'}($params)->auth(${authList.join(
|
|
162
|
+
","
|
|
163
|
+
)})->setData(['pageTitle' => "${pageTitle}"]);\n` +
|
|
164
|
+
`});\n\n`;
|
|
165
|
+
|
|
166
|
+
fs.appendFileSync(routesPath, routeLine, "utf8");
|
|
167
|
+
log.success(`Route added to ${routesPath}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = { addPluginEntry };
|
package/utils/helpers.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const readline = require("readline");
|
|
2
2
|
const editJsonFile = require("edit-json-file");
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Prompt user for input with a message
|
|
@@ -52,78 +55,42 @@ const isObject = (item) =>
|
|
|
52
55
|
/**
|
|
53
56
|
* Deep merge multiple objects into one
|
|
54
57
|
*/
|
|
55
|
-
const mergeDeep = async (target,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (!target[key]) Object.assign(target, { [key]: {} });
|
|
63
|
-
await mergeDeep(target[key], source[key]);
|
|
64
|
-
} else {
|
|
65
|
-
Object.assign(target, { [key]: source[key] });
|
|
66
|
-
}
|
|
58
|
+
const mergeDeep = async (target, source) => {
|
|
59
|
+
for (const key in source) {
|
|
60
|
+
if (isObject(source[key])) {
|
|
61
|
+
if (!target[key]) target[key] = {};
|
|
62
|
+
await mergeDeep(target[key], source[key]);
|
|
63
|
+
} else {
|
|
64
|
+
target[key] = source[key];
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
|
-
|
|
70
|
-
return mergeDeep(target, ...sources);
|
|
67
|
+
return target;
|
|
71
68
|
};
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*/
|
|
76
|
-
const mergeConfig = async (config) => {
|
|
77
|
-
const file = editJsonFile("./config.json");
|
|
78
|
-
|
|
70
|
+
const mergeConfig = async (config, filePath = "./config.json") => {
|
|
71
|
+
const file = editJsonFile(filePath);
|
|
79
72
|
for (const key in config) {
|
|
80
73
|
const existing = file.get(key);
|
|
81
74
|
if (existing === undefined) {
|
|
82
75
|
file.set(key, config[key]);
|
|
83
76
|
} else if (isObject(existing)) {
|
|
84
77
|
const merged = await mergeDeep(existing, config[key]);
|
|
78
|
+
file.set(key, existing); // ensure file uses the updated object reference
|
|
85
79
|
file.set(key, merged);
|
|
86
80
|
} else {
|
|
87
81
|
file.set(key, config[key]);
|
|
88
82
|
}
|
|
89
83
|
}
|
|
90
|
-
|
|
91
84
|
file.save();
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Add a new entry point to a plugin's config.json file
|
|
96
|
-
* @param {string} pluginName - The plugin name
|
|
97
|
-
* @param {string} entryName - The entry directory name
|
|
98
|
-
*/
|
|
99
|
-
function addPluginEntry(pluginName, entryName) {
|
|
100
|
-
const configPath = path.join("EvoJsDev", "Modules", pluginName, "config.json");
|
|
101
|
-
|
|
102
|
-
if (!fs.existsSync(configPath)) {
|
|
103
|
-
log.error(`Config file not found: ${configPath}`);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
85
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
order: 0
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
if (!current.entry.includes(entryPath)) {
|
|
117
|
-
current.entry.push(entryPath);
|
|
86
|
+
// Run build process if entry config changes
|
|
87
|
+
try {
|
|
88
|
+
console.log("\n[INFO] Running npm run dev to recompile JS bundle...");
|
|
89
|
+
execSync("npm run dev", { stdio: "inherit" });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("[ERROR] Failed to run 'npm run dev':", error.message);
|
|
118
92
|
}
|
|
119
|
-
|
|
120
|
-
file.set(`modules.${pluginName}`, current);
|
|
121
|
-
file.save();
|
|
122
|
-
log.success(`Added entry "${entryPath}" to ${pluginName} config.`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const fs = require("fs");
|
|
126
|
-
const path = require("path");
|
|
93
|
+
};
|
|
127
94
|
|
|
128
95
|
/**
|
|
129
96
|
* Save a key-value pair to .env.development (creates or updates)
|
|
@@ -156,6 +123,5 @@ module.exports = {
|
|
|
156
123
|
readLineAsync,
|
|
157
124
|
splitPluginName,
|
|
158
125
|
mergeConfig,
|
|
159
|
-
addPluginEntry,
|
|
160
126
|
saveToEnv
|
|
161
127
|
};
|