evophp 1.3.3 → 1.4.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 +61 -13
- package/commands/dependencies.js +2 -12
- package/commands/help.js +9 -6
- package/commands/install.js +26 -4
- package/commands/new.js +118 -43
- package/package.json +1 -1
- package/templates/plugin/js/Entry/index.js +11 -0
- package/templates/plugin/js/Entry/router/index.js +27 -0
- package/templates/plugin/js/Entry/views/Dashboard.vue +8 -0
- package/templates/plugin/js/config.json +14 -0
- package/templates/plugin/js/dependencies.json +4 -0
- package/templates/plugin/js/store/sample.js +76 -0
- package/templates/plugin/php/Controller.php +34 -0
- package/templates/plugin/php/Install.php +3 -0
- package/templates/plugin/php/Routes.php +13 -0
- package/templates/plugin/php/Views/Entry/index.blade.php +2 -0
- package/templates/plugin/php/gitignore +1 -0
- package/templates/theme/Assets/style.css +0 -0
- package/templates/theme/Data/index.data.php +27 -0
- package/templates/theme/Data/login.data.php +32 -0
- package/templates/theme/Views/index.blade.php +7 -0
- package/templates/theme/Views/layouts/loginparts.blade.php +25 -0
- package/templates/theme/Views/layouts/parts.blade.php +48 -0
- package/templates/theme/Views/login.blade.php +21 -0
- package/templates/theme/gitignore +1 -0
- package/utils/dependencies.js +64 -1
- package/utils/github.js +27 -1
- package/utils/template.js +75 -0
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ EvoPHP CLI is a command-line interface designed to simplify the development and
|
|
|
17
17
|
* [Dependency Management](#dependency-management)
|
|
18
18
|
* [Entry Management](#entry-management)
|
|
19
19
|
* [Plugin & Theme Dependencies](#plugin--theme-dependencies)
|
|
20
|
+
* [Adding to Another Plugin](#adding-to-another-plugin)
|
|
20
21
|
* [Authentication & PAT Storage](#authentication--pat-storage)
|
|
21
22
|
* [Further Documentation](#further-documentation)
|
|
22
23
|
|
|
@@ -62,32 +63,53 @@ evophp init [project-name]
|
|
|
62
63
|
### Plugin/Theme Creation
|
|
63
64
|
|
|
64
65
|
```bash
|
|
65
|
-
evophp new plugin <name>
|
|
66
|
-
evophp new theme <name>
|
|
66
|
+
evophp new plugin <name> [--local] [--for <parent-plugin>]
|
|
67
|
+
evophp new theme <name> [--local] [--for <parent-plugin>]
|
|
67
68
|
evophp n plugin <name>
|
|
68
69
|
evophp n theme <name>
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
* Creates a new plugin or theme from
|
|
72
|
+
* Creates a new plugin or theme from the boilerplates **bundled with the CLI** in its `templates/` folder — no network access is needed to scaffold:
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
| Type | Template | Copied to |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| plugin (PHP) | `templates/plugin/php` | `Public/Modules/<name>` |
|
|
77
|
+
| plugin (JS) | `templates/plugin/js` | `EvoJsDev/Modules/<name>` |
|
|
78
|
+
| theme | `templates/theme` | `Public/Themes/<name>` |
|
|
79
|
+
|
|
80
|
+
* Each new folder is initialised as its own git repo with an initial commit.
|
|
81
|
+
|
|
82
|
+
* **GitHub (optional):** if a GitHub token is available (see [Authentication](#authentication--pat-storage)), EvoPHP then creates private repos in your account — `<name>` (and `<name>.js` for plugins) — and pushes to them. If GitHub can't be reached, you just get a warning; the plugin/theme is still created locally.
|
|
83
|
+
|
|
84
|
+
* Options:
|
|
85
|
+
* `--local` → skip GitHub entirely (fully offline). Leaving the token prompt blank does the same.
|
|
86
|
+
* `--for <parent-plugin>` (alias `--parent`) → also record the new plugin/theme as a dependency of an existing plugin. See [Adding to Another Plugin](#adding-to-another-plugin).
|
|
76
87
|
|
|
77
88
|
* Examples:
|
|
78
89
|
|
|
79
90
|
```bash
|
|
80
|
-
evophp new plugin MyPlugin
|
|
81
|
-
evophp n theme MyTheme
|
|
91
|
+
evophp new plugin MyPlugin # Scaffold + create/push GitHub repos
|
|
92
|
+
evophp n theme MyTheme --local # Scaffold only, no GitHub
|
|
93
|
+
evophp new plugin Reviews --for Shop # Scaffold and add as a dependency of Shop
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
* To add a GitHub remote later for a `--local` plugin:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cd Public/Modules/MyPlugin
|
|
100
|
+
git remote add origin https://github.com/<you>/MyPlugin.git
|
|
101
|
+
git push -u origin main
|
|
82
102
|
```
|
|
83
103
|
|
|
104
|
+
> **Updating the templates:** the boilerplates are copies of `kinsokafor/EvoPlugin`, `kinsokafor/EvoPlugin.js` and `kinsokafor/EvoTheme`. When those change, refresh `templates/` in the CLI and publish a new version. Template `.gitignore` files are stored as `gitignore` (npm strips `.gitignore` on publish) and renamed back when copied.
|
|
105
|
+
|
|
84
106
|
---
|
|
85
107
|
|
|
86
108
|
### Plugin/Theme Installation
|
|
87
109
|
|
|
88
110
|
```bash
|
|
89
|
-
evophp install plugin <name>
|
|
90
|
-
evophp install theme <name>
|
|
111
|
+
evophp install plugin <name> [--for <parent-plugin>]
|
|
112
|
+
evophp install theme <name> [--for <parent-plugin>]
|
|
91
113
|
evophp i plugin <name>
|
|
92
114
|
evophp i theme <name>
|
|
93
115
|
```
|
|
@@ -98,11 +120,15 @@ evophp i theme <name>
|
|
|
98
120
|
* Just `<name>` → Clones from your GitHub account (token required)
|
|
99
121
|
* `<author>/<name>` → Clones from public GitHub repo (no token required)
|
|
100
122
|
|
|
123
|
+
* Options:
|
|
124
|
+
* `--for <parent-plugin>` (alias `--parent`) → also record it as a dependency of an existing plugin. See [Adding to Another Plugin](#adding-to-another-plugin).
|
|
125
|
+
|
|
101
126
|
* Examples:
|
|
102
127
|
|
|
103
128
|
```bash
|
|
104
129
|
evophp install plugin MyPlugin
|
|
105
130
|
evophp i theme johndoe/EvoTheme
|
|
131
|
+
evophp i plugin kinsokafor/Payments --for Shop
|
|
106
132
|
```
|
|
107
133
|
|
|
108
134
|
* **Automatic dependency installation**: If the plugin or theme includes a `dependencies.json` file, EvoPHP will automatically install all listed JS/PHP packages and any dependent EvoPHP plugins or themes. See [Plugin & Theme Dependencies](#plugin--theme-dependencies).
|
|
@@ -244,6 +270,24 @@ When you run `evophp install`, EvoPHP will:
|
|
|
244
270
|
3. Install all listed npm (`prod`/`dev`) and Composer (`prod`) packages.
|
|
245
271
|
4. Recursively install any EvoPHP plugins or themes listed under `"plugin"` and `"theme"` — each of which may declare their own `dependencies.json`, resolved the same way.
|
|
246
272
|
|
|
273
|
+
### Adding to Another Plugin
|
|
274
|
+
|
|
275
|
+
`evophp install` and `evophp new` accept `--for <parent-plugin>` (alias `--parent`) to install or create a plugin/theme **and** record it in the parent's `EvoJsDev/Modules/<parent>/dependencies.json` in one step:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
evophp install plugin kinsokafor/Payments --for Shop # install from GitHub, add to Shop
|
|
279
|
+
evophp new plugin Reviews --for Shop # create from template, add to Shop
|
|
280
|
+
evophp new theme ShopSkin --local --for Shop # works offline too
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
* The entry is written as `author/name` under `"plugin"` or `"theme"`, so `evophp install` / `evophp d <parent> --sync` pick it up for anyone else.
|
|
284
|
+
* The parent plugin must already exist in the project, and a plugin can't depend on itself — both are checked before anything is installed or created.
|
|
285
|
+
* Entries are de-duplicated; if the plugin is already installed, `install --for` just records it.
|
|
286
|
+
* For `new`, the `author` is the owner of the GitHub repo that was created. When offline or using `--local`, you're asked for the GitHub username/org once and it is saved as `GITHUB_USER` in `.env.development`. Leaving it blank records just the name, with a warning to add the owner later.
|
|
287
|
+
* The parent's `dependencies.json` is changed but not committed — commit it in the parent plugin's repo.
|
|
288
|
+
|
|
289
|
+
This is equivalent to `evophp d <parent> --evoplugin` / `--evotheme`, but without the extra prompt, and it works for plugins you've just created.
|
|
290
|
+
|
|
247
291
|
### Automatic Uninstallation
|
|
248
292
|
|
|
249
293
|
When you run `evophp uninstall`, EvoPHP will:
|
|
@@ -268,15 +312,19 @@ evophp uninstall plugin PluginA
|
|
|
268
312
|
|
|
269
313
|
## Authentication & PAT Storage
|
|
270
314
|
|
|
271
|
-
When
|
|
315
|
+
When a command needs GitHub (e.g. `evophp install plugin MyPlugin`, or `evophp new` creating your repos), EvoPHP CLI will:
|
|
272
316
|
|
|
273
317
|
* Prompt you for a **GitHub Personal Access Token** (PAT) the first time
|
|
274
|
-
* Automatically store it in `.env.development` for future use
|
|
318
|
+
* Automatically store it in `.env.development` as `GITHUB_TOKEN` for future use
|
|
275
319
|
* Use that token to fetch your GitHub username and manage private/public repos
|
|
276
320
|
|
|
321
|
+
For `evophp new`, the token is optional — leave the prompt blank (or pass `--local`) to work offline.
|
|
322
|
+
|
|
323
|
+
`.env.development` may also hold `GITHUB_USER`, the owner used for `author/name` dependency entries when `evophp new --for` runs offline.
|
|
324
|
+
|
|
277
325
|
> ⚠️ You can only create repositories in your own GitHub account.
|
|
278
326
|
|
|
279
|
-
> To change your token later, simply edit or delete `.env.development`.
|
|
327
|
+
> To change your token or username later, simply edit or delete `.env.development`.
|
|
280
328
|
|
|
281
329
|
---
|
|
282
330
|
|
package/commands/dependencies.js
CHANGED
|
@@ -4,7 +4,7 @@ const { execSync } = require("child_process");
|
|
|
4
4
|
const { readLineAsync } = require("../utils/helpers");
|
|
5
5
|
const { getUserData } = require("../utils/github");
|
|
6
6
|
const { cloneRepo } = require("../utils/git");
|
|
7
|
-
const { installDependencies, extractDependencies } = require("../utils/dependencies");
|
|
7
|
+
const { installDependencies, extractDependencies, addEvoRef } = require("../utils/dependencies");
|
|
8
8
|
const log = require("../utils/logger");
|
|
9
9
|
require("dotenv").config({ path: "./.env.development" });
|
|
10
10
|
|
|
@@ -56,17 +56,7 @@ async function installEvoDep(depType, dep, gitHubToken, dependencies) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Record as "author/name" in dependencies.json, deduplicated
|
|
59
|
-
|
|
60
|
-
const existing = (dependencies[key] || "")
|
|
61
|
-
.split(" ")
|
|
62
|
-
.map(d => d.trim())
|
|
63
|
-
.filter(Boolean);
|
|
64
|
-
|
|
65
|
-
const fullRef = `${author}/${depName}`;
|
|
66
|
-
if (!existing.some(d => d === fullRef || d.split("/").pop() === depName)) {
|
|
67
|
-
existing.push(fullRef);
|
|
68
|
-
}
|
|
69
|
-
dependencies[key] = existing.join(" ");
|
|
59
|
+
addEvoRef(dependencies, depType, `${author}/${depName}`);
|
|
70
60
|
}
|
|
71
61
|
|
|
72
62
|
module.exports = {
|
package/commands/help.js
CHANGED
|
@@ -16,14 +16,17 @@ module.exports = {
|
|
|
16
16
|
→ Example: evophp init my-coop-project
|
|
17
17
|
|
|
18
18
|
🛠️ Plugin & Theme Creation:
|
|
19
|
-
evophp new plugin <name>
|
|
20
|
-
evophp new theme <name>
|
|
19
|
+
evophp new plugin <name> [--local]
|
|
20
|
+
evophp new theme <name> [--local]
|
|
21
|
+
→ Copied from the bundled templates/plugin and templates/theme
|
|
22
|
+
boilerplates (no network). Private GitHub repos are then created and
|
|
23
|
+
pushed if a token is available; use --local or leave the token blank
|
|
24
|
+
to skip.
|
|
25
|
+
→ Add --for <plugin> to also record it as a dependency of an
|
|
26
|
+
existing plugin (works with "evophp install" too).
|
|
21
27
|
evophp n plugin <name>
|
|
22
28
|
evophp n theme <name>
|
|
23
|
-
→
|
|
24
|
-
→ Supports:
|
|
25
|
-
• just <name> → uses your GitHub account
|
|
26
|
-
• author/name → uses any GitHub account
|
|
29
|
+
→ Short aliases for the above.
|
|
27
30
|
|
|
28
31
|
⬇️ Plugin & Theme Installation:
|
|
29
32
|
evophp install plugin <name>
|
package/commands/install.js
CHANGED
|
@@ -4,7 +4,13 @@ const { execSync } = require('child_process');
|
|
|
4
4
|
const { readLineAsync, mergeConfig, saveToEnv } = require('../utils/helpers');
|
|
5
5
|
const { getUserData } = require('../utils/github');
|
|
6
6
|
const { cloneRepo } = require('../utils/git');
|
|
7
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
installDependencies,
|
|
9
|
+
extractDependencies,
|
|
10
|
+
mergeDependencies,
|
|
11
|
+
parentDependenciesFile,
|
|
12
|
+
addEvoDependency,
|
|
13
|
+
} = require('../utils/dependencies');
|
|
8
14
|
const log = require('../utils/logger');
|
|
9
15
|
require("dotenv").config({ path: "./.env.development" });
|
|
10
16
|
|
|
@@ -107,9 +113,14 @@ module.exports = {
|
|
|
107
113
|
|
|
108
114
|
builder: yargs => yargs
|
|
109
115
|
.positional('type', { describe: 'plugin or theme', type: 'string' })
|
|
110
|
-
.positional('name', { describe: 'name or author/name', type: 'string' })
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
.positional('name', { describe: 'name or author/name', type: 'string' })
|
|
117
|
+
.option('for', {
|
|
118
|
+
alias: 'parent',
|
|
119
|
+
describe: 'Also record it as a dependency of this existing plugin',
|
|
120
|
+
type: 'string',
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
handler: async ({ type, name, for: parent }) => {
|
|
113
124
|
try {
|
|
114
125
|
if (!type || !['plugin', 'theme'].includes(type.toLowerCase())) {
|
|
115
126
|
type = await readLineAsync("What are you installing? (plugin/theme): ");
|
|
@@ -124,6 +135,9 @@ module.exports = {
|
|
|
124
135
|
name = await readLineAsync(`Enter the ${type} name (e.g., myplugin or johndoe/myplugin): `);
|
|
125
136
|
}
|
|
126
137
|
|
|
138
|
+
// Fail fast if the parent plugin isn't in this project
|
|
139
|
+
if (parent) parentDependenciesFile(parent);
|
|
140
|
+
|
|
127
141
|
let author, pluginName;
|
|
128
142
|
let gitHubToken = process.env.GITHUB_TOKEN;
|
|
129
143
|
|
|
@@ -146,9 +160,15 @@ module.exports = {
|
|
|
146
160
|
? [path.join('Public', 'Modules', pluginName), path.join('EvoJsDev', 'Modules', pluginName)]
|
|
147
161
|
: [path.join('Public', 'Themes', pluginName)];
|
|
148
162
|
|
|
163
|
+
if (parent === pluginName) {
|
|
164
|
+
log.error(`A plugin cannot depend on itself.`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
149
168
|
for (const p of paths) {
|
|
150
169
|
if (fs.existsSync(p)) {
|
|
151
170
|
log.warn(`${type} "${pluginName}" already exists at ${p}`);
|
|
171
|
+
if (parent) addEvoDependency(parent, type, `${author}/${pluginName}`);
|
|
152
172
|
process.exit(0);
|
|
153
173
|
}
|
|
154
174
|
}
|
|
@@ -162,6 +182,8 @@ module.exports = {
|
|
|
162
182
|
|
|
163
183
|
mergeConfig({});
|
|
164
184
|
|
|
185
|
+
if (parent) addEvoDependency(parent, type, `${author}/${pluginName}`);
|
|
186
|
+
|
|
165
187
|
log.success(`The ${type} "${pluginName}" has been installed successfully.`);
|
|
166
188
|
|
|
167
189
|
} catch (err) {
|
package/commands/new.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const { readLineAsync, mergeConfig } = require("../utils/helpers");
|
|
4
|
-
const {
|
|
5
|
-
const {
|
|
4
|
+
const { createEmptyRepo } = require("../utils/github");
|
|
5
|
+
const { copyTemplate, initRepo, pushRepo } = require("../utils/template");
|
|
6
|
+
const { parentDependenciesFile, addEvoDependency } = require("../utils/dependencies");
|
|
6
7
|
const { editFile, renameFile } = require("../utils/edit");
|
|
7
8
|
const { test } = require("../utils/validate");
|
|
8
9
|
const log = require("../utils/logger");
|
|
@@ -22,20 +23,20 @@ module.exports = {
|
|
|
22
23
|
.positional("name", {
|
|
23
24
|
describe: "The plugin or theme name (without author)",
|
|
24
25
|
type: "string",
|
|
26
|
+
})
|
|
27
|
+
.option("local", {
|
|
28
|
+
describe: "Skip creating GitHub repos (fully offline)",
|
|
29
|
+
type: "boolean",
|
|
30
|
+
default: false,
|
|
31
|
+
})
|
|
32
|
+
.option("for", {
|
|
33
|
+
alias: "parent",
|
|
34
|
+
describe: "Also record it as a dependency of this existing plugin",
|
|
35
|
+
type: "string",
|
|
25
36
|
}),
|
|
26
37
|
|
|
27
|
-
handler: async ({ type, name }) => {
|
|
38
|
+
handler: async ({ type, name, local, for: parent }) => {
|
|
28
39
|
try {
|
|
29
|
-
let gitHubToken = process.env.GITHUB_TOKEN;
|
|
30
|
-
|
|
31
|
-
if (!gitHubToken) {
|
|
32
|
-
gitHubToken = await readLineAsync(
|
|
33
|
-
"Enter GitHub Personal Access Token: "
|
|
34
|
-
);
|
|
35
|
-
const { saveToEnv } = require("../utils/helpers");
|
|
36
|
-
saveToEnv("GITHUB_TOKEN", gitHubToken);
|
|
37
|
-
log.success("GitHub token saved to .env.development");
|
|
38
|
-
}
|
|
39
40
|
type = type.toLowerCase();
|
|
40
41
|
|
|
41
42
|
if (!["plugin", "theme"].includes(type)) {
|
|
@@ -43,21 +44,49 @@ module.exports = {
|
|
|
43
44
|
process.exit(1);
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
// Get authenticated username from token
|
|
47
|
-
const userData = await getUserData(gitHubToken);
|
|
48
|
-
const author = userData.login;
|
|
49
47
|
const pluginName = name;
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
if (parent) {
|
|
50
|
+
if (parent === pluginName) {
|
|
51
|
+
log.error("A plugin cannot depend on itself.");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
// Fail fast if the parent plugin isn't in this project
|
|
55
|
+
parentDependenciesFile(parent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Plugins and themes are scaffolded from the bundled templates/ boilerplates.
|
|
59
|
+
// GitHub is only used afterwards to create and push to remote repos.
|
|
60
|
+
const gitHubToken = local ? null : await getToken();
|
|
53
61
|
|
|
62
|
+
log.info(`Creating ${type} "${pluginName}" from local template...`);
|
|
63
|
+
|
|
64
|
+
let owner;
|
|
54
65
|
if (type === "plugin") {
|
|
55
|
-
const entry = await setupPlugin(
|
|
66
|
+
const entry = await setupPlugin(pluginName);
|
|
67
|
+
owner = await publishRepos(gitHubToken, [
|
|
68
|
+
{ dest: path.join("Public", "Modules", pluginName), name: pluginName },
|
|
69
|
+
{ dest: path.join("EvoJsDev", "Modules", pluginName), name: `${pluginName}.js` },
|
|
70
|
+
]);
|
|
56
71
|
log.success(`Plugin "${pluginName}" created with entry "${entry}"`);
|
|
57
72
|
} else {
|
|
58
|
-
await setupTheme(
|
|
73
|
+
await setupTheme(pluginName);
|
|
74
|
+
owner = await publishRepos(gitHubToken, [
|
|
75
|
+
{ dest: path.join("Public", "Themes", pluginName), name: pluginName },
|
|
76
|
+
]);
|
|
59
77
|
log.success(`Theme "${pluginName}" created`);
|
|
60
78
|
}
|
|
79
|
+
|
|
80
|
+
if (parent) {
|
|
81
|
+
owner = owner || (await getOwner());
|
|
82
|
+
addEvoDependency(parent, type, owner ? `${owner}/${pluginName}` : pluginName);
|
|
83
|
+
if (!owner) {
|
|
84
|
+
log.warn(
|
|
85
|
+
`Recorded "${pluginName}" without an owner. Edit it to "owner/${pluginName}" in ` +
|
|
86
|
+
`EvoJsDev/Modules/${parent}/dependencies.json so others can install it.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
61
90
|
} catch (err) {
|
|
62
91
|
log.error(`Creation failed: ${err.message}`);
|
|
63
92
|
process.exit(1);
|
|
@@ -65,8 +94,44 @@ module.exports = {
|
|
|
65
94
|
},
|
|
66
95
|
};
|
|
67
96
|
|
|
68
|
-
|
|
69
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Get the GitHub token from env or prompt for it.
|
|
99
|
+
* A blank answer returns null, meaning skip GitHub.
|
|
100
|
+
*/
|
|
101
|
+
async function getToken() {
|
|
102
|
+
let gitHubToken = process.env.GITHUB_TOKEN;
|
|
103
|
+
if (gitHubToken) return gitHubToken;
|
|
104
|
+
|
|
105
|
+
gitHubToken = await readLineAsync(
|
|
106
|
+
"Enter GitHub Personal Access Token (leave blank to skip GitHub): "
|
|
107
|
+
);
|
|
108
|
+
if (!gitHubToken) return null;
|
|
109
|
+
|
|
110
|
+
const { saveToEnv } = require("../utils/helpers");
|
|
111
|
+
saveToEnv("GITHUB_TOKEN", gitHubToken);
|
|
112
|
+
log.success("GitHub token saved to .env.development");
|
|
113
|
+
return gitHubToken;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* GitHub owner to use in "owner/name" dependency refs when no repo was
|
|
118
|
+
* created (offline). Cached as GITHUB_USER in .env.development.
|
|
119
|
+
*/
|
|
120
|
+
async function getOwner() {
|
|
121
|
+
if (process.env.GITHUB_USER) return process.env.GITHUB_USER;
|
|
122
|
+
|
|
123
|
+
const owner = await readLineAsync(
|
|
124
|
+
"GitHub username/org that will own this repo (leave blank to skip): "
|
|
125
|
+
);
|
|
126
|
+
if (!owner) return null;
|
|
127
|
+
|
|
128
|
+
const { saveToEnv } = require("../utils/helpers");
|
|
129
|
+
saveToEnv("GITHUB_USER", owner);
|
|
130
|
+
return owner;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function setupPlugin(pluginName) {
|
|
134
|
+
const entry = await setupPluginRepo(pluginName);
|
|
70
135
|
const pluginPrefix = entry.replace("Main", "");
|
|
71
136
|
|
|
72
137
|
await editFile(`./Public/Modules/${pluginName}/Routes.php`, {
|
|
@@ -92,20 +157,9 @@ async function setupPlugin(token, author, pluginName) {
|
|
|
92
157
|
return entry;
|
|
93
158
|
}
|
|
94
159
|
|
|
95
|
-
async function setupPluginRepo(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
author,
|
|
99
|
-
pluginName,
|
|
100
|
-
path.join("Public", "Modules", pluginName)
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
await createGitHubRepo(token, "kinsokafor/EvoPlugin.js", `${pluginName}.js`);
|
|
104
|
-
await cloneRepo(
|
|
105
|
-
author,
|
|
106
|
-
`${pluginName}.js`,
|
|
107
|
-
path.join("EvoJsDev", "Modules", pluginName)
|
|
108
|
-
);
|
|
160
|
+
async function setupPluginRepo(pluginName) {
|
|
161
|
+
copyTemplate("plugin/php", path.join("Public", "Modules", pluginName));
|
|
162
|
+
copyTemplate("plugin/js", path.join("EvoJsDev", "Modules", pluginName));
|
|
109
163
|
|
|
110
164
|
const pluginPrefix = await getValidatedInput("Enter Plugin Prefix?", 3, true);
|
|
111
165
|
const entryURI = await getValidatedInput("Entry URI?", 20);
|
|
@@ -129,13 +183,34 @@ async function setupPluginRepo(token, author, pluginName) {
|
|
|
129
183
|
return entry;
|
|
130
184
|
}
|
|
131
185
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Commit both plugin folders as fresh repos, then (if a token was given and
|
|
188
|
+
* GitHub is reachable) create private remotes and push. Offline is fine.
|
|
189
|
+
*/
|
|
190
|
+
async function publishRepos(token, repos) {
|
|
191
|
+
let owner = null;
|
|
192
|
+
|
|
193
|
+
for (const { dest, name } of repos) {
|
|
194
|
+
const remoteUrl = token
|
|
195
|
+
? await createEmptyRepo(token, name, `${name}, created from an evophp template.`)
|
|
196
|
+
: null;
|
|
197
|
+
initRepo(dest, remoteUrl);
|
|
198
|
+
if (remoteUrl) {
|
|
199
|
+
pushRepo(dest);
|
|
200
|
+
// https://github.com/<owner>/<name>.git
|
|
201
|
+
owner = owner || remoteUrl.split("/").slice(-2)[0];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!token) {
|
|
206
|
+
log.info("Skipped GitHub. Add remotes later with: git remote add origin <url> && git push -u origin main");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return owner;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function setupTheme(themeName) {
|
|
213
|
+
copyTemplate("theme", path.join("Public", "Themes", themeName));
|
|
139
214
|
|
|
140
215
|
await editFile(`./Public/Themes/${themeName}/Data/index.data.php`, {
|
|
141
216
|
themeName,
|
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {createApp} from 'vue'
|
|
2
|
+
import app from '@/components/AppMount.vue'
|
|
3
|
+
import router from './router'
|
|
4
|
+
import { createPinia } from 'pinia'
|
|
5
|
+
import restricted from '@/plugins/restricted'
|
|
6
|
+
|
|
7
|
+
createApp(app)
|
|
8
|
+
.use(createPinia())
|
|
9
|
+
.use(restricted)
|
|
10
|
+
.use(router)
|
|
11
|
+
.mount("#app")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createRouter, createWebHashHistory } from 'vue-router'
|
|
2
|
+
import NotFound from '@/components/NotFound.vue'
|
|
3
|
+
import Dashboard from '../views/Dashboard.vue'
|
|
4
|
+
|
|
5
|
+
const router = createRouter({
|
|
6
|
+
history: createWebHashHistory(),
|
|
7
|
+
routes: [{
|
|
8
|
+
path: '/:pathMatch(.*)*',
|
|
9
|
+
name: 'NotFound',
|
|
10
|
+
component: NotFound,
|
|
11
|
+
meta: { title: "404 Not Found" }
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
path: '/',
|
|
15
|
+
name: 'Dashboard',
|
|
16
|
+
component: Dashboard,
|
|
17
|
+
meta: { title: "Dashboard" }
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
router.beforeEach((to, from, next) => {
|
|
23
|
+
document.title = `${to.meta.title}`;
|
|
24
|
+
next();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default router;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { defineStore } from 'pinia';
|
|
2
|
+
import { dbTable, storeGetter, updateStoreDataSingle, updateStoreData, storeLoaderController } from '@/helpers';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
|
|
5
|
+
export const useSampleStore = defineStore('useSampleStore', {
|
|
6
|
+
state: () => {
|
|
7
|
+
return {
|
|
8
|
+
data: [],
|
|
9
|
+
processing: false,
|
|
10
|
+
fetching: false,
|
|
11
|
+
limit: 100,
|
|
12
|
+
offset: 0,
|
|
13
|
+
dbtable: new dbTable,
|
|
14
|
+
lastTimeOut: null
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
actions: {
|
|
18
|
+
async loadFromServer(params = {}) {
|
|
19
|
+
for (var k in params) {
|
|
20
|
+
//return when all data are not ready
|
|
21
|
+
if (params[k] == undefined) return;
|
|
22
|
+
}
|
|
23
|
+
this.fetching = true;
|
|
24
|
+
this.processing = true;
|
|
25
|
+
await this.dbtable.get('tableName', {
|
|
26
|
+
limit: this.limit,
|
|
27
|
+
offset: this.offset,
|
|
28
|
+
order: 'asc',
|
|
29
|
+
order_by: 'id',
|
|
30
|
+
...params
|
|
31
|
+
}).then(r => {
|
|
32
|
+
if ("id" in params) {
|
|
33
|
+
this.data = updateStoreDataSingle(this.data, r.data)
|
|
34
|
+
} else {
|
|
35
|
+
this.data = updateStoreData(this.data, r.data)
|
|
36
|
+
if (r.data.length >= this.limit && params?.limit === undefined) {
|
|
37
|
+
this.offset = this.limit + this.offset
|
|
38
|
+
storeLoaderController(this, () => {
|
|
39
|
+
this.loadFromServer(params)
|
|
40
|
+
})
|
|
41
|
+
} else {
|
|
42
|
+
this.offset = 0
|
|
43
|
+
if(this.lastTimeOut != null) {
|
|
44
|
+
clearTimeout(this.lastTimeOut)
|
|
45
|
+
}
|
|
46
|
+
this.lastTimeOut = setTimeout(() => {
|
|
47
|
+
this.fetching = false
|
|
48
|
+
}, 3600000)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
})
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
abort() {
|
|
56
|
+
this.dbtable.abort()
|
|
57
|
+
this.fetching = false;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
getters: {
|
|
61
|
+
all: (state) => {
|
|
62
|
+
if (!state.fetching) {
|
|
63
|
+
state.loadFromServer()
|
|
64
|
+
}
|
|
65
|
+
return state.data;
|
|
66
|
+
},
|
|
67
|
+
get: (state) => {
|
|
68
|
+
const data = state.data
|
|
69
|
+
return (params = {}, ...exclude) => {
|
|
70
|
+
return storeGetter(state, data, (tempParams) => {
|
|
71
|
+
state.loadFromServer(tempParams)
|
|
72
|
+
}, params, exclude)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Public\Modules\<pluginName>;
|
|
4
|
+
|
|
5
|
+
use EvoPhp\Api\Controllers;
|
|
6
|
+
use EvoPhp\Api\Config;
|
|
7
|
+
|
|
8
|
+
class <pluginPrefix>Controller extends Controllers
|
|
9
|
+
{
|
|
10
|
+
|
|
11
|
+
public function __construct()
|
|
12
|
+
{
|
|
13
|
+
parent::__construct();
|
|
14
|
+
$this->viewPath = __DIR__.'/Views';
|
|
15
|
+
$this->config = new Config;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public function getData($data) {
|
|
19
|
+
if(method_exists($this, $this->dataMethod)) {
|
|
20
|
+
$callback = $this->dataMethod;
|
|
21
|
+
return $this->$callback($data);
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public function addResources() {
|
|
27
|
+
if(method_exists($this, $this->resourceMethod)) {
|
|
28
|
+
$callback = $this->resourceMethod;
|
|
29
|
+
return $this->$callback();
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Public\Modules\<pluginName>\<pluginPrefix>Controller;
|
|
4
|
+
use EvoPhp\Api\Requests\Requests;
|
|
5
|
+
|
|
6
|
+
//API End points
|
|
7
|
+
|
|
8
|
+
//Pages
|
|
9
|
+
|
|
10
|
+
$router->get('/<entryURI>', function($params){
|
|
11
|
+
$controller = new <pluginPrefix>Controller;
|
|
12
|
+
$controller->{'<entry>/index'}($params)->auth(2,3,4)->setData(["pageTitle" => "Admin"]);
|
|
13
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/Views/cache
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use EvoPhp\Resources\Options;
|
|
4
|
+
use Assoto\CSS;
|
|
5
|
+
use Assoto\JS;
|
|
6
|
+
use Assoto\Stack;
|
|
7
|
+
|
|
8
|
+
// CSS::stylesheet(
|
|
9
|
+
// "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css",
|
|
10
|
+
// "bootstrap5"
|
|
11
|
+
// );
|
|
12
|
+
|
|
13
|
+
// JS::file(
|
|
14
|
+
// "./Public/Themes/<themeName>/Assets/js/jquery.min.js",
|
|
15
|
+
// "jQuery",
|
|
16
|
+
// array(),
|
|
17
|
+
// Stack::STACK_HEAD
|
|
18
|
+
// );
|
|
19
|
+
|
|
20
|
+
// JS::file(
|
|
21
|
+
// "./Public/Themes/<themeName>/Assets/js/main-min.js",
|
|
22
|
+
// "ThemeMain"
|
|
23
|
+
// );
|
|
24
|
+
|
|
25
|
+
return [
|
|
26
|
+
"inputFieldClass" => "form-control form-control-user"
|
|
27
|
+
];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use EvoPhp\Resources\Options;
|
|
4
|
+
use Assoto\CSS;
|
|
5
|
+
use Assoto\JS;
|
|
6
|
+
use Assoto\Stack;
|
|
7
|
+
|
|
8
|
+
// CSS::stylesheet(
|
|
9
|
+
// "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css",
|
|
10
|
+
// "bootstrap5"
|
|
11
|
+
// );
|
|
12
|
+
|
|
13
|
+
// JS::file(
|
|
14
|
+
// "./Public/Themes/<themeName>/Assets/js/jquery.min.js",
|
|
15
|
+
// "jQuery",
|
|
16
|
+
// array(),
|
|
17
|
+
// Stack::STACK_HEAD
|
|
18
|
+
// );
|
|
19
|
+
|
|
20
|
+
// JS::file(
|
|
21
|
+
// "./Public/Themes/<themeName>/Assets/js/main-min.js",
|
|
22
|
+
// "ThemeMain"
|
|
23
|
+
// );
|
|
24
|
+
|
|
25
|
+
return [
|
|
26
|
+
"loginFormClass" => "user",
|
|
27
|
+
"loginInputClass" => "form-control form-control-user",
|
|
28
|
+
"loginButtonClass" => "btn btn-primary btn-user btn-block"
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
?>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
|
|
6
|
+
<meta charset="utf-8">
|
|
7
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
9
|
+
<meta name="description" content="">
|
|
10
|
+
<meta name="author" content="">
|
|
11
|
+
<title></title>
|
|
12
|
+
<link href="/color-scheme.css" rel="stylesheet" />
|
|
13
|
+
<!-- Custom styles for this template-->
|
|
14
|
+
<link href="@yield('assets')style.css" rel="stylesheet">
|
|
15
|
+
{!! Assoto\Stack::display('head') !!}
|
|
16
|
+
</head>
|
|
17
|
+
|
|
18
|
+
<body>
|
|
19
|
+
@section('content')
|
|
20
|
+
@yield('content')
|
|
21
|
+
@show
|
|
22
|
+
{!! Assoto\Stack::display('footer') !!}
|
|
23
|
+
</body>
|
|
24
|
+
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
|
|
6
|
+
<meta charset="utf-8">
|
|
7
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
9
|
+
<meta name="description" content="">
|
|
10
|
+
<meta name="author" content="">
|
|
11
|
+
<title></title>
|
|
12
|
+
<link href="/color-scheme.css" rel="stylesheet" />
|
|
13
|
+
<!-- Custom styles for this template-->
|
|
14
|
+
<link href="@yield('assets')style.css" rel="stylesheet">
|
|
15
|
+
{!! Assoto\Stack::display('head') !!}
|
|
16
|
+
</head>
|
|
17
|
+
|
|
18
|
+
<body>
|
|
19
|
+
<script>0</script>
|
|
20
|
+
<!-- Nav Items -->
|
|
21
|
+
@foreach($mainMenu as $menu)
|
|
22
|
+
@if(\EvoPhp\Api\Operations::checkAccess($menu['access']))
|
|
23
|
+
<!-- {{$menu['iconClass']}} -->
|
|
24
|
+
<!-- {{$loop->index}} -->
|
|
25
|
+
<!-- @isset($menu['submenu']) collapsed @endisset -->
|
|
26
|
+
<!-- {{$menu['link']}} -->
|
|
27
|
+
<!-- {{$menu['label']}} -->
|
|
28
|
+
@isset($menu['submenu'])
|
|
29
|
+
@foreach($menu['submenu'] as $submenu)
|
|
30
|
+
@if(isset($submenu['access']) && \EvoPhp\Api\Operations::checkAccess($submenu['access']))
|
|
31
|
+
<!-- {{$submenu['link']}} -->
|
|
32
|
+
<!-- {{$submenu['label']}} -->
|
|
33
|
+
@endif
|
|
34
|
+
@endforeach
|
|
35
|
+
@endisset
|
|
36
|
+
@endif
|
|
37
|
+
@endforeach
|
|
38
|
+
|
|
39
|
+
@section('content')
|
|
40
|
+
@yield('content')
|
|
41
|
+
@show
|
|
42
|
+
|
|
43
|
+
<!-- <span>Copyright © {{$site_name}} {{date('Y')}}</span> -->
|
|
44
|
+
|
|
45
|
+
{!! Assoto\Stack::display('footer') !!}
|
|
46
|
+
</body>
|
|
47
|
+
|
|
48
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@extends('layouts.loginparts')
|
|
2
|
+
|
|
3
|
+
@section('assets', '/Public/Themes/<themeName>/Assets/')
|
|
4
|
+
|
|
5
|
+
@section('content')
|
|
6
|
+
{!! $content !!}
|
|
7
|
+
<hr>
|
|
8
|
+
<a href="index.html" class="btn btn-google btn-user btn-block">
|
|
9
|
+
<i class="fab fa-google fa-fw"></i> Login with Google
|
|
10
|
+
</a>
|
|
11
|
+
<a href="index.html" class="btn btn-facebook btn-user btn-block">
|
|
12
|
+
<i class="fab fa-facebook-f fa-fw"></i> Login with Facebook
|
|
13
|
+
</a>
|
|
14
|
+
<hr>
|
|
15
|
+
<div class="text-center">
|
|
16
|
+
<a class="small" href="forgot-password.html">Forgot Password?</a>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="text-center">
|
|
19
|
+
<a class="small" href="register.html">Create an Account!</a>
|
|
20
|
+
</div>
|
|
21
|
+
@endsection
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/Views/cache
|
package/utils/dependencies.js
CHANGED
|
@@ -58,4 +58,67 @@ async function mergeDependencies(newDep, oldDep) {
|
|
|
58
58
|
return res;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Add an evophp plugin/theme reference to an in-memory dependencies object,
|
|
63
|
+
* deduplicated by full ref or bare name. Returns true if it was added.
|
|
64
|
+
*
|
|
65
|
+
* @param {object} dependencies
|
|
66
|
+
* @param {'plugin'|'theme'} key
|
|
67
|
+
* @param {string} ref - "author/name" (or bare "name" when the author is unknown)
|
|
68
|
+
*/
|
|
69
|
+
function addEvoRef(dependencies, key, ref) {
|
|
70
|
+
const name = ref.split("/").pop();
|
|
71
|
+
const existing = (dependencies[key] || "")
|
|
72
|
+
.split(" ")
|
|
73
|
+
.map(d => d.trim())
|
|
74
|
+
.filter(Boolean);
|
|
75
|
+
|
|
76
|
+
if (existing.some(d => d === ref || d.split("/").pop() === name)) return false;
|
|
77
|
+
|
|
78
|
+
existing.push(ref);
|
|
79
|
+
dependencies[key] = existing.join(" ");
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Path to a parent plugin's evophp dependencies.json. Exits if the parent
|
|
85
|
+
* plugin is not present in this project.
|
|
86
|
+
* @param {string} parent
|
|
87
|
+
*/
|
|
88
|
+
function parentDependenciesFile(parent) {
|
|
89
|
+
const dir = path.join("EvoJsDev", "Modules", parent);
|
|
90
|
+
if (!fs.existsSync(dir)) {
|
|
91
|
+
log.error(`Parent plugin "${parent}" not found at ${dir}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
return path.join(dir, "dependencies.json");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Record an evophp plugin/theme as a dependency of a parent plugin.
|
|
99
|
+
* @param {string} parent - parent plugin name
|
|
100
|
+
* @param {'plugin'|'theme'} type
|
|
101
|
+
* @param {string} ref - "author/name"
|
|
102
|
+
*/
|
|
103
|
+
function addEvoDependency(parent, type, ref) {
|
|
104
|
+
const file = parentDependenciesFile(parent);
|
|
105
|
+
const dependencies = fs.existsSync(file)
|
|
106
|
+
? JSON.parse(fs.readFileSync(file, "utf-8"))
|
|
107
|
+
: {};
|
|
108
|
+
|
|
109
|
+
if (addEvoRef(dependencies, type, ref)) {
|
|
110
|
+
fs.writeFileSync(file, JSON.stringify(dependencies, null, 2));
|
|
111
|
+
log.success(`Added ${type} "${ref}" as a dependency of "${parent}".`);
|
|
112
|
+
} else {
|
|
113
|
+
log.info(`${type} "${ref}" is already a dependency of "${parent}".`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
installDependencies,
|
|
119
|
+
extractDependencies,
|
|
120
|
+
mergeDependencies,
|
|
121
|
+
addEvoRef,
|
|
122
|
+
parentDependenciesFile,
|
|
123
|
+
addEvoDependency,
|
|
124
|
+
};
|
package/utils/github.js
CHANGED
|
@@ -46,4 +46,30 @@ async function createGitHubRepo(token, templateRepo, pluginName) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Create an empty private repo for the authenticated user.
|
|
51
|
+
* Returns the clone URL, or null if it could not be created (e.g. offline).
|
|
52
|
+
*/
|
|
53
|
+
async function createEmptyRepo(token, repoName, description = "") {
|
|
54
|
+
try {
|
|
55
|
+
const res = await axios.post(
|
|
56
|
+
"https://api.github.com/user/repos",
|
|
57
|
+
{ name: repoName, private: true, description },
|
|
58
|
+
{
|
|
59
|
+
headers: {
|
|
60
|
+
Authorization: `token ${token}`,
|
|
61
|
+
Accept: "application/vnd.github.v3+json",
|
|
62
|
+
},
|
|
63
|
+
timeout: 15000,
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
log.success(`Created GitHub repo ${res.data.full_name}`);
|
|
67
|
+
return res.data.clone_url;
|
|
68
|
+
} catch (e) {
|
|
69
|
+
const msg = e.response?.data?.message || e.message;
|
|
70
|
+
log.warn(`Could not create GitHub repo ${repoName}: ${msg}`);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { getUserData, createGitHubRepo, createEmptyRepo };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const rimraf = require("rimraf");
|
|
5
|
+
const log = require("./logger");
|
|
6
|
+
|
|
7
|
+
const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Copy a bundled boilerplate from templates/ into a destination folder
|
|
11
|
+
* and initialise it as a fresh git repo. No network access needed.
|
|
12
|
+
*
|
|
13
|
+
* npm strips ".gitignore" files on publish, so templates store them as
|
|
14
|
+
* "gitignore" and they are renamed back here.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} template - Path relative to templates/ (e.g. "plugin/php")
|
|
17
|
+
* @param {string} dest - Local destination path
|
|
18
|
+
*/
|
|
19
|
+
function copyTemplate(template, dest) {
|
|
20
|
+
const src = path.join(TEMPLATES_DIR, template);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(src)) {
|
|
23
|
+
log.error(`Template not found: ${src}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (fs.existsSync(dest)) {
|
|
28
|
+
log.warn(`Deleting existing folder: ${dest}`);
|
|
29
|
+
rimraf.sync(dest);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
log.info(`Copying template ${template} → ${dest}`);
|
|
33
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
34
|
+
|
|
35
|
+
const ignore = path.join(dest, "gitignore");
|
|
36
|
+
if (fs.existsSync(ignore)) {
|
|
37
|
+
fs.renameSync(ignore, path.join(dest, ".gitignore"));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Initialise a git repo with an initial commit, optionally adding a remote.
|
|
43
|
+
* @param {string} dest
|
|
44
|
+
* @param {string|null} remoteUrl
|
|
45
|
+
*/
|
|
46
|
+
function initRepo(dest, remoteUrl = null) {
|
|
47
|
+
try {
|
|
48
|
+
execSync("git init -b main", { cwd: dest, stdio: "ignore" });
|
|
49
|
+
execSync("git add -A", { cwd: dest, stdio: "ignore" });
|
|
50
|
+
execSync('git commit -m "Initial commit from evophp template"', {
|
|
51
|
+
cwd: dest,
|
|
52
|
+
stdio: "ignore",
|
|
53
|
+
});
|
|
54
|
+
if (remoteUrl) {
|
|
55
|
+
execSync(`git remote add origin ${remoteUrl}`, { cwd: dest, stdio: "ignore" });
|
|
56
|
+
}
|
|
57
|
+
log.success(`Initialised git repo in ${dest}`);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
log.warn(`Could not initialise git repo in ${dest}: ${e.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Push the local repo to its origin remote. Failure is non-fatal.
|
|
65
|
+
* @param {string} dest
|
|
66
|
+
*/
|
|
67
|
+
function pushRepo(dest) {
|
|
68
|
+
try {
|
|
69
|
+
execSync("git push -u origin main", { cwd: dest, stdio: "inherit" });
|
|
70
|
+
} catch (e) {
|
|
71
|
+
log.warn(`Push failed for ${dest}; push manually later with "git push -u origin main".`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { copyTemplate, initRepo, pushRepo };
|