diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f4abc88..cb9cf95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -116,6 +116,12 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + + - name: Fetch and checkout the latest commit + run: | + git fetch origin main + git checkout origin/main + - uses: pnpm/action-setup@v2 with: version: 9 diff --git a/README.md b/README.md index 696a99f..87785fa 100644 --- a/README.md +++ b/README.md @@ -94,43 +94,43 @@ Once your theme has been created, it will not be changed again by the Inertia Wo If you ran the theme bootstrapper command above, you should have something like the above. -### app.php +> ### app.php See [InertiaJS.com > Server-Side Setup > Root Template](https://inertiajs.com/server-side-setup#root-template) -### controllers +> ### controllers These files will be the bridge between Wordpress and Inertia for serving GET requests. Each file should contain a single class that extends the `EvoMark\InertiaWordpress\InertiaController` class and provides a `handle` method. The handle method must return a call to `$this->render()`. The files in this directory are loaded in accordance with [Wordpress' template hierarchy rules](https://developer.wordpress.org/themes/basics/template-hierarchy/), albeit moved from the root of your theme to this `controllers` directory. -### ecosystem.config.cjs +> ### ecosystem.config.cjs This is an optional file provided to make running your InertiaJS SSR process easier. It is read by the PM2 process manager and keeps your process running reliably. For more information see the page on [PM2 Ecosystem Files](https://pm2.keymetrics.io/docs/usage/application-declaration/) -### functions.php +> ### functions.php -The standard entry point for your theme. The bootstrapper will populate this with a call to REST API. +The standard entry point for your theme. The bootstrapper will populate this with a small amount of code. -The REST API is a call to [WP Rest Registration](https://evomark.co.uk/open-source-software/wp-rest-registration/) which allows you to easily register REST endpoints that can validated input. +The code `new RestApi` is a call to [WP Rest Registration](https://evomark.co.uk/open-source-software/wp-rest-registration/) which allows you to easily register REST API endpoints that can validate input. -We'll be using these to process information submitted by our InertiaJS frontend. See `rest-api` below for more details. +We'll be using these endpoints to process information submitted by our InertiaJS frontend. See `rest-api` below for more details. -### index.php +> ### index.php Intentionally left empty to prevent directory indexing -### package.json +> ### package.json A file used by NodeJS package managers to install dependencies required by your theme. Common package managers are `NPM`, `PNPM`, and `Yarn`. -### resources +> ### resources This is where the majority of your themes frontend will live. It's no different from a standard InertiaJS project setup, so we won't go into much detail on the file structure. -### rest-api +> ### rest-api Since InertiaJS sends its data via AJAX requests, we can't use our `controllers` classes to process it. Instead, we must use special REST API controllers. @@ -138,11 +138,11 @@ All classes in this directory are automatically registered by the call the `new See the [WP REST Registration documentation](https://evomark.co.uk/open-source-software/wp-rest-registration/) for more details. -### style.css +> ### style.css Required by Wordpress. -### vite.config.js +> ### vite.config.js Used by Vite to bundle/build your theme's JavaScript and CSS files. You shouldn't need to change anything here to get Vite working, but feel free to add more plugins. @@ -202,7 +202,7 @@ createInertiaApp({ The `resolveInertiaPage` accepts two arguments, the first is your pages glob. With `eager: true` the pages will all be bundled into a single file, whereas with `eager: false` Vite will use code-splitting when bundling your app. -The second argument the `resolveInertiaPage` accepts is your [Default Layout](https://inertiajs.com/pages#default-layouts). This can either be a standard layout/layout-array: +The second argument the `resolveInertiaPage` accepts is your [Default Layout](https://inertiajs.com/pages#default-layouts). ```js import Layout from './Layout' @@ -213,7 +213,7 @@ resolve: resolveInertiaPage( ), ``` -or you can pass a function that should return a layout or layout-array: +A third argument can be an optional callback that receives the page name and page object and should return a valid Layout. ```js import Layout1 from './Layout'; @@ -222,6 +222,7 @@ import Layout2 from './Layout2'; // ... resolve: resolveInertiaPage( import.meta.glob("./pages/**/*.vue", { eager: false }), + null, // Notice that the 2nd argument is null /** * @param { string } name The page name that is being loaded * @param { VNode } resolvedPage The resolved page vNode @@ -237,10 +238,14 @@ resolve: resolveInertiaPage( If you check your Inertia page props, you'll see a few provided objects pre-loaded: -- **$page.props.wp.adminBar**: _Coming Soon_ +- **$page.props.wp.name**: The name of your site +- **$page.props.wp.homeUrl**: The URL of your site's homepage - **$page.props.wp.restUrl**: The base URL for making REST API requests - **$page.props.wp.user**: The user object for the currently logged in user - **$page.props.wp.userCapabilities**: An object of the current users capabilities/permission +- **$page.props.wp.logo**: An image resource containing your site logo as set in Wordpress' Appearance->Customise menu +- **$page.props.wp.menus**: A nested object containing your registered menus, keyed by location +- **$page.props.wp.adminBar**: _Coming Soon_ ## Wordpress Settings diff --git a/changelog/next.md b/changelog/next.md index e69de29..b91a352 100644 --- a/changelog/next.md +++ b/changelog/next.md @@ -0,0 +1,7 @@ +- **Feature**: Svelte template now available via `wp inertia:create-theme` command +- **Feature**: React template now available via `wp inertia:create-theme` command +- **Feature**: Edit and Comment nodes returned via `wp.adminBar` on Inertia requests + +- **Improvement**: Included more detailed templates/layouts for theme bootstrapper + +- **BugFix**: Duplicated inertia instances when using code-splitting on pages diff --git a/composer.json b/composer.json index ec794e4..4a105f7 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "require": { "php": ">=8.2", "evo-mark/evo-wp-rest-registration": "^4.1", - "evo-mark/wp-vite": "^1.1", + "evo-mark/wp-vite": "^1.2", "guzzlehttp/guzzle": "^7.9", "illuminate/collections": "^11.35", "nesbot/carbon": "^3.8", @@ -58,4 +58,4 @@ }, "minimum-stability": "dev", "prefer-stable": true -} +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index 074502d..ac57fac 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "395eb20e0a536260c917661856afe4db", + "content-hash": "e8564996de0d9f760fa9f6d0dea02d5d", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -126,16 +126,16 @@ }, { "name": "evo-mark/wp-vite", - "version": "v1.1.1", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/evo-mark/wp-vite.git", - "reference": "852fa07ae207366fb3d5d643230a48cf3292312b" + "reference": "25e5ed52b433ec7f7da0a6ad9d7172bf1abfc818" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/evo-mark/wp-vite/zipball/852fa07ae207366fb3d5d643230a48cf3292312b", - "reference": "852fa07ae207366fb3d5d643230a48cf3292312b", + "url": "https://api.github.com/repos/evo-mark/wp-vite/zipball/25e5ed52b433ec7f7da0a6ad9d7172bf1abfc818", + "reference": "25e5ed52b433ec7f7da0a6ad9d7172bf1abfc818", "shasum": "" }, "require": { @@ -161,9 +161,9 @@ ], "support": { "issues": "https://github.com/evo-mark/wp-vite/issues", - "source": "https://github.com/evo-mark/wp-vite/tree/v1.1.1" + "source": "https://github.com/evo-mark/wp-vite/tree/v1.2.1" }, - "time": "2024-11-17T13:30:37+00:00" + "time": "2024-12-21T12:17:50+00:00" }, { "name": "guzzlehttp/guzzle", @@ -492,16 +492,16 @@ }, { "name": "illuminate/collections", - "version": "v11.35.1", + "version": "v11.36.1", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "b8be9c0fedfc5be1524b290fed640d4b7d42c813" + "reference": "21868f9ac221a42d4346dc56495d11ab7e0d339a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/b8be9c0fedfc5be1524b290fed640d4b7d42c813", - "reference": "b8be9c0fedfc5be1524b290fed640d4b7d42c813", + "url": "https://api.github.com/repos/illuminate/collections/zipball/21868f9ac221a42d4346dc56495d11ab7e0d339a", + "reference": "21868f9ac221a42d4346dc56495d11ab7e0d339a", "shasum": "" }, "require": { @@ -544,11 +544,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-12-10T14:54:28+00:00" + "time": "2024-12-13T13:58:10+00:00" }, { "name": "illuminate/conditionable", - "version": "v11.35.1", + "version": "v11.36.1", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -594,7 +594,7 @@ }, { "name": "illuminate/contracts", - "version": "v11.35.1", + "version": "v11.36.1", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", @@ -642,7 +642,7 @@ }, { "name": "illuminate/macroable", - "version": "v11.35.1", + "version": "v11.36.1", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -790,10 +790,6 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -803,6 +799,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { @@ -2512,13 +2512,13 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - }, "phpstan": { "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-main": "3.x-dev" } }, "autoload": { diff --git a/resources/plugins/index.js b/resources/plugins/index.js index ada1477..8f2bbec 100644 --- a/resources/plugins/index.js +++ b/resources/plugins/index.js @@ -1,21 +1,25 @@ -export const resolveInertiaPage = (glob, layout = null) => { - return async function (name) { - let resolvedPage = glob[`./pages/${name}.vue`]; - if (!resolvedPage) { - console.error(`[Inertia] Couldn't find page matching "${name}"`); - return null; - } +export const resolveInertiaPage = ( + glob, + layout = null, + layoutCallback = null +) => { + return async function (name) { + let resolvedPage = glob[`./pages/${name}.vue`]; + if (!resolvedPage) { + console.error(`[Inertia] Couldn't find page matching "${name}"`); + return null; + } - if (typeof resolvedPage === "function") { - resolvedPage = await resolvedPage(); - } + if (typeof resolvedPage === "function") { + resolvedPage = await resolvedPage(); + } - if (typeof layout === "function") { - resolvedPage.default.layout = layout(name, resolvedPage); - } else if (layout) { - resolvedPage.default.layout = layout; - } + if (layoutCallback) { + resolvedPage.default.layout = layoutCallback(name, resolvedPage); + } else if (layout) { + resolvedPage.default.layout = resolvedPage.default.layout || layout; + } - return resolvedPage; - }; + return resolvedPage; + }; }; diff --git a/resources/plugins/package.json b/resources/plugins/package.json index 4533c68..042ed7f 100644 --- a/resources/plugins/package.json +++ b/resources/plugins/package.json @@ -8,6 +8,14 @@ ".": { "import": "./index.js", "require": "./index.js" + }, + "./react": { + "import": "./react.jsx", + "require": "./react.jsx" + }, + "./svelte": { + "import": "./svelte.js", + "require": "./svelte.js" } }, "scripts": { diff --git a/resources/plugins/react.jsx b/resources/plugins/react.jsx new file mode 100644 index 0000000..f715195 --- /dev/null +++ b/resources/plugins/react.jsx @@ -0,0 +1,26 @@ +export const resolveInertiaPage = ( + glob, + Layout = null, + layoutCallback = null +) => { + return async function (name) { + let resolvedPage = glob[`./pages/${name}.jsx`]; + if (!resolvedPage) { + console.error(`[Inertia] Couldn't find page matching "${name}"`); + return null; + } + + if (typeof resolvedPage === "function") { + resolvedPage = await resolvedPage(); + } + + if (layoutCallback) { + resolvedPage.default.layout = layoutCallback(name, resolvedPage); + } else if (Layout) { + resolvedPage.default.layout = + resolvedPage.default.layout || ((page) => ); + } + + return resolvedPage; + }; +}; diff --git a/resources/plugins/svelte.js b/resources/plugins/svelte.js new file mode 100644 index 0000000..638c41e --- /dev/null +++ b/resources/plugins/svelte.js @@ -0,0 +1,29 @@ +export const resolveInertiaPage = ( + glob, + layout = null, + layoutCallback = null +) => { + return async function (name) { + let resolvedPage = glob[`./pages/${name}.svelte`]; + if (!resolvedPage) { + console.error(`[Inertia] Couldn't find page matching "${name}"`); + return null; + } + + if (typeof resolvedPage === "function") { + resolvedPage = await resolvedPage(); + } + + if (layoutCallback) { + return { + default: resolvedPage.default, + layout: layoutCallback(name, resolvedPage), + }; + } else { + return { + default: resolvedPage.default, + layout: resolvedPage.layout || layout, + }; + } + }; +}; diff --git a/src/Commands/BaseCommand.php b/src/Commands/BaseCommand.php index 197f178..baebc64 100644 --- a/src/Commands/BaseCommand.php +++ b/src/Commands/BaseCommand.php @@ -23,7 +23,7 @@ public function __construct() /** * Ask the user for input and return it */ - protected function ask(string $question, callable $validationCallback = null): string + protected function ask(string $question, ?callable $validationCallback = null): string { $q = new Question(rtrim($question) . ' '); if (!empty($validationCallback)) { diff --git a/src/Commands/CreateThemeCommand.php b/src/Commands/CreateThemeCommand.php index 6306123..7a29d92 100644 --- a/src/Commands/CreateThemeCommand.php +++ b/src/Commands/CreateThemeCommand.php @@ -7,6 +7,7 @@ use EvoMark\InertiaWordpress\Container; use EvoMark\InertiaWordpress\Helpers\Path; use EvoMark\InertiaWordpress\Helpers\Strings; +use EvoMark\InertiaWordpress\Helpers\Settings; use Symfony\Component\Process\Exception\ProcessFailedException; defined('\\ABSPATH') || exit; @@ -14,6 +15,9 @@ class CreateThemeCommand extends BaseCommand { public array $replacements; + public string $template; + public string $extension = "js"; + public string $sfcExtension; /** * Create an Inertia-based theme @@ -30,8 +34,18 @@ public function __invoke($args, $assocArgs) $name = $args[0] ?? $this->ask( 'What should the theme be called?', ); - $template = $this->choice("Which template should be used?", ['Vue'], 0); + $template = $this->choice("Which template should be used?", ['Vue', 'Svelte', 'React'], 0); $template = strtolower($template); + $this->template = $template; + + if ($template === "react") { + $this->extension = "jsx"; + } + $this->sfcExtension = match ($template) { + 'vue' => 'vue', + 'svelte' => 'svelte', + 'react' => 'jsx' + }; $slug = sanitize_title($name); $this->replacements = [ @@ -101,11 +115,38 @@ public function __invoke($args, $assocArgs) // MAIN JS wp_mkdir_p(Path::join($themeDir, 'resources', 'js')); - $this->copyStub(Path::join($stubsPath, 'resources', 'js', 'main.js.' . $template . '.stub'), Path::join($themeDir, 'resources', 'js', 'main.js')); - $this->copyStub(Path::join($stubsPath, 'resources', 'js', 'ssr.js.' . $template . '.stub'), Path::join($themeDir, 'resources', 'js', 'ssr.js')); + $this->copyStub( + Path::join( + $stubsPath, + 'resources', + 'js', + 'main.' . $this->extension . '.' . $template . '.stub' + ), + Path::join( + $themeDir, + 'resources', + 'js', + 'main.' . $this->extension + ) + ); + $this->copyStub( + Path::join( + $stubsPath, + 'resources', + 'js', + 'ssr.' . $this->extension . '.' . $template . '.stub' + ), + Path::join( + $themeDir, + 'resources', + 'js', + 'ssr.' . $this->extension + ) + ); // TEMPLATE FILES - if ($template === "vue") $this->copyVueTemplateFiles($stubsPath, $themeDir); + $this->copyTemplateFiles($stubsPath, $themeDir); + // THEME CSS wp_mkdir_p(Path::join($themeDir, 'resources', 'css')); @@ -123,25 +164,31 @@ public function __invoke($args, $assocArgs) WP_CLI::log("Unable to initiate composer in theme folder, please run `composer install` before use."); } + if ($template === "react") { + Settings::set('entry_file', 'resources/js/main.jsx'); + } else { + Settings::set('entry_file', 'resources/js/main.js'); + } + WP_CLI::runcommand('theme activate ' . $slug); return true; } - private function copyVueTemplateFiles($stubsPath, $themeDir) + private function copyTemplateFiles($stubsPath, $themeDir) { wp_mkdir_p(Path::join($themeDir, 'resources', 'js', 'pages')); wp_mkdir_p(Path::join($themeDir, 'resources', 'js', 'layouts')); - $pages = glob(Path::join($stubsPath, 'resources', 'js', 'pages', '*.vue.stub')); + $pages = glob(Path::join($stubsPath, 'resources', 'js', 'pages', '*.' . $this->template . '.stub')); foreach ($pages as $page) { - $filename = basename($page, '.stub'); // Remove the .stub extension + $filename = basename($page, $this->template . '.stub') . $this->sfcExtension; // Remove the .stub extension $targetFile = Path::join($themeDir, 'resources', 'js', 'pages', $filename); $this->copyStub($page, $targetFile); } - $layouts = glob(Path::join($stubsPath, 'resources', 'js', 'layouts', '*.vue.stub')); + $layouts = glob(Path::join($stubsPath, 'resources', 'js', 'layouts', '*.' . $this->template . '.stub')); foreach ($layouts as $layout) { - $filename = basename($layout, '.stub'); // Remove the .stub extension + $filename = basename($layout, $this->template . '.stub') . $this->sfcExtension; // Remove the .stub extension $targetFile = Path::join($themeDir, 'resources', 'js', 'layouts', $filename); $this->copyStub($layout, $targetFile); } diff --git a/src/Helpers/HookActions.php b/src/Helpers/HookActions.php new file mode 100644 index 0000000..087a4d5 --- /dev/null +++ b/src/Helpers/HookActions.php @@ -0,0 +1,8 @@ +initialize(); - do_action('admin_bar_init'); - do_action('admin_bar_menu', $GLOBALS['wp_admin_bar']); - $GLOBALS['wp_admin_bar']->add_menus(); + /** + * Fired before the admin bar update HTML elements are extracted + * + * @since 0.3.0 + * + * @param \WP_Admin_Bar $bar A slimmed down version of the admin bar + */ + do_action(HookActions::PRE_RENDER_ADMIN_BAR_UPDATE, $bar); - dd($GLOBALS['wp_admin_bar']); ob_start(); - $GLOBALS['wp_admin_bar']->render(); + $bar->render(); $html = ob_get_clean(); - dd($html); - return $html; */ + + $dom = new DOMDocument(); + $dom->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING); + + $xpath = new \DOMXPath($dom); + $elements = $xpath->query("//li"); + + $listItems = []; + + foreach ($elements as $element) { + $listItems[] = $dom->saveHTML($element); + } + return $listItems; } } diff --git a/src/Resources/ImageResource.php b/src/Resources/ImageResource.php index cdaed2e..e6e9b8e 100644 --- a/src/Resources/ImageResource.php +++ b/src/Resources/ImageResource.php @@ -4,7 +4,7 @@ class ImageResource { - public static function single(int $attachment_id = null, array $args = []) + public static function single(?int $attachment_id = null, array $args = []) { $fallbackToPostImage = !isset($args['fallback']) || $args['fallback'] !== false; diff --git a/src/Responses/InertiaResponse.php b/src/Responses/InertiaResponse.php index 9a3491f..a523c38 100644 --- a/src/Responses/InertiaResponse.php +++ b/src/Responses/InertiaResponse.php @@ -28,6 +28,7 @@ public static function handle() header('Vary: Accept'); header(ucwords(Header::INERTIA, "-") . ": true"); + wp_send_json($page); } } diff --git a/src/Theme/ThemeSetup.php b/src/Theme/ThemeSetup.php index 1a4ded5..50aec8e 100644 --- a/src/Theme/ThemeSetup.php +++ b/src/Theme/ThemeSetup.php @@ -7,6 +7,7 @@ use EvoMark\InertiaWordpress\Theme\Utils; use EvoMark\InertiaWordpress\Helpers\Path; use EvoMark\InertiaWordpress\Helpers\Settings; +use EvoMark\InertiaWordpress\Inertia; class ThemeSetup { @@ -23,7 +24,6 @@ public static function addThemeSupport() { add_theme_support('custom-logo'); add_theme_support('post-thumbnails'); - add_theme_support('title-tag'); } public static function enqueueScripts() @@ -31,10 +31,13 @@ public static function enqueueScripts() $entryFile = Settings::get('entry_file'); $entryNamespace = Settings::get('entry_namespace'); + $isReact = str_ends_with($entryFile, ".jsx"); + $vite = new WpVite(); $vite->enqueue([ 'input' => $entryFile, - 'namespace' => $entryNamespace + 'namespace' => $entryNamespace, + 'react' => $isReact ]); } @@ -69,7 +72,15 @@ public static function handleTemplateInclude($template) $controller = new $class(); echo $controller->handle(); - } else return $template; + } else { + $class = Utils::getClass(Path::join($controllerDir, 'error.php')); + if (in_array('EvoMark\InertiaWordpress\Contracts\InertiaControllerContract', class_implements($class)) === false) { + return $template; + } + Inertia::share('error', 404); + $controller = new $class(); + echo $controller->handle(); + } } /** diff --git a/stubs/theme/controllers/error.php.stub b/stubs/theme/controllers/error.php.stub new file mode 100644 index 0000000..5e85c4b --- /dev/null +++ b/stubs/theme/controllers/error.php.stub @@ -0,0 +1,13 @@ +render("Error"); + } +} diff --git a/stubs/theme/functions.php.stub b/stubs/theme/functions.php.stub index 43b860e..a580c3b 100644 --- a/stubs/theme/functions.php.stub +++ b/stubs/theme/functions.php.stub @@ -8,5 +8,9 @@ new RestApi([ 'namespace' => '##NAMESPACE##\\RestApi\\', 'version' => 1, 'directory' => __DIR__ . '/rest-api', - 'base_url' => 'riley' + 'base_url' => '##SLUG##' +]); + +register_nav_menus([ + 'header-menu' => __('Header Menu'), ]); diff --git a/stubs/theme/package.json.react.stub b/stubs/theme/package.json.react.stub new file mode 100644 index 0000000..a71a6f7 --- /dev/null +++ b/stubs/theme/package.json.react.stub @@ -0,0 +1,24 @@ +{ + "name": "##SLUG##", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build && vite build --ssr", + "ssr": "pm2 start ecosystem.config.cjs" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@evo-mark/inertia-wordpress": "link:../../plugins/inertia-wordpress/resources/plugins", + "@inertiajs/react": "^2.0.0", + "@vitejs/plugin-react": "^4.3.2", + "vite": "^6.0.3", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "wordpress-vite-plugin": "^1.2.6" + }, +} diff --git a/stubs/theme/package.json.svelte.stub b/stubs/theme/package.json.svelte.stub new file mode 100644 index 0000000..f68e08c --- /dev/null +++ b/stubs/theme/package.json.svelte.stub @@ -0,0 +1,23 @@ +{ + "name": "##SLUG##", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build && vite build --ssr", + "ssr": "pm2 start ecosystem.config.cjs" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@evo-mark/inertia-wordpress": "link:../../plugins/inertia-wordpress/resources/plugins", + "@inertiajs/svelte": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "vite": "^6.0.0", + "svelte": "^5.0.0", + "wordpress-vite-plugin": "^1.2.6" + } +} diff --git a/stubs/theme/package.json.vue.stub b/stubs/theme/package.json.vue.stub index 7d873e9..2ccd0ac 100644 --- a/stubs/theme/package.json.vue.stub +++ b/stubs/theme/package.json.vue.stub @@ -18,7 +18,7 @@ "@vitejs/plugin-vue": "^5.2.1", "vite": "^6.0.3", "vue": "^3.5.13", - "wordpress-vite-plugin": "^1.1.0" + "wordpress-vite-plugin": "^1.2.6" }, "devDependencies": { "@vue/server-renderer": "^3.5.13" diff --git a/stubs/theme/resources/css/style.postcss.stub b/stubs/theme/resources/css/style.postcss.stub index 49e2f4b..3ba003a 100644 --- a/stubs/theme/resources/css/style.postcss.stub +++ b/stubs/theme/resources/css/style.postcss.stub @@ -1,9 +1,65 @@ +body { + margin: 0; + padding: 0; + font-family: Avenir, Montserrat, Corbel, "URW Gothic", source-sans-pro, + sans-serif; + font-weight: normal; +} + #app { - display: flex; - flex-direction: column; - min-height: calc(100vh - var(--wp-admin--admin-bar--height)); + display: flex; + flex-direction: column; + min-height: calc(100vh - var(--wp-admin--admin-bar--height)); } main { - flex-grow: 1; + flex-grow: 1; +} + +header, +footer { + display: flex; + justify-content: space-between; + align-items: center; + background-color: hsl(204, 15%, 13%); + color: #ffffff; + padding: 1rem 2rem; +} + +footer { + margin-top: 2rem; +} + +#header-menu { + display: flex; + gap: 1rem; + + ul, + li { + list-style-type: none; + } + + a { + text-decoration: none; + color: white; + font-weight: bold; + text-transform: uppercase; + padding: 0.5rem 1rem; + + &:hover { + color: steelblue; + } + } +} + +.flex-centre { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +#featured-image { + max-width: 500px; + max-height: 280px; } diff --git a/stubs/theme/resources/js/layouts/DefaultLayout.react.stub b/stubs/theme/resources/js/layouts/DefaultLayout.react.stub new file mode 100644 index 0000000..5bd8aec --- /dev/null +++ b/stubs/theme/resources/js/layouts/DefaultLayout.react.stub @@ -0,0 +1,56 @@ +import { Link, usePage } from "@inertiajs/react"; +import { useMemo } from "react"; + +function Layout({ children }) { + const { name, homeUrl, logo, menus } = usePage().props.wp; + + const headerMenu = useMemo(() => menus?.["header-menu"] ?? null, [menus]); + + console.log(menus); + + return ( + <> +
+
+ + {logo ? ( + {logo.metadata.alt} + ) : ( +

{name}

+ )} + +
+ +
+
{children}
+ + + ); +} + +export default Layout; diff --git a/stubs/theme/resources/js/layouts/DefaultLayout.svelte.stub b/stubs/theme/resources/js/layouts/DefaultLayout.svelte.stub new file mode 100644 index 0000000..f81f9b5 --- /dev/null +++ b/stubs/theme/resources/js/layouts/DefaultLayout.svelte.stub @@ -0,0 +1,16 @@ +
+
+ Home + About + Contact +
+
+ {@render children()} +
+
+ + diff --git a/stubs/theme/resources/js/main.js.svelte.stub b/stubs/theme/resources/js/main.js.svelte.stub new file mode 100644 index 0000000..a4062c6 --- /dev/null +++ b/stubs/theme/resources/js/main.js.svelte.stub @@ -0,0 +1,20 @@ +import { createInertiaApp } from '@inertiajs/svelte'; +import { hydrate, mount } from 'svelte'; +import { resolveInertiaPage } from "@evo-mark/inertia-wordpress/svelte"; +import DefaultLayout from "./layouts/DefaultLayout.svelte"; +import "../css/style.postcss"; + +createInertiaApp({ + resolve: resolveInertiaPage( + // TODO: Setting eager to 'false' seems to cause issues currently + import.meta.glob("./pages/**/*.svelte", { eager: true }), + DefaultLayout + ), + setup({ el, App, props }) { + if (el.dataset.serverRendered === 'true') { + hydrate(App, { target: el, props }) + } else { + mount(App, { target: el, props }) + } + }, +}); diff --git a/stubs/theme/resources/js/main.jsx.react.stub b/stubs/theme/resources/js/main.jsx.react.stub new file mode 100644 index 0000000..5fb0b79 --- /dev/null +++ b/stubs/theme/resources/js/main.jsx.react.stub @@ -0,0 +1,16 @@ +import { createInertiaApp } from "@inertiajs/react"; +import { hydrateRoot, createRoot } from "react-dom/client"; +import { resolveInertiaPage } from "@evo-mark/inertia-wordpress/react"; +import DefaultLayout from "./layouts/DefaultLayout.jsx"; +import "../css/style.postcss"; + +createInertiaApp({ + resolve: resolveInertiaPage( + import.meta.glob("./pages/**/*.jsx", { eager: false }), + DefaultLayout + ), + setup({ el, App, props }) { + /* hydrateRoot(el, ); */ + createRoot(el).render(); + }, +}); diff --git a/stubs/theme/resources/js/pages/Archive.react.stub b/stubs/theme/resources/js/pages/Archive.react.stub new file mode 100644 index 0000000..5d9c85d --- /dev/null +++ b/stubs/theme/resources/js/pages/Archive.react.stub @@ -0,0 +1,15 @@ +import { Head, usePage } from "@inertiajs/react"; + +const Archive = () => { + const { wp, archive } = usePage().props; + return ( + <> + +
+

Welcome to {wp.name}

+
+ + ); +}; + +export default Archive; diff --git a/stubs/theme/resources/js/pages/Error.react.stub b/stubs/theme/resources/js/pages/Error.react.stub new file mode 100644 index 0000000..3e38b7f --- /dev/null +++ b/stubs/theme/resources/js/pages/Error.react.stub @@ -0,0 +1,15 @@ +import { usePage, Link } from "@inertiajs/react"; + +const Error = () => { + const { error, wp } = usePage().props; + return ( + <> +
+

Error: {error}

+ Go Home +
+ + ); +}; + +export default Error; diff --git a/stubs/theme/resources/js/pages/Error.svelte.stub b/stubs/theme/resources/js/pages/Error.svelte.stub new file mode 100644 index 0000000..fce5c71 --- /dev/null +++ b/stubs/theme/resources/js/pages/Error.svelte.stub @@ -0,0 +1,11 @@ + + + + Error + + +
+

Error: {error}

+
diff --git a/stubs/theme/resources/js/pages/Error.vue.stub b/stubs/theme/resources/js/pages/Error.vue.stub new file mode 100644 index 0000000..545064d --- /dev/null +++ b/stubs/theme/resources/js/pages/Error.vue.stub @@ -0,0 +1,12 @@ + + + diff --git a/stubs/theme/resources/js/pages/Home.react.stub b/stubs/theme/resources/js/pages/Home.react.stub new file mode 100644 index 0000000..bff56c2 --- /dev/null +++ b/stubs/theme/resources/js/pages/Home.react.stub @@ -0,0 +1,15 @@ +import { Head, usePage } from "@inertiajs/react"; + +const Home = () => { + const { wp } = usePage().props; + return ( + <> + +
+

Welcome to {wp.name}

+
+ + ); +}; + +export default Home; diff --git a/stubs/theme/resources/js/pages/Home.svelte.stub b/stubs/theme/resources/js/pages/Home.svelte.stub new file mode 100644 index 0000000..f17d479 --- /dev/null +++ b/stubs/theme/resources/js/pages/Home.svelte.stub @@ -0,0 +1,11 @@ + + Welcome + + +
+

Welcome to {wp.name}

+
+ + diff --git a/stubs/theme/resources/js/pages/Post.react.stub b/stubs/theme/resources/js/pages/Post.react.stub new file mode 100644 index 0000000..80f73e0 --- /dev/null +++ b/stubs/theme/resources/js/pages/Post.react.stub @@ -0,0 +1,34 @@ +import { Head, usePage } from "@inertiajs/react"; +import { useMemo } from "react"; + +const Post = () => { + const { post } = usePage().props; + const publishedDate = useMemo(() => { + const d = new Date(post.createdAt); + return d.toLocaleString(undefined, { + dateStyle: "long", + timeStyle: "medium", + }); + }, [post]); + + return ( + <> + +
+

{post.title}

+ {post.featuredImage && ( + + )} +
+
+ Written by {post.author.name} on {publishedDate} +
+
+ + ); +}; + +export default Post; diff --git a/stubs/theme/resources/js/pages/Post.vue.stub b/stubs/theme/resources/js/pages/Post.vue.stub index a340592..a888fbd 100644 --- a/stubs/theme/resources/js/pages/Post.vue.stub +++ b/stubs/theme/resources/js/pages/Post.vue.stub @@ -1,5 +1,36 @@ - + diff --git a/stubs/theme/resources/js/ssr.js.svelte.stub b/stubs/theme/resources/js/ssr.js.svelte.stub new file mode 100644 index 0000000..943d135 --- /dev/null +++ b/stubs/theme/resources/js/ssr.js.svelte.stub @@ -0,0 +1,17 @@ +import { createInertiaApp } from '@inertiajs/svelte'; +import createServer from '@inertiajs/svelte/server'; +import { render } from 'svelte/server'; +import DefaultLayout from "./layouts/DefaultLayout.svelte"; + +createServer(page => + createInertiaApp({ + page, + resolve: resolveInertiaPage( + import.meta.glob("./pages/**/*.svelte", { eager: true }), + DefaultLayout + ), + setup({ App, props }) { + return render(App, { props }) + }, + }), +) diff --git a/stubs/theme/resources/js/ssr.jsx.react.stub b/stubs/theme/resources/js/ssr.jsx.react.stub new file mode 100644 index 0000000..9375798 --- /dev/null +++ b/stubs/theme/resources/js/ssr.jsx.react.stub @@ -0,0 +1,17 @@ +import { createInertiaApp } from '@inertiajs/react'; +import createServer from '@inertiajs/react/server'; +import * as ReactDOMServer from 'react-dom/server'; +import DefaultLayout from "./layouts/DefaultLayout.jsx"; + +createServer((page) => + createInertiaApp({ + page, + render: ReactDOMServer.renderToString, + title: (title) => `${title} - InertiaJS`, + resolve: resolveInertiaPage( + import.meta.glob("./pages/**/*.jsx", { eager: true }), + DefaultLayout + ), + setup: ({ App, props }) => , + }), +); diff --git a/stubs/theme/vite.config.react.stub b/stubs/theme/vite.config.react.stub new file mode 100644 index 0000000..5351fed --- /dev/null +++ b/stubs/theme/vite.config.react.stub @@ -0,0 +1,16 @@ +import { defineConfig } from "vite"; +import { wordpress } from "wordpress-vite-plugin"; +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [ + react({}), + wordpress({ + input: "resources/js/main.jsx", + ssr: "resources/js/ssr.jsx", + namespace: "theme-inertia", + splitVendor: true, + localReact: true + }), + ], +}); diff --git a/stubs/theme/vite.config.svelte.stub b/stubs/theme/vite.config.svelte.stub new file mode 100644 index 0000000..c6f3eae --- /dev/null +++ b/stubs/theme/vite.config.svelte.stub @@ -0,0 +1,15 @@ +import { defineConfig } from "vite"; +import { wordpress } from "wordpress-vite-plugin"; +import { svelte } from '@sveltejs/vite-plugin-svelte'; + +export default defineConfig({ + plugins: [ + svelte({}), + wordpress({ + input: "resources/js/main.js", + ssr: "resources/js/ssr.js", + namespace: "theme-inertia", + splitVendor: true + }), + ], +}); diff --git a/stubs/theme/vite.config.vue.stub b/stubs/theme/vite.config.vue.stub index 9611871..9f3b10a 100644 --- a/stubs/theme/vite.config.vue.stub +++ b/stubs/theme/vite.config.vue.stub @@ -16,6 +16,7 @@ export default defineConfig({ input: "resources/js/main.js", ssr: "resources/js/ssr.js", namespace: "theme-inertia", + splitVendor: true }), ], });