Git Product home page Git Product logo

Comments (4)

ben-rogerson avatar ben-rogerson commented on July 20, 2024 1

Thanks for the tips!

I've updated the webpack config with the publicPath as you've suggested: a4298e0
And I've added useAbsoluteUrl to the gist example.

Thanks again

from agency-webpack-mix-config.

gopeter avatar gopeter commented on July 20, 2024 1

Got it:

mix.options({
  hmrOptions: {
    port: config.webpackDevServerPort,
  },
})

should be

mix.options({
  hmrOptions: {
    host: 'localhost',
    port: config.devServerPort,
  },
})

from agency-webpack-mix-config.

gopeter avatar gopeter commented on July 20, 2024

Hi @saltymouse and @ben-rogerson

I'm currently working on a setup which includes Craft 3, laravel-mix and Twigpack. I've used this laravel-mix config, which works well so far, but HMR does not work. The hot file, which gets created in the dist folder, contains just http://undefined:undefined/. It would be great if someone could have a look 😄

That's my config:

config/twigpack.php:

<?php
return [
    // Global settings
    '*' => [
        // If `devMode` is on, use webpack-dev-server to all for HMR (hot module reloading)
        'useDevServer' => true,
        // Don't force fully qualified absolute URLs
        'useAbsoluteUrl' => false,
        // The JavaScript entry from the manifest.json to inject on Twig error pages
        'errorEntry' => '',
        // Manifest file names
        'manifest' => [
            'legacy' => 'mix-manifest.json',
            'modern' => 'mix-manifest.json',
        ],
        // Public server config
        'server' => [
            'manifestPath' => '@webroot/',
            'publicPath' => '/dist/',
        ],
        // webpack-dev-server config
        'devServer' => [
            'manifestPath' => 'http://localhost:8080/',
            'publicPath' => 'http://localhost:8080/',
        ],
        // Local files config
        'localFiles' => [
            'basePath' => '@webroot/',
            'criticalPrefix' => 'dist/',
            'criticalSuffix' => '-critical.css',
        ],
    ],
    // Live (production) environment
    'live' => [
    ],
    // Staging (pre-production) environment
    'staging' => [
    ],
    // Local (development) environment
    'local' => [
        // If `devMode` is on, use webpack-dev-server to all for HMR (hot module reloading)
        'useDevServer' => true,
    ],
];

webpack.mix.js:

/**
 * ===========================
 * Agency Webpack-Mix Config
 * A capable website/webapp config built for the modern web agency.
 * https://github.com/ben-rogerson/agency-webpack-mix-config
 * ===========================
 *
 * Contents
 *
 * 🎚️ Settings
 * 🏠 Templates
 * 🎭 Hashing
 * 🎨 Styles
 * 🎨 Styles: CriticalCSS
 * 🎨 Styles: PurgeCSS
 * 🎨 Styles: PostCSS
 * 🎨 Styles: Polyfills
 * 🎨 Styles: Other
 * 📑 Scripts
 * 📑 Scripts: Polyfills
 * 📑 Scripts: Vendor
 * 📑 Scripts: Linting
 * 🏞 Images
 * 🎆 Icons
 * 🗂️ Static
 * 🚧 Webpack-dev-server
 */

// 🎚️ Base config
const config = {
  // Dev domain to proxy
  devProxyDomain: process.env.DEFAULT_SITE_URL || 'http://my-project.test',
  // Paths to observe for changes then trigger a full page reload
  devWatchPaths: ['templates'],
  // Port to use with webpack-dev-server
  devServerPort: 8080,
  // Folders where purgeCss can look for used selectors
  purgeCssGrabFolders: ['src'],
  // Build a static site from the src/template files
  buildStaticSite: false,
  // Urls for CriticalCss to look for "above the fold" Css
  criticalCssUrls: [
    // { urlPath: "/", label: "index" },
    // { urlPath: "/about", label: "about" },
  ],
  // Folder served to users
  publicFolder: 'web',
  // Foldername for built src assets (publicFolder base)
  publicBuildFolder: 'dist',
}

// 🎚️ Imports
require('laravel-mix-react-typescript-extension')
const mix = require('laravel-mix')
const path = require('path')
const globby = require('globby')
const tailwindcss = require('tailwindcss')

// 🎚️ Source folders
const source = {
  icons: path.resolve('src/icons'),
  images: path.resolve('src/images'),
  scripts: path.resolve('src/scripts'),
  styles: path.resolve('src/styles'),
  static: path.resolve('src/static'),
  templates: path.resolve('templates'),
}

// 🎚️ Misc
mix.setPublicPath(config.publicFolder)
mix.disableNotifications()
mix.webpackConfig({ resolve: { alias: source } })
!mix.inProduction() && mix.sourceMaps()

/**
 * 🎭 Hashing (for non-static sites)
 * Mix has querystring hashing by default, eg: main.css?id=abcd1234
 * This script converts it to filename hashing, eg: main.abcd1234.css
 * https://github.com/JeffreyWay/laravel-mix/issues/1022#issuecomment-379168021
 */
if (mix.inProduction() && !config.buildStaticSite) {
  // Allow versioning in production
  mix.version()
  // Get the manifest filepath for filehash conversion
  const manifestPath = path.join(config.publicFolder, 'mix-manifest.json')
  // Run after mix finishes
  mix.then(() => {
    const convertToFileHash = require('laravel-mix-make-file-hash')
    convertToFileHash({
      publicPath: config.publicFolder,
      manifestFilePath: manifestPath,
    })
  })
}

/**
 * 🎨 Styles: Main
 * Uses dart-sass which has a replica API to Node-Sass
 * https://laravel-mix.com/docs/4.0/css-preprocessors
 * https://github.com/sass/node-sass#options
 */
// Get a list of style files within the base styles folder
const styleFiles = globby.sync(`${source.styles}/*.{scss,sass}`)
// Data to send to style files
const styleData = `$isDev: ${!mix.inProduction()};`
// Create an asset for every style file
styleFiles.forEach((styleFile) => {
  mix.sass(styleFile, path.join(config.publicFolder, config.publicBuildFolder), { prependData: styleData }).options({
    processCssUrls: false,
    postCss: [tailwindcss('./tailwind.config.js')],
  })
})

/**
 * 🎨 Styles: CriticalCSS
 * https://github.com/addyosmani/critical#options
 */
const criticalDomain = config.devProxyDomain
if (criticalDomain && config.criticalCssUrls && config.criticalCssUrls.length) {
  require('laravel-mix-critical')
  const url = require('url')
  mix.critical({
    enabled: mix.inProduction(),
    urls: config.criticalCssUrls.map((page) => ({
      src: url.resolve(criticalDomain, page.urlPath),
      dest: path.join(config.publicFolder, config.publicBuildFolder, `${page.label}-critical.css`),
    })),
    options: {
      width: 1200,
      height: 1200,
    },
  })
}

/**
 * 🎨 Styles: PurgeCSS
 * https://github.com/spatie/laravel-mix-purgecss#usage
 */
if (config.purgeCssGrabFolders.length) {
  require('laravel-mix-purgecss')
  mix.purgeCss({
    enabled: mix.inProduction(),
    folders: config.purgeCssGrabFolders, // Folders scanned for selectors
    whitelist: ['html', 'body', 'active', 'wf-active', 'wf-inactive'],
    whitelistPatterns: [],
    extensions: ['php', 'twig', 'html', 'js', 'mjs', 'ts', 'tsx'],
  })
}

/**
 * 🎨 Styles: PostCSS
 * Extend Css with plugins
 * https://laravel-mix.com/docs/4.0/css-preprocessors#postcss-plugins
 */
const postCssPlugins = [
  // https://tailwindcss.com/docs/installation/#laravel-mix
  tailwindcss,

  /**
   * 🎨 Styles: Polyfills
   * Postcss preset env lets you use pre-implemented css features
   * See https://cssdb.org/ for supported features
   * https://github.com/csstools/postcss-preset-env#readme
   */
  require('postcss-preset-env')({ stage: 2 }),
]
mix.options({ postCss: postCssPlugins })

/**
 * 🎨 Styles: Other
 * https://laravel-mix.com/docs/4.0/options
 */
mix.options({
  // Disable processing css urls for speed
  // https://laravel-mix.com/docs/4.0/css-preprocessors#css-url-rewriting
  processCssUrls: false,
})

/**
 * 📑 Scripts: Main
 * Script files are transpiled to vanilla JavaScript
 * https://laravel-mix.com/docs/4.0/mixjs
 */
const scriptFiles = globby.sync(`${source.scripts}/*.{js,mjs,ts,tsx}`)
scriptFiles.forEach((scriptFile) => {
  mix.reactTypeScript(scriptFile, config.publicBuildFolder)
})

/**
 * 📑 Scripts: Polyfills
 * Automatically add polyfills for target browsers with core-js@3
 * See "browserslist" in package.json for your targets
 * https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md
 * https://github.com/scottcharlesworth/laravel-mix-polyfill#options
 */
require('laravel-mix-polyfill')
mix.polyfill({
  enabled: mix.inProduction(),
  useBuiltIns: 'usage', // Only add a polyfill when a feature is used
  targets: false, // "false" makes the config use browserslist targets in package.json
  corejs: 3,
  debug: false, // "true" to check which polyfills are being used
})

/**
 * 📑 Scripts: Vendor
 * Separate the JavaScript code imported from node_modules
 * https://laravel-mix.com/docs/4.0/extract
 * Without mix.extract you'll see an annoying js error after
 * launching the dev server - this should be fixed in webpack 5
 */
mix.extract([], path.join(config.publicBuildFolder, 'vendor')) // Empty params = separate all node_modules
// mix.extract(['jquery']) // Specify packages to add to the vendor file

/**
 * 📑 Scripts: Linting
 */
if (!mix.inProduction()) {
  require('laravel-mix-eslint')
  mix.eslint()
}

/**
 * 🏞 Images
 * Images are optimized and copied to the build directory
 * https://github.com/CupOfTea696/laravel-mix-imagemin
 * https://github.com/Klathmon/imagemin-webpack-plugin#api
 *
 * Important: laravel-mix-imagemin is incompatible with
 * copy-webpack-plugin > 5.1.1, so keep that dependency at that version.
 * See: https://github.com/CupOfTea696/laravel-mix-imagemin/issues/9
 */
require('laravel-mix-imagemin')
mix.imagemin(
  {
    from: path.join(source.images, '**/*'),
    to: config.publicBuildFolder,
    context: 'src/images',
  },
  {},
  {
    gifsicle: { interlaced: true },
    mozjpeg: { progressive: true, arithmetic: false },
    optipng: { optimizationLevel: 3 }, // Lower number = speedier/reduced compression
    svgo: {
      plugins: [
        { convertPathData: false },
        { convertColors: { currentColor: false } },
        { removeDimensions: true },
        { removeViewBox: false },
        { cleanupIDs: false },
      ],
    },
  },
)

/**
 * 🎆 Icons
 * Individual SVG icons are optimised then combined into a single cacheable SVG
 * https://github.com/kisenka/svg-sprite-loader#configuration
 */
require('laravel-mix-svg-sprite')
mix.svgSprite(source.icons, path.join(config.publicBuildFolder, 'sprite.svg'), {
  symbolId: (filePath) => `icon-${path.parse(filePath).name}`,
  extract: true,
})

// Icon options
mix.options({
  imgLoaderOptions: {
    svgo: {
      plugins: [{ convertColors: { currentColor: true } }, { removeDimensions: false }, { removeViewBox: false }],
    },
  },
})

/**
 * 🗂️ Static
 * Additional folders with no transform requirements are copied to your build folders
 */
mix.copyDirectory(source.static, path.join(config.publicFolder, config.publicBuildFolder))

/**
 * 🚧 Webpack-dev-server
 * https://webpack.js.org/configuration/dev-server/
 */
mix.webpackConfig({
  devServer: {
    clientLogLevel: 'none', // Hide console feedback so eslint can take over
    open: true,
    overlay: true,
    port: config.devServerPort,
    public: `localhost:${config.devServerPort}`,
    host: '0.0.0.0', // Allows access from network
    https: false,
    contentBase: config.devWatchPaths.length ? config.devWatchPaths : undefined,
    watchContentBase: config.devWatchPaths.length > 0,
    watchOptions: {
      aggregateTimeout: 200,
      poll: 200, // Lower for faster reloads (more cpu intensive)
      ignored: ['storage', 'node_modules', 'vendor'],
    },
    disableHostCheck: true, // Fixes "Invalid Host header error" on assets
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    proxy: {
      '**': {
        target: config.devProxyDomain,
        changeOrigin: true,
        secure: false,
      },
    },
    publicPath: '/',
  },
})
mix.options({
  hmrOptions: {
    port: config.webpackDevServerPort,
  },
})

templates/_layout.twig:

{# @var craft \craft\web\twig\variables\CraftVariable #}
{# @var entry \craft\elements\Entry #}
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> 
    <meta content="ie=edge" http-equiv="X-UA-Compatible">

    {{ craft.twigpack.includeCriticalCssTags() }}
    {{ craft.twigpack.includeCssModule("/dist/app.css") }}
  </head>

  <body>
    {% include '_partials/_header.twig' %}

    <main>
        {% block content %}
        {% endblock %}
    </main>

    {% include '_partials/_footer.twig' %}

    <script type="module" src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js"></script>
    <script nomodule src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine-ie11.min.js" defer></script>

    {{ craft.twigpack.includeJsModule("/dist/manifest.js") }}
    {{ craft.twigpack.includeJsModule("/dist/vendor.js") }}
    {{ craft.twigpack.includeJsModule("/dist/main.js") }}
  </body>
</html>

from agency-webpack-mix-config.

ben-rogerson avatar ben-rogerson commented on July 20, 2024

I've added your fix, thanks for looking into it

from agency-webpack-mix-config.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.