vuetifyjs-mix-extension icon indicating copy to clipboard operation
vuetifyjs-mix-extension copied to clipboard

extract option doesn't work

Open lukkur opened this issue 4 years ago • 18 comments

Hi,

I am getting this error:

TypeError: Cannot read property 'publicPath' of undefined
    at Vuetify.addExtract (H:\Projects\Homestead\test1\node_modules\vuetifyjs-mix-extension\index.js:106:43)
    at Vuetify.webpackConfig (H:\Projects\Homestead\test1\node_modules\vuetifyjs-mix-extension\index.js:83:38)
    at H:\Projects\Homestead\test1\node_modules\laravel-mix\src\components\ComponentRegistrar.js:106:54
    at H:\Projects\Homestead\test1\node_modules\laravel-mix\src\Dispatcher.js:37:62
    at Array.map (<anonymous>)
    at Dispatcher.fire (H:\Projects\Homestead\test1\node_modules\laravel-mix\src\Dispatcher.js:37:47)
    at Mix.dispatch (H:\Projects\Homestead\test1\node_modules\laravel-mix\src\Mix.js:224:32)
    at WebpackConfig.build (H:\Projects\Homestead\test1\node_modules\laravel-mix\src\builder\WebpackConfig.js:36:19)
    at async H:\Projects\Homestead\test1\node_modules\webpack-cli\lib\webpack-cli.js:908:37
    at async Promise.all (index 0)
    at async evaluateConfig (H:\Projects\Homestead\test1\node_modules\webpack-cli\lib\webpack-cli.js:899:35)
    at async Promise.all (index 0)
    at async WebpackCLI.resolveConfig (H:\Projects\Homestead\test1\node_modules\webpack-cli\lib\webpack-cli.js:930:38)
    at async WebpackCLI.createCompiler (H:\Projects\Homestead\test1\node_modules\webpack-cli\lib\webpack-cli.js:1329:22)
    at async WebpackCLI.bundleCommand (H:\Projects\Homestead\test1\node_modules\webpack-cli\lib\webpack-cli.js:1433:20)
    at async Command.<anonymous> (H:\Projects\Homestead\test1\node_modules\webpack-cli\lib\webpack-cli.js:384:25)

when I use extract css option.

Here is my webpack config:

const mix = require('laravel-mix');
const path = require('path');
// const cssImport = require('postcss-import');
// const cssNesting = require('postcss-nesting');
require('vuetifyjs-mix-extension');

mix.options({
    terser: {
        terserOptions: {
            compress: {
                drop_console: true
            }
        }
    }
});

mix
    .js('resources/js/app.js', 'public/js')
    .vuetify('vuetify-loader', {
        extract: 'css/app-v.css'
    })
    .vue({
        extractStyles: true
    })
    .webpackConfig({
        resolve: {
            alias: {
                '@': path.resolve('resources/js'),
            },
        },
    })
    .copy('resources/assets/img', 'public/img')
    .disableNotifications();

lukkur avatar Jan 26 '21 18:01 lukkur

This is really strange, are you working with laravel ? or a standalone project, and can you share your package.json?

Nothing-Works avatar Jan 26 '21 21:01 Nothing-Works

I'm having similar issues working with Laravel Mix.

With the following option in webpack.mix.js:

.vuetify('vuetify-loader', {
       extract: 'css/capture.css'
})

I get the following error:

[webpack-cli] ValidationError: Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'options'. These properties are valid:
   object { filename?, chunkFilename?, ignoreOrder?, insert?, attributes?, linkType? }
    at validate (/var/www/html/node_modules/schema-utils/dist/validate.js:104:11)
    at new MiniCssExtractPlugin (/var/www/html/node_modules/mini-css-extract-plugin/dist/index.js:47:31)
    at Vuetify.addExtract (/var/www/html/node_modules/vuetifyjs-mix-extension/index.js:100:13)
    at Vuetify.webpackConfig (/var/www/html/node_modules/vuetifyjs-mix-extension/index.js:83:38)
    at /var/www/html/node_modules/laravel-mix/src/components/ComponentRegistrar.js:106:54
    at /var/www/html/node_modules/laravel-mix/src/Dispatcher.js:37:62
    at Array.map (<anonymous>)
    at Dispatcher.fire (/var/www/html/node_modules/laravel-mix/src/Dispatcher.js:37:47)
    at Mix.dispatch (/var/www/html/node_modules/laravel-mix/src/Mix.js:224:32)
    at WebpackConfig.build (/var/www/html/node_modules/laravel-mix/src/builder/WebpackConfig.js:36:19)
    at async /var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:908:37
    at async Promise.all (index 0)
    at async evaluateConfig (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:899:35)
    at async Promise.all (index 0)
    at async WebpackCLI.resolveConfig (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:930:38)
    at async WebpackCLI.createCompiler (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:1329:22) {
  errors: [
    {
      keyword: 'additionalProperties',
      dataPath: '',
      schemaPath: '#/additionalProperties',
      params: [Object],
      message: 'should NOT have additional properties',
      schema: false,
      parentSchema: [Object],
      data: [Object]
    }
  ],
  schema: {
    type: 'object',
    additionalProperties: false,
    properties: {
      filename: [Object],
      chunkFilename: [Object],
      ignoreOrder: [Object],
      insert: [Object],
      attributes: [Object],
      linkType: [Object]
    }
  },
  headerName: 'Mini CSS Extract Plugin',
  baseDataPath: 'options',
  postFormatter: null
}

kaysersoze avatar Jan 26 '21 22:01 kaysersoze

@kaysersoze That's something I am fixing atm.

Nothing-Works avatar Jan 27 '21 01:01 Nothing-Works

No worries. Thanks for your help and your work!

kaysersoze avatar Jan 27 '21 01:01 kaysersoze

@Nothing-Works I am working with Laravel, it happens even if my app.js is empty, here is my packages.json:

{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "mix",
        "watch": "mix watch",
        "watch-poll": "mix watch -- --watch-options-poll=1000",
        "hot": "mix watch --hot",
        "prod": "npm run production",
        "production": "mix --production"
    },
    "devDependencies": {
        "@inertiajs/inertia": "^0.8.2",
        "@inertiajs/inertia-vue": "^0.5.4",
        "@mdi/js": "^5.8.55",
        "axios": "^0.21.1",
        "deepmerge": "^4.2.2",
        "postcss": "^8.2.4",
        "laravel-mix": "^6.0.11",
        "sass": "^1.30.0",
        "sass-loader": "^9.0.3",
        "vue": "^2.6.12",
        "vue-template-compiler": "^2.6.12",
        "vuetify-loader": "1.7.1",
        "vuetifyjs-mix-extension": "^0.0.17"
    },
    "dependencies": {
        "@inertiajs/progress": "^0.2.3",
        "@vue/composition-api": "^1.0.0-beta.25",
        "lodash": "^4.17.20",
        "vee-validate": "^3.4.5",
        "vue-loader": "^15.9.5",
        "vue-meta": "^2.4.0",
        "vuetify": "^2.4.0",
        "vuex": "^3.6.0"
    }
}

lukkur avatar Jan 27 '21 18:01 lukkur

I have updated vuetifyjs-mix-extension to v0.0.19 and the error "Cannot read property 'publicPath' of undefined" is gone but there is new one now:

Error: Conflict: Multiple chunks emit assets to the same filename css/app-v.css (chunks /js/app and css\vue-styles)
    at H:\Projects\Homestead\test1\node_modules\webpack\lib\Compilation.js:3497:12
    at H:\Projects\Homestead\test1\node_modules\webpack\lib\Cache.js:93:5
    at Hook.eval [as callAsync] (eval at create (H:\Projects\Homestead\test1\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:16:1)
    at Cache.get (H:\Projects\Homestead\test1\node_modules\webpack\lib\Cache.js:75:18)
    at ItemCacheFacade.get (H:\Projects\Homestead\test1\node_modules\webpack\lib\CacheFacade.js:117:15)
    at H:\Projects\Homestead\test1\node_modules\webpack\lib\Compilation.js:3443:22
    at arrayEach (H:\Projects\Homestead\test1\node_modules\neo-async\async.js:2405:9)
    at Object.each (H:\Projects\Homestead\test1\node_modules\neo-async\async.js:2846:9)
    at H:\Projects\Homestead\test1\node_modules\webpack\lib\Compilation.js:3432:14
    at symbolEach (H:\Projects\Homestead\test1\node_modules\neo-async\async.js:2444:9)
    at Object.each (H:\Projects\Homestead\test1\node_modules\neo-async\async.js:2849:16)
    at Compilation.createChunkAssets (H:\Projects\Homestead\test1\node_modules\webpack\lib\Compilation.js:3410:12)
    at H:\Projects\Homestead\test1\node_modules\webpack\lib\Compilation.js:2241:14
    at H:\Projects\Homestead\test1\node_modules\webpack\lib\Compilation.js:2377:5
    at H:\Projects\Homestead\test1\node_modules\neo-async\async.js:2818:7
    at done (H:\Projects\Homestead\test1\node_modules\neo-async\async.js:3522:9)

lukkur avatar Jan 27 '21 18:01 lukkur

@lukkur @kaysersoze Hi sorry about the bugs, v0.0.20 should resolve all the errors, however it generates an extra file within your js folder, which I think is a laravel-mix bug and I logged here. If anyone can help that would be great.

Nothing-Works avatar Jan 27 '21 21:01 Nothing-Works

@Nothing-Works I got this error. I use v0.0.20

[webpack-cli] Error: Conflict: Multiple chunks emit assets to the same filename css/vuetify-components.css (chunks /js/app and css/app)
    at C:\laragon\www\vuetify\node_modules\webpack\lib\Compilation.js:3497:12
    at C:\laragon\www\vuetify\node_modules\webpack\lib\Cache.js:93:5
    at Hook.eval [as callAsync] (eval at create (C:\laragon\www\vuetify\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:16:1)
    at Cache.get (C:\laragon\www\vuetify\node_modules\webpack\lib\Cache.js:75:18)
    at ItemCacheFacade.get (C:\laragon\www\vuetify\node_modules\webpack\lib\CacheFacade.js:117:15)
    at C:\laragon\www\vuetify\node_modules\webpack\lib\Compilation.js:3443:22
    at arrayEach (C:\laragon\www\vuetify\node_modules\neo-async\async.js:2405:9)
    at Object.each (C:\laragon\www\vuetify\node_modules\neo-async\async.js:2846:9)
    at C:\laragon\www\vuetify\node_modules\webpack\lib\Compilation.js:3432:14
    at symbolEach (C:\laragon\www\vuetify\node_modules\neo-async\async.js:2444:9)
    at Object.each (C:\laragon\www\vuetify\node_modules\neo-async\async.js:2849:16)
    at Compilation.createChunkAssets (C:\laragon\www\vuetify\node_modules\webpack\lib\Compilation.js:3410:12)
    at C:\laragon\www\vuetify\node_modules\webpack\lib\Compilation.js:2241:14
    at C:\laragon\www\vuetify\node_modules\webpack\lib\Compilation.js:2377:5
    at C:\laragon\www\vuetify\node_modules\neo-async\async.js:2818:7
    at done (C:\laragon\www\vuetify\node_modules\neo-async\async.js:3522:9)

jump-style avatar Jan 28 '21 04:01 jump-style

@Nothing-Works I'm getting the same error as @jump-style above when using v0.0.20. Working around it for now (I'm just importing all of Vuetify scss via my own scss file), but would love to be able to do the extraction whenever that becomes possible.

I'm currently trying to get an initial version of a project using this out the door, but once I do that, I can help look into why that laravel-mix bug is happening and do some more in-depth debugging.

Thanks again for all of your work. It is appreciated.

kaysersoze avatar Jan 28 '21 18:01 kaysersoze

@kaysersoze @jump-style @lukkur I am sorry I cannot really reproduce this seems ok when I tested it. Can any of you provide a repo with the minimum code that has this problem so that is easy for me to debug, please?

Nothing-Works avatar Jan 31 '21 20:01 Nothing-Works

@Nothing-Works please try this https://github.com/lukkur/example I commented some code in app.js, I hope it helps :)

lukkur avatar Jan 31 '21 20:01 lukkur

Hi, I am getting the same error

Error: Conflict: Multiple chunks emit assets to the same filename public/css/vuetify/vuetify-components.css (chunks css/admin and css/app)

I see @lukkur already provided a test repo. Since after update the not-extracted css files also don't work and this was a workaround, is there any way to fix it for now - or do I just manually import the css files?

InToSSH avatar Feb 15 '21 16:02 InToSSH

Still getting [webpack-cli] Error: Conflict: Multiple chunks emit assets to the same filename public/css/vuetify.css (chunks css/app and js/vue) anybody got a workaround?

Tofandel avatar Jun 29 '21 20:06 Tofandel

After much struggle, it seems the issue is caused when you add .sass('resources/sass/app.scss', 'public/css')

The problem being that the extract name is not respected

 .vuetify('vuetify-loader', {
    extract: 'css/vuetify.css',
  })

Will output to /public/css/app.css instead of vuetify.css

Tofandel avatar Jun 29 '21 20:06 Tofandel

So the problem is caused because there is 2 MiniCssExtract plugin, the second addition is not needed, there should be a check, if it's already present in the plugins list then don't add it

Then to actually get the file to be output in the correct place it's missing a chunk definition

const mix = require('laravel-mix')
const Chunks = require('laravel-mix/src/Chunks').Chunks;
const File = require('laravel-mix/src/File');
const getPath = require('./src/getPath')
const resolveOptions = require('./src/resolveOptions')
const path = require('path')

class Vuetify {
    constructor() {
        this.vuetifyPath = getPath('node_modules/vuetify')
    }

    withVuetifyLoader() {
        return this.vuetifyLoader === 'vuetify-loader'
    }

    withExtract() {
        return !!this.extract
    }

    withPostcss() {
        return !!this.postcss
    }

    register(loader, ...options) {
        this.vuetifyLoader = loader
        this.resolve(options)
    }

    resolve(options) {
        const resolved = resolveOptions(options)
        this.vuetifyLoaderOptions = resolved.vuetifyLoaderOptions
        this.sassArray = resolved.sassArray
        this.extract = resolved.extract
        this.postcss = resolved.postcss
    }

    dependencies() {
        this.requiresReload = true

        const deps = ['vuetify', 'sass', 'sass-loader', 'deepmerge']

        if (this.withVuetifyLoader()) deps.push('vuetify-loader@next')

        if (this.withExtract()) deps.push('mini-css-extract-plugin')

        if (this.withPostcss()) deps.push('postcss-loader')

        return deps
    }

    generateRules() {
        return this.sassArray.map((t) => ({
            test: t.sass,
            include: [this.vuetifyPath],
            use: [
                this.withExtract()
                    ? require('mini-css-extract-plugin').loader
                    : 'vue-style-loader',
                'css-loader',
                ...this.addPostcssIfNeeded(),
                {
                    loader: 'sass-loader',
                    options: {
                        additionalData: t.data,
                        implementation: require('sass'),
                        sassOptions: {
                            indentedSyntax: true
                        }
                    }
                }
            ]
        }))
    }

    webpackRules() {
        return this.generateRules()
    }

    webpackConfig(config) {
        this.excludeVuetifyPath(config)

        if (this.withVuetifyLoader()) this.addVuetifyLoader(config)

        if (this.withExtract()) this.addExtract(config)
    }

    addPostcssIfNeeded() {
        return this.withPostcss() ? ['postcss-loader'] : []
    }

    addVuetifyLoader(config) {
        const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')

        config.plugins.push(new VuetifyLoaderPlugin(this.vuetifyLoaderOptions))
    }

    updateChunks(config) {
        const chunks = Chunks.instance();
        const re = /(?<!node_modules)[\\/]node_modules[\\/](vuetify[\\/])/i;
        const groups = config.optimization.splitChunks.cacheGroups;
        Object.keys(groups).forEach((k) => {
            if (typeof groups[k] === 'object') {
                if (groups[k].type === 'css/mini-extract' && groups[k].chunks === 'all') {
                    const orig = groups[k].test;
                    groups[k].test = (module, context) => {
                        const name = module.nameForCondition();

                        return name && !re.test(name) && orig(module, context);
                    };
                }
            }
        })

        const output = new File(this.extract);

        chunks.add(
            'styles-vuetify',
            output.normalizedOutputPath(),
            [re, module => module.type === 'css/mini-extract'],
            {
                chunks: 'all',
                enforce: true,
                type: 'css/mini-extract'
            }
        );
    }

    addExtract(config) {
        const MiniCssExtractPlugin = require('mini-css-extract-plugin')
        const plugin = config.plugins.find((p) => p instanceof MiniCssExtractPlugin);
        if (!plugin) {
            config.plugins.push(
                new MiniCssExtractPlugin({
                    filename: '[name].css',
                    chunkFilename: '[name].css',
                    ignoreOrder: true,
                })
            )
        } else {
            plugin.options.ignoreOrder = true;
        }

        this.updateChunks(config);
    }

    excludeVuetifyPath(config) {
        for (const i of this.sassArray)
            config.module.rules
                .find((r) => String(r.test) === String(i.sass))
                .exclude.push(this.vuetifyPath)
    }
}

mix.extend('vuetify', new Vuetify())

Just noting that if you also do .extract('vuetify') you'll get the css in js/vendor.css and not the path specified in the extract option

Tofandel avatar Jun 29 '21 22:06 Tofandel

@tofandel

In my case, it doesn't go into the vendor.css - but it's included inside the .js file... I am doing a mix-extract as follows:

.extract(['vuetify'], 'js/vendor/vuetify.js')

ThaDaVos avatar Dec 03 '21 14:12 ThaDaVos

So the problem is caused because there is 2 MiniCssExtract plugin, the second addition is not needed, there should be a check, if it's already present in the plugins list then don't add it

Then to actually get the file to be output in the correct place it's missing a chunk definition

const mix = require('laravel-mix')
const Chunks = require('laravel-mix/src/Chunks').Chunks;
const File = require('laravel-mix/src/File');
const getPath = require('./src/getPath')
const resolveOptions = require('./src/resolveOptions')
const path = require('path')

class Vuetify {
    constructor() {
        this.vuetifyPath = getPath('node_modules/vuetify')
    }

    withVuetifyLoader() {
        return this.vuetifyLoader === 'vuetify-loader'
    }

    withExtract() {
        return !!this.extract
    }

    withPostcss() {
        return !!this.postcss
    }

    register(loader, ...options) {
        this.vuetifyLoader = loader
        this.resolve(options)
    }

    resolve(options) {
        const resolved = resolveOptions(options)
        this.vuetifyLoaderOptions = resolved.vuetifyLoaderOptions
        this.sassArray = resolved.sassArray
        this.extract = resolved.extract
        this.postcss = resolved.postcss
    }

    dependencies() {
        this.requiresReload = true

        const deps = ['vuetify', 'sass', 'sass-loader', 'deepmerge']

        if (this.withVuetifyLoader()) deps.push('vuetify-loader@next')

        if (this.withExtract()) deps.push('mini-css-extract-plugin')

        if (this.withPostcss()) deps.push('postcss-loader')

        return deps
    }

    generateRules() {
        return this.sassArray.map((t) => ({
            test: t.sass,
            include: [this.vuetifyPath],
            use: [
                this.withExtract()
                    ? require('mini-css-extract-plugin').loader
                    : 'vue-style-loader',
                'css-loader',
                ...this.addPostcssIfNeeded(),
                {
                    loader: 'sass-loader',
                    options: {
                        additionalData: t.data,
                        implementation: require('sass'),
                        sassOptions: {
                            indentedSyntax: true
                        }
                    }
                }
            ]
        }))
    }

    webpackRules() {
        return this.generateRules()
    }

    webpackConfig(config) {
        this.excludeVuetifyPath(config)

        if (this.withVuetifyLoader()) this.addVuetifyLoader(config)

        if (this.withExtract()) this.addExtract(config)
    }

    addPostcssIfNeeded() {
        return this.withPostcss() ? ['postcss-loader'] : []
    }

    addVuetifyLoader(config) {
        const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')

        config.plugins.push(new VuetifyLoaderPlugin(this.vuetifyLoaderOptions))
    }

    updateChunks(config) {
        const chunks = Chunks.instance();
        const re = /(?<!node_modules)[\\/]node_modules[\\/](vuetify[\\/])/i;
        const groups = config.optimization.splitChunks.cacheGroups;
        Object.keys(groups).forEach((k) => {
            if (typeof groups[k] === 'object') {
                if (groups[k].type === 'css/mini-extract' && groups[k].chunks === 'all') {
                    const orig = groups[k].test;
                    groups[k].test = (module, context) => {
                        const name = module.nameForCondition();

                        return name && !re.test(name) && orig(module, context);
                    };
                }
            }
        })

        const output = new File(this.extract);

        chunks.add(
            'styles-vuetify',
            output.normalizedOutputPath(),
            [re, module => module.type === 'css/mini-extract'],
            {
                chunks: 'all',
                enforce: true,
                type: 'css/mini-extract'
            }
        );
    }

    addExtract(config) {
        const MiniCssExtractPlugin = require('mini-css-extract-plugin')
        const plugin = config.plugins.find((p) => p instanceof MiniCssExtractPlugin);
        if (!plugin) {
            config.plugins.push(
                new MiniCssExtractPlugin({
                    filename: '[name].css',
                    chunkFilename: '[name].css',
                    ignoreOrder: true,
                })
            )
        } else {
            plugin.options.ignoreOrder = true;
        }

        this.updateChunks(config);
    }

    excludeVuetifyPath(config) {
        for (const i of this.sassArray)
            config.module.rules
                .find((r) => String(r.test) === String(i.sass))
                .exclude.push(this.vuetifyPath)
    }
}

mix.extend('vuetify', new Vuetify())

Just noting that if you also do .extract('vuetify') you'll get the css in js/vendor.css and not the path specified in the extract option

I can confirm this works - sadly one cannot use .extract(['vuetify'], 'js/vendor/vuetify.js') as then there won't be any css exported

ThaDaVos avatar Dec 03 '21 14:12 ThaDaVos

I changed it to the following, extract is now just a simple boolean - and both, the CSS and JS are getting extracted correctly:

const mix = require('laravel-mix')
const Chunks = require('laravel-mix/src/Chunks').Chunks;
const File = require('laravel-mix/src/File');
const getPath = require('vuetifyjs-mix-extension/src/getPath')
const resolveOptions = require('vuetifyjs-mix-extension/src/resolveOptions')
const path = require('path')

class Vuetify {
    constructor() {
        this.vuetifyPath = getPath('node_modules/vuetify')
    }

    withVuetifyLoader() {
        return this.vuetifyLoader === 'vuetify-loader'
    }

    withExtract() {
        return !!this.extract
    }

    withPostcss() {
        return !!this.postcss
    }

    register(loader, ...options) {
        this.vuetifyLoader = loader
        this.resolve(options)
    }

    resolve(options) {
        const resolved = resolveOptions(options)
        this.vuetifyLoaderOptions = resolved.vuetifyLoaderOptions
        this.sassArray = resolved.sassArray
        this.extract = resolved.extract
        this.postcss = resolved.postcss
    }

    dependencies() {
        this.requiresReload = true

        const deps = ['vuetify', 'sass', 'sass-loader', 'deepmerge']

        if (this.withVuetifyLoader()) deps.push('vuetify-loader@next')

        if (this.withExtract()) deps.push('mini-css-extract-plugin')

        if (this.withPostcss()) deps.push('postcss-loader')

        return deps
    }

    generateRules() {
        return this.sassArray.map((t) => ({
            test: t.sass,
            include: [this.vuetifyPath],
            use: [
                this.withExtract()
                    ? require('mini-css-extract-plugin').loader
                    : 'vue-style-loader',
                'css-loader',
                ...this.addPostcssIfNeeded(),
                {
                    loader: 'sass-loader',
                    options: {
                        additionalData: t.data,
                        implementation: require('sass'),
                        sassOptions: {
                            indentedSyntax: true
                        }
                    }
                }
            ]
        }))
    }

    webpackRules() {
        return this.generateRules()
    }

    webpackConfig(config) {
        this.excludeVuetifyPath(config)

        if (this.withVuetifyLoader()) this.addVuetifyLoader(config)

        if (this.withExtract()) this.addExtract(config)
    }

    addPostcssIfNeeded() {
        return this.withPostcss() ? ['postcss-loader'] : []
    }

    addVuetifyLoader(config) {
        const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin')

        config.plugins.push(new VuetifyLoaderPlugin(this.vuetifyLoaderOptions))
    }

    updateChunks(config) {
        if (!this.withExtract()) {
            return;
        }

        const chunks = Chunks.instance();
        const re = /(?<!node_modules)[\\/]node_modules[\\/](vuetify[\\/])/i;
        const groups = config.optimization.splitChunks.cacheGroups;
        Object.keys(groups).forEach((k) => {
            if (typeof groups[k] === 'object') {
                if (groups[k].type === 'css/mini-extract' && groups[k].chunks === 'all') {
                    const orig = groups[k].test;
                    groups[k].test = (module, context) => {
                        const name = module.nameForCondition();

                        return name && !re.test(name) && orig(module, context);
                    };
                }
            }
        });

        chunks.add(
            'styles-vuetify',
            new File('css/vendors/vuetify.css').normalizedOutputPath(),
            [re, module => module.type === 'css/mini-extract'],
            {
                chunks: 'all',
                enforce: true,
                type: 'css/mini-extract'
            }
        );

        chunks.add(
            'script-vuetify',
            new File('js/vendors/vuetify.js').normalizedOutputPath(),
            [re, module => module.type === 'javascript/auto'],
            {
                chunks: 'all',
                enforce: true,
                type: 'javascript/auto'
            }
        );
    }

    addExtract(config) {
        const MiniCssExtractPlugin = require('mini-css-extract-plugin')
        const plugin = config.plugins.find((p) => p instanceof MiniCssExtractPlugin);
        if (!plugin) {
            config.plugins.push(
                new MiniCssExtractPlugin({
                    filename: '[name].css',
                    chunkFilename: '[name].css',
                    ignoreOrder: true,
                })
            )
        } else {
            plugin.options.ignoreOrder = true;
        }

        this.updateChunks(config);
    }

    excludeVuetifyPath(config) {
        for (const i of this.sassArray)
            config.module.rules
                .find((r) => String(r.test) === String(i.sass))
                .exclude.push(this.vuetifyPath)
    }
}

mix.extend('vuetify', new Vuetify())

ThaDaVos avatar Dec 03 '21 15:12 ThaDaVos