Webpack encore include external js - symfony

I have a problem i can't understand :
webpack.config.js
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
.copyFiles({
from: './assets/media',
// if versioning is enabled, add the file hash too
to: 'media/[path][name].[hash:8].[ext]',
})
// 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/')
.addEntry('app', ['./assets/js/app.js'])
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
[...]
;
app.js
// require jQuery normally
const $ = require('jquery');
// create global $ and jQuery variables
global.$ = global.jQuery = $;
require('../../assets/vendors/general/jquery/dist/jquery.js');
require('../../assets/vendors/general/popper.js/dist/umd/popper.js');
require('../../assets/vendors/general/bootstrap/dist/js/bootstrap.min.js');
require('../../assets/vendors/general/js-cookie/src/js.cookie.js');
require('../../assets/vendors/general/tooltip.js/dist/umd/tooltip.min.js');
require('../../assets/vendors/general/perfect-scrollbar/dist/perfect-scrollbar.js');
require('../../assets/vendors/general/sticky-js/dist/sticky.min.js');
require('../../assets/vendors/general/wnumb/wNumb.js');
require('../../assets/js/demo1/scripts.bundle.js');
require('../../assets/css/demo1/style.bundle.css');
require('../../assets/css/demo1/skins/header/base/light.css');
require('../../assets/css/demo1/skins/header/menu/light.css');
require('../../assets/css/demo1/skins/brand/dark.css');
require('../../assets/css/demo1/skins/aside/dark.css');
But On my page i get this error :
jquery.js:3850 Uncaught ReferenceError: PerfectScrollbar is not
defined
I don't understand why because perfect scrollbar is well required. Same thing if i do it with import. And same thing if i remove perfect scroll bar i got the same message with sticky js.
Thanks for your help,
Alex

Ok found the solution with Metronic support :
Encore.addPlugin(new webpack.ProvidePlugin({
PerfectScrollbar: require.resolve("perfect-scrollbar"),
}));

Related

How to stop Webpack wrapping my JS in an IIFE so I can call my functions in .cshtml views

I've recently added WebPack 5 to my build process in my .NET Core MVC 7 application.
My goal is to be able to call my javascript functions from the javascript files WebPack generates inside my views.
I have a simple Index.cshtml file that includes a partial view and the generated javascript from webpack:
Index.cshtml
<div>
#await Html.PartialAsync("SettingsTab")
</div>
#section Scripts
{
<script defer src="~/dist/settings.entry.js"></script>
}
The SettingsTab constains a button that is trying to trigger a method from the settings.entry.js file:
SettingsTab.cshtml
<div>
<button type="button" onclick="saveProfileSettings()">Save Profile</button>
</div>
The settings.js file before webpack bundles it into the dist folder looks like this:
settings.js
import ('../css/settings.css')
function saveProfileSettings() {
// do stuff
}
When I wasn't using webpack I could directly call this function like I am trying to above.
However now when I reference the bundled js file it cannot call it.
Looking at the end of the settings.entry.js file it looks like webpack has bundled my code into an IIFE:
End of settings.entry.js
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";
// omitted contents of settings.js
})();
/******/ })()
;
I would like to instruct WebPack not to wrap my code like this so I can directly call the functions elsewhere.
Now I did find out that I can use library in my webpack.config.js like so:
output: {
filename: "[name].entry.js",
path: path.resolve('wwwroot', 'dist'),
publicPath: "/dist/",
library: ['WebPack', '[name]']
},
With this I can successfully reference my functions using WebPack.settings.saveProfileSettings() but I do not want to do this. I want to be able to reference my functions directly as saveProfileSettings().
How can I change how WebPack packs my code so I can do this?

Vue 3 components with slots in vendor folder

I have an issue where I want to extract all my components out into a separate repository but I want to load them in via composer instead of npm (business decision).
This was all working fine. Add it as a dependancy in the composer.json file and then add the following to the webpack.mix.js file;
mix.webpackConfig({
resolve: {
alias: {
'#ui' : path.resolve(__dirname, 'vendor/companyname/ui/src'),
}
}
});
And then use the following to import them in;
import PrimaryButton from "#ui/buttons/primary";
Which all worked fine!
Until I wanted to use slots. If I include a slot I get the following error;
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'nodeName')
at new create (svg.js?1929:3616)
at globalRef.SVG (svg.js?1929:31)
at Proxy.mounted (Editor.vue?721f:63)
at callWithErrorHandling (runtime-core.esm-bundler.js?5c40:154)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js?5c40:163)
at Array.hook.__weh.hook.__weh (runtime-core.esm-bundler.js?5c40:2909)
at flushPostFlushCbs (runtime-core.esm-bundler.js?5c40:357)
at flushJobs (runtime-core.esm-bundler.js?5c40:393)
In this example the component looks like this;
<template>
<button #click="click">
<slot></slot>
</button>
</template>
<script>
export default {
emits: ['click'],
methods: {
click() {
this.$emit('click')
}
}
}
</script>
Now, if I copy this component into the project repository and load it in that way, there is no error and works as intended!
Can anyone point me in the direction of where I am going wrong please.
Update
I have removed other components from view I'm rendering and now have a new error;
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'isCE')
at renderSlot (runtime-core.esm-bundler.js?c099:5832)
at Proxy.render (primary.vue?f782:3)
at renderComponentRoot (runtime-core.esm-bundler.js?5c40:1166)
at componentEffect (runtime-core.esm-bundler.js?5c40:5201)
at reactiveEffect (reactivity.esm-bundler.js?a1e9:42)
at effect (reactivity.esm-bundler.js?a1e9:17)
at setupRenderEffect (runtime-core.esm-bundler.js?5c40:5154)
at mountComponent (runtime-core.esm-bundler.js?5c40:5113)
at processComponent (runtime-core.esm-bundler.js?5c40:5071)
at patch (runtime-core.esm-bundler.js?5c40:4673)
Update
This is not the answer but the reason for this, from what I can tell is that there were two instances of Vue in play, one from the library and one in my application. I have no idea how to stop this but if I push the contents up to a repo and pull it in via npm then it works normally. Still would like to know how to get this running locally without using npm.

How to build a Vue.js project to use it in wordpress

Update : see edit at the end
Follow up of my previous question here : Include Vue.js component in Wordpress plugin without CDN
The very pertinent answer from Okba is causing me headaches. How do I build something with Vue.js that I can use in wordpress ?
I work with Vue-cli. I have tried to build my project as a library using this command line :
vue-cli-service build --target lib --name myVueLibrary ./src/main.js
And then importing the dist/* files in wordpress :
wp_enqueue_script('myPlugin', plugin_dir_url(__FILE__) . './my-plugin.js', [], '0.1', true);
wp_enqueue_script('myVueLibrary', './vue-plugin/myVueLibrary.common.js', [], '0.1.0');
where my-plugin.js looks like this (I am using shortcodes to replace a <div> content with whatever my plugin puts there) :
var elements = document.querySelectorAll('[my-plugin-atts]');
elements.forEach(function (element) {
var atts = JSON.parse(element.getAttribute('my-plugin-atts'));
var vm = new Vue({
el: element,
created: function () {
this.atts = atts;
},
template: '<div class="plugin-container">{{atts.id}}</div>'
});
});
And I get the following error :
Uncaught ReferenceError: Vue is not defined
But ! If I replace my vueBaoViz.common.js import by a Vue CDN import, my wordpress plugin displays what I want it to display.
wp_enqueue_script('myPlugin', plugin_dir_url(__FILE__) . './my-plugin.js', [], '0.1', true);
wp_enqueue_script('vue', 'https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js', [], '2.5.16');
My guess is my Vue build is not ok, how do I build something I can use like that, and that packs Vue.js within itself ?
So, turns out i'm an idiot, I forgot to add plugin_dir_url(__FILE__) before the path when loading a local file. This solved my problem.
1 - You can use vue js - non spa, from cdn or with webpack look at the https://v2.vuejs.org/v2/guide/#Composing-with-Components for how you will use vue js in regular js files.
2- You can use vue-cli like a standart spa project with wordpress rest api : https://developer.wordpress.org/rest-api/

How to integrate Tinymce with Symfony encore?

I have a Symfony4 project using flex and encore. I would like to add tinymce.
So I add tinymce project:
$ yarn add tinymce
I edited my app.js file:
require('../css/app.scss');
// Import TinyMCE
import tinymce from 'tinymce/tinymce';
// A theme is also required
import 'tinymce/themes/modern/theme';
// Any plugins you want to use has to be imported
import 'tinymce/plugins/paste';
import 'tinymce/plugins/link';
// Initialize the app
tinymce.init({
selector: 'textarea',
plugins: ['paste', 'link']
});
I compiled:
$ yarn run encore dev
Compilation is successfull:
Running webpack ...
DONE Compiled successfully in 17600ms
I 8 files written to public\build
Done in 20.23s.
My textareas are replaced by a blank page.
I found the solution in documentation and it works fine when I copy the node_modules/tinymce/skins directory to /public/build/skins. But I still have to do it after each yarn compilation
Is there a way to automatically copy this node_modules/tinymce/skins directory to /public/build/skins? IS it possible to update the webpack.config.js to do it?
PS: I read some recommandations with the webpack-loader, but I don't understand what I have to do.
OPTION1: RECOMMENDED: the buit-in copyFiles function
Use Encore's built-in copyFiles function.
var Encore = require('#symfony/webpack-encore');
//...
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')
// copy tinymce's skin files
.copyFiles({
from: 'node_modules/tinymce/skins',
to: 'skins/[path]/[name].[ext]'
})
Encore's API reference.
OPTION2 : The copy webpack plugin
I added the copy webpack plugin
yarn add copy-webpack-plugin --dev
I edited my webpack.config.js to only add 4 lines:
var Encore = require('#symfony/webpack-encore');
//DECLARATION OF THE NEW PLUGIN
var CopyWebpackPlugin = require('copy-webpack-plugin');
Encore
// the project directory where all compiled assets will be stored
.setOutputPath('public/build/')
// the public path used by the web server to access the previous directory
.setPublicPath('/build')
// will create public/build/admin/app.js and public/build/admin/app.css
.addEntry('admin', './assets/js/app.js')
//Some project lines
//...
//...
//I call the plugin with its new syntax (since 3.11)
.addPlugin(new CopyWebpackPlugin({
patterns: [
// Copy the skins from tinymce to the build/skins directory
{ from: 'node_modules/tinymce/skins', to: 'skins' },
],
}))
//Some project lines
//...
//...
;
// export the final configuration
module.exports = Encore.getWebpackConfig();
Thanks for your post... It solved my issue with a slight difference for me. I had to put the skins in
/public/build/js/skins
So I changed hte webpack config from
.addPlugin(new CopyWebpackPlugin([
// Copy the skins from tinymce to the build/skins directory
{ from: 'node_modules/tinymce/skins', to: 'skins' },
]))
to
.addPlugin(new CopyWebpackPlugin([
// Copy the skins from tinymce to the build/skins directory
{ from: 'node_modules/tinymce/skins', to: 'js/skins' },
]))
And it works ! thanks again !
Note that since v3.11 of the copy plugin, the syntax changed and previous answers won't work anymore. The new syntax requires the patterns field:
var Encore = require('#symfony/webpack-encore');
// Plugins
var CopyWebpackPlugin = require('copy-webpack-plugin');
...
Encore
.setOutputPath('public/build/')
.setPublicPath('/build')
.addEntry('app', './assets/js/app.js')
.addPlugin(new CopyWebpackPlugin({
patterns: [
// Copy the skins from tinymce to the build/skins directory
{ from: 'node_modules/tinymce/skins', to: 'skins' },
],
}))
...
Instead of using the third party plugin copy-webpack-plugin I'd recommend using Encore's built-in copyFiles function.
var Encore = require('#symfony/webpack-encore');
//...
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')
// copy tinymce's skin files
.copyFiles({
from: 'node_modules/tinymce/skins',
to: 'skins/[path]/[name].[ext]'
})
See Encore's API reference.

Vue + Webpack + Meteor Client Side

I want integrate meteor-client-side from NPM in a vue.js Webpack project.
The project is generated with vueCli.
This is my main.js file:
import Vue from 'vue'
import App from './App'
require('meteor-client-side')
console.log(Meteor.status())
/* eslint-disable no-new */
new Vue({
el: 'body',
components: { App }
})
With console.log i get a eslint error 'Meteor is not defined', but when i try Meteor.status() on the browser console it works fine.
What i doing wrong?
I don't want use vue in meteor, i need meteor-client-side in this non meteor project.
Many thanks for help
Try
var Meteor = require('meteor-client-side');
console.log(Meteor.status())
If you want to use a node module in the code you need to assign it to a variable, like var $ = require('jquery');. But if the JS just does things on its own and you don't need a reference to it, you can just require it, so like:
var $ = var jQuery = require('jquery'); //sets $ to jquery
require('bootstrap'); //just extends jquery, no need to save reference

Resources