React Module parse failed: Unexpected character '@'
Asked Answered
P

4

10

I am getting an error when trying to import the following in my react component:

import FontIconPicker from '@fonticonpicker/react-fonticonpicker';
import '@fonticonpicker/react-fonticonpicker/dist/fonticonpicker.base-theme.react.css';

I'm using this module: https://fonticonpicker.github.io/react-fonticonpicker/

I get this error:

./node_modules/@fonticonpicker/react-fonticonpicker/dist/fonticonpicker.base-theme.react.css Module parse failed: Unexpected character '@' (18:0) You may need an appropriate loader to handle this file type. | * | / | @font-face{font-family:fontIconPicker;src:url(assets/fontIconPicker.ttf) format("truetype"),url(assets/fontIconPicker.woff) format("woff"),url(assets/fontIconPicker.svg#fontIconPicker) format("svg");font-weight:400;font-style:normal}[class=" fipicon-"],[class^=fipicon-]{font-family:fontIconPicker!important;speak:none;font-style .......

The error can be reproduced with my code from github: https://github.com/gregbia/my-app

Use npm install, and npm start and the error will show.

My webpack looks like this:

/**
 * Webpack Configuration
 *
 * Working of a Webpack can be very simple or complex. This is an intenally simple
 * build configuration.
 *
 * Webpack basics — If you are new the Webpack here's all you need to know:
 *     1. Webpack is a module bundler. It bundles different JS modules together.
 *     2. It needs and entry point and an ouput to process file(s) and bundle them.
 *     3. By default it only understands common JavaScript but you can make it
 *        understand other formats by way of adding a Webpack loader.
 *     4. In the file below you will find an entry point, an ouput, and a babel-loader
 *        that tests all .js files excluding the ones in node_modules to process the
 *        ESNext and make it compatible with older browsers i.e. it converts the
 *        ESNext (new standards of JavaScript) into old JavaScript through a loader
 *        by Babel.
 *
 * TODO: Instructions.
 *
 * @since 1.0.0
 */

const paths = require( './paths' );
const autoprefixer = require( 'autoprefixer' );
const ExtractTextPlugin = require( 'extract-text-webpack-plugin' );

// Extract style.css for both editor and frontend styles.
const blocksCSSPlugin = new ExtractTextPlugin( {
    filename: './dist/blocks.style.build.css',
} );

// Extract editor.css for editor styles.
const editBlocksCSSPlugin = new ExtractTextPlugin( {
    filename: './dist/blocks.editor.build.css',
} );

// Configuration for the ExtractTextPlugin — DRY rule.
const extractConfig = {
    use: [
        // "postcss" loader applies autoprefixer to our CSS.
        { loader: 'raw-loader' },
        {
            loader: 'postcss-loader',
            options: {
                ident: 'postcss',
                plugins: [
                    autoprefixer( {
                        browsers: [
                            '>1%',
                            'last 4 versions',
                            'Firefox ESR',
                            'not ie < 9', // React doesn't support IE8 anyway
                        ],
                        flexbox: 'no-2009',
                    } ),
                ],
            },
        },
        // "sass" loader converts SCSS to CSS.
        {
            loader: 'sass-loader',
            options: {
                // Add common CSS file for variables and mixins.
                data: '@import "./src/common.scss";\n',
                outputStyle: 'nested',
            },
        },
    ],
};

// Export configuration.
module.exports = {
    entry: {
        './dist/blocks.build': paths.pluginBlocksJs, // 'name' : 'path/file.ext'.
    },
    output: {
        // Add /* filename */ comments to generated require()s in the output.
        pathinfo: true,
        // The dist folder.
        path: paths.pluginDist,
        filename: '[name].js', // [name] = './dist/blocks.build' as defined above.
    },
    // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
    devtool: 'cheap-eval-source-map',
    module: {
        rules: [
            {
                test: /\.(js|jsx|mjs)$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        // @remove-on-eject-begin
                        babelrc: false,
                        presets: [ require.resolve( 'babel-preset-cgb' ) ],
                        // @remove-on-eject-end
                        // 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,
                    },
                },
            },
            {
                test: /style\.s?css$/,
                exclude: /(node_modules|bower_components)/,
                use: blocksCSSPlugin.extract( extractConfig ),
            },
            {
                test: /editor\.s?css$/,
                exclude: /(node_modules|bower_components)/,
                use: editBlocksCSSPlugin.extract( extractConfig ),
            },
        ],
    },
    // Add plugins.
    plugins: [ blocksCSSPlugin, editBlocksCSSPlugin ],
    stats: 'minimal',
    // stats: 'errors-only',
    // Add externals.
    externals: {
        react: 'React',
        'react-dom': 'ReactDOM',
        ga: 'ga', // Old Google Analytics.
        gtag: 'gtag', // New Google Analytics.
        jquery: 'jQuery', // import $ from 'jquery' // Use the WordPress version.
    },
};
Perishable answered 20/12, 2018 at 7:56 Comment(0)
G
6

Actually, I'm so surprised that you used SCSS webpack configs beside the PostCSS because with a little configuration you can pre-process your CSSes and then post-process them to a compressed version by SCSS syntax. I set a config you in this link. I know it is not your main problem but I think your project configuration is not optimized.

The above link of webpack config will help you to make your configuration better and you can see the webpack configs of fonts. any way...

For your exact error, you should fix your font configuration on webpack just like below:

{
  test: /\.(woff|woff2|eot|ttf|svg)$/,
  exclude: /node_modules/,
  loader: 'file-loader',
  options: {
    limit: 1024,
    name: '[name].[ext]',
    publicPath: 'dist/assets/',
    outputPath: 'dist/assets/'
  }
},

Update after work on the repo:

I write the webpack.config.dev.js file like below:

const paths = require('./paths');
const externals = require('./externals');
const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

// Extract style.css for both editor and frontend styles.
const blocksCSSPlugin = new ExtractTextPlugin({
    filename: './dist/blocks.style.build.css',
});

// Extract editor.css for editor styles.
const editBlocksCSSPlugin = new ExtractTextPlugin({
    filename: './dist/blocks.editor.build.css',
});

// Configuration for the ExtractTextPlugin — DRY rule.
const extractConfig = {
    fallback: 'style-loader',
    use: [
        // "postcss" loader applies autoprefixer to our CSS.
        {loader: 'css-loader'},
        {
            loader: 'postcss-loader',
            options: {
                ident: 'postcss',
                plugins: [
                    autoprefixer({
                        browsers: [
                            '>1%',
                            'last 4 versions',
                            'Firefox ESR',
                            'not ie < 9', // React doesn't support IE8 anyway
                        ],
                        flexbox: 'no-2009',
                    }),
                ],
            },
        },
        // "sass" loader converts SCSS to CSS.
        {
            loader: 'sass-loader',
            options: {
                // Add common CSS file for variables and mixins.
                data: '@import "./src/common.scss";\n',
                outputStyle: 'nested',
            },
        },
    ],
};

// Export configuration.
module.exports = {
    entry: {
        './dist/blocks.build': paths.pluginBlocksJs, // 'name' : 'path/file.ext'.
    },
    output: {
        // Add /* filename */ comments to generated require()s in the output.
        pathinfo: true,
        // The dist folder.
        path: paths.pluginDist,
        filename: '[name].js', // [name] = './dist/blocks.build' as defined above.
    },
    // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
    devtool: 'cheap-eval-source-map',
    module: {
        rules: [
            {
                test: /\.(js|jsx|mjs)$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        // @remove-on-eject-begin
                        babelrc: false,
                        presets: [require.resolve('babel-preset-cgb')],
                        // @remove-on-eject-end
                        // 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,
                    },
                },
            },
            {
                test: /style\.s?css$/,
                exclude: /(node_modules|bower_components)/,
                use: blocksCSSPlugin.extract(extractConfig),
            },
            {
                test: /editor\.s?css$/,
                exclude: /(node_modules|bower_components)/,
                use: editBlocksCSSPlugin.extract(extractConfig),
            },
            {
                test: /\.css$/,
                include: /(node_modules\/@fonticonpicker\/react-fonticonpicker\/dist)/,
                loaders: ['style-loader', 'css-loader']
            },
            {
                test: /\.(woff|woff2|eot|ttf|svg)$/,
                include: /(node_modules\/@fonticonpicker\/react-fonticonpicker\/dist)/,
                loader: 'file-loader',
                options: {
                    limit: 1024,
                    name: '[name].[ext]',
                    publicPath: 'dist/assets/',
                    outputPath: 'dist/assets/'
                }
            }
        ],
    },
    // Add plugins.
    plugins: [blocksCSSPlugin, editBlocksCSSPlugin],
    stats: 'minimal',
    // stats: 'errors-only',
    // Add externals.
    externals: externals,
};

And also install css-loader and file-loader.

npm install file-loader css-loader

Hint: it seems fonts need to have an outputPath in webpack configuration.

Gladwin answered 26/12, 2018 at 7:26 Comment(0)
M
4

The problem is webpack is not loading your fonts @font-face in node_modules. You are excluding the loading of css from node_modules. But your @fonticonpicker/react-fonticonpicker/dist/fonticonpicker.base-theme.react.css is in node_modules.

Change this snippet in your webpack config

{
  test: /style\.s?css$/,
  exclude: /(node_modules|bower_components)/,
  use: blocksCSSPlugin.extract( extractConfig ),
},
{
  test: /editor\.s?css$/,
  exclude: /(node_modules|bower_components)/,
  use: editBlocksCSSPlugin.extract( extractConfig ),
},

to

{
  test: /style\.s?css$/,
  use: blocksCSSPlugin.extract( extractConfig ),
},
{
  test: /editor\.s?css$/,
  use: editBlocksCSSPlugin.extract( extractConfig ),
},
{ test: /(\.css$)/, // you need to load all css imported from node_modules
  loaders: ['style-loader', 'css-loader', 'postcss-loader']
}
Merrythought answered 23/12, 2018 at 11:42 Comment(0)
N
3

Seems like you're missing css-loader for .css stored in node_modules. That is why you are facing this issue. Run npm i -D css-loader and add this rule to your node_modules > cgb-scrips > config > webpack.config.<env>.js file:

module: {
  rules: [
    // ...

    {
      test: /\.css$/,
      use: [
        { loader: 'raw-loader' },
        { loader: 'css-loader' },
      ]
    },

    // ...
  ],
},

Alternatively, to skip editing webpack.config.js you could simply import your files like so:

import 'raw-loader!css-loader!@fonticonpicker/react-fonticonpicker/dist/fonticonpicker.base-theme.react.css';
import 'raw-loader!css-loader!@fonticonpicker/react-fonticonpicker/dist/fonticonpicker.material-theme.react.css';
Nosegay answered 22/12, 2018 at 11:37 Comment(0)
M
2

Your loader config in webpack doesn't match the CSS file Route.

import '@fonticonpicker/react-fonticonpicker/dist/fonticonpicker.base-theme.react.css';

It is neither style.css or editor.css. Hence you get an error. Also you are ignoring node_modules in your webpack loader config but you import the css from node_modules.

Adding

  {
       test: /react\.s?css$/,
       use: [{
        loader: 'css-loader',
        options: {
          modules: true
        }
      }],
  },

should work

Mauro answered 20/12, 2018 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.