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

Adding CKEditor5 Plugins #21

Closed
Tenurian opened this issue Jul 31, 2018 · 25 comments
Closed

Adding CKEditor5 Plugins #21

Tenurian opened this issue Jul 31, 2018 · 25 comments
Labels
resolution:duplicate This issue is a duplicate of another issue and was merged into it.

Comments

@Tenurian
Copy link

Tenurian commented Jul 31, 2018

Greetings! I'm trying to add the alignment plugin into the react component built using the InlineEditor. I tried to follow your docs, but no matter what I do I can't seem to get this to work. Any time I pass in the new Alignment plugin into the plugins inside the config prop, whether by itself or with the rest of the default plugins, the toolbar doesn't show up anymore, even if I specify the toolbar layout as follows:

import React, {Component} from 'react'
import CKEditor from '@ckeditor/ckeditor5-react'

import InlineEditor from '@ckeditor/ckeditor5-build-inline'
/** Essentials, Heading, Paragraph, Bold, Italic, BlockQuote, Alignment, List, Link **/

import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment'
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials'
import Heading from '@ckeditor/ckeditor5-heading/src/heading'
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic'
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote'
import List from '@ckeditor/ckeditor5-list/src/list'
import Link from '@ckeditor/ckeditor5-link/src/link'

export default class MyComponent extends Component {
  handleEditorChange = () => {} //TO-DO: Write event handler
  render(){
    return (
      <div style={{height: '100%'}}>
        <CKEditor
          config={{
            plugins: [Essentials, Heading, Paragraph, Bold, Italic, BlockQuote, Alignment, List, Link],
            toolbar: ['Heading', '|', 'Bold', 'Italic', 'Alignment', 'Link', 'List', 'ListUI', 'BlockQuote', 'Undo', 'Redo'],
            removePlugins: ['Image', 'ImageCaption', 'ImageStyle', 'ImageToolbar', 'ImageUpload']
          }}
          editor={InlineEditor}
          data={this.state.content.value || ''}
          onChange={this.handleEditorChange}
        />
      </div>
    )
  }
}

As stated above, I've also tried it without the toolbar option, and even with plugins literally only containing Alignment, but nothing seems to work. I've also tried it without importing the other plugins with no luck.

@pomek pomek added the pending:feedback This issue is blocked by necessary feedback. label Jul 31, 2018
@Tenurian
Copy link
Author

If it helps. here's the error message I'm getting:

CKEditorError: model-selection-added-not-range: Trying to add an object that is not an instance of Range. Read more: https://docs.ckeditor.com/ckeditor5/latest/framework/guides/support/error-codes.html#error-model-selection-added-not-range
    at newRanges.some.newRange (http://localhost:3000/static/js/bundle.js:11744:11)
    at Array.some (<anonymous>)
    at Selection._setRanges (http://localhost:3000/static/js/bundle.js:11742:33)
    at Selection.setTo (http://localhost:3000/static/js/bundle.js:11687:9)
    at new Selection (http://localhost:3000/static/js/bundle.js:11419:9)
    at injectAndroidBackspaceMutationsHandling (http://localhost:3000/static/js/bundle.js:26720:25)
    at Delete.init (http://localhost:3000/static/js/bundle.js:25911:106)

@pomek
Copy link
Member

pomek commented Jul 31, 2018

Hello, @Tenurian.

At this moment, there is no easy way to add plugins to build. You can read more about the issue here – ckeditor/ckeditor5#667.

But your problem can be solved easily by using an inline editor instead of build.

Install the editor: npm install --save @ckeditor/ckeditor5-editor-inline, then use it in your app:

import InlineEditor from '@ckeditor/ckeditor5-editor-inline/src/inlineeditor'
// Import rests plugin that you need.

class App extends React.Component {

	constructor( props ) {
		super( props );

		this.state = {
			content: '<p>Initial data.</p>'
		};
	}

	handleEditorChange( event, editor ) {
		console.log( { event, editor } );
	}


	render() {
		return (
			<div className="App">
				<CKEditor
					config={{
						plugins: [ Essentials, Heading, Paragraph, Bold, Italic, BlockQuote, Alignment, List, Link ],
						toolbar: [ 'Heading', '|', 'Bold', 'Italic', 'Alignment', 'Link', 'List', 'ListUI', 'BlockQuote', 'Undo', 'Redo' ],
					}}
					editor={InlineEditor}
					data={this.state.content || ''}
					onChange={this.handleEditorChange}
				/>
			</div>
		);
	}

// ...

}

I'm closing this issue as a duplicate (ckeditor/ckeditor5#667) but if something still won't work, let me know.

@pomek pomek closed this as completed Jul 31, 2018
@pomek pomek added resolution:duplicate This issue is a duplicate of another issue and was merged into it. and removed pending:feedback This issue is blocked by necessary feedback. labels Jul 31, 2018
@Tenurian
Copy link
Author

Tenurian commented Jul 31, 2018

I appreciate the help, but even with the Inline Editor, I'm still getting the same bug as before. Only now, no matter what I seem to pass in, the toolbar won't show up.

@pomek
Copy link
Member

pomek commented Jul 31, 2018

It sounds weird. A component pasted below:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import CKEditor from '@ckeditor/ckeditor5-react';

import InlineEditor from '@ckeditor/ckeditor5-editor-inline/src/inlineeditor'
import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment'
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials'
import Heading from '@ckeditor/ckeditor5-heading/src/heading'
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic'
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote'
import List from '@ckeditor/ckeditor5-list/src/list'
import Link from '@ckeditor/ckeditor5-link/src/link'

class App extends Component {
	constructor( props ) {
		super( props );

		this.state = {
			content: '<p>Initial data.</p>'
		};
	}

	handleEditorChange( event, editor ) {
		console.log( { event, editor } );
	}

	render() {
		return (
			<div className="App">
				<header className="App-header">
					<img src={logo} className="App-logo" alt="logo"/>
					<h1 className="App-title">Welcome to React</h1>
				</header>
				<CKEditor
					config={{
						plugins: [ Essentials, Heading, Paragraph, Bold, Italic, BlockQuote, Alignment, List, Link ],
						toolbar: [ 'Heading', '|', 'Bold', 'Italic', 'Alignment', 'Link', 'List', 'ListUI', 'BlockQuote', 'Undo', 'Redo' ],
						removePlugins: [ 'Image', 'ImageCaption', 'ImageStyle', 'ImageToolbar', 'ImageUpload' ]
					}}
					editor={InlineEditor}
					data={this.state.content || ''}
					onChange={this.handleEditorChange}
				/>
			</div>
		);
	}
}

export default App;

Works for me (with create-react-app).

@Tenurian
Copy link
Author

Tenurian commented Jul 31, 2018

Yeah not sure why yours is working, but I literally copy/pasted that code straight from github into my app.js file and same thing, no toolbar.
I'm using create-react-app as well

Also, I'm getting this error:

TypeError: Cannot read property 'getAttribute' of null
    at IconView._updateXMLContent (iconview.js:100)
    at IconView.render (iconview.js:76)
    at IconView.on (observablemixin.js:250)
    at IconView.fire (emittermixin.js:196)
    at IconView.(:3000/anonymous function) [as render] (http://localhost:3000/static/js/bundle.js:75590:16)
    at ViewCollection.on (viewcollection.js:68)
    at ViewCollection.fire (emittermixin.js:196)
    at ViewCollection.add (collection.js:182)
    at DropdownButtonView.render (dropdownbuttonview.js:66)
    at DropdownButtonView.on (observablemixin.js:250)
    at DropdownButtonView.fire (emittermixin.js:196)
    at DropdownButtonView.(:3000/anonymous function) [as render] (http://localhost:3000/static/js/bundle.js:75590:16)
    at Template._renderElementChildren (template.js:701)
    at Template._renderElement (template.js:434)
    at Template._renderNode (template.js:416)
    at Template.render (template.js:139)
    at DropdownView.render (view.js:475)
    at DropdownView.render (dropdownview.js:208)
    at DropdownView.on (observablemixin.js:250)
    at DropdownView.fire (emittermixin.js:196)
    at DropdownView.(:3000/anonymous function) [as render] (http://localhost:3000/static/js/bundle.js:75590:16)
    at ViewCollection.on (viewcollection.js:68)
    at ViewCollection.fire (emittermixin.js:196)
    at ViewCollection.add (collection.js:182)
    at config.map.name (toolbarview.js:164)
    at Array.map (<anonymous>)
    at ToolbarView.fillFromConfig (toolbarview.js:160)
    at InlineEditorUI.init (inlineeditorui.js:75)
    at editor.initPlugins.then (inlineeditor.js:181)

@pomek
Copy link
Member

pomek commented Jul 31, 2018

Your webpack configuration does not contain loaders for CKEditor 5 files.

Please follow this instruction – https://github.com/ckeditor/ckeditor5-react#changes-required-in-webpackconfigprodjs.

@pomek
Copy link
Member

pomek commented Jul 31, 2018

PS: Toolbar in an inline editor will appear only if you focus the editable.

Before focus:

image

After focus:

image

@Tenurian
Copy link
Author

Tenurian commented Jul 31, 2018

I knew that last part.

I think I edited the correct file, but since I've never had to modify the webpack.config files while using create-react-app I'm not sure if I did it right, and I'm still not getting the toolbar. Same error.

The webpack files are under <project_root>/node_modules/react-scripts/config/, correct?

@pomek
Copy link
Member

pomek commented Jul 31, 2018

The webpack files are under <project_root>/node_modules/react-scripts/config/, correct?

Unfortunately, no. You shouldn't modify any file which is located in node_modules directory.

Webpack configuration from React-create-app are located in <project_root>/config directory:

image

I paste my configurations which seem to work. After your modification, you will be able to compare these files with your results. May it will somehow help you:

ckeditor5-react-example/config/webpack.config.dev.js
'use strict';

const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getClientEnvironment = require('./env');
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
const paths = require('./paths');

// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
const publicPath = '/';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
  // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
  // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
  devtool: 'cheap-module-source-map',
  // These are the "entry points" to our application.
  // This means they will be the "root" imports that are included in JS bundle.
  // The first two entry points enable "hot" CSS and auto-refreshes for JS.
  entry: [
    // We ship a few polyfills by default:
    require.resolve('./polyfills'),
    // Include an alternative client for WebpackDevServer. A client's job is to
    // connect to WebpackDevServer by a socket and get notified about changes.
    // When you save a file, the client will either apply hot updates (in case
    // of CSS changes), or refresh the page (in case of JS changes). When you
    // make a syntax error, this client will display a syntax error overlay.
    // Note: instead of the default WebpackDevServer client, we use a custom one
    // to bring better experience for Create React App users. You can replace
    // the line below with these two lines if you prefer the stock client:
    // require.resolve('webpack-dev-server/client') + '?/',
    // require.resolve('webpack/hot/dev-server'),
    require.resolve('react-dev-utils/webpackHotDevClient'),
    // Finally, this is your app's code:
    paths.appIndexJs,
    // We include the app code last so that if there is a runtime error during
    // initialization, it doesn't blow up the WebpackDevServer client, and
    // changing JS code would still trigger a refresh.
  ],
  output: {
    // Add /* filename */ comments to generated require()s in the output.
    pathinfo: true,
    // This does not produce a real file. It's just the virtual path that is
    // served by WebpackDevServer in development. This is the JS bundle
    // containing code from all our entry points, and the Webpack runtime.
    filename: 'static/js/bundle.js',
    // There are also additional JS chunk files if you use code splitting.
    chunkFilename: 'static/js/[name].chunk.js',
    // This is the URL that app is served from. We use "/" in development.
    publicPath: publicPath,
    // Point sourcemap entries to original disk location (format as URL on Windows)
    devtoolModuleFilenameTemplate: info =>
      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
  },
  resolve: {
    // This allows you to set a fallback for where Webpack should look for modules.
    // We placed these paths second because we want `node_modules` to "win"
    // if there are any conflicts. This matches Node resolution mechanism.
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: ['node_modules', paths.appNodeModules].concat(
      // It is guaranteed to exist because we tweak it in `env.js`
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),
    // These are the reasonable defaults supported by the Node ecosystem.
    // We also include JSX as a common component filename extension to support
    // some tools, although we do not recommend using it, see:
    // https://github.com/facebookincubator/create-react-app/issues/290
    // `web` extension prefixes have been added for better support
    // for React Native Web.
    extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
    alias: {
      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    },
    plugins: [
      // Prevents users from importing files from outside of src/ (or node_modules/).
      // This often causes confusion because we only process files within src/ with babel.
      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
      // please link the files into your node_modules/ and let module-resolution kick in.
      // Make sure your source files are compiled, as they will not be processed in any way.
      new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
    ],
  },
  module: {
    strictExportPresence: true,
    rules: [
      // TODO: Disable require.ensure as it's not a standard language feature.
      // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
      // { parser: { requireEnsure: false } },

      // First, run the linter.
      // It's important to do this before Babel processes the JS.
      {
        test: /\.(js|jsx|mjs)$/,
        enforce: 'pre',
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
              
            },
            loader: require.resolve('eslint-loader'),
          },
        ],
        include: paths.appSrc,
      },
      {
        // "oneOf" will traverse all following loaders until one will
        // match the requirements. When no loader matches it will fall
        // back to the "file" loader at the end of the loader list.
        oneOf: [
          // "url" loader works like "file" loader except that it embeds assets
          // smaller than specified limit in bytes as data URLs to avoid requests.
          // A missing `test` is equivalent to a match.
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
          // Process JS with Babel.
          {
            test: /\.(js|jsx|mjs)$/,
            include: paths.appSrc,
            loader: require.resolve('babel-loader'),
            options: {
              
              // This is a feature of `babel-loader` for webpack (not Babel itself).
              // It enables caching results in ./node_modules/.cache/babel-loader/
              // directory for faster rebuilds.
              cacheDirectory: true,
            },
          },
          // "postcss" loader applies autoprefixer to our CSS.
          // "css" loader resolves paths in CSS and adds assets as dependencies.
          // "style" loader turns CSS into JS modules that inject <style> tags.
          // In production, we use a plugin to extract that CSS to a file, but
          // in development "style" loader enables hot editing of CSS.
          {
            test: /\.css$/,
	          exclude: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                },
              },
              {
                loader: require.resolve('postcss-loader'),
                options: {
                  // Necessary for external CSS imports to work
                  // https://github.com/facebookincubator/create-react-app/issues/2677
                  ident: 'postcss',
                  plugins: () => [
                    require('postcss-flexbugs-fixes'),
                    autoprefixer({
                      browsers: [
                        '>1%',
                        'last 4 versions',
                        'Firefox ESR',
                        'not ie < 9', // React doesn't support IE8 anyway
                      ],
                      flexbox: 'no-2009',
                    }),
                  ],
                },
              },
            ],
          },
	        {
		        test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
		        use: [ 'raw-loader' ]
	        },
	        {
		        test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
		        use: [
			        {
				        loader: 'style-loader',
				        options: {
					        singleton: true
				        }
			        },
			        {
				        loader: 'postcss-loader',
				        options: styles.getPostCssConfig( {
					        themeImporter: {
						        themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
					        },
					        minify: true
				        } )
			        }
		        ]
	        },
          // "file" loader makes sure those assets get served by WebpackDevServer.
          // When you `import` an asset, you get its (virtual) filename.
          // In production, they would get copied to the `build` folder.
          // This loader doesn't use a "test" so it will catch all modules
          // that fall through the other loaders.
          {
            // Exclude `js` files to keep "css" loader working as it injects
            // its runtime that would otherwise processed through "file" loader.
            // Also exclude `html` and `json` extensions so they get processed
            // by webpacks internal loaders.
	          exclude: [
		          /\.(js|jsx|mjs)$/,
		          /\.html$/,
		          /\.json$/,
		          /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
		          /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/
	          ],
            loader: require.resolve('file-loader'),
            options: {
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
        ],
      },
      // ** STOP ** Are you adding a new loader?
      // Make sure to add the new loader(s) before the "file" loader.
    ],
  },
  plugins: [
    // Makes some environment variables available in index.html.
    // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // In development, this will be an empty string.
    new InterpolateHtmlPlugin(env.raw),
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
    }),
    // Add module names to factory functions so they appear in browser profiler.
    new webpack.NamedModulesPlugin(),
    // Makes some environment variables available to the JS code, for example:
    // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
    new webpack.DefinePlugin(env.stringified),
    // This is necessary to emit hot updates (currently CSS only):
    new webpack.HotModuleReplacementPlugin(),
    // Watcher doesn't work well if you mistype casing in a path so we use
    // a plugin that prints an error when you attempt to do this.
    // See https://github.com/facebookincubator/create-react-app/issues/240
    new CaseSensitivePathsPlugin(),
    // If you require a missing module and then `npm install` it, you still have
    // to restart the development server for Webpack to discover it. This plugin
    // makes the discovery automatic so you don't have to restart.
    // See https://github.com/facebookincubator/create-react-app/issues/186
    new WatchMissingNodeModulesPlugin(paths.appNodeModules),
    // Moment.js is an extremely popular library that bundles large locale files
    // by default due to how Webpack interprets its code. This is a practical
    // solution that requires the user to opt into importing specific locales.
    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
    // You can remove this if you don't use Moment.js:
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  ],
  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty',
  },
  // Turn off performance hints during development because we don't do any
  // splitting or minification in interest of speed. These warnings become
  // cumbersome.
  performance: {
    hints: false,
  },
};
ckeditor5-react-example/config/webpack.config.prod.js
'use strict';

const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const UglifyJsWebpackPlugin = require( 'uglifyjs-webpack-plugin' );
const paths = require('./paths');
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
const getClientEnvironment = require('./env');

// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
const publicPath = paths.servedPath;
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
  throw new Error('Production builds must have NODE_ENV=production.');
}

// Note: defined here because it will be used more than once.
const cssFilename = 'static/css/[name].[contenthash:8].css';

// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
const extractTextPluginOptions = shouldUseRelativeAssetPaths
  ? // Making sure that the publicPath goes back to to build folder.
    { publicPath: Array(cssFilename.split('/').length).join('../') }
  : {};

// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file.
module.exports = {
  // Don't attempt to continue if there are any errors.
  bail: true,
  // We generate sourcemaps in production. This is slow but gives good results.
  // You can exclude the *.map files from the build during deployment.
  devtool: shouldUseSourceMap ? 'source-map' : false,
  // In production, we only want to load the polyfills and the app code.
  entry: [require.resolve('./polyfills'), paths.appIndexJs],
  output: {
    // The build folder.
    path: paths.appBuild,
    // Generated JS file names (with nested folders).
    // There will be one main bundle, and one file per asynchronous chunk.
    // We don't currently advertise code splitting but Webpack supports it.
    filename: 'static/js/[name].[chunkhash:8].js',
    chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
    // We inferred the "public path" (such as / or /my-project) from homepage.
    publicPath: publicPath,
    // Point sourcemap entries to original disk location (format as URL on Windows)
    devtoolModuleFilenameTemplate: info =>
      path
        .relative(paths.appSrc, info.absoluteResourcePath)
        .replace(/\\/g, '/'),
  },
  resolve: {
    // This allows you to set a fallback for where Webpack should look for modules.
    // We placed these paths second because we want `node_modules` to "win"
    // if there are any conflicts. This matches Node resolution mechanism.
    // https://github.com/facebookincubator/create-react-app/issues/253
    modules: ['node_modules', paths.appNodeModules].concat(
      // It is guaranteed to exist because we tweak it in `env.js`
      process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
    ),
    // These are the reasonable defaults supported by the Node ecosystem.
    // We also include JSX as a common component filename extension to support
    // some tools, although we do not recommend using it, see:
    // https://github.com/facebookincubator/create-react-app/issues/290
    // `web` extension prefixes have been added for better support
    // for React Native Web.
    extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
    alias: {
      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    },
    plugins: [
      // Prevents users from importing files from outside of src/ (or node_modules/).
      // This often causes confusion because we only process files within src/ with babel.
      // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
      // please link the files into your node_modules/ and let module-resolution kick in.
      // Make sure your source files are compiled, as they will not be processed in any way.
      new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
    ],
  },
  module: {
    strictExportPresence: true,
    rules: [
      // TODO: Disable require.ensure as it's not a standard language feature.
      // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
      // { parser: { requireEnsure: false } },

      // First, run the linter.
      // It's important to do this before Babel processes the JS.
      {
        test: /\.(js|jsx|mjs)$/,
        enforce: 'pre',
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint'),
              
            },
            loader: require.resolve('eslint-loader'),
          },
        ],
        include: paths.appSrc,
      },
      {
        // "oneOf" will traverse all following loaders until one will
        // match the requirements. When no loader matches it will fall
        // back to the "file" loader at the end of the loader list.
        oneOf: [
          // "url" loader works just like "file" loader but it also embeds
          // assets smaller than specified size as data URLs to avoid requests.
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
          // Process JS with Babel.
          {
            test: /\.(js|jsx|mjs)$/,
            include: paths.appSrc,
            loader: require.resolve('babel-loader'),
            options: {
              
              compact: true,
            },
          },
          // The notation here is somewhat confusing.
          // "postcss" loader applies autoprefixer to our CSS.
          // "css" loader resolves paths in CSS and adds assets as dependencies.
          // "style" loader normally turns CSS into JS modules injecting <style>,
          // but unlike in development configuration, we do something different.
          // `ExtractTextPlugin` first applies the "postcss" and "css" loaders
          // (second argument), then grabs the result CSS and puts it into a
          // separate file in our build process. This way we actually ship
          // a single CSS file in production instead of JS code injecting <style>
          // tags. If you use code splitting, however, any async bundles will still
          // use the "style" loader inside the async code so CSS from them won't be
          // in the main CSS file.
          {
            test: /\.css$/,
	          exclude: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
            loader: ExtractTextPlugin.extract(
              Object.assign(
                {
                  fallback: {
                    loader: require.resolve('style-loader'),
                    options: {
                      hmr: false,
                    },
                  },
                  use: [
                    {
                      loader: require.resolve('css-loader'),
                      options: {
                        importLoaders: 1,
                        minimize: true,
                        sourceMap: shouldUseSourceMap,
                      },
                    },
                    {
                      loader: require.resolve('postcss-loader'),
                      options: {
                        // Necessary for external CSS imports to work
                        // https://github.com/facebookincubator/create-react-app/issues/2677
                        ident: 'postcss',
                        plugins: () => [
                          require('postcss-flexbugs-fixes'),
                          autoprefixer({
                            browsers: [
                              '>1%',
                              'last 4 versions',
                              'Firefox ESR',
                              'not ie < 9', // React doesn't support IE8 anyway
                            ],
                            flexbox: 'no-2009',
                          }),
                        ],
                      },
                    },
                  ],
                },
                extractTextPluginOptions
              )
            ),
            // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
          },
	        {
		        test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
		        use: [ 'raw-loader' ]
	        },
	        {
		        test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
		        use: [
			        {
				        loader: 'style-loader',
				        options: {
					        singleton: true
				        }
			        },
			        {
				        loader: 'postcss-loader',
				        options: styles.getPostCssConfig( {
					        themeImporter: {
						        themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
					        },
					        minify: true
				        } )
			        }
		        ]
	        },
          // "file" loader makes sure assets end up in the `build` folder.
          // When you `import` an asset, you get its filename.
          // This loader doesn't use a "test" so it will catch all modules
          // that fall through the other loaders.
          {
            loader: require.resolve('file-loader'),
            // Exclude `js` files to keep "css" loader working as it injects
            // it's runtime that would otherwise processed through "file" loader.
            // Also exclude `html` and `json` extensions so they get processed
            // by webpacks internal loaders.
	          exclude: [
		          /\.(js|jsx|mjs)$/,
		          /\.html$/,
		          /\.json$/,
		          /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
		          /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/
	          ],
            options: {
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
          // ** STOP ** Are you adding a new loader?
          // Make sure to add the new loader(s) before the "file" loader.
        ],
      },
    ],
  },
  plugins: [
    // Makes some environment variables available in index.html.
    // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    // In production, it will be an empty string unless you specify "homepage"
    // in `package.json`, in which case it will be the pathname of that URL.
    new InterpolateHtmlPlugin(env.raw),
    // Generates an `index.html` file with the <script> injected.
    new HtmlWebpackPlugin({
      inject: true,
      template: paths.appHtml,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true,
        minifyJS: true,
        minifyCSS: true,
        minifyURLs: true,
      },
    }),
    // Makes some environment variables available to the JS code, for example:
    // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
    // It is absolutely essential that NODE_ENV was set to production here.
    // Otherwise React will be compiled in the very slow development mode.
    new webpack.DefinePlugin(env.stringified),
    // Minify the code.
	new UglifyJsWebpackPlugin( {
      uglifyOptions: {
        compress: {
          warnings: false,
          // Disabled because of an issue with Uglify breaking seemingly valid code:
          // https://github.com/facebookincubator/create-react-app/issues/2376
          // Pending further investigation:
          // https://github.com/mishoo/UglifyJS2/issues/2011
          comparisons: false,
        },
        mangle: {
            safari10: true,
        },
        output: {
            comments: false,
            // Turned on because emoji and regex is not minified properly using default
            // https://github.com/facebookincubator/create-react-app/issues/2488
            ascii_only: true,
        },
      },
      sourceMap: shouldUseSourceMap,
    }),
    // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
    new ExtractTextPlugin({
      filename: cssFilename,
    }),
    // Generate a manifest file which contains a mapping of all asset filenames
    // to their corresponding output file so that tools can pick it up without
    // having to parse `index.html`.
    new ManifestPlugin({
      fileName: 'asset-manifest.json',
    }),
    // Generate a service worker script that will precache, and keep up to date,
    // the HTML & assets that are part of the Webpack build.
    new SWPrecacheWebpackPlugin({
      // By default, a cache-busting query parameter is appended to requests
      // used to populate the caches, to ensure the responses are fresh.
      // If a URL is already hashed by Webpack, then there is no concern
      // about it being stale, and the cache-busting can be skipped.
      dontCacheBustUrlsMatching: /\.\w{8}\./,
      filename: 'service-worker.js',
      logger(message) {
        if (message.indexOf('Total precache size is') === 0) {
          // This message occurs for every build and is a bit too noisy.
          return;
        }
        if (message.indexOf('Skipping static resource') === 0) {
          // This message obscures real errors so we ignore it.
          // https://github.com/facebookincubator/create-react-app/issues/2612
          return;
        }
        console.log(message);
      },
      minify: true,
      // For unknown URLs, fallback to the index page
      navigateFallback: publicUrl + '/index.html',
      // Ignores URLs starting from /__ (useful for Firebase):
      // https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
      navigateFallbackWhitelist: [/^(?!\/__).*/],
      // Don't precache sourcemaps (they're large) and build asset manifest:
      staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
    }),
    // Moment.js is an extremely popular library that bundles large locale files
    // by default due to how Webpack interprets its code. This is a practical
    // solution that requires the user to opt into importing specific locales.
    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
    // You can remove this if you don't use Moment.js:
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  ],
  // Some libraries import Node modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty',
  },
};

@Tenurian
Copy link
Author

Well this is a bit of a puzzle then, because I have no config folder
no config directory

I'll try making the directory and adding the files you posted, but I doubt that will have any effect since the folder didn't exist beforehand so it's probably not referenced anywhere

@pomek
Copy link
Member

pomek commented Jul 31, 2018

Did you call npm run eject? It creates the configuration files in the application.

@Tenurian
Copy link
Author

Disregard my previous comment, I am still new to the final stages of using create-react-app and skipped over that step entirely by accident

@Tenurian
Copy link
Author

Alrighty, took some work but after going through the section you pointed out on the github (after I ejected) made a lot more sense and I was able to successfully get it working again.

Thank you!

@pomek
Copy link
Member

pomek commented Aug 1, 2018

Glad that I could help. A feedback after using the component will be welcome (what was good, what could be improved, etc).

@Tenurian
Copy link
Author

Tenurian commented Aug 1, 2018

After learning how to install the plugin, everything else was smooth sailing. I'd recommend you add a "React Component" section to your official documentation so developers like myself don't ask the same questions.

On an unrelated note, is there currently a plugin that changes the text color? I saw one on npm that was not an official plugin (nor did it work) but considering you already have FontFamily, Alignment, FontSize, I'm a bit surprised you don't have a text color option.

@pomek
Copy link
Member

pomek commented Aug 1, 2018

On an unrelated note, is there currently a plugin that changes the text color?

https://github.com/ckeditor/ckeditor5-highlight

@pomek
Copy link
Member

pomek commented Aug 1, 2018

I'd recommend you add a "React Component" section to your official documentation so developers like myself don't ask the same questions.

There is a section about installing CKEditor 5 from the source in a React application – https://github.com/ckeditor/ckeditor5-react#integrating-ckeditor-5-from-source

@Tenurian
Copy link
Author

Tenurian commented Aug 1, 2018

As far as the colors/highlight goes, is there any way to use a full rage color picker for the text instead of having to define a number of "pens"?

@pomek
Copy link
Member

pomek commented Aug 1, 2018

AFAIK not yet. But you can create a feature request in the highlight repository.

@SKGGitHub
Copy link

Hi Tenurian,

Apparently the issue you were facing is resolved. Actually similar issue we are facing detailed here : #22

Can you help us on this as we are stucked here. Let me know if you need any further details for it.

Thanks.

@Tenurian
Copy link
Author

Tenurian commented Aug 7, 2018

Hey @pomek , I do have another question. I'm trying to get my component ready for deployment as an internal package for our company (ie, it will be hosted on our company git repo but not public, nor posted to npm) and when I tried to copy my code over to our package template and modified that webpack.config I'm getting the same TypeError: Cannot read property 'getAttribute' of null error again. Here's our webpack.config if this helps out:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const {styles} = require('@ckeditor/ckeditor5-dev-utils')

const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'

const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin')

const extractSASS = new ExtractTextPlugin('[name].css')

let config = {
  entry: {
    app: [
      'babel-polyfill',
      './src/index.js'
    ]
  },
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist'
  },
  plugins: [
    new UglifyJsWebpackPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
          // Disabled because of an issue with Uglify breaking seemingly valid code:
          // https://github.com/facebookincubator/create-react-app/issues/2376
          // Pending further investigation:
          // https://github.com/mishoo/UglifyJS2/issues/2011
          comparisons: false
        },
        mangle: {
          safari10: true
        },
        output: {
          comments: false,
          // Turned on because emoji and regex is not minified properly using default
          // https://github.com/facebookincubator/create-react-app/issues/2488
          ascii_only: true
        }
      },
      sourceMap: shouldUseSourceMap
    }),
    new CleanWebpackPlugin(['dist']),
    // Minify the code.
    new HtmlWebpackPlugin({
      template: './src/index.ejs'
    }),
    extractSASS
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    alias: {
      'c2-email-editor': path.join(__dirname, '../src')
    }
  },
  module: {
    rules: [
      {
        test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
        use: [
          {
            loader: 'style-loader',
            options: {
              singleton: true
            }
          },
          {
            loader: 'postcss-loader',
            options: styles.getPostCssConfig({
              themeImporter: {
                themePath: require.resolve('@ckeditor/ckeditor5-theme-lark')
              },
              minify: true
            })
          }
        ]
      },
      {
        test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
        use: ['raw-loader']
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {loader: 'babel-loader'}
      },
      {
        test: /\.css$/,
        exclude: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css/,
        loader: extractSASS.extract({
          fallback: 'style-loader',
          use: ['css-loader']
        })
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader', // creates style nodes from JS strings
          'css-loader', // translates CSS into CommonJS
          'sass-loader' // compiles Sass to CSS, using Node Sass by default
        ]
      },
      // {
      //   test: /\.scss/,
      //   loader: extractSASS.extract({
      //     fallback: 'style-loader',
      //     use: ['css-loader', 'sass-loader?sourceMap']
      //   })
      // },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: ['file-loader']
      },
      {
        test: /\.mp3$/,
        use: ['file-loader']
      },
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: [{
          loader: 'url-loader',
          options: {limit: 10000, mimetype: 'application/font-woff'}
        }]
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: [{
          loader: 'url-loader',
          options: {limit: 10000, mimetype: 'application/octet-stream'}
        }]
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: [{
          loader: 'url-loader',
          options: {limit: 10000, mimetype: 'image/svg+xml'}
        }]
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [{
          loader: 'url-loader',
          options: {limit: 8192}
        }]
      }
    ]
  }
}

if (process.env.NODE_ENV === 'production') {
  config.output.path = path.join(__dirname, '../docs')
  config.output.publicPath = './'
}

module.exports = config

and as you can see I've attempted to do all the changes CK5's documentation says to do with no luck.

@pomek
Copy link
Member

pomek commented Aug 8, 2018

You have duplicated SVG loader. Try to exclude CKEditor 5's icons from the second one and we will see what happen:

      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
       // Add the line below.
       exclude: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
        use: [{
          loader: 'url-loader',
          options: {limit: 10000, mimetype: 'image/svg+xml'}
        }]
      },

@Tenurian
Copy link
Author

Tenurian commented Aug 8, 2018

Awesome. After the rebuild, everything seems to be working!

@nishithamarella
Copy link

Hi,
I am using ckeditor 5 in my react project. I am Facing some issue while adding plugins. I try to follow document but it didn’t work for me. Please help me.

import React, { Component } from 'react';
import CKEditor from '@ckeditor/ckeditor5-react';
import InlineEditor from '@ckeditor/ckeditor5-editor-inline/src/inlineeditor'
import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment'
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials'
import Heading from '@ckeditor/ckeditor5-heading/src/heading'
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic'
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote'
import List from '@ckeditor/ckeditor5-list/src/list'
import Link from '@ckeditor/ckeditor5-link/src/link'

App.js:
class App extends Component {
constructor( props ) {
super( props );

	this.state = {
		data: ' '
	};
}

handleCkeditorState = (event, editor) => {
        this.setState({
          data = editor.getData();
        });
      };


render() {
	return (
		<div className="App">
		   <CKEditor
                            editor={InlineEditor}
                            config={{
                              plugins: [ Essentials, Heading, Paragraph, Bold, Italic, BlockQuote, Alignment, List ],
                              toolbar: [ 'Heading', '|', 'Bold', 'Italic', 'Alignment', 'Link', 'ListUI', 'BlockQuote', 'Undo', 'Redo' ],
                              removePlugins: [ 'Image', 'ImageCaption', 'ImageStyle', 'ImageToolbar', 'ImageUpload' ]
                            }}
                            data={this.state.data}
                            onInit={editor => {
                              // You can store the "editor" and use when it is needed.
                              //  ${signature.data.userSignatures[0].text}
                              this.setState({
                                data: `<div> 
                                        <br></br><br></br>
                                       
                                    </div>`,
                              });
                              console.log('Editor is ready to use!', editor);
                            }}
              
                            onChange={this.handleCkeditorState}
                          />
		</div>
	);
}

}

export default App;

package.json:
"@ckeditor/ckeditor5-alignment": "^27.1.0",
"@ckeditor/ckeditor5-basic-styles": "^27.1.0",
"@ckeditor/ckeditor5-block-quote": "^27.1.0",
"@ckeditor/ckeditor5-editor-inline": "^23.1.0",
"@ckeditor/ckeditor5-essentials": "^27.1.0",
"@ckeditor/ckeditor5-heading": "^27.1.0",
"@ckeditor/ckeditor5-list": "^27.1.0",
"@ckeditor/ckeditor5-paragraph": "^27.1.0",
"@ckeditor/ckeditor5-react": "^3.0.2",

I am getting this error.
CKEditorError: ckeditor-duplicated-modules
Read more: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-ckeditor-duplicated-modules

@BossBele
Copy link

BossBele commented Dec 1, 2022

I had this issue on NextJS, I can confirm the best way to work with CKEditor is using a custom build:

  1. Use the CKEditor online builder to select the plugins that you want with your editor
  2. Download the customized editor in the final step of the builder
  3. Uncompressed the zip file
  4. Place it in you repo the docs recommend to put it at the same level as you node_modules
  5. Run yarn add file:./ckeditor5 or pnpm add file:./ckeditor5 (ckeditor5 is the default name of the uncompressed zip build)
  6. Import the ck custom editor on your react app

Reference:
ckeditor/ckeditor5#7376 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
resolution:duplicate This issue is a duplicate of another issue and was merged into it.
Projects
None yet
Development

No branches or pull requests

5 participants