InterNACHI/modular

Inertia

Opened this issue · 9 comments

Thanks for the great library. I know Inertia should work out well since you have the resources folder and what not.
Just curious if anyone has examples of package.json or vite.config to make sure everything builds with the main application.

I assume it might be like Nova and how they do it.

I think easier for this is to publish them as if they were a composer package (within a tag using Laravel's vendor:publish)

I was thinking about this as well to one of my projects as its the only one based on Inertia

Anyway to the matter, I think the Laravel vite plugin actually support an array of inputs, tho I think it produces different files at the output so the problem is they aren't linked together

@alnutile

App.ts File

...
createInertiaApp({
    title: (title) => `${title}`,
    resolve: (name) => {
        let isModule = name.split("::");
        if (isModule.length > 1) {
            console.log(isModule);
            let moduleName = isModule[0].toString().toLowerCase();
            let fileName = isModule[1];
            console.log(isModule, moduleName, fileName)
            console.log(moduleName, fileName);
            return resolvePageComponent(
                `../../Modules/${moduleName}/resources/views/${fileName}.vue`,
                import.meta.glob<DefineComponent>(
                    "../../Modules/*/resources/views/**/*.vue",
                ),
            );
        } else {
            return resolvePageComponent(
                `./Pages/${name}.vue`,
                import.meta.glob<DefineComponent>("./Pages/**/*.vue"),
            );
        }
    },
    setup({el, App, props, plugin}) {
        createApp({render: () => h(App, props)})
...

Vite.config.ts*

...
export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/js/app.ts", "resources/css/errors.css"],
            refresh: true,
        }),
....
 alias: {
            "@": "/resources/js",
            "@images": "/resources/js/images",
            "@modules": path.resolve(__dirname + '/Modules' )     //aliases for helping IDE....
        },

And don't forgot to add web middleware to you route group on your modules routes

Did anyone manage to make it work with React + TypeScript? The code below renders the page components under the modules, but it still looks for the page component under the default resources/js/Pages directory on the initial page load which returns a 404 and subsequently retrieves the correct page component from the modules directory.

createInertiaApp({
  title: (title) => `${title} - ${appName}`,
  resolve: (name) => {
    const pages = import.meta.glob([
      "./Pages/**/*.tsx",
      "../../app-modules/*/resources/js/Pages/**/*.tsx",
    ]);

    const regex = /([^:]+)::(.+)/;
    const matches = regex.exec(name);

    if (matches && matches.length > 2) {
      const module = matches[1].toLowerCase();
      const pageName = matches[2];

      return pages[`../../app-modules/${moduleName}/resources/js/Pages/${pageName}.tsx`]();
    } else {
      return pages[`./Pages/${name}.tsx`]();
    }
  },
  setup({ el, App, props }) {
    const root = createRoot(el);

    root.render(<App {...props} />);
  },
  progress: {
    color: "#4B5563",
  },
});

image

It turned out that the 404 issue on the initial page load was caused by the app.blade.php. I removed resources/js/Pages/{$page['component']}.tsx from the @vite directive and it no longer attempts to fetch the page component from the default directory on the initial page load.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title inertia>{{ config('app.name', 'Laravel') }}</title>

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

        <!-- Scripts -->
        @routes
        @viteReactRefresh
        @vite(['resources/js/app.tsx', "resources/js/Pages/{$page['component']}.tsx"]) <-- this!
        @inertiaHead
    </head>
    <body class="font-sans antialiased">
        @inertia
    </body>
</html>

It turned out that the 404 issue on the initial page load was caused by the app.blade.php. I removed resources/js/Pages/{$page['component']}.tsx from the @vite directive and it no longer attempts to fetch the page component from the default directory on the initial page load.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title inertia>{{ config('app.name', 'Laravel') }}</title>

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

        <!-- Scripts -->
        @routes
        @viteReactRefresh
        @vite(['resources/js/app.tsx', "resources/js/Pages/{$page['component']}.tsx"]) <-- this!
        @inertiaHead
    </head>
    <body class="font-sans antialiased">
        @inertia
    </body>
</html>

@avosalmon
Try this approach in you app.blade.php file.

  @routes
    @if(count(explode('::',$page['component'])) > 1)
        @php
            $module = explode('::',$page['component'])[0];

            $moduleLower = strtolower($module);

            $path = explode('::',$page['component'])[1];
        @endphp
        @vite(['resources/js/app.tsx', "app-modules/$moduleLower/resources/js/Pages/$path.tsx" ])
    @else
        @vite(['resources/js/app.tsx', "resources/js/Pages/{$page['component']}.tsx"])
    @endif
    @inertiaHead

@inkomomutane It worked. Thanks!

How are you guys handling (React/Vue) components and being able to import them?

I would love to have a setup like:

app-modules
└───{module}
    └───resources
        └───js
            ├───Pages
            │   └───StoreLocator.tsx
            └───components
                └───Store.tsx

Do you have an internal npm package or a different pattern?

How are you guys handling (React/Vue) components and being able to import them?

I would love to have a setup like:

app-modules
└───{module}
    └───resources
        └───js
            ├───Pages
            │   └───StoreLocator.tsx
            └───components
                └───Store.tsx

Do you have an internal npm package or a different pattern?

@ahinkle I have the same directory structure. You need to append the module name when you render the page component in the controller. For example, if you want to render the Login page in the "auth" module, it would look like this.

Inertia::render('Auth::Login');

Also, you might want to add a path alias to tsconfig.json so that you can import TS files from module aliases.

{
    "compilerOptions": {
        "allowJs": true,
        "module": "ESNext",
        "moduleResolution": "bundler",
        "jsx": "react-jsx",
        "strict": true,
        "isolatedModules": true,
        "target": "ESNext",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "noEmit": true,
        "paths": {
            "@/*": ["./resources/js/*"],
            "@auth/*": ["./app-modules/auth/resources/js/*"]
        },
        "types": ["@testing-library/jest-dom"]
    },
    "include": [
      "resources/js/**/*.ts",
      "resources/js/**/*.tsx",
      "resources/js/**/*.d.ts",
      "app-modules/*/resources/js/**/*.ts",
      "app-modules/*/resources/js/**/*.tsx",
      "app-modules/*/resources/js/**/*.d.ts"
    ]
}

In your vite.config.ts, add vite-tsconfig-paths plugin to make sure Vite understands the module aliases.

import react from "@vitejs/plugin-react";
import laravel from "laravel-vite-plugin";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [
    laravel({
      input: "resources/js/app.tsx",
      refresh: true,
    }),
    react(),
    tsconfigPaths(), // Required to resolve path aliases defined in tsconfig.json.
  ],
  ...
});

@alnutile

App.ts File

...
createInertiaApp({
    title: (title) => `${title}`,
    resolve: (name) => {
        let isModule = name.split("::");
        if (isModule.length > 1) {
            console.log(isModule);
            let moduleName = isModule[0].toString().toLowerCase();
            let fileName = isModule[1];
            console.log(isModule, moduleName, fileName)
            console.log(moduleName, fileName);
            return resolvePageComponent(
                `../../Modules/${moduleName}/resources/views/${fileName}.vue`,
                import.meta.glob<DefineComponent>(
                    "../../Modules/*/resources/views/**/*.vue",
                ),
            );
        } else {
            return resolvePageComponent(
                `./Pages/${name}.vue`,
                import.meta.glob<DefineComponent>("./Pages/**/*.vue"),
            );
        }
    },
    setup({el, App, props, plugin}) {
        createApp({render: () => h(App, props)})
...

Vite.config.ts*

...
export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/js/app.ts", "resources/css/errors.css"],
            refresh: true,
        }),
....
 alias: {
            "@": "/resources/js",
            "@images": "/resources/js/images",
            "@modules": path.resolve(__dirname + '/Modules' )     //aliases for helping IDE....
        },

And don't forgot to add web middleware to you route group on your modules routes

please help share repository of example modular inertia js with vue js.
i want to know how share component from modular to modular (example: dashboard sidebar) and middleware.