diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c70aa7e..b8ae262 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -41,6 +41,7 @@ jobs:
- wp-launchpad/ajax
- wp-launchpad/cron
- wp-launchpad/context
+ - wp-launchpad/front-take-off
name: ${{ matrix.component }} WP ${{ matrix.wp-versions }} with PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }}.
diff --git a/composer.json b/composer.json
index 0c6c4c2..fd51d44 100644
--- a/composer.json
+++ b/composer.json
@@ -25,6 +25,8 @@
"wp-launchpad/renderer": "self.version",
"wp-launchpad/filesystem": "self.version",
"wp-launchpad/uninstaller": "self.version",
+ "wp-launchpad/front": "self.version",
+ "wp-launchpad/bud-assets": "self.version",
"wp-launchpad/dispatcher": "self.version"
},
"config": {
diff --git a/packages/front-take-off/.gitignore b/packages/front-take-off/.gitignore
new file mode 100644
index 0000000..5c272e4
--- /dev/null
+++ b/packages/front-take-off/.gitignore
@@ -0,0 +1,3 @@
+vendor
+.idea
+composer.lock
diff --git a/packages/front-take-off/composer.json b/packages/front-take-off/composer.json
new file mode 100644
index 0000000..12ad118
--- /dev/null
+++ b/packages/front-take-off/composer.json
@@ -0,0 +1,49 @@
+{
+ "name": "wp-launchpad/front-take-off",
+ "description": "Front-end initialisation library for the Launchpad framework",
+ "minimum-stability": "stable",
+ "license": "proprietary",
+ "type": "library",
+ "authors": [
+ {
+ "name": "coquardcyr",
+ "email": "coquardcyr@gmail.com"
+ }
+ ],
+ "require": {
+ "wp-launchpad/cli": "^3.1",
+ "ext-mbstring": "*",
+ "ext-json": "*",
+ "composer/composer": "^2.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5 || ^8 || ^9",
+ "wp-media/phpunit": "^3.0",
+ "wp-launchpad/front": "^3.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "LaunchpadFrontTakeOff\\": "inc/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "LaunchpadFrontTakeOff\\Tests\\": "tests/"
+ }
+ },
+ "extra": {
+ "launchpad": {
+ "provider" : "LaunchpadFrontTakeOff\\ServiceProvider",
+ "command": "front:install",
+ "libraries": {
+ "wp-launchpad/front": "^3.1"
+ }
+ }
+ },
+ "scripts": {
+ "test-integration": "\"vendor/bin/phpunit\" --testsuite integration --colors=always --configuration tests/Integration/phpunit.xml.dist --exclude-group AdminOnly",
+ "run-tests": [
+ "@test-integration"
+ ]
+ }
+}
diff --git a/packages/front-take-off/front/react/.gitignore b/packages/front-take-off/front/react/.gitignore
new file mode 100644
index 0000000..1c8d802
--- /dev/null
+++ b/packages/front-take-off/front/react/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+.budfiles
+package-lock.json
diff --git a/packages/front-take-off/front/react/.npmrc b/packages/front-take-off/front/react/.npmrc
new file mode 100644
index 0000000..521a9f7
--- /dev/null
+++ b/packages/front-take-off/front/react/.npmrc
@@ -0,0 +1 @@
+legacy-peer-deps=true
diff --git a/packages/front-take-off/front/react/bud.config.js b/packages/front-take-off/front/react/bud.config.js
new file mode 100644
index 0000000..afe1e91
--- /dev/null
+++ b/packages/front-take-off/front/react/bud.config.js
@@ -0,0 +1,17 @@
+/**
+ * @typedef {import('@roots/bud').Bud} Bud
+ *
+ * @param {Bud} bud
+ */
+module.exports = async bud => {
+ bud.externals({
+ jQuery: 'window.jquery',
+ wp: 'window.wp',
+ })
+ await bud
+ .setPath('@dist', '../assets')
+ .entry({
+ app: 'app.js',
+ })
+ .when( bud.isProduction, () => bud.splitChunks().minimize() )
+}
diff --git a/packages/front-take-off/front/react/package.json b/packages/front-take-off/front/react/package.json
new file mode 100644
index 0000000..3eda2c7
--- /dev/null
+++ b/packages/front-take-off/front/react/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "launchpad-front",
+ "version": "1.0.0",
+ "browserslist": {
+ "production": [
+ ">0.5%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "scripts": {
+ "dev": "bud dev",
+ "build": "bud build",
+ "bud": "bud"
+ },
+ "devDependencies": {
+ "-": "0.0.1",
+ "@roots/bud": "^6.11.0",
+ "@roots/bud-react": "^6.11.0",
+ "caniuse-lite": "^1.0.30001464"
+ }
+}
diff --git a/packages/front-take-off/front/react/src/App.jsx b/packages/front-take-off/front/react/src/App.jsx
new file mode 100644
index 0000000..58e5586
--- /dev/null
+++ b/packages/front-take-off/front/react/src/App.jsx
@@ -0,0 +1,7 @@
+import React from 'react'
+
+function App(props) {
+ return
;
+}
+
+export default App;
diff --git a/packages/front-take-off/front/react/src/app.js b/packages/front-take-off/front/react/src/app.js
new file mode 100644
index 0000000..5d1f1f8
--- /dev/null
+++ b/packages/front-take-off/front/react/src/app.js
@@ -0,0 +1,11 @@
+import App from "./App";
+import React from 'react';
+import {createRoot} from 'react-dom/client';
+
+jQuery(function () {
+ const root = document.getElementById('app');
+
+ if(root){
+ createRoot(root).render();
+ }
+});
diff --git a/packages/front-take-off/front/vanilla/.gitignore b/packages/front-take-off/front/vanilla/.gitignore
new file mode 100644
index 0000000..1c8d802
--- /dev/null
+++ b/packages/front-take-off/front/vanilla/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+.budfiles
+package-lock.json
diff --git a/packages/front-take-off/front/vanilla/.npmrc b/packages/front-take-off/front/vanilla/.npmrc
new file mode 100644
index 0000000..521a9f7
--- /dev/null
+++ b/packages/front-take-off/front/vanilla/.npmrc
@@ -0,0 +1 @@
+legacy-peer-deps=true
diff --git a/packages/front-take-off/front/vanilla/bud.config.js b/packages/front-take-off/front/vanilla/bud.config.js
new file mode 100644
index 0000000..1d85210
--- /dev/null
+++ b/packages/front-take-off/front/vanilla/bud.config.js
@@ -0,0 +1,18 @@
+/**
+ * @typedef {import('@roots/bud').Bud} Bud
+ *
+ * @param {Bud} bud
+ */
+module.exports = async bud => {
+ bud.externals({
+ jQuery: 'window.jquery',
+ wp: 'window.wp',
+ react: 'window.React'
+ })
+ await bud
+ .setPath('@dist', '../assets')
+ .entry({
+ app: 'app.js',
+ })
+ .when( bud.isProduction, () => bud.splitChunks().minimize() )
+}
diff --git a/packages/front-take-off/front/vanilla/package.json b/packages/front-take-off/front/vanilla/package.json
new file mode 100644
index 0000000..2d9bab1
--- /dev/null
+++ b/packages/front-take-off/front/vanilla/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "launchpad-front",
+ "version": "1.0.0",
+ "dependencies": {},
+ "browserslist": {
+ "production": [
+ ">0.5%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "scripts": {
+ "dev": "bud dev",
+ "build": "bud build",
+ "bud": "bud"
+ },
+ "devDependencies": {
+ "-": "0.0.1",
+ "@roots/bud": "^6.11.0",
+ "caniuse-lite": "^1.0.30001464"
+ }
+}
diff --git a/packages/front-take-off/front/vanilla/src/app.js b/packages/front-take-off/front/vanilla/src/app.js
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/packages/front-take-off/front/vanilla/src/app.js
@@ -0,0 +1 @@
+
diff --git a/packages/front-take-off/front/vue/.gitignore b/packages/front-take-off/front/vue/.gitignore
new file mode 100644
index 0000000..1c8d802
--- /dev/null
+++ b/packages/front-take-off/front/vue/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+.budfiles
+package-lock.json
diff --git a/packages/front-take-off/front/vue/.npmrc b/packages/front-take-off/front/vue/.npmrc
new file mode 100644
index 0000000..521a9f7
--- /dev/null
+++ b/packages/front-take-off/front/vue/.npmrc
@@ -0,0 +1 @@
+legacy-peer-deps=true
diff --git a/packages/front-take-off/front/vue/bud.config.js b/packages/front-take-off/front/vue/bud.config.js
new file mode 100644
index 0000000..afe1e91
--- /dev/null
+++ b/packages/front-take-off/front/vue/bud.config.js
@@ -0,0 +1,17 @@
+/**
+ * @typedef {import('@roots/bud').Bud} Bud
+ *
+ * @param {Bud} bud
+ */
+module.exports = async bud => {
+ bud.externals({
+ jQuery: 'window.jquery',
+ wp: 'window.wp',
+ })
+ await bud
+ .setPath('@dist', '../assets')
+ .entry({
+ app: 'app.js',
+ })
+ .when( bud.isProduction, () => bud.splitChunks().minimize() )
+}
diff --git a/packages/front-take-off/front/vue/package.json b/packages/front-take-off/front/vue/package.json
new file mode 100644
index 0000000..ad1c549
--- /dev/null
+++ b/packages/front-take-off/front/vue/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "launchpad-front",
+ "version": "1.0.0",
+ "browserslist": {
+ "production": [
+ ">0.5%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "scripts": {
+ "dev": "bud dev",
+ "build": "bud build",
+ "bud": "bud"
+ },
+ "devDependencies": {
+ "-": "0.0.1",
+ "@roots/bud": "^6.11.0",
+ "@roots/bud-vue": "^6.11.0",
+ "caniuse-lite": "^1.0.30001464"
+ }
+}
diff --git a/packages/front-take-off/front/vue/src/App.vue b/packages/front-take-off/front/vue/src/App.vue
new file mode 100644
index 0000000..6beff51
--- /dev/null
+++ b/packages/front-take-off/front/vue/src/App.vue
@@ -0,0 +1,2 @@
+
+
diff --git a/packages/front-take-off/front/vue/src/app.js b/packages/front-take-off/front/vue/src/app.js
new file mode 100644
index 0000000..d7b8fdb
--- /dev/null
+++ b/packages/front-take-off/front/vue/src/app.js
@@ -0,0 +1,6 @@
+import { createApp } from "vue";
+import App from "./App";
+
+const app = createApp(App);
+
+app.mount("#app");
diff --git a/packages/front-take-off/inc/Commands/InstallCommand.php b/packages/front-take-off/inc/Commands/InstallCommand.php
new file mode 100644
index 0000000..2bc474e
--- /dev/null
+++ b/packages/front-take-off/inc/Commands/InstallCommand.php
@@ -0,0 +1,85 @@
+front_end_installation_manage = $front_end_installation_manage;
+ $this->project_manager = $project_manager;
+ $this->configs_manager = $configs_manager;
+
+ $this
+ ->argument('[type]', 'Type from the frontend to install')
+ // Usage examples:
+ ->usage(
+ // append details or explanation of given example with ` ## ` so they will be uniformly aligned when shown
+ ' $0 front:install react ## Install a frontend'
+ );
+ }
+
+ public function interact(Interactor $io)
+ {
+ // Collect missing opts/args
+ if (!$this->type) {
+
+ $types = [
+ FrontVersion::REACT => 'React',
+ FrontVersion::VANILLA => 'Vanilla',
+ FrontVersion::VUE => 'Vue',
+ ];
+
+ $this->set('type', $io->choice('Select a frontend', $types, 'vanilla'));
+ }
+ }
+
+ public function execute($type) {
+ $io = $this->app()->io();
+
+ try {
+ $type = new FrontVersion($type);
+ } catch (InvalidValue $value) {
+ $io->error('The type of frontend is not valid');
+ return;
+ }
+
+ $this->front_end_installation_manage->create_template_folder();
+ $this->front_end_installation_manage->move_front_assets($type);
+ $this->configs_manager->set_up_provider();
+ $this->project_manager->cleanup();
+ }
+}
diff --git a/packages/front-take-off/inc/ObjectValues/FrontVersion.php b/packages/front-take-off/inc/ObjectValues/FrontVersion.php
new file mode 100644
index 0000000..2536225
--- /dev/null
+++ b/packages/front-take-off/inc/ObjectValues/FrontVersion.php
@@ -0,0 +1,57 @@
+validate($value);
+ $this->value = $value;
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Validate the value.
+ *
+ * @param string $value Value to validate.
+ *
+ * @return void
+ * @throws InvalidValue
+ */
+ protected function validate(string $value) {
+ if(! in_array($value, self::VALUES, true)) {
+ throw new InvalidValue();
+ }
+ }
+}
diff --git a/packages/front-take-off/inc/ServiceProvider.php b/packages/front-take-off/inc/ServiceProvider.php
new file mode 100644
index 0000000..f3a1a9b
--- /dev/null
+++ b/packages/front-take-off/inc/ServiceProvider.php
@@ -0,0 +1,67 @@
+configs = $configs;
+ $this->project_filesystem = $filesystem;
+
+ $adapter = new Local(
+ // Determine root directory
+ __DIR__ . '/../'
+ );
+
+ // The FilesystemOperator
+ $this->library_filesystem = new Filesystem($adapter);
+ }
+
+ public function attach_commands(App $app): App
+ {
+
+ $frontend_installation_manager = new FrontEndInstallationManage($this->project_filesystem, $this->library_filesystem);
+ $config_manager = new ConfigsManager($this->project_filesystem, $this->configs);
+ $project_manager = new ProjectManager($this->project_filesystem);
+ $app->add(new InstallCommand($frontend_installation_manager, $project_manager, $config_manager));
+
+ return $app;
+ }
+}
diff --git a/packages/front-take-off/inc/Services/ConfigsManager.php b/packages/front-take-off/inc/Services/ConfigsManager.php
new file mode 100644
index 0000000..3a8c05e
--- /dev/null
+++ b/packages/front-take-off/inc/Services/ConfigsManager.php
@@ -0,0 +1,53 @@
+filesystem = $filesystem;
+ $this->configurations = $configurations;
+ }
+
+
+ public function set_up_provider() {
+ $base_namespace = $this->configurations->getBaseNamespace();
+ $providers_path = 'configs/providers.php';
+
+ if ( ! $this->filesystem->has( $providers_path ) ) {
+ return;
+ }
+
+ $content = $this->filesystem->read( $providers_path );
+
+ if(! preg_match('/(?return\s\[(?(?:[^[\]]+|(?R))*)\]\s*;\s*$)/', $content, $results)) {
+ return;
+ }
+
+ $result_content = $results['content'];
+ $result_content = "\n \\$base_namespace" . "Dependencies\\" . ServiceProvider::class . "::class," . $result_content;
+ $content = str_replace($results['content'], $result_content, $content);
+
+ $this->filesystem->update($providers_path, $content);
+ }
+
+}
diff --git a/packages/front-take-off/inc/Services/FrontEndInstallationManage.php b/packages/front-take-off/inc/Services/FrontEndInstallationManage.php
new file mode 100644
index 0000000..daf3a38
--- /dev/null
+++ b/packages/front-take-off/inc/Services/FrontEndInstallationManage.php
@@ -0,0 +1,91 @@
+project_filesystem = $project_filesystem;
+ $this->library_filesystem = $library_filesystem;
+ }
+
+ public function move_front_assets( FrontVersion $version ) {
+ $library_root = 'front' . DIRECTORY_SEPARATOR . $version->getValue();
+ $library_root_regex = preg_quote($library_root);
+ $project_root = '_dev';
+ foreach ($this->library_filesystem->listContents($library_root, true) as $path) {
+ $new_path = preg_replace("#^$library_root_regex#", $project_root, $path['path']);
+ if($path['type'] === 'file') {
+ $content = $this->library_filesystem->read($path['path']);
+ $this->project_filesystem->write($new_path, $content);
+ continue;
+ }
+
+ if($path['type'] === 'dir') {
+ $this->project_filesystem->createDir($new_path);
+ }
+ }
+ }
+
+ public function create_template_folder() {
+ $template_dir = 'templates';
+
+ $params_path = 'configs/parameters.php';
+
+ if ( $this->project_filesystem->has($template_dir) ) {
+ return;
+ }
+
+ $this->project_filesystem->createDir($template_dir);
+
+ if ( ! $this->project_filesystem->has( $params_path ) ) {
+ return;
+ }
+
+ $content = $this->project_filesystem->read( $params_path );
+
+ $content = $this->add_template_param($content);
+ $this->project_filesystem->update($params_path, $content);
+ }
+
+ protected function add_template_param(string $content) {
+ if(preg_match('/[\'"]assets_url[\'"]\s=>/', $content)) {
+ return $content;
+ }
+
+ if(! preg_match('/(?return\s\[(?:[^[\]]+|(?R))*\]\s*;\s*$)/', $content, $results)) {
+ return $content;
+ }
+
+ $array = $results['array'];
+
+ if(! preg_match('/(?\h*)[\'"].*[\'"]\s=>/', $array, $results)) {
+ return $content;
+ }
+
+ $indents = $results['indents'];
+ $new_content = "$indents'template_path' => \$plugin_launcher_path . 'templates/',\n";
+ $new_content .= "$indents'assets_url' => plugins_url('assets/', \$plugin_launcher_path . '/' . basename( \$plugin_launcher_path ) . '.php'),\n";
+ $new_content .= "];\n";
+
+ return preg_replace('/]\s*;\s*$/', $new_content, $content);
+ }
+}
diff --git a/packages/front-take-off/inc/Services/ProjectManager.php b/packages/front-take-off/inc/Services/ProjectManager.php
new file mode 100644
index 0000000..bf9a9f7
--- /dev/null
+++ b/packages/front-take-off/inc/Services/ProjectManager.php
@@ -0,0 +1,48 @@
+filesystem = $filesystem;
+ }
+
+ public function cleanup() {
+ $content = $this->filesystem->read(self::BUILDER_FILE);
+
+ $content = preg_replace('/\n *\\\\' . preg_quote(ServiceProvider::class) . '::class,\n/', '', $content);
+
+ $this->filesystem->update(self::BUILDER_FILE, $content);
+
+ $content = $this->filesystem->read(self::PROJECT_FILE);
+
+ $json = json_decode($content, true);
+
+ if(key_exists('require-dev', $json) && key_exists('wp-launchpad/front-take-off', $json['require-dev'])) {
+ unset($json['require-dev']['wp-launchpad/front-take-off']);
+ }
+
+ if(key_exists('require', $json) && key_exists('wp-launchpad/front-take-off', $json['require'])) {
+ unset($json['require']['wp-launchpad/front-take-off']);
+ }
+
+ $content = json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES) . "\n";
+
+ $this->filesystem->update(self::PROJECT_FILE, $content);
+ }
+}
diff --git a/packages/front-take-off/tests/Fixtures/files/bin/generator b/packages/front-take-off/tests/Fixtures/files/bin/generator
new file mode 100644
index 0000000..6d4f880
--- /dev/null
+++ b/packages/front-take-off/tests/Fixtures/files/bin/generator
@@ -0,0 +1,10 @@
+#!/usr/bin/php
+=7.0",
+ "berlindb/core": "^2.0",
+ "composer/installers": "^1.0 || ^2.0",
+ "monolog/monolog": "^1.0"
+ },
+ "require-dev": {
+ "php": "^7 || ^8",
+ "brain/monkey": "^2.0",
+ "coenjacobs/mozart": "^0.7",
+ "crochetfeve0251/rocket-launcher-builder": "^0.0.5",
+ "crochetfeve0251/rocket-launcher-take-off": "^0.0.2",
+ "wp-launchpad/front-take-off": "^0.0.1",
+ "crochetfeve0251/rocket-launcher-core": "^0.0.1",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "league/container": "^3.3",
+ "mnsami/composer-custom-directory-installer": "^2.0",
+ "phpcompatibility/phpcompatibility-wp": "^2.0",
+ "phpstan/phpstan": "^0.12",
+ "phpunit/phpunit": "^7.5 || ^8 || ^9",
+ "psr/container": "1.0.0",
+ "roave/security-advisories": "dev-master",
+ "szepeviktor/phpstan-wordpress": "^0.7.0",
+ "wp-coding-standards/wpcs": "^2",
+ "wp-media/phpunit": "^3.0"
+ },
+ "autoload": {
+ "classmap": [
+ "inc/classes"
+ ],
+ "psr-4": {
+ "RocketLauncher\\": "inc/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "RocketLauncher\\Tests\\": "tests/"
+ }
+ },
+ "extra": {
+ "installer-paths": {
+ "vendor/{$vendor}/{$name}/": ["type:wordpress-plugin"]
+ },
+ "mozart": {
+ "dep_namespace": "RocketLauncher\\Dependencies\\",
+ "dep_directory": "/inc/Dependencies/",
+ "classmap_directory": "/inc/classes/dependencies/",
+ "classmap_prefix": "RocketLauncher",
+ "packages": [
+ "berlindb/core",
+ "league/container",
+ "crochetfeve0251/rocket-launcher-core"
+ ]
+ }
+ },
+ "scripts": {
+ "test-unit": "\"vendor/bin/phpunit\" --testsuite unit --colors=always --configuration tests/Unit/phpunit.xml.dist",
+ "test-integration": "\"vendor/bin/phpunit\" --testsuite integration --colors=always --configuration tests/Integration/phpunit.xml.dist --exclude-group AdminOnly",
+ "test-integration-adminonly": "\"vendor/bin/phpunit\" --testsuite integration --colors=always --configuration tests/Integration/phpunit.xml.dist --group AdminOnly",
+ "run-tests": [
+ "@test-unit",
+ "@test-integration",
+ "@test-integration-adminonly"
+ ],
+ "run-stan": "vendor/bin/phpstan analyze --memory-limit=2G --no-progress",
+ "install-codestandards": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
+ "phpcs": "phpcs --basepath=.",
+ "phpcs-changed": "./bin/phpcs-changed.sh",
+ "phpcs:fix": "phpcbf",
+ "post-install-cmd": [
+ "\"vendor/bin/mozart\" compose",
+ "composer dump-autoload"
+ ],
+ "post-update-cmd": [
+ "\"vendor/bin/mozart\" compose",
+ "composer dump-autoload"
+ ],
+ "code-coverage": "\"vendor/bin/phpunit\" --testsuite unit --colors=always --configuration tests/Unit/phpunit.xml.dist --coverage-clover=tests/report/coverage.clover"
+ },
+ "minimum-stability": "stable",
+ "prefer-stable": true
+}
diff --git a/packages/front-take-off/tests/Fixtures/files/configs/parameters.php b/packages/front-take-off/tests/Fixtures/files/configs/parameters.php
new file mode 100644
index 0000000..e73c227
--- /dev/null
+++ b/packages/front-take-off/tests/Fixtures/files/configs/parameters.php
@@ -0,0 +1,16 @@
+
+ $plugin_name,
+ 'plugin_slug' => sanitize_key( $plugin_name ),
+ 'plugin_version' => '1.0.0',
+ 'plugin_launcher_file' => $plugin_launcher_path . '/' . basename($plugin_launcher_path) . '.php',
+ 'plugin_launcher_path' => $plugin_launcher_path,
+ 'plugin_inc_path' => realpath( $plugin_launcher_path . 'inc/' ) . '/',
+ 'prefix' => 'rocket_launcher',
+];
diff --git a/packages/front-take-off/tests/Fixtures/files/configs/providers.php b/packages/front-take-off/tests/Fixtures/files/configs/providers.php
new file mode 100644
index 0000000..ee4afff
--- /dev/null
+++ b/packages/front-take-off/tests/Fixtures/files/configs/providers.php
@@ -0,0 +1,7 @@
+ '/',
+ 'structure' => [
+ 'configs' => [
+ 'parameters.php' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/configs/parameters.php'),
+ 'providers.php' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/configs/providers.php'),
+ ],
+ 'bin' => [
+ 'generator' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/bin/generator'),
+ ],
+ 'composer.json' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/composer.json'),
+ ],
+ 'test_data' => [
+ 'installReactShouldConfigureProject' => [
+ 'config' => [
+ 'parameters' => ' react',
+ 'files' => [
+ 'configs/parameters.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/configs/parameters.php'),
+ ],
+ 'composer.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/composer.json'),
+ ],
+ 'bin/generator' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/bin/generator'),
+ ],
+ '_dev/package.json' => [
+ 'exists' => false,
+ ],
+ '_dev/bud.config.js' => [
+ 'exists' => false,
+ ],
+ '_dev/.npmrc' => [
+ 'exists' => false,
+ ],
+ '_dev/.gitignore' => [
+ 'exists' => false,
+ ],
+ '_dev/src/app.js' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.jsx' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.vue' => [
+ 'exists' => false,
+ ],
+ ]
+ ],
+ 'expected' => [
+ 'files' => [
+ 'configs/parameters.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/configs/parameters.php'),
+ ],
+ 'configs/providers.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/configs/providers.php'),
+ ],
+ 'composer.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/composer.json'),
+ ],
+ 'bin/generator' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/bin/generator'),
+ ],
+ '_dev/package.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/react/package.json' ),
+ ],
+ '_dev/bud.config.js' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/react/bud.config.js' ),
+ ],
+ '_dev/.npmrc' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/react/.npmrc' ),
+ ],
+ '_dev/.gitignore' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/react/.gitignore' ),
+ ],
+ '_dev/src/app.js' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/react/src/app.js' ),
+ ],
+ '_dev/src/App.jsx' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/react/src/App.jsx' ),
+ ],
+ '_dev/src/App.vue' => [
+ 'exists' => false,
+ ],
+ ]
+ ]
+ ],
+ 'installVanillaShouldConfigureProject' => [
+ 'config' => [
+ 'parameters' => ' vanilla',
+ 'files' => [
+ 'configs/parameters.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/configs/parameters.php'),
+ ],
+ 'composer.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/composer.json'),
+ ],
+ 'bin/generator' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/bin/generator'),
+ ],
+ '_dev/package.json' => [
+ 'exists' => false,
+ ],
+ '_dev/bud.config.js' => [
+ 'exists' => false,
+ ],
+ '_dev/.npmrc' => [
+ 'exists' => false,
+ ],
+ '_dev/.gitignore' => [
+ 'exists' => false,
+ ],
+ '_dev/src/app.js' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.jsx' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.vue' => [
+ 'exists' => false,
+ ],
+ ]
+ ],
+ 'expected' => [
+ 'files' => [
+ 'configs/parameters.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/configs/parameters.php'),
+ ],
+ 'configs/providers.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/configs/providers.php'),
+ ],
+ 'composer.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/composer.json'),
+ ],
+ 'bin/generator' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/bin/generator'),
+ ],
+ '_dev/package.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vanilla/package.json' ),
+ ],
+ '_dev/bud.config.js' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vanilla/bud.config.js' ),
+ ],
+ '_dev/.npmrc' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vanilla/.npmrc' ),
+ ],
+ '_dev/.gitignore' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vanilla/.gitignore' ),
+ ],
+ '_dev/src/app.js' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vanilla/src/app.js' ),
+ ],
+ '_dev/src/App.jsx' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.vue' => [
+ 'exists' => false,
+ ],
+ ]
+ ]
+ ],
+ 'installVueShouldConfigureProject' => [
+ 'config' => [
+ 'parameters' => ' vue',
+ 'files' => [
+ 'configs/parameters.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/configs/parameters.php'),
+ ],
+ 'composer.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/composer.json'),
+ ],
+ 'bin/generator' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_TESTS_FIXTURES_DIR . '/files/bin/generator'),
+ ],
+ '_dev/package.json' => [
+ 'exists' => false,
+ ],
+ '_dev/bud.config.js' => [
+ 'exists' => false,
+ ],
+ '_dev/.npmrc' => [
+ 'exists' => false,
+ ],
+ '_dev/.gitignore' => [
+ 'exists' => false,
+ ],
+ '_dev/src/app.js' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.jsx' => [
+ 'exists' => false,
+ ],
+ '_dev/src/App.vue' => [
+ 'exists' => false,
+ ],
+ ]
+ ],
+ 'expected' => [
+ 'files' => [
+ 'configs/parameters.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/configs/parameters.php'),
+ ],
+ 'configs/providers.php' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/configs/providers.php'),
+ ],
+ 'composer.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/composer.json'),
+ ],
+ 'bin/generator' => [
+ 'exists' => true,
+ 'content' => file_get_contents(__DIR__ . '/files/bin/generator'),
+ ],
+ '_dev/package.json' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vue/package.json' ),
+ ],
+ '_dev/bud.config.js' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vue/bud.config.js' ),
+ ],
+ '_dev/.npmrc' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vue/.npmrc' ),
+ ],
+ '_dev/.gitignore' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vue/.gitignore' ),
+ ],
+ '_dev/src/app.js' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vue/src/app.js' ),
+ ],
+ '_dev/src/App.vue' => [
+ 'exists' => true,
+ 'content' => file_get_contents(LAUNCHPAD_FRONT_TAKE_OFF_DIR . '/front/vue/src/App.vue' ),
+ ],
+ '_dev/src/App.jsx' => [
+ 'exists' => false,
+ ],
+ ]
+ ]
+ ],
+ ],
+];
diff --git a/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/bin/generator b/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/bin/generator
new file mode 100644
index 0000000..9d15fe9
--- /dev/null
+++ b/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/bin/generator
@@ -0,0 +1,8 @@
+#!/usr/bin/php
+=7.0",
+ "berlindb/core": "^2.0",
+ "composer/installers": "^1.0 || ^2.0",
+ "monolog/monolog": "^1.0"
+ },
+ "require-dev": {
+ "php": "^7 || ^8",
+ "brain/monkey": "^2.0",
+ "coenjacobs/mozart": "^0.7",
+ "crochetfeve0251/rocket-launcher-builder": "^0.0.5",
+ "crochetfeve0251/rocket-launcher-take-off": "^0.0.2",
+ "crochetfeve0251/rocket-launcher-core": "^0.0.1",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+ "league/container": "^3.3",
+ "mnsami/composer-custom-directory-installer": "^2.0",
+ "phpcompatibility/phpcompatibility-wp": "^2.0",
+ "phpstan/phpstan": "^0.12",
+ "phpunit/phpunit": "^7.5 || ^8 || ^9",
+ "psr/container": "1.0.0",
+ "roave/security-advisories": "dev-master",
+ "szepeviktor/phpstan-wordpress": "^0.7.0",
+ "wp-coding-standards/wpcs": "^2",
+ "wp-media/phpunit": "^3.0"
+ },
+ "autoload": {
+ "classmap": [
+ "inc/classes"
+ ],
+ "psr-4": {
+ "RocketLauncher\\": "inc/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "RocketLauncher\\Tests\\": "tests/"
+ }
+ },
+ "extra": {
+ "installer-paths": {
+ "vendor/{$vendor}/{$name}/": [
+ "type:wordpress-plugin"
+ ]
+ },
+ "mozart": {
+ "dep_namespace": "RocketLauncher\\Dependencies\\",
+ "dep_directory": "/inc/Dependencies/",
+ "classmap_directory": "/inc/classes/dependencies/",
+ "classmap_prefix": "RocketLauncher",
+ "packages": [
+ "berlindb/core",
+ "league/container",
+ "crochetfeve0251/rocket-launcher-core"
+ ]
+ }
+ },
+ "scripts": {
+ "test-unit": "\"vendor/bin/phpunit\" --testsuite unit --colors=always --configuration tests/Unit/phpunit.xml.dist",
+ "test-integration": "\"vendor/bin/phpunit\" --testsuite integration --colors=always --configuration tests/Integration/phpunit.xml.dist --exclude-group AdminOnly",
+ "test-integration-adminonly": "\"vendor/bin/phpunit\" --testsuite integration --colors=always --configuration tests/Integration/phpunit.xml.dist --group AdminOnly",
+ "run-tests": [
+ "@test-unit",
+ "@test-integration",
+ "@test-integration-adminonly"
+ ],
+ "run-stan": "vendor/bin/phpstan analyze --memory-limit=2G --no-progress",
+ "install-codestandards": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
+ "phpcs": "phpcs --basepath=.",
+ "phpcs-changed": "./bin/phpcs-changed.sh",
+ "phpcs:fix": "phpcbf",
+ "post-install-cmd": [
+ "\"vendor/bin/mozart\" compose",
+ "composer dump-autoload"
+ ],
+ "post-update-cmd": [
+ "\"vendor/bin/mozart\" compose",
+ "composer dump-autoload"
+ ],
+ "code-coverage": "\"vendor/bin/phpunit\" --testsuite unit --colors=always --configuration tests/Unit/phpunit.xml.dist --coverage-clover=tests/report/coverage.clover"
+ },
+ "minimum-stability": "stable",
+ "prefer-stable": true
+}
diff --git a/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/configs/parameters.php b/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/configs/parameters.php
new file mode 100644
index 0000000..b3f2641
--- /dev/null
+++ b/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/configs/parameters.php
@@ -0,0 +1,18 @@
+
+ $plugin_name,
+ 'plugin_slug' => sanitize_key( $plugin_name ),
+ 'plugin_version' => '1.0.0',
+ 'plugin_launcher_file' => $plugin_launcher_path . '/' . basename($plugin_launcher_path) . '.php',
+ 'plugin_launcher_path' => $plugin_launcher_path,
+ 'plugin_inc_path' => realpath( $plugin_launcher_path . 'inc/' ) . '/',
+ 'prefix' => 'rocket_launcher',
+ 'template_path' => $plugin_launcher_path . 'templates/',
+ 'assets_url' => plugins_url('assets/', $plugin_launcher_path . '/' . basename( $plugin_launcher_path ) . '.php'),
+];
diff --git a/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/configs/providers.php b/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/configs/providers.php
new file mode 100644
index 0000000..320db36
--- /dev/null
+++ b/packages/front-take-off/tests/Fixtures/inc/Commands/InstallCommand/files/configs/providers.php
@@ -0,0 +1,8 @@
+config ) ) {
+ $this->loadTestDataConfig();
+ }
+
+ $this->init();
+
+ }
+
+ public function configTestData() {
+ if ( empty( $this->config ) ) {
+ $this->loadTestDataConfig();
+ }
+
+ return isset( $this->config['test_data'] )
+ ? $this->config['test_data']
+ : $this->config;
+ }
+
+ protected function launch_app(string $command) {
+
+ list($command, $args_mappings) = $this->save_param_with_spaces($command);
+
+ $argv = array_merge(['index.php'], explode(' ', $command));
+
+ foreach ($args_mappings as $id => $value) {
+ foreach ($argv as $index => $arg) {
+ $argv[$index] = str_replace($id, $value, $arg);
+ }
+ }
+ $_SERVER['argv'] = $argv;
+ AppBuilder::enable_test_mode();
+ AppBuilder::init($this->rootVirtualUrl, [
+ ServiceProvider::class,
+ ]);
+ unset($_SERVER['argv']);
+ }
+
+ protected function save_param_with_spaces(string $command) {
+ if ( ! preg_match_all('/(?"[^"]*")/m', $command, $results)) {
+ return [$command, []];
+ }
+ $args_mapping = [];
+
+ foreach ($results['content'] as $content) {
+ $id = uniqid('args_mapping_');
+ $args_mapping[$id] = $content;
+ }
+
+ foreach ($args_mapping as $id => $value) {
+ $command = str_replace($value, $id, $command);
+ }
+
+ return [$command, $args_mapping];
+ }
+
+ protected function loadTestDataConfig() {
+ $obj = new ReflectionObject( $this );
+ $filename = $obj->getFileName();
+
+ $this->config = $this->getTestData( dirname( $filename ), basename( $filename, '.php' ) );
+ }
+}
+
diff --git a/packages/front-take-off/tests/Integration/bootstrap.php b/packages/front-take-off/tests/Integration/bootstrap.php
new file mode 100644
index 0000000..2d1cc3f
--- /dev/null
+++ b/packages/front-take-off/tests/Integration/bootstrap.php
@@ -0,0 +1,5 @@
+ $file) {
+ $this->assertSame($file['exists'], $this->filesystem->exists($path), $file['exists'] ? "$path should exists" : "$path should not exists");
+ if($file['exists']) {
+ $this->assertSame($file['content'], $this->filesystem->get_contents($path), "$path should have same content");
+ }
+ }
+ $this->launch_app("front:install{$config['parameters']}");
+
+ foreach ($expected['files'] as $path => $file) {
+ $this->assertSame($file['exists'], $this->filesystem->exists($path), $file['exists'] ? "$path should exists" : "$path should not exists");
+ if($file['exists']) {
+ $this->assertSame($file['content'], $this->filesystem->get_contents($path), "$path should have same content");
+ }
+ }
+ }
+}
diff --git a/packages/front-take-off/tests/Integration/init-tests.php b/packages/front-take-off/tests/Integration/init-tests.php
new file mode 100644
index 0000000..4fee6ff
--- /dev/null
+++ b/packages/front-take-off/tests/Integration/init-tests.php
@@ -0,0 +1,11 @@
+
+
+
+
+ ../../inc
+
+
+
+
+ inc
+
+
+