Hi,
I'm trying to add material.io components to a Symfony project, using Encore. Apparently I'm doing something wrong since the resulting JS contains raw ES6 from Material components. However, when I use webpack directly, not configured via Encore, everything transpiles normally. Same files, same node_modules, just plain webpack config.
Here's my Encore webpack config that does not work
Encore
.setOutputPath('web/build/')
.setPublicPath('/build')
.addEntry('app', './assets/js/app.js')
.addStyleEntry('bundle', './assets/css/app.scss')
.enableSassLoader(function(options) {
options.includePaths = ['node_modules']
})
.autoProvidejQuery()
.enableSourceMaps(!Encore.isProduction())
.cleanupOutputBeforeBuild()
.configureBabel(config => {
config.presets = ['es2015'];
});
And this is plain webpack config that DOES work
const path = require('path');
const glob = require('glob');
module.exports = [];
module.exports.push({
entry: './assets/css/app.scss',
output: {
// This is necessary for webpack to compile
// But we never use style-bundle.js
filename: 'style-bundle.js',
},
module: {
rules: [{
test: /\.scss$/,
use: [
{
loader: 'file-loader',
options: {
name: './web/build/bundle.css',
},
},
{ loader: 'extract-loader' },
{ loader: 'css-loader' },
{
loader: 'sass-loader',
options: {
includePaths: ['node_modules', 'node_modules/@material/!*']
.map((d) => path.join(__dirname, d))
.map((g) => glob.sync(g))
.reduce((a, c) => a.concat(c), [])
}
}
]
}]
},
});
module.exports.push({
entry: "./assets/js/app.js",
output: {
filename: "./web/build/build.js"
},
module: {
loaders: [{
test: /\.js$/,
loader: 'babel-loader',
options: {
presets: ['env']
}
}]
},
});
Don't get confused by different output files - I did this so I can have both to compare. But everything that is produced by Encore contains raw ES6 while same sources processed with standard webpack config produces ES5. The babel config I added (add es2015 preset) was an attempt to force transpiling, however normal webpack config works fine with preset: env...
Here's my package.json:
{
"devDependencies": {
"@symfony/webpack-encore": "^0.19.0",
"webpack-notifier": "^1.6.0",
"babel-preset-es2015": "^6.24.1",
"extract-loader": "^2.0.1",
"material-components-web": "^0.33.0",
"node-sass": "^4.8.3",
"sass-loader": "^6.0.7"
},
"dependencies": {
"fullcalendar": "^3.9.0",
"moment-timezone": "^0.5.14",
"symfony-collection": "^2.1.25"
}
}
Ignore dependencies - not used in source code at all (at the moment)
The source is also very simple (for testing), it merely imports one Material component and intialises it, like this
// app.js
import {MDCTemporaryDrawer} from '@material/drawer';
let drawer = new MDCTemporaryDrawer(document.querySelector('#main-drawer'));
drawer.open = true;
This actually works in FF, because it understands ES6, but I see raw ES6 in resulting packed file, and because of that I start getting errors when I try to add more complex stuff.
Any help will be appreciated - I'd like to stick to Encore because Symfony is sticking with it, but I'm out of ideas :)
Thanks
Vic
Hey @vkost,
I did a quick test and got the following error from UglifyJs: Unexpected token: name (MDCPersistentDrawerFoundation), so that issue is probably related to #139 (you'll find some workarounds there).
To sum up why it fails:
node_modules files from being processed by Babel, so these files are not processed: https://github.com/symfony/webpack-encore/blob/ccbae35a7f8dc34467493cba84233e88838f0024/lib/config-generator.js#L136Hi @Lyrkan, thank you for testing this... I understand your explanation but I'd like to learn more on this, if possible:
Thanks
@vkost For the first question I'd say that a lot of libs already provide a compiled version and that making them going through Babel would just be a waste of compilation time. As I said earlier you can find some workarounds in the other thread (for instance, if you wish to remove the exclude: https://github.com/symfony/webpack-encore/issues/139#issuecomment-322592989).
OK @Lyrkan
I understand why you excluded node_modules, it does make sense to use the pre-compiled version. I have just checked, and there is a precompiled version of @material modules in /dist, relative to module path. Can you point out any lib with precompiled modules, I'd like to learn more on the issue?
Thanks
@vkost as pointed out in #139 Bootstrap also has that issue if, for instance, you require bootstrap/js/src/modal instead of bootstrap/js/dist/modal. An in that case their doc recommend to use the "dist" version: https://getbootstrap.com/docs/4.1/getting-started/webpack/#importing-javascript
Note that this will not be needed with Webpack 4 since it includes a version of Uglify-JS that supports ES6+.
I don't know if the issue is solved but here's my configuration (which works with Material Components):
const Encore = require('@symfony/webpack-encore');
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.cleanupOutputBeforeBuild()
.enableSourceMaps(!Encore.isProduction())
// uncomment to create hashed filenames (e.g. app.abc123.css)
// .enableVersioning(Encore.isProduction())
.enableTypeScriptLoader()
.enableVueLoader()
.enableSassLoader()
.addLoader(
{
test: /\.scss$/,
use: [
{
loader: 'sass-loader',
options: {
importer: function(url, prev) {
if(url.indexOf('@material') === 0) {
const filePath = url.split('@material')[1];
const nodeModulePath = `./node_modules/@material/${filePath}`;
return {
file: require('path').resolve(nodeModulePath)
};
}
return {
file: url
};
}
}
}
]
}
)
.addLoader(
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}
)
// Style
.addStyleEntry('core', './assets/scss/public/core.scss')
.addStyleEntry('registration', './assets/scss/public/registration.scss')
// Javascript
.addEntry('form', './assets/javascript/components/form.js')
.addEntry('snackbar', './assets/javascript/components/form/snackbar.js')
// PWA
.addEntry('serviceWorker', './assets/javascript/pwa/app.js')
.addEntry('sw', './assets/javascript/pwa/sw.js')
// Vue
.addEntry('vue', './assets/vue/public/index.js')
;
module.exports = Encore.getWebpackConfig();
Here's the package.json one:
{
"devDependencies": {
"@material/base": "^0.34.0",
"@symfony/webpack-encore": "^0.20.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-es2015": "^6.24.1",
"css-loader": "^0.28.11",
"extract-loader": "^2.0.1",
"file-loader": "^1.1.11",
"node-sass": "^4.8.3",
"sass-loader": "^7.0.1",
"ts-loader": "3.5.0",
"typescript": "^2.7.2",
"vue": "^2.5.16",
"vue-loader": "^14.2.2",
"vue-template-compiler": "^2.5.16"
},
"license": "UNLICENSED",
"private": true,
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production"
},
"dependencies": {
"@material/auto-init": "^0.32.0",
"@material/button": "^0.33.0",
"@material/snackbar": "^0.34.0",
"material-components-web": "^0.33.0"
}
}
As far as I can see, the loader for JS should be loaded via a new Loader, even if Encore use the default one, I don't really know why but Encore seems to not call the right loader at the compilation time.
Most helpful comment
I don't know if the issue is solved but here's my configuration (which works with Material Components):
Here's the
package.jsonone:As far as I can see, the loader for JS should be loaded via a new Loader, even if Encore use the default one, I don't really know why but Encore seems to not call the right loader at the compilation time.