Eptagone / Vite.AspNetCore

Small library to integrate Vite into ASP.NET projects
MIT License
243 stars 34 forks source link

Specify Outdir as a subfolder of wwwroot #53

Closed cayroso closed 11 months ago

cayroso commented 1 year ago

I'm having issue where I want to generated artifacts by vite to be place under wwwroot (e.g. wwwroot/app) I'm able to make it generate the files (js/css/etc) to that folder, but the manifest isn't correct during prod deploy. Example.

{ "store/main.js": { "file": "js/store.85ce33a6.js", "imports": [ "_toast-chunk.js" ], "isEntry": true, "src": "store/main.js" } }

Not sure if this is just a wrong config for vite, or can be resolved by Vite.AspNetCore

Thanks!

cayroso commented 1 year ago

my current workaround for the issue

<environment include="Development"> <script type="module" vite-src="~/administrator/main.js"></script> </environment> <environment exclude="Development"> <script type="module" vite-src="~/app/administrator/main.js"></script> </environment>

Had to specify the subfolder "app" when in prod

Eptagone commented 1 year ago

Hi, can i see you vite config file? and your configuration for Vite.AspNetCore

cayroso commented 1 year ago

vite.config.js

const config = {
    appType: 'custom',
    root: 'ClientApp',
    publicDir: 'public',
    build: {
        manifest: appsettings.Vite.Manifest,
        emptyOutDir: true,
        outDir: '../wwwroot/app',
        assetsDir: 'assets',
        rollupOptions: {                
            input: {                    
                administrator: 'ClientApp/administrator/main.js',
                store: 'ClientApp/store/main.js'
            },
            output: {
                // Save entry files to the appropriate folder
                entryFileNames: 'js/[name].[hash].js',
                // Save chunk files to the js folder
                chunkFileNames: 'js/[name]-[hash]-chunk.js',
                // Save asset files to the appropriate folder
                assetFileNames: (info) => {
                    if (info.name) {
                        // If the file is a CSS file, save it to the css folder
                        if (cssPattern.test(info.name)) {
                            return 'css/[name]-[hash][extname]';
                        }
                        // If the file is an image file, save it to the images folder
                        if (imagePattern.test(info.name)) {
                            return 'images/[name]-[hash][extname]';
                        }
                        // If the file is any other type of file, save it to the assets folder 
                        return 'assets/[name]-[hash][extname]';
                    } else {
                        // If the file name is not specified, save it to the output directory
                        return '[name]-[hash][extname]';
                    }
                },
            }
        },
    },
    server: {
        port: appsettingsDev.Vite.Server.Port,
        strictPort: true,
        https: {
            cert: certFilePath,
            key: keyFilePath
        },
        hmr: {
            clientPort: appsettingsDev.Vite.Server.Port
        }
    },
    optimizeDeps: {
        include: []
    },
    plugins: [
        vue()
    ],
    resolve: {
        alias: {
            vue: 'vue/dist/vue.esm-bundler.js',
        },
    },
}

appsettings.json

"Vite": {
"Manifest": "vue-vite.manifest.json",
"Base": "app"
}

appsettings.Development.json

"Vite": {    
"Server": {
"AutoRun": true,
"Host": "localhost",
"Port": 7205,
"Https": true
}
}

Generated manifest, running npm run build

{
"_toast-f914df88-chunk.js": {
"css": [
"css/toast-2a09caf2.css"
],
"file": "js/toast-f914df88-chunk.js"
},
"administrator/main.css": {
"file": "css/main-6de25083.css",
"src": "administrator/main.css"
},
"administrator/main.js": {
"css": [
"css/main-6de25083.css"
],
"file": "js/administrator.b30ae83e.js",
"imports": [
"_toast-f914df88-chunk.js"
],
"isEntry": true,
"src": "administrator/main.js"
},
"store/main.js": {
"file": "js/store.f724f213.js",
"imports": [
"_toast-f914df88-chunk.js"
],
"isEntry": true,
"src": "store/main.js"
},
"toast.css": {
"file": "css/toast-2a09caf2.css",
"src": "toast.css"
}
}

Folder structure

seangwright commented 1 year ago

Did you see the guide in the wiki?

I have my Release build output going to ~/wwwroot/dist so it doesn't clobber other static assets in ~/wwwroot. It works correctly for me!

appsettings.json

  "Vite": {
    "Base": "/dist/",
    "Manifest": "manifest.json"
  }

vite.config.js

  /** @type {import('vite').UserConfig} */
  const config = {
    appType: "custom",
    root: "./Client",
    // See https://github.com/Eptagone/Vite.AspNetCore/wiki#how-to-configure-a-subfolder-as-output-for-my-vite-assets
    base: "/dist/",
    publicDir: "",
    build: {
      manifest: appsettings.Vite.Manifest || "manifest.json",
      assetsDir: "",
      emptyOutDir: true,
      outDir: "../wwwroot/dist",
      // CSS sourcemaps are not currently supported by Vite https://github.com/vitejs/vite/issues/2830
      sourcemap: true,
      rollupOptions: {
        input: [
          /**
           * We have explicity entrypoints so we can create small esmodule outputs
           * which can be used with dynamic imports
           * Example: const mod = await import('/path/file.js');
           *
           * Having a separate scss entrypoint also prevents a flash of unstyled content
           * (FOUC) on page navigations. See: https://github.com/Eptagone/Vite.AspNetCore/issues/50
           */
          "Client/js/main.js",
          "Client/js/other.js",
          "Client/js/another.js",
          "Client/styles/style.scss",
        ],
        output: {
          preserveModules: false,
        },
        preserveEntrySignatures: "strict",

_Layout.cshtml

<script type="module" src='@ClientAssets.ViteAssetPath("dist/js/main.js")'></script>

I have ClientAssets as an injected service in my _ViewImports.cshtml.

ClientAssets.ViteAssetPath does some of what the vite-href/vite-src tag helper does, but it's more flexible for my use cases.

public class ClientAssets
{
    private readonly IWebHostEnvironment env;
    private readonly IViteManifest manifest;

    public ClientAssets(IWebHostEnvironment env, IViteManifest manifest)
    {
        this.env = env;
        this.manifest = manifest;
    }

    /// <summary>
    /// Returns the normalized path to the asset for the Vite.js build system.
    /// </summary>
    /// <param name="assetPath"></param>
    /// <returns></returns>
    public string ViteAssetPath(string assetPath)
    {
        if (env.IsDevelopment())
        {
            return $"/{assetPath}";
        }

        if (manifest.ContainsKey(assetPath))
        {
            return $"/{manifest[assetPath].File}";
        }

        return "";
    }
}
cayroso commented 1 year ago

Did you see the guide in the wiki?

I have my Release build output going to ~/wwwroot/dist so it doesn't clobber other static assets in ~/wwwroot. It works correctly for me!

appsettings.json

  "Vite": {
    "Base": "/dist/",
    "Manifest": "manifest.json"
  }

vite.config.js

  /** @type {import('vite').UserConfig} */
  const config = {
    appType: "custom",
    root: "./Client",
    // See https://github.com/Eptagone/Vite.AspNetCore/wiki#how-to-configure-a-subfolder-as-output-for-my-vite-assets
    base: "/dist/",
    publicDir: "",
    build: {
      manifest: appsettings.Vite.Manifest || "manifest.json",
      assetsDir: "",
      emptyOutDir: true,
      outDir: "../wwwroot/dist",
      // CSS sourcemaps are not currently supported by Vite https://github.com/vitejs/vite/issues/2830
      sourcemap: true,
      rollupOptions: {
        input: [
          /**
           * We have explicity entrypoints so we can create small esmodule outputs
           * which can be used with dynamic imports
           * Example: const mod = await import('/path/file.js');
           *
           * Having a separate scss entrypoint also prevents a flash of unstyled content
           * (FOUC) on page navigations. See: https://github.com/Eptagone/Vite.AspNetCore/issues/50
           */
          "Client/js/main.js",
          "Client/js/other.js",
          "Client/js/another.js",
          "Client/styles/style.scss",
        ],
        output: {
          preserveModules: false,
        },
        preserveEntrySignatures: "strict",

_Layout.cshtml

<script type="module" src='@ClientAssets.ViteAssetPath("dist/js/main.js")'></script>

I have ClientAssets as an injected service in my _ViewImports.cshtml.

ClientAssets.ViteAssetPath does some of what the vite-href/vite-src tag helper does, but it's more flexible for my use cases.

public class ClientAssets
{
    private readonly IWebHostEnvironment env;
    private readonly IViteManifest manifest;

    public ClientAssets(IWebHostEnvironment env, IViteManifest manifest)
    {
        this.env = env;
        this.manifest = manifest;
    }

    /// <summary>
    /// Returns the normalized path to the asset for the Vite.js build system.
    /// </summary>
    /// <param name="assetPath"></param>
    /// <returns></returns>
    public string ViteAssetPath(string assetPath)
    {
        if (env.IsDevelopment())
        {
            return $"/{assetPath}";
        }

        if (manifest.ContainsKey(assetPath))
        {
            return $"/{manifest[assetPath].File}";
        }

        return "";
    }
}

I will try your approach. Looking from it, it seems that is the correct way to do it. Thank you sir!.