Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get TinyMCE to work with webpack and babel #2836

Closed
hkjorgensen opened this issue Apr 7, 2016 · 56 comments
Closed

How to get TinyMCE to work with webpack and babel #2836

hkjorgensen opened this issue Apr 7, 2016 · 56 comments

Comments

@hkjorgensen
Copy link

I have a couple of projects that uses TinyMCE and I think it's one of the better editors when HTML is the document model.

I wanted to use TinyMCE with a webpack setup so I tried: npm i --save tinymce and then import tinymce from 'tinymce' - but that didn't work.

After some time researching I got a solution to work. I just want to share it here if it can help others with the same issue. A couple of people have already asked for it.

This is not an issue but just a simple guide to get up and running :-)

Requirements

A working webpack setup with Babel (es2015 presets)

Dependencies

You need tinymce and a couple of loaders for webpack.

npm i --save tinymce
npm i --save-dev imports-loader exports-loader

Webpack config

Use window as this in the wrapping function generated by webpack

const config = {
  module: {
    loaders: [
      {
        test: require.resolve('tinymce/tinymce'),
        loaders: [
          'imports?this=>window',
          'exports?window.tinymce'
        ]
      },
      {
        test: /tinymce\/(themes|plugins)\//,
        loaders: [
          'imports?this=>window'
        ]
      }    
    ]
  }
}

Implementation

Create a file in your source and add the following:

// Core - these two are required :-)
import tinymce from 'tinymce/tinymce'
import 'tinymce/themes/modern/theme'

// Plugins
import 'tinymce/plugins/paste/plugin'
import 'tinymce/plugins/link/plugin'
import 'tinymce/plugins/autoresize/plugin'

// Initialize
tinymce.init({
  selector: '#tinymce',
  skin: false,
  plugins: ['paste', 'link', 'autoresize']
})

I've added skin: false because I assume the project want to use it's own pipeline to provide the CSS as a bundle. TinyMCE won't work until the CSS is included(!)

@spocke
Copy link
Member

spocke commented Apr 7, 2016

We are going to look into improving webpack/browserify support. We really want people to be able to stitch together their own editor using the parts. Thanks for sharing these details.

@ahmedelgabri
Copy link

Thanks @hkjorgensen this works perfectly! 👍

@spocke this would be really great if we have proper support.

@artokun
Copy link

artokun commented Apr 27, 2016

Ah, this works awesome! This is exactly what I needed

@f0rdream
Copy link

f0rdream commented Jul 7, 2016

Thanks for sharing!

@sassanh
Copy link

sassanh commented Jul 14, 2016

@hkjorgensen Thanks for sharing this, what about browserify?

@sassanh
Copy link

sassanh commented Jul 14, 2016

Some plugins like imagetools don't work and I guess they need other files to be imported in bundle. If I don't use these plugins everything works fine.

@lebron199174
Copy link

Thanks a lot!

@samturrell
Copy link

Has anyone figured out how to set content_css using webpack?

@gesposito
Copy link

gesposito commented Aug 3, 2016

One possible solution is to use ExtractTextPlugin so the CSS is actually bundled into a CSS file that you can later import into content_css;

Only caveat I see is that you loose Hot Module Replacement for CSS.

@hojt
Copy link

hojt commented Aug 12, 2016

Thanks @hkjorgensen for sharing!

Works great (though I'm skipping the babel-part, just using regular require()'s).

However I can't seem to get the paste-plugin to work for me. 😢
I keep getting this error:
Uncaught module definition dependecy not found: tinymce/util/Tools plugin.js:19

Commenting out the require of the paste-plugin gets core tinymce work like a charm, though.

@svnm
Copy link

svnm commented Sep 8, 2016

@ChrisJons same issue with using any plugins and themes for me. Also not sure how you add in additional tinymce themes using npm. I have had to use tinymce as a global due to these issues.

@phdesign
Copy link

I found that the loader test didn't work on Windows, this regex seems to be more compatible.

{
    test: /tinymce[\\/](themes|plugins)[\\/]/,
    loader: 'imports?this=>window'
},

@victorpfm
Copy link

I'm getting these errors:

Uncaught module definition dependency not found tinymce/util/tools
Uncaught type error: Cannot read property Factory of undefined

Don't know more what to do, I think I'll give up and use the cdn.

@phdesign
Copy link

@victorpfm Make sure you are importing all the plugins, themes and skins you're using as well. I found it best to use the file loader to include the skin styles / fonts in the output folder so they were available for the stylesheet manager to import at runtime. I did this by using require.context to require everything in the skins folder, like this:

require.context('tinymce/skins', true, /.*/);

Then in my webpack.config.js I use file-loader for everything in the skins folder, e.g:

{
  test: /tinymce[\\/]skins[\\/]/,
  loader: 'file?name=[path][name].[ext]&context=node_modules/tinymce'
},

Unfortunately with this approach you have to exclude the tinymce/skins folder from every other loader. I'm sure there's a better approach if I dug deeper.

{
  test: /\.css$/,
  exclude: /tinymce[\\/]skins[\\/]/,
  loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},

@fyrkant
Copy link

fyrkant commented Oct 3, 2016

@phdesign If you only use the file loader for the tinymce/skins folder it might be easiest to skip the webpack.config.js settings and just put the settings in the require call, like so:

require.context(
  'file?name=[path][name].[ext]&context=node_modules/tinymce!tinymce/skins', 
  true, 
  /.*/
)

Then you shouldn't have to exclude the tinymce/skins folder from all the other loaders, right? 🤔

@IAMtheIAM
Copy link

IAMtheIAM commented Oct 5, 2016

Doesn't work with webpack and Angular2 typescript as described here
Error: Uncaught (in promise): Error: Error in ./TicketComponent class TicketComponent_Host - inline template:0:0 caused by: Cannot read property 'init' of undefined

If I use import tinymce from 'tinymce/tinymce'
It adds default to my code

      tinymce.init({
         skin: false,
         selector: '#textarea__problem-statement', // theme: 'modern',
         // theme_url: 'pathToMyTheme',

      });

after transpiled by typescript

        tinymce_1.default.init({
            skin: false,
            selector: '#textarea__problem-statement',
        });

If I change it to
import 'tinymce/tinymce' then it does not add default but then back to the original issue
Cannot read property 'Factory' of undefined

@fyrkant
Copy link

fyrkant commented Oct 6, 2016

@IAMtheIAM Does it work if you use import 'tinymce/tinymce' and then for the init use window.tinymce.init({/* stuff */})?

@ghost
Copy link

ghost commented Oct 14, 2016

@fyrkant Where do we put the:

require.context(
  'file?name=[path][name].[ext]&context=node_modules/tinymce!tinymce/skins', 
  true, 
  /.*/
)

I have been trying to get tinymce to function with webpack, but none of the css is functioning. (It correctly finds the field, and initializes, but no css is showing / rendering. (It just makes a blank space.)

@jarrettj
Copy link

@hkjorgensen Thanks for the post. But if I import tinymce from the tinymce folder it does not work.

import tinymce from '../../node_modules/tinymce'

It says it can't find module. Adjusted your example to point at my node_modules folder.

@fyrkant
Copy link

fyrkant commented Oct 27, 2016

To show how to put the tips in this thread together I made a simple lite example app here.

@Darkside73
Copy link

@fyrkant Thanks for the great workaround with require.context.
But there is a small notice to get things work: you should overwrite any loaders by putting ! at the beginning:

require.context(
  '!file?name=[path][name].[ext]&context=node_modules/tinymce!tinymce/skins', 
  true, 
  /.*/
)

In this case webpack emit the actual content of CSS files instead of JS wrapping

@hkjorgensen
Copy link
Author

hkjorgensen commented Oct 31, 2016

@jarrettj you shouldn't require npm packages with a relative path, you should use the import tinymce from 'tinymce/tinymce'. Webpack will resolve that internally to PROJECT_DIR/node_modules/tinymce/tinymce.js. I hope that helps.

@dmi3y
Copy link

dmi3y commented Oct 31, 2016

Have issue with skin styles bleed over to global style scope (body).
Basically the skin being imported into javascript like so:

import 'tinymce/skins/lightgray/skin.min.css'
import 'tinymce/skins/lightgray/content.min.css'

With following webpack config related part:

    ...
    {
      test: /\.css$/,
      loader: 'style-loader!css-loader'
    },
    ...

And as a result the lightgray skin applied its styles globally when it suppose to be only tinymce sandboxed iframe. What the less cumbersome way to address this issue?

I might missing something obvious and doing it wrong way though...

UPD:

Figured it out thanks for @fyrkant and @Darkside73 posts above.

@foodaka
Copy link

foodaka commented May 12, 2017

is there a way to import all plugins in one line from tinymce ?

@fyrkant
Copy link

fyrkant commented May 12, 2017

@foodaka No there is no way to import all plugins, and it really isn't something I would recommend doing either. Just importing the plugins you need will keep your bundle as small as possible.

@ghost
Copy link

ghost commented Jul 8, 2017

Is there a solution to it right now? I do not get any errors, but still my component do not render it correctly. I don't see the element with the eye, it's invisible and in the element inspector all the classes are added...

@guangmiw8
Copy link

I used the approach listed in tinymce document: https://www.tinymce.com/docs/advanced/usage-with-module-loaders/#gettingtheskin. It works but the consumer of my bundle has to specify the url to the skin folder via the config because tinymce skin manager looks for skins folder under root and in my single page application the root is not where the output js bundle and skins folder are.

Would it be possible to bundle all resources file along with the js files into a single file? It seems unrealistic because the skin manager of tinymce dynamically loads skin files. I am asking in case I missed anything.

@lakesare
Copy link

lakesare commented Aug 9, 2017

This comment worked for me perfectly well, with Webpack 2: #2836 (comment). Together with skins and everything.
It links to this example: https://github.com/fyrkant/simple-tinymce-webpack.

@guangmiw8
Copy link

@lakesare thanks! that's how I am doing it now, but it's not bundling the css inline with the js files. What I wanted is a single js file that contains everything.

@ssilve1989
Copy link

ssilve1989 commented Aug 21, 2017

Does anyone have an example of how to use content_css with webpack to get CSS working on the tinyMCE iframe?

In addition when using the codesample plugin, the prism.css fails to load, even if i explicitly import it.

@Arima-s
Copy link

Arima-s commented Sep 29, 2017

@aneurysmjs Is this problem solved?

@Arima-s
Copy link

Arima-s commented Sep 30, 2017

good job

@kopax
Copy link

kopax commented Oct 30, 2017

Did anyone solved this for webpack > 3.5.5 ?

@szityi
Copy link

szityi commented Nov 23, 2017

Has someone fixed the issue reported by @ssilve1989 in this comment?

@wcrisman
Copy link

This post was a huge help. I just spend hours trying to figure out why Tinymce was almost working, but not quite. Adding the imports for all the plugins was what I was missing.

@Fasani
Copy link

Fasani commented Sep 26, 2018

I just wanted to add my solution for a custom skin here because I couldn't find a simple explanation and this is working for me plus our webpack setup is a little complex.

We made a custom skin using http://skin.tiny.cloud/

Then I manually copy the whole folder to a different location, we also use different relative paths so I ended up doing something like this:

webpack.config.js additions:

let CopyWebpackPlugin = require('copy-webpack-plugin');

let plugins = [
    // Used to copy TinyMCE custom skin
    new CopyWebpackPlugin([{
        from: path.join(root, 'tiny-mce-squirro-skin'),
        to: 'tiny-mce-squirro-skin',
    }]),
];

Then the Tiny MCE part:

const skinUrl = DEVELOPMENT ?
    'static/js/require/tiny-mce-squirro-skin' :
    'static/min/js/require/tiny-mce-squirro-skin';

TinyMCE.init({
    // Options Here
    skin_url: skinUrl,
});

Hope that helps some people who are also stick on this.

@ryanerato
Copy link

For those looking to bundle the skin and/or content CSS, this can be achieved with a combo of css-loader and style-loader. Due to limitations in the asset pipeline, I needed to output just a single bundle.

webpack.config.js

rules: [
  {
    test: /\.css$/,
    use: ['css-loader']
  },
  {
    test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
    loader: 'url-loader'
  }
]
import tinymce from "tinymce/tinymce";

import "style-loader!tinymce/skins/lightgray/skin.min.css";
import contentStyle from "tinymce/skins/lightgray/content.min.css";

tinymce.init({
  skin: false,
  content_style: contentStyle.toString()
});

@rasco
Copy link

rasco commented Nov 13, 2018

thanks @ryanerato
#2836 (comment) this worked for me!

@szityi
Copy link

szityi commented Feb 20, 2019

@ryanerato, I just tried your approach, and it's still not working for me. I'm using TinyMCE5, and Symfony's WebpackEncore bundler system. Below are the affected lines of my config:

webpack.config.js

    .addLoader({
        test: require.resolve('tinymce/tinymce'),
        loaders: [
            'imports-loader?this=>window',
            'exports-loader?window.tinymce'
        ]
    })
    .addLoader({
        test: /tinymce[\\/]themes[\\/]/,
        loader: 'imports-loader?this=>window'
    })
    .addLoader({
        test: /tinymce[\\/]plugins[\\/].*\.js$/,
        loader: 'babel-loader'
    })

my separate js module responsible for returning default configs:

import 'tinymce';
import 'tinymce/themes/silver';

import 'tinymce/plugins/advlist';
import 'tinymce/plugins/autolink';
import 'tinymce/plugins/link';
import 'tinymce/plugins/image';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/charmap';
import 'tinymce/plugins/print';
import 'tinymce/plugins/preview';
import 'tinymce/plugins/hr';
import 'tinymce/plugins/anchor';
import 'tinymce/plugins/pagebreak';
import 'tinymce/plugins/spellchecker';
import 'tinymce/plugins/searchreplace';
import 'tinymce/plugins/wordcount';
import 'tinymce/plugins/visualblocks';
import 'tinymce/plugins/visualchars';
import 'tinymce/plugins/code';
import 'tinymce/plugins/fullscreen';
import 'tinymce/plugins/insertdatetime';
import 'tinymce/plugins/media';
import 'tinymce/plugins/nonbreaking';
import 'tinymce/plugins/save';
import 'tinymce/plugins/table';
import 'tinymce/plugins/directionality';
import 'tinymce/plugins/emoticons';
import 'tinymce/plugins/template';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/importcss';

import '!style-loader!css-loader!tinymce/skins/ui/oxide/skin.min.css';
import contentStyle from 'tinymce/skins/ui/oxide/content.min.css';

        exports.TinyMce.getDefaultTinyMceConfig = function (base_path, file_browser_url) {
            return {
                selector: 'textarea.tinymce',
                width: 'auto',
                height: '300',
                theme: 'silver',
                skin: false,
                // content_style: false,
                content_style: contentStyle.toString(),
            ...
            };
        };

And I'm getting the following errors in browser's console:
image

However it seems like TinyMCE gets initialized properly but the errors annoys me anyway. Thanks in advance!

@zhangsanshi
Copy link

It works with TinyMCE5 and webpack4.

// Import TinyMCE
import 'tinymce/tinymce';

// A theme is also required
import 'tinymce/themes/silver';
import 'tinymce/skins/ui/oxide/skin.css';

// Any plugins you want to use has to be imported
import 'tinymce/plugins/advlist';
import 'tinymce/plugins/autolink';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/link';
import 'tinymce/plugins/image';
import 'tinymce/plugins/charmap';
import 'tinymce/plugins/print';
import 'tinymce/plugins/preview';
import 'tinymce/plugins/anchor';
import 'tinymce/plugins/searchreplace';
import 'tinymce/plugins/visualblocks';
import 'tinymce/plugins/code';
import 'tinymce/plugins/fullscreen';
import 'tinymce/plugins/insertdatetime';
import 'tinymce/plugins/media';
import 'tinymce/plugins/table';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/help';
import 'tinymce/plugins/wordcount';
import contentStyle from '!!raw-loader!tinymce/skins/ui/oxide/content.css';
import contentStyle2 from '!!raw-loader!tinymce/skins/content/default/content.css';
import Editor from '@tinymce/tinymce-vue';
export default {
    extends: Editor,
    props: {
        init: {
            type: Object,
            default() {
                return {
                    skin: false,
                    content_css: false,
                    content_style: contentStyle.toString() + '\n' + contentStyle2.toString(),
                };
            },
        },
    },
};

@Townsheriff
Copy link

Got me working with react:

// Import TinyMCE
import "tinymce/tinymce";

import "tinymce/icons/default";

// A theme is also required
import "tinymce/themes/silver";
import "tinymce/skins/ui/oxide/skin.css";

// Any plugins you want to use has to be imported
import "tinymce/plugins/advlist";
import "tinymce/plugins/autolink";
import "tinymce/plugins/lists";
import "tinymce/plugins/link";
import "tinymce/plugins/image";
import "tinymce/plugins/charmap";
import "tinymce/plugins/print";
import "tinymce/plugins/preview";
import "tinymce/plugins/anchor";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/code";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/media";
import "tinymce/plugins/table";
import "tinymce/plugins/paste";
import "tinymce/plugins/help";
import "tinymce/plugins/wordcount";
export { Editor } from "@tinymce/tinymce-react";

const getGlobal = (): any => (typeof window !== "undefined" ? window : global);

export const getTinyMCE = (): any | null => {
  const global = getGlobal();

  return global && global.tinymce ? global.tinymce : null;
};

@yanqing6628780
Copy link

Hi zhangsanshi, Can you share the webpack.config.js about TinyMCE?
It does not work for me.

import 'tinymce/tinymce';

I fellow the TinyMCE's document, here is my config:

module:
  rules: [
     {
        test: /skin\.css$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'],
      },
      {
        test: /content\.css$/i,
        use: ['css-loader'],
      },
  ]
....
optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 3,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          name: 'vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
          chunks: 'all',
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
        tinymceVendor: {
          chunks: 'all',
          test: /[\\/]node_modules[\\/](tinymce)[\\/](.*js|.*skin.css)|[\\/]plugins[\\/]/,
          name: 'tinymce',
          priority: 20,
        },
        vue: {
          name: 'vue',
          test: /vue/,
          priority: 10,
          chunks: 'all'
        }
      },
    }
  },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests