webpackckeditoriconstheming

NormalModuleReplacementPlugin does not work when modifying resourceRegExp slightly


As officially suggested on the CKEditor5 homepage, I want to replace the standard icons which are pre-shipped with several plugins. Since each plugin (e.g. ckeditor5-core or ckeditor5-alignment) offers its own .svg set in [PLUGIN_DIR]/theme/icons, the proposed strategy - to replace them with one's own modified - is understandable and clear:

(...) use webpack’s NormalModuleReplacementPlugin plugin

...
plugins: [
  new webpack.NormalModuleReplacementPlugin(
    /bold\.svg/,
    '/absolute/path/to/my/icon.svg'
  )
]

Based on this code I experimented and ended up with this:

...
plugins: [
  new webpack.NormalModuleReplacementPlugin(
    /\/theme\/icons\/[^/]+\.svg$/,
    resource => {
      console.log(resource.request);
      resource.request = path.resolve(
        THEME_PATH,
        "../../icons/coffee-solid.svg"
      );
    }
  ),
]

which cause following (wanted/expected) changes: before after

partial console output:

../../theme/icons/bold.svg
../../theme/icons/italic.svg
@ckeditor/ckeditor5-core/theme/icons/quote.svg
@ckeditor/ckeditor5-core/theme/icons/image.svg
../theme/icons/numberedlist.svg
../theme/icons/bulletedlist.svg
../theme/icons/align-justify.svg
../theme/icons/link.svg
(node:10394) DeprecationWarning: Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead
@ckeditor/ckeditor5-core/theme/icons/object-right.svg
@ckeditor/ckeditor5-core/theme/icons/object-center.svg
@ckeditor/ckeditor5-core/theme/icons/object-left.svg
@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg
@ckeditor/ckeditor5-core/theme/icons/low-vision.svg
../theme/icons/drag-handler.svg
@ckeditor/ckeditor5-core/theme/icons/cancel.svg
../theme/icons/redo.svg
../../../theme/icons/next-arrow.svg
../../../theme/icons/previous-arrow.svg
../theme/icons/undo.svg
../../../theme/icons/dropdown-arrow.svg
@ckeditor/ckeditor5-core/theme/icons/cancel.svg
@ckeditor/ckeditor5-core/theme/icons/check.svg
@ckeditor/ckeditor5-core/theme/icons/check.svg
@ckeditor/ckeditor5-core/theme/icons/pencil.svg
../../theme/icons/unlink.svg
../../theme/icons/image_placeholder.svg
../theme/icons/align-center.svg
../theme/icons/align-right.svg
../theme/icons/align-left.svg

However when I want to restrict the folder to @ckeditor by passing through a respective regex, this is what happens:

...
plugins: [
  new webpack.NormalModuleReplacementPlugin(
    /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg$/,
    resource => {
      console.log(resource.request);
      resource.request = path.resolve(
        THEME_PATH,
        "../../icons/coffee-solid.svg"
      );
    }
  ),
]

before (see above)

after

partial console output (2):

@ckeditor/ckeditor5-core/theme/icons/quote.svg
@ckeditor/ckeditor5-core/theme/icons/image.svg
(node:10368) DeprecationWarning: Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead
@ckeditor/ckeditor5-core/theme/icons/object-right.svg
@ckeditor/ckeditor5-core/theme/icons/object-center.svg
@ckeditor/ckeditor5-core/theme/icons/object-left.svg
@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg
@ckeditor/ckeditor5-core/theme/icons/low-vision.svg
@ckeditor/ckeditor5-core/theme/icons/pencil.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/bold.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/italic.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-list/theme/icons/numberedlist.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-list/theme/icons/bulletedlist.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-alignment/theme/icons/align-justify.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-link/theme/icons/link.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-widget/theme/icons/drag-handler.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-undo/theme/icons/redo.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-undo/theme/icons/undo.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-ui/theme/icons/next-arrow.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-ui/theme/icons/previous-arrow.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg
@ckeditor/ckeditor5-core/theme/icons/cancel.svg
@ckeditor/ckeditor5-core/theme/icons/check.svg
@ckeditor/ckeditor5-core/theme/icons/cancel.svg
@ckeditor/ckeditor5-core/theme/icons/check.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-link/theme/icons/unlink.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-alignment/theme/icons/align-center.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-alignment/theme/icons/align-right.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-alignment/theme/icons/align-left.svg
[PATH_TO_WS]/workspace/my-cute-editor/node_modules/raw-loader/dist/cjs.js![PATH_TO_WS]/workspace/my-cute-editor/node_modules/@ckeditor/ckeditor5-image/theme/icons/image_placeholder.svg

Why does it happen?

Note: I must use the syntax with resource (the single other alternative as far as I know), because I want to load the icons dynamically later on.


Solution

  • As I've answered in the https://github.com/ckeditor/ckeditor5/issues/1831:

    As you can see at the source code of the NormalModuleReplacementPlugin the regexp is tested two times, once before the resolution on the request and once after the resolution on the resource.

    As you might see most of the times your function is called after the resolution (as the requests don't match the regexp). The request after the resolution is enhanced by the loaders (that's why the paths are so long and contain weird characters) and actually, this option isn't used anymore. That's why you should change the result.resource instead of the result.request.

    E.g. try the following plugin:

    new webpack.NormalModuleReplacementPlugin(
        /ckeditor5-[^/]+\/theme\/icons\/bold\.svg/,
        result => {
            if ( result.resource ) {
                result.resource = result.resource.replace( 'bold', 'code' );
            }
        }
    )