How to integrate Tinymce with Symfony encore? - symfony

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.

Related

Nextjs module federation with standalone servers

I'm trying to use module federation (webpack 5) beetween nextjs applications.
I started from this example (two nextjs applications) and everything works as expected. From my point of view the problem is that this works only if i have both app on the same host.
The relevant webpack configuration part on next.config.js is below (the same in the other app)
....
remotes: {
next1: isServer
? path.resolve(
__dirname,
"../next1/.next/server/static/runtime/remoteEntry.js"
)
: "next1",
},
...
If i just remove the server configuration it doesn't works.
It is possible to use module federation between nextjs app without configure the remote server by folder path and reference the remote app only by url ?
It is possible, but it won't work with SSR. Just leave the remote with the global for client side:
// next.config.js
....
remotes: {
next1: "next1",
},
...
Create your component and import the remote:
// component.js
const Component = (await import("next1/component")).default
export default Component
And finally in your page, lazy load your remote component with SSR disabled:
// mypage.js
import dynamic from 'next/dynamic'
const RemoteComponent = dynamic(
() => import('../components/component.js'),
{ ssr: false }
)
const MyPage = () => (<RemoteComponent />)
export default MyPage
There's a working sample here: https://mf-shell.vercel.app/
And the code: https://github.com/schalela/mf-nextjs

Webpack encore include external js

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"),
}));

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/

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

Use wiredep to inject custom CSS

I need to inject custom CSS files everytime they are created after being compiled by gulp-less. So I tried to use wiredep with custom configurations, but without success.
I changed the tag from 'bower:css' to 'custom:css', specifically to my custom task. The bower:css for default wiredep injection is still there. But after run myinjection task nothing is injected even running the task without errors.
Another weird thing, when I run my task, the files injected by wiredep (the default) disappear.
What I'm missing?
Basicaly my files structure is like this:
|---app
|---styles
|---***
*.css
*.html
.custom.json
I'm not sure if I really need a file similar to bower.json, but I made may own custom.json
{
"name": "custom",
"version": "0.0.1",
"main": [
"app/styles/**/*.css",
"app/scripts/**/*.js //pretend to inject custom js later
]
}
The task in gulpfile.js is like this:
gulp.task('myinject', function () {
var myinject = require('wiredep').stream;
var combined = Combine (
gulp.src('app/*.html'),
myinject({
directory: 'app',
bowerJson: require('./custom.json'),
dependencies: false,
html:
{
block: /(([ \t]*)<!--\s*custom:*(\S*)\s*-->)(\n|\r|.)*?(<!--\s*endcustom\s*-->)/gi,
detect: {
js: /<script.*src=['"](.+)['"]>/gi,
css: /<link.*href=['"](.+)['"]/gi
},
replace: {
js: '<script src="{{filePath}}"></script>',
css: '<link rel="stylesheet" href="{{filePath}}" />'
}
}
}),
gulp.dest('app')
);
combined.on('error', function(err) {
console.warn(err.message);
});
return combined;
});
Thanks in advance
I had a similar issue that I resolved with gulp-inject rather than wiredep.
Wiredep works fine with me when I need to include a third-party dependency (e.g. a bower package with a valid main file declared in the bower.json or a file with the same name as the directory).
However, when you want to include only a specific file (e.g. only the main.css of html5-boilerplate) gulp-inject is much simpler. Here is an extract of the doc :
gulp.task('index', function () {
var target = gulp.src('./src/index.html');
var sources = gulp.src(['./src/**/*.js', './src/**/*.css'], {read: false});
return target.pipe(inject(sources))
.pipe(gulp.dest('./src'));
});

Resources