laravel / vite-plugin

Laravel plugin for Vite.
MIT License
800 stars 151 forks source link

Configuration ("publicDirectory") does not support absolute paths on Linux/Unix #119

Closed MarkL4YG closed 2 years ago

MarkL4YG commented 2 years ago

Hi there 👋🏼

I'm quite new to using Vite and Laravel together but I'm already excited on the benefits of using Vite. It appears, I've stumbled on a bug that appears when this plugin generates the Vite outDir on Unix-like systems.

Since this bug seems fairly straight forward, I've not taken the time to confirm it on a fresh Laravel project. Let me know if you want me to confirm it from there in case it turns out to not be that clear.

Versions (from lockfiles)

Description:

When setting publicDirectory to an absolute path, the build output appears empty on Unix systems.

The reason is that the leading slash goes missing in the generated Vite option build.outDir which causes the output to be written to a relative path and not the expected one.

Example vite.config.js

import {defineConfig} from 'vite';
import laravel from "laravel-vite-plugin";
import {resolve} from "path";

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/js/app.js'],
            publicDirectory: resolve(__dirname, 'public'), // <-- resolve returns an absolute path
        }),
    ],
});

Using npx vite build --debug --mode development, the resulting Vite configuration can be examined:

  vite:config bundled config file loaded in 330.23ms +0ms
  vite:config using resolved config: {
  vite:config   plugins: [
  vite:config     'vite:pre-alias',
  vite:config     'alias',
  vite:config     'vite:modulepreload-polyfill',
  vite:config     'vite:optimized-deps',
  vite:config     'vite:resolve',
  vite:config     'vite:html-inline-proxy',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm-helper',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite:wasm-fallback',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:worker-import-meta-url',
  vite:config     'vite:dynamic-import-vars',
  vite:config     'vite:import-glob',
  vite:config     'laravel',
  vite:config     'vite:client-inject',
  vite:config     'vite:import-analysis'
  vite:config   ],
  vite:config   mode: 'development',
  vite:config   optimizeDeps: {
  vite:config     disabled: 'build',
  vite:config     force: undefined,
  vite:config     esbuildOptions: { preserveSymlinks: undefined }
  vite:config   },
  vite:config   server: {
  vite:config     preTransformRequests: true,
  vite:config     origin: '__laravel_vite_placeholder__',
  vite:config     middlewareMode: false,
  vite:config     fs: { strict: true, allow: [Array], deny: [Array] }
  vite:config   },
  vite:config   base: '/',
  vite:config   publicDir: '',
  vite:config   build: {
  vite:config     target: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari13' ],
  vite:config     polyfillModulePreload: true,
  vite:config     outDir: 'home/markl4yg/rtmp-manager/backend/public/build',      <-- THIS LINE is missing the leading '/'
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     cssTarget: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari13' ],
  vite:config     sourcemap: false,
  vite:config     rollupOptions: { input: [Array] },
  vite:config     minify: 'esbuild',
  vite:config     terserOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     manifest: true,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     reportCompressedSize: true,
  vite:config     chunkSizeWarningLimit: 500,
  vite:config     watch: null,
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] }
  vite:config   },
  vite:config   resolve: { alias: [ [Object], [Object], [Object] ] },
  vite:config   ssr: {
  vite:config     format: 'esm',
  vite:config     target: 'node',
  vite:config     noExternal: [ 'laravel-vite-plugin' ],
  vite:config     optimizeDeps: { disabled: true, esbuildOptions: [Object] }
  vite:config   },
  vite:config   configFile: '/home/markl4yg/rtmp-manager/backend/vite.config.js',
  vite:config   configFileDependencies: [ '/home/markl4yg/rtmp-manager/backend/vite.config.js' ],
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: 'development',
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     optimizeDeps: { force: undefined },
  vite:config     server: {}
  vite:config   },
  vite:config   root: '/home/markl4yg/rtmp-manager/backend',
  vite:config   cacheDir: '/home/markl4yg/rtmp-manager/backend/node_modules/.vite',
  vite:config   command: 'serve',
  vite:config   isWorker: false,
  vite:config   mainConfig: null,
  vite:config   isProduction: false,
  vite:config   preview: {
  vite:config     port: undefined,
  vite:config     strictPort: undefined,
  vite:config     host: undefined,
  vite:config     https: undefined,
  vite:config     open: undefined,
  vite:config     proxy: undefined,
  vite:config     cors: undefined,
  vite:config     headers: undefined
  vite:config   },
  vite:config   env: { BASE_URL: '/', MODE: 'development', DEV: true, PROD: false },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     warnOnce: [Function: warnOnce],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen],
  vite:config     hasErrorLogged: [Function: hasErrorLogged]
  vite:config   },
  vite:config   packageCache: Map(0) {},
  vite:config   createResolver: [Function: createResolver],
  vite:config   worker: {
  vite:config     format: 'iife',
  vite:config     plugins: [
  vite:config       'vite:pre-alias',
  vite:config       'alias',
  vite:config       'vite:modulepreload-polyfill',
  vite:config       'vite:optimized-deps',
  vite:config       'vite:resolve',
  vite:config       'vite:html-inline-proxy',
  vite:config       'vite:css',
  vite:config       'vite:esbuild',
  vite:config       'vite:json',
  vite:config       'vite:wasm-helper',
  vite:config       'vite:worker',
  vite:config       'vite:asset',
  vite:config       'vite:wasm-fallback',
  vite:config       'vite:define',
  vite:config       'vite:css-post',
  vite:config       'vite:worker-import-meta-url',
  vite:config       'vite:dynamic-import-vars',
  vite:config       'vite:import-glob',
  vite:config       'vite:client-inject',
  vite:config       'vite:import-analysis'
  vite:config     ],
  vite:config     rollupOptions: {}
  vite:config   },
  vite:config   appType: 'spa',
  vite:config   experimental: { importGlobRestoreExtension: false, hmrPartialAccept: false }
  vite:config } +151ms

Steps To Reproduce:

  1. Use Vite to build any JS project in Laravel
  2. Use laravel-vite-plugin
  3. Configure the publicDirectory by using path.resolve(...)
  4. Attempt run vite build inside any Unix environment.

Workaround:

Manually setting build.ourDir in vite.config.js resolves the issue as the path is no longer generated by this plugin.

(...)
    build: {
        outDir: resolve(__dirname, 'public/build'),
        emptyOutDir: false,
    },
(...)
timacdonald commented 2 years ago

The public directory should not be an absolute path, but instead a relative path for Laravel.

Also, this should only be set if you are changing the public directory in your Laravel project.

If your public directory was `"public_html", you would set this to:

publicDirectory: 'public_html',

Try setting it relative and let us know if you have any issues.

monyxie commented 1 year ago

@timacdonald Correct me if I'm wrong, but Laravel itself seems free of this limitation. I could set my Laravel's public path to anywhere (of course I would also need to change the hard-coded paths in index.php) and it would work without problem. So it's a bit strange I can't do this in the vite plugin. Could you give us the rationale behind this restriction?

timacdonald commented 1 year ago

@monyxie this issue seems related to relative vs absolute paths rather than any kind of restriction? What restriction are you referring to?

monyxie commented 1 year ago

@timacdonald I was referring to the restriction that it's not possible to use a directory outside of project root as the public directory, for the fact that the output files still ends up in a subdirectory in the project root when publicDirectory is set to an absolute path. However, I've now come to realize that it is not the case and I can do it with ...

I'm terribly sorry for not thinking this through before commenting.