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 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 a template in **your own GitHub account**.
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
- * Supports:
74
- * Just `<name>` → Uses your GitHub account (token required)
75
- * ⚠️ You can only create repositories under your GitHub account.
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 # Creates in your GitHub
81
- evophp n theme MyTheme # Creates in your GitHub
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 using a command with just `<name>` (e.g. `evophp new plugin MyPlugin`), EvoPHP CLI will:
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
 
@@ -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
- const key = depType; // "plugin" or "theme"
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
- → Create a plugin or theme from template repo.
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>
@@ -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 { installDependencies, extractDependencies, mergeDependencies } = require('../utils/dependencies');
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
- handler: async ({ type, name }) => {
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 { getUserData, createGitHubRepo } = require("../utils/github");
5
- const { cloneRepo } = require("../utils/git");
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
- log.info(`Authenticated as ${author}`);
52
- log.info(`Creating ${type} "${pluginName}"...`);
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(gitHubToken, author, pluginName);
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(gitHubToken, author, pluginName);
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
- async function setupPlugin(token, author, pluginName) {
69
- const entry = await setupPluginRepo(token, author, pluginName);
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(token, author, pluginName) {
96
- await createGitHubRepo(token, "kinsokafor/EvoPlugin", pluginName);
97
- await cloneRepo(
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
- async function setupTheme(token, author, themeName) {
133
- await createGitHubRepo(token, "kinsokafor/EvoTheme", themeName);
134
- await cloneRepo(
135
- author,
136
- themeName,
137
- path.join("Public", "Themes", themeName)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evophp",
3
- "version": "1.3.3",
3
+ "version": "1.4.0",
4
4
  "description": "Command-line installer for the Evolution PHP framework",
5
5
  "bin": {
6
6
  "evophp": "bin/evophp.js"
@@ -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,8 @@
1
+ <template>
2
+ <div>
3
+ </div>
4
+ </template>
5
+ <script setup>
6
+ </script>
7
+ <style lang="scss" scoped>
8
+ </style>
@@ -0,0 +1,14 @@
1
+ {
2
+ "modules":
3
+ {
4
+ "<pluginName>":
5
+ {
6
+ "entry": [
7
+ "<entry>/index"
8
+ ],
9
+ "active": true,
10
+ "installed": false,
11
+ "order": 1
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "prod": "",
3
+ "dev": ""
4
+ }
@@ -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,3 @@
1
+ <?php
2
+
3
+ ?>
@@ -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,2 @@
1
+ <div id="app">{!! $appLoader !!}</div>
2
+ <div id="app-data" content="{{$encoded}}"></div>
@@ -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,7 @@
1
+ @extends('layouts.parts')
2
+
3
+ @section('assets', '/Public/Themes/<themeName>/Assets/')
4
+
5
+ @section('content')
6
+ {!! $content !!}
7
+ @endsection
@@ -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 &copy; {{$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
@@ -58,4 +58,67 @@ async function mergeDependencies(newDep, oldDep) {
58
58
  return res;
59
59
  }
60
60
 
61
- module.exports = { installDependencies, extractDependencies, mergeDependencies };
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
- module.exports = { getUserData, createGitHubRepo };
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 };