Webpack dev server watches Twig - symfony

I'm using Symfony 4 with Symfony Encore to handle assets, and some useful features, such as HMR.
Currently, I can handle Sass files, CSS files, JS, etc, and it works fine with HMR.
Now I would like to be able to make Weback dev server watch *.twig files for changes and trigger a live reload (as hot reload wouldn't be an option for server-side rendered templates).
I've seen things about --watchContentBase and contentBase options, but it doesn't do anything in my case:
WDS CLI :
./node_modules/.bin/encore dev-server --hot --disable-host-check --watchContentBase --contentBase ./templates/ --reload
webpack.config.js :
const Encore = require('#symfony/webpack-encore');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.cleanupOutputBeforeBuild()
.autoProvidejQuery()
.addPlugin(new MiniCssExtractPlugin('[name].css'))
.enableSourceMaps(!Encore.isProduction())
.addLoader({
test: /\.(sc|sa|c)ss$/,
use: ['css-hot-loader'].concat(
MiniCssExtractPlugin.loader,
{
loader: 'css-loader'
},
{
loader: 'postcss-loader'
},
// {
// loader: 'postcss-loader'
// },
{
loader: 'sass-loader'
}
),
},)
.addLoader({
test: /\.twig$/,
loader: 'raw-loader'
},)
.enableVersioning(Encore.isProduction())
.addEntry('autocall-main', './assets/js/index.js')
// .addStyleEntry('autocall-main', ['./assets/scss/index.scss'])
.splitEntryChunks()
.enableSingleRuntimeChunk()
;
const config = Encore.getWebpackConfig();
module.exports = config;
My project files / folders follows the classic Symfony 4 structure: https://github.com/symfony/demo
What do I miss there?

Today, the year 2020, i have two solutions:
Webpack config solution
As you had said: I've seen things about --watchContentBase and contentBase options..., this has nothing to do with encore. Its a default webpack configurations and you can learn more from webpack doc here
According to Advanced Webpack Config docs here you can extend webpack configs by calling var config = Encore.getWebpackConfig();
I have implemented as shown in the code below. For my case its working fine.
// webpack.config.js
var Encore = require('#symfony/webpack-encore');
var path = require('path');
// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
.setPublicPath('/build')
.addEntry('global', './assets/app.js')
// ... Your other encore code
// EXTEND/OVERRIDE THE WEBPACK CONFIG
const fullConfig = Encore.getWebpackConfig();
fullConfig.name = 'full';
// watch options poll is used to reload the site after specific set time
// polling is useful when running Encore inside a Virtual Machine
// more: https://webpack.js.org/configuration/watch/
fullConfig.watchOptions = {
poll: true,
ignored: /node_modules/
};
fullConfig.devServer = {
public: 'http://localhost:3000',
allowedHosts: ['0.0.0.0'],
// extend folder to watch in a symfony project
// use of content base
// customize the paths below as per your needs, for this simple
//example i will leave them as they are for now.
contentBase: [
path.join(__dirname, 'templates/'), // watch twig templates folder
path.join(__dirname, 'src/') // watch the src php folder
],
// enable watching them
watchContentBase: true,
compress: true,
open: true,
disableHostCheck: true,
progress: true,
watchOptions: {
watch: true,
poll: true
}
};
// export it
module.exports = fullConfig;
Another solution
If you need a simple implementation you can use: webpack-watch-files-plugin. I prefer this, by the time you are reading this answer it might be abandoned but there many others with same functionality. In Symfony docs here you can implement Custom Loaders & Plugins as below. Using the above mentioned plugin we can implent it as follow:
// webpack.config.js
const WatchExternalFilesPlugin = require('webpack-watch-files-plugin').default;
Encore
// ...your code
.addPlugin(new WatchExternalFilesPlugin({
files: [
'/templates', // watch files in templates folder
'/src', // watch files in src folder
'!../var', // don't watch files in var folder (exclude)
],
verbose: true
}))
//...your code
;
Cheers. Happy coding!

With Symfony 5.4 and Encore 1.0.0 you can configure devServer mannualy in webpack.config.js
...
.configureDevServerOptions((options) => {
options.liveReload = true;
options.hot = true;
options.watchFiles = [
'./templates/**/*',
'./src/**/*'
]
})
...

The loader needs to know also the location of the .twig files, which in Symfony 4 are in /templates directory. Considering the default structure, this should make it work for you:
...
.addLoader({
test: /\.twig$/,
loader: 'raw-loader',
include: [
path.resolve(__dirname, "templates")
],
},)
...

It seems that there is no way to do that (read more on this). As the questioner mentions you can do it with BrowserSync. My preferred Symfony setup is to have two terminals running:
Prerequisites
Install BrowserSync:
npm install -g browser-sync
First terminal
Start the Symfony server on https://127.0.0.1:8000/ and build the assets on https://localhost:8010/:
symfony server:start -d ; yarn encore dev-server --https --port 8010
Second terminal
Reload your browser's request for https://localhost:3000/ every time a Twig file gets changed:
browser-sync start --proxy "https://127.0.0.1:8000/" --files "templates"

If you need only browser-sync you can create bs-config.js:
module.exports = {
"files": [
"templates/**/*.twig",
"src/**/*.php"
],
"proxy": "https://localhost:8000",
};
and then run
browser-sync start --config bs-config.js
remember to accept the "dangerous site" at startup

Related

Why is eslint not working after migrating from CRA to Next.js?

I am currently working on migrating an app from CRA to Next.js. Eslint was working beautifully when using CRA (eslint works out-of-the-box).
I want to use the same CRA linting rules in the Next.js app so I installed eslint-config-react-app. I followed the instructions as provided on the NPM page: https://www.npmjs.com/package/eslint-config-react-app:
npm install --save-dev eslint-config-react-app #typescript-eslint/eslint-plugin#^4.0.0 #typescript-eslint/parser#^4.0.0 babel-eslint#^10.0.0 eslint#^7.5.0 eslint-plugin-flowtype#^5.2.0 eslint-plugin-import#^2.22.0 eslint-plugin-jsx-a11y#^6.3.1 eslint-plugin-react#^7.20.3 eslint-plugin-react-hooks#^4.0.8
create the .eslintrc.json file with the following content:
{ "extends": "react-app" }
However, the linting is not showing up neither in the development console nor in the browser console.
Thanks!
Ok so I found the solution.
I wanted the eslint output to show up in the console when saving edits to a file directly (just like with CRA). Initially, I did not realize that Next.js has no eslint plugin/loader specified in their webpack config so I extended theirs by adding my own. Now everything works as expected, just like it did when using CRA.
I actually used (although slightly modified) CRA's config for the eslint-webpack-plugin plugin. If anyone else wants to have an eslint setup in Next.js similar to CRA, add the following to your next.config.js file:
const path = require('path');
const fs = require('fs');
const ESLintPlugin = require('eslint-webpack-plugin')
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
webpack(config) {
config.plugins.push(new ESLintPlugin({
// Plugin options
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
eslintPath: require.resolve('eslint'),
context: resolveApp('src'),
cache: true,
cacheLocation: path.resolve(
resolveApp('node_modules'),
'.cache/.eslintcache'
),
// ESLint class options
cwd: resolveApp('.'),
resolvePluginsRelativeTo: __dirname,
baseConfig: {
extends: [require.resolve('eslint-config-react-app/base')],
rules: {},
},
}))
return config
}
}
Note, that when using the above you will need to install eslint-config-react-app and its dependencies (see: https://www.npmjs.com/package/eslint-config-react-app).
Finally, note that since Next.js only renders (compiles) a page when it is needed in development, eslint will only run on a page when it is displayed in the browser as pointed out here: https://github.com/vercel/next.js/issues/9904. This makes adding the following script to package.json as suggested by Roman very useful if you want to do a "full-scan" of your project.
"lint": "eslint ."
You can add a command like this in the scripts section of your package.json:
"lint": "eslint ."
And then you can check your eslint results by running:
npm run lint

Workbox wbepack encore dont work (in symfony 4 application)

I apologize if the question already was to find, and that it has an answer, but in research on intenert, I did not find anything which worked.
Here is my problem, I started with workbox, and I tried to integrate it into my Symfony 4 application which still uses webpack, but it does not work properly. So I tried to generate the service worker with the cli, but I still can't get it to work.
My webpack encore configuration:
const Encore = require('#symfony/webpack-encore');
const CompressionPlugin = require('compression-webpack-plugin');
const {GenerateSW, InjectManifest} = require('workbox-webpack-plugin');
const path = require('path')
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.addEntry('app', './assets/js/app.js')
.addEntry('cart', './assets/js/cart.js')
.addEntry('site', './assets/js/site.js')
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
.enableVersioning(Encore.isProduction())
.enableSassLoader()
.enableVueLoader(() => {}, { runtimeCompilerBuild: false })
.addLoader({
test: /\.exec\.js$/,
loader: 'script-loader'
})
.autoProvidejQuery()
.autoProvideVariables({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
})
.enableSingleRuntimeChunk()
.addPlugin(new CompressionPlugin())
.addPlugin(new GenerateSW({swDest: 'sw.js'}))
.addPlugin(new InjectManifest({
swSrc: 'sw.js',
exclude: [
/\.map$/,
/manifest$/,
/\.htaccess$/,
/service-worker\.js$/,
/sw\.js$/,
],
}));
module.exports = Encore.getWebpackConfig();
for webpack generate error i have found this: https://github.com/GoogleChrome/workbox/issues/1513 but
After cli generateSW i have this error:
error
Can't find self.__WB_MANIFEST in your SW source.
for wb manifest i have found this:
Webpack workbox Can't find self.__WB_MANIFEST in your SW source but doesn't work for me
I think the problem comes from the url generated by webpack and i have found this:
Can't install service worker of workbox-webpack-plugin in root url for React app, it's installing at localhost:8080/dist
But I don't see how it helps me in my configuration.
I have tried with path.resolve (because on forum i have seen work), but don't work for me, i get this error:
Can't find self.__WB_MANIFEST in your SW source.
Thank for your help !
import { precacheAndRoute } from 'workbox-precaching';
// Your other import statements go here.
precacheAndRoute(self.__WB_MANIFEST);
On your sw.js

Cannot get / with encore dev-server in Symfony

I would like to point out that I am on a classic Symfony application with the default configuration for Webpack. And that the watch command works very well.
Hello,
When I try to start the Webpack Encore development server, the compilation is done without any problem but when I access the page, I get the error "Cannot GET /". I have no errors in the console
(source: casimages.com)
But interestingly, http://localhost:8000/webpack-dev-server seems to work.
(source: casimages.com)
var Encore = require('#symfony/webpack-encore');
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')ne CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/js/app.js')
//.addEntry('page1', './assets/js/page1.js')
//.addEntry('page2', './assets/js/page2.js')
.splitEntryChunks()
.enableSingleRuntimeChunk()
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel(() => {}, {
useBuiltIns: 'usage',
corejs: 3
})
//.enableSassLoader()
//.enableTypeScriptLoader()
//.enableIntegrityHashes(Encore.isProduction())
.autoProvidejQuery()
.autoProvideVariables({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
})
.enableReactPreset()
;
module.exports = Encore.getWebpackConfig();
I got "Cannot GET /" when trying to reach the server through localhost. Finally Iil I let Symfony open the webpage with the command:
symfony open:local
It opened on https://127.0.0.1:8000
You can check if your files are created in public/build folder. If your files are created completely check if they have appropriate access to read.

Symfony Encore/Webpack never completes

Anytime I try to run encore, it doesn't complete and hangs at...
$ /vagrant/app/node_modules/.bin/encore dev
Running webpack ...
I've removed node_modules and re-installed numerous times following the Symfony documentation. Tried using only npm and also tried only using yarn. Any suggestions would be much appreciated. I've included package.json and webpack.config.js, if there is anything else needed to help troubleshoot, please let me know.
package.json
{
"devDependencies": {
"#symfony/webpack-encore": "^0.20.1",
"node-sass": "^4.9.0",
"sass-loader": "^7.0.3"
}
}
webpack.config.js
var Encore = require('#symfony/webpack-encore');
Encore
// the project directory where compiled assets will be stored
.setOutputPath('public/build/')
// the public path used by the web server to access the previous directory
.setPublicPath('/app/build')
.setManifestKeyPrefix('build')
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
// uncomment if you use Sass/SCSS files
.enableSassLoader()
// uncomment to create hashed filenames (e.g. app.abc123.css)
// .enableVersioning(Encore.isProduction())
// uncomment to define the assets of the project
// .addEntry('js/app', './assets/js/app.js')
.addStyleEntry('css/style', './assets/sass/style.scss')
// .addStyleEntry('css/print', './assets/sass/print.scss')
// uncomment for legacy applications that require $/jQuery as a global variable
// .autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();
I figured out the issue. I had to disable the resolveUrlLoader in my sass loader options.
.enableSassLoader(function(options) {}, {
resolveUrlLoader: false
})

CSS loader error with webpack v3 after editing a webpack watched file in a server-side app

I am working on a node express api which is building a view via react and CSS modules (one CSS file within each component imported directly into the component). The react output is serialised with renderToStaticMarkup() which will be sent back to the requester in the JSON response. I also intend to send the compiled CSS in this response too.
I have a working build process via webpack which bundles my server app to one file. I am also currently bundling my CSS (modules) into one file (with the intention of reading this in later).
I am using webpack with its watch facility as follows (can't use webpack-dev-server as the api requires POST and there is no 'page' to update anyway):
cross-env NODE_ENV=development webpack -w --colors
My issue however is that whilst this all works fine on first compile, as soon as I change any file, I get a webpack error stating that I need an appropriate loader for the imported CSS file.
ERROR in ./src/app/components/Suggestions/Suggestions.css
Module parse failed: /home/me/myproject/src/app/components/Suggestions/Suggestions.css Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| .suggestions {
| background: blue;
| color: orange;
# ./src/app/components/Suggestions/Suggestions.js 11:19-47
# ./src/app/components/Suggestions/index.js
# ./src/server/middleware/buildSuggestions.js
# ./src/server/routes/index.js
# ./src/server/server.js
# multi babel-polyfill ./src/server/server.js
I have simplified my webpack config as much as possible and still get the issue. My simplified config (not extracting css to file and no PostCSS) is as follows:
webpack.config.babel.js
import path from 'path';
import nodeExternals from 'webpack-node-externals';
import PATHS from './config/paths';
// Host and port settings to spawn the dev server on
const HOST = 'localhost';
const PORT = 3000;
const LOCAL = `http://${HOST}:${PORT}`;
const DEV = process.env.NODE_ENV === 'development';
let serverConfig = {
entry: [
"babel-polyfill",
path.resolve(PATHS.src, 'server/server.js'),
],
output: {
filename: 'server.js',
path: PATHS.dist,
publicPath: '/'
},
module: {
rules: [
{
test: /\.jsx?$/,
include: PATHS.src,
use: {
loader: 'babel-loader',
options: {
// babelrc at project root only for compiling this webpack
babelrc: false,
presets: [
'env',
'react'
],
plugins: [
'transform-object-rest-spread',
'syntax-dynamic-import',
'transform-class-properties',
]
}
}
},
{
test: /\.css$/,
use: [
{
loader : 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[local]-[hash:base64]',
sourceMap: DEV
},
}
]
}
],
},
plugins: [
],
target: 'node',
externals: [nodeExternals()]
};
export default serverConfig;
So my question is, why does this work okay on first compile but not on a recompile after a change?
Stranger than fiction!
So I realised that if I run my build without the watcher...
cross-env NODE_ENV=development webpack --colors
and that process had ended, if I edited a file I still saw the error!!! Even though there was supposedly no watcher running. I left that terminal window alone with no running process, opened another terminal and edited a file within my src directory using vi (closed WebStorm in case it had some odd watcher running). Incredibly, the error popped up again in the original terminal window!!!
So it seems my issue was caused by a rogue webpack watch process that hadn't been killed properly. Couldn't find the process to manually kill it so had to do reboot. Literally hours lost on this bizzare issue. At least my whole build process is working again.

Resources