Search code examples
javascriptwebpackember.jsckeditor

Can't load module that imports css via ember-auto-import


I am trying to build an ember 3.25 application that imports the CkEditor via ember-auto-import

I was able to get the editor working fine by adding the following to my package.json:

"@ckeditor/ckeditor5-build-classic": "^27.0.0",

and adding the following to my ember component:

import ClassicEditor from '@ckeditor/ckeditor5-build-classic';

...

didInsertElement() {
  var editor = ClassicEditor
    .create( document.querySelector( '#editor' ), {
      ...
    });
}

But when I tried to add the ImageResize module via:

"@ckeditor/ckeditor5-image": "^27.0.0",

and in my component (as instructed here):

import Image from '@ckeditor/ckeditor5-image/src/image';
import ImageResize from '@ckeditor/ckeditor5-image/src/image-resize';

I was initially seeing errors that read: 'UnhandledPromiseRejectionWarning: Error: webpack returned errors to ember-auto-import so I ran AUTO_IMPORT_VERBOSE=true ember serve

I now am seeing errors that ckeditor can't @import nested .css:

ERROR in ./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/toolbar.css 6:0
Module parse failed: Unexpected character '@' (6:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|  */
|
> @import "../../mixins/_unselectable.css";

Or use ES6 syntax:

ERROR in ./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/toolbardropdown.css 6:0
Module parse failed: Unexpected token (6:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|  */
|
> :root {

and errors where it tries to include svg files:

ERROR in ./node_modules/@ckeditor/ckeditor5-widget/theme/icons/drag-handle.svg 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M4 0v1H1v3H0V.5A.5.5 0 0 1 .5 0H4zm8 0h3.5a.5.5 0 0 1 .5.5V4h-1V1h-3V0zM4 16H.5a.5.5 0 0 1-.5-.5V12h1v3h3v1zm8 0v-1h3v-3h1v3.5a.5.5 0
 0 1-.5.5H12z"/><path fill-opacity=".256" d="M1 1h14v14H1z"/><g class="ck-icon__selected-indicator"><path d="M7 0h2v1H7V0zM0 7h1v2H0V7zm15 0h1v2h-1V7zm-8 8h2v1H7v-1z"/><path fill-opacity=".254" d="M1 1h14
v14H1z"/></g></svg>

Based on this message:

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

It seems like I need to add some webpack loaders to ember, but I'm really not sure how to do that. Can anyone help?


Solution

  • @ckeditor/ckeditor5-image/src/image imports a CSS file. As CKEditor uses CSS-in-JS and other features not part of ECMAScript specification you need to configure webpack to support it. CKEditor's documentation includes a minimal configuration example:

    const CKEditorWebpackPlugin = require("@ckeditor/ckeditor5-dev-webpack-plugin");
    const { styles } = require("@ckeditor/ckeditor5-dev-utils");
    
    module.exports = {
      plugins: [
        // ...
    
        new CKEditorWebpackPlugin({
          // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
          language: "pl",
        }),
      ],
    
      module: {
        rules: [
          {
            test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
            use: ["raw-loader"],
          },
          {
            test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
            use: [
              {
                loader: "style-loader",
                options: {
                  injectType: "singletonStyleTag",
                  attributes: {
                    "data-cke": true,
                  },
                },
              },
              {
                loader: "postcss-loader",
                options: styles.getPostCssConfig({
                  themeImporter: {
                    themePath: require.resolve("@ckeditor/ckeditor5-theme-lark"),
                  },
                  minify: true,
                }),
              },
            ],
          },
        ],
      },
    };
    

    Ember Auto Import allows you to provide custom webpack configuration as autoImport.webpack configuration key:

    // ember-cli-build.js
    
    let app = new EmberApp(defaults, {
      autoImport: {
        webpack: {
          // extra webpack configuration goes here
        },
      },
    });
    

    Please reach out to Ember Auto Import's documentation for details.

    Putting both together something like this should work:

    // ember-cli-build.js
    
    const CKEditorWebpackPlugin = require('@ckeditor/ckeditor5-dev-webpack-plugin');
    const { styles } = require('@ckeditor/ckeditor5-dev-utils');
    
    let app = new EmberApp(defaults, {
      autoImport: {
        webpack: {
          plugins: [
            new CKEditorWebpackPlugin({
              // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
              language: "pl",
            }),
          ],
          module: {
            rules: [
              {
                test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
                use: ["raw-loader"],
              },
              {
                test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
                use: [
                  {
                    loader: "style-loader",
                    options: {
                      injectType: "singletonStyleTag",
                      attributes: {
                        "data-cke": true,
                      },
                    },
                  },
                  {
                    loader: "postcss-loader",
                    options: styles.getPostCssConfig({
                      themeImporter: {
                        themePath: require.resolve(
                          "@ckeditor/ckeditor5-theme-lark"
                        ),
                      },
                      minify: true,
                    }),
                  },
                ],
              },
            ],
          },
        },
      },
    });
    

    UPDATE

    This solution almost worked but there are a few things to note:

    Importing ImageResize from '@ckeditor/ckeditor5-image/src/image-resize'; along side the ClassicEditor from '@ckeditor/ckeditor5-build-classic' did not work! Apparently that will cause duplicate-module-errors.

    So instead, follow the instructions on how to build from source after doing that, I found that the ckeditor styling was not loading in ember until I followed the instructions on extracting CSS and then everything worked!

    Here are the relevant pieces from my package.json:

    "@ckeditor/ckeditor5-adapter-ckfinder": "^27.0.0",
    "@ckeditor/ckeditor5-autoformat": "^27.0.0",
    "@ckeditor/ckeditor5-basic-styles": "^27.0.0",
    "@ckeditor/ckeditor5-block-quote": "^27.0.0",
    "@ckeditor/ckeditor5-ckfinder": "^27.0.0",
    "@ckeditor/ckeditor5-cloud-services": "^27.0.0",
    "@ckeditor/ckeditor5-core": "^27.0.0",
    "@ckeditor/ckeditor5-dev-utils": "^24.0.0",
    "@ckeditor/ckeditor5-dev-webpack-plugin": "^24.4.2",
    "@ckeditor/ckeditor5-easy-image": "^27.0.0",
    "@ckeditor/ckeditor5-editor-classic": "^27.0.0",
    "@ckeditor/ckeditor5-essentials": "^27.0.0",
    "@ckeditor/ckeditor5-heading": "^27.0.0",
    "@ckeditor/ckeditor5-image": "^27.0.0",
    "@ckeditor/ckeditor5-indent": "^27.0.0",
    "@ckeditor/ckeditor5-link": "^27.0.0",
    "@ckeditor/ckeditor5-list": "^27.0.0",
    "@ckeditor/ckeditor5-media-embed": "^27.0.0",
    "@ckeditor/ckeditor5-paragraph": "^27.0.0",
    "@ckeditor/ckeditor5-paste-from-office": "^27.0.0",
    "@ckeditor/ckeditor5-table": "^27.0.0",
    "@ckeditor/ckeditor5-theme-lark": "^27.0.0",
    "@ckeditor/ckeditor5-typing": "^27.0.0",
    "css-loader": "^5.2.2",
    "mini-css-extract-plugin": "^1.5.0",
    "postcss-loader": "^3.0.0",
    "raw-loader": "^3.1.0",
    "style-loader": "^2.0.0"
    

    And my ember-cli-build.js file:

      const CKEditorWebpackPlugin = require("@ckeditor/ckeditor5-dev-webpack-plugin");
      const { styles } = require("@ckeditor/ckeditor5-dev-utils");
      const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );
    
    
      module.exports = function (defaults) {
        let app = new EmberApp(defaults, {
    
         cssModules: {
           includeExtensionInModulePath: true,
         },
    
          sassOptions: {
           inputFiles: [  
             '/app/styles/app.scss',  
            ],
            includePaths: ['app', 'app/components']
          },
          // Add options here
          autoImport: {
            webpack: {
              plugins: [
               new CKEditorWebpackPlugin({
                  // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
                  // language: "en",
                }),
                new MiniCssExtractPlugin( {
                  filename: 'ckeditor.css'
                } )
              ],
              module: {
                rules: [
                  {
                    test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
                    use: ["raw-loader"],
                  },
                  {
                    test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
                    use: [
                      MiniCssExtractPlugin.loader,
                      'css-loader',
                      {
                        loader: "postcss-loader",
                        options: styles.getPostCssConfig({
                        themeImporter: {
                          themePath: require.resolve(
                            "@ckeditor/ckeditor5-theme-lark"
                          ),
                        },
                        minify: true,
                      }),
                    },
                  ],
                },
              ],
            },
          },
        },
      });
    
    
      return app.toTree();
    };
    

    And my ck-editor.js file (that I keep in the same directory as my component):

    import ClassicEditorBase from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
    import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials';
    import UploadAdapterPlugin from '@ckeditor/ckeditor5-adapter-ckfinder/src/uploadadapter';
    import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat';
    import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold';
    import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic';
    import BlockQuotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote';
    // import EasyImagePlugin from '@ckeditor/ckeditor5-easy-image/src/easyimage';
    import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading';
    import ImagePlugin from '@ckeditor/ckeditor5-image/src/image';
    import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
    import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle';
    import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
    import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload';
    import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
    import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
    import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';
    import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize';
    
    export default class ClassicEditor extends ClassicEditorBase {}
    
    ClassicEditor.builtinPlugins = [
        EssentialsPlugin,
        UploadAdapterPlugin,
        AutoformatPlugin,
        BoldPlugin,
        ItalicPlugin,
        BlockQuotePlugin,
        // EasyImagePlugin,
        HeadingPlugin,
        ImagePlugin,
        ImageCaptionPlugin,
        ImageStylePlugin,
        ImageToolbarPlugin,
        ImageUploadPlugin,
            ImageResize,
        LinkPlugin,
        ListPlugin,
        ParagraphPlugin
    ];
    
    ClassicEditor.defaultConfig = {
        toolbar: {
            items: [
                'heading',
                '|',
                'bold',
                'italic',
                'link',
                'bulletedList',
                'numberedList',
                'uploadImage',
                'blockQuote',
                'undo',
                'redo'
            ]
        },
        image: {
            toolbar: [
                'imageStyle:full',
                'imageStyle:side',
                '|',
                'imageTextAlternative'
            ]
        },
        language: 'en'
    };
    

    Now, from my component, I can just call:

    import ClassicEditor from './ck-editor';
    
    ...
    
    didInsertElement() {
      var editor = ClassicEditor
        .create( document.querySelector( '#editor' ), {
          ...
        });
    }