How to configure Next.js with Antd / Less and Sass / CSS modules - sass

I want to use Next.js with Sass and CSS modules but also want to use Ant Design and wanted to use the Less styles for smaller building size.
I'm able to enable either CSS modules or Less loader but not both at the same time. The examples from Next.js were not helping me complete that problem.

Edit: This answer is definitely outdated for current versions of next.js, check the other answers below.
After multiple hours of research I found now finally the right solution and wanted to share it:
.babelrc (no magic here)
{
"presets": ["next/babel"],
"plugins": [
[
"import",
{
"libraryName": "antd",
"style": true
}
]
]
}
next.config.js:
/* eslint-disable */
const withLess = require('#zeit/next-less');
const withSass = require('#zeit/next-sass');
const lessToJS = require('less-vars-to-js');
const fs = require('fs');
const path = require('path');
// Where your antd-custom.less file lives
const themeVariables = lessToJS(
fs.readFileSync(path.resolve(__dirname, './assets/antd-custom.less'), 'utf8')
);
module.exports = withSass({
cssModules: true,
...withLess({
lessLoaderOptions: {
javascriptEnabled: true,
modifyVars: themeVariables, // make your antd custom effective
importLoaders: 0
},
cssLoaderOptions: {
importLoaders: 3,
localIdentName: '[local]___[hash:base64:5]'
},
webpack: (config, { isServer }) => {
//Make Ant styles work with less
if (isServer) {
const antStyles = /antd\/.*?\/style.*?/;
const origExternals = [...config.externals];
config.externals = [
(context, request, callback) => {
if (request.match(antStyles)) return callback();
if (typeof origExternals[0] === 'function') {
origExternals[0](context, request, callback);
} else {
callback();
}
},
...(typeof origExternals[0] === 'function' ? [] : origExternals)
];
config.module.rules.unshift({
test: antStyles,
use: 'null-loader'
});
}
return config;
}
})
});
The final hint how to write the withSass withLess use and to put the cssModules: true in the outer object came from this comment here.
While I was already trying different combinations derived from the examples before:
next+ant+less
next+sass
For completion here the dependencies in my package.json:
...
"dependencies": {
"#zeit/next-less": "^1.0.1",
"#zeit/next-sass": "^1.0.1",
"antd": "^4.1.3",
"babel-plugin-import": "^1.13.0",
"less": "^3.11.1",
"less-vars-to-js": "^1.3.0",
"next": "^9.3.4",
"node-sass": "^4.13.1",
"null-loader": "^3.0.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"sass": "^1.26.3"
}
...
I hope this helps other people to find this solution faster. :)

#zeit/next-less is deprecated and disables Next's built in CSS support. It also uses a very old version of less and less-loader.
I created a package that injects Less support to Next.js by duplicating the SASS rules and setting them for Less files and less-loader. It works with webpack5 flag.
https://github.com/elado/next-with-less
https://www.npmjs.com/package/next-with-less

While the above answers may work for versions of NextJS lower than 11, they do not work for 11+. I've found excellent success with the following plugin...
https://github.com/SolidZORO/next-plugin-antd-less

I am using elado's package which is -
https://github.com/elado/next-with-less
you will need less and less-loader as dependencies.
after that create a global.less file on styles folder. so it's like ,root> style > global.less and paste this code
#import '~antd/lib/style/themes/default.less';
#import '~antd/dist/antd.less';
#primary-color: #ff9b18;
#border-radius-base: 20px;
and add below code in your next.config.js file which you will create on your root folder.
// next.config.js
const withLess = require("next-with-less");
module.exports = withLess({
lessLoaderOptions: {
/* ... */
},
});

To add Less to the Next.js is easy way.
Need to add 'next-with-less' library (also install less and less-loader) and 'next-compose-plugin'.
To your next.config.js add:
/** #type {import('next').NextConfig} */
const withPlugins = require('next-compose-plugins');
const withLess = require('next-with-less');
const plugins = [
[
withLess,
{
lessLoaderOptions: {},
},
],
];
module.exports = withPlugins(plugins, {
reactStrictMode: true,
swcMinify: true,
});

In our project, there were old scss and css files. They were not using the Next js guidline for CSS modules. So I had to override webpack.config.js. So that works fine. But when we moved the file to the monorepo shared package, babel was not transpiling them. I used the below things, but those did not work for SCSS modules without a .module extension.
next-transile-module.
experimental: { externalDir: true, } in next Js config with root babel.cofig.json
Finally symlink hack worked for external shared files
Use symlinks for shared folder by updating next.config.js
module.exports = {
//...
resolve: {
symlinks: false,
},
};

For anyone who is still having trouble, you don't need any extra package other than our lovely mini-css-extract-plugin. Here is how you solve the issue.
PS: I also added sass to my webpack config. You can use both less and sass/scss files in your project.
next.config.js:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
webpack(config) {
config.module.rules.push(
{
// this part is for css
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader' }],
},
{
// this part is for sass
test: /\.module\.(scss|sass)$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
},
{
// this part is for less
test: /\.less$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
},
{
loader: 'less-loader',
options: {
sourceMap: true,
lessOptions: {
javascriptEnabled: true,
},
},
},
],
}
);
config.plugins.push(
new MiniCssExtractPlugin({
filename: 'static/css/[name].css',
chunkFilename: 'static/css/[contenthash].css',
})
);
return config;
},
};
package.json:
"dependencies": {
"#next/font": "13.1.1",
"css-loader": "^6.7.3",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.7.2",
"next": "13.1.1",
"next-transpile-modules": "^10.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"sass": "^1.57.1",
"sass-loader": "^13.2.0",
}

Related

React Native Web implement SSR

https://necolas.github.io/react-native-web/docs/rendering/
After reading the SSR example from the document, I still don't know how to implement SSR
And I don't want to apply SSR with other framework like NextJS
Can anyone show me an example or give me some advice
I'm posting this, not as a direct answer to the original question, because it's targeted directly SSR with NextJS, and the OP needed SSR independently from frameworks like NextJS. However, understanding it with NextJS can get anyone closer with things, because they key relies in Webpack config that NextJS also use as SSR in its encapsulation config.
First thing to know is that, once a Package has been written for React Native, it need to be transpiled first to be able to be used in Web, with webpack config.externals.
let modulesToTranspile = [
'react-native',
'react-native-dotenv',
'react-native-linear-gradient',
'react-native-media-query',
'react-native-paper',
'react-native-view-more-text',
// 'react-native-vector-icons',
];
Then you need to alias some react-native packages to react-native-web equivalent to let package use web version of modules like:
config.resolve.alias = {
...(config.resolve.alias || {}),
// Transform all direct `react-native` imports to `react-native-web`
'react-native$': 'react-native-web',
'react-native-linear-gradient': 'react-native-web-linear-gradient',
};
At this point, you almost get the essential. The rest is normal Webpack config for the normal Application. Also, it needs some additional config in native config file too. I will post all configs content.
For NextJS: next.config.js :
const path = require('path');
let modulesToTranspile = [
'react-native',
'react-native-dotenv',
'react-native-linear-gradient',
'react-native-media-query',
'react-native-paper',
'react-native-view-more-text',
// 'react-native-vector-icons',
];
// console.log('modules to transpile', modulesToTranspile);
// import ntm = from 'next-transpile-modules';
// const withTM = ntm(modulesToTranspile);
// logic below for externals has been extracted from 'next-transpile-modules'
// we won't use this modules as they don't allow package without 'main' field...
// https://github.com/martpie/next-transpile-modules/issues/170
const getPackageRootDirectory = m =>
path.resolve(path.join(__dirname, 'node_modules', m));
const modulesPaths = modulesToTranspile.map(getPackageRootDirectory);
const hasInclude = (context, request) => {
return modulesPaths.some(mod => {
// If we the code requires/import an absolute path
if (!request.startsWith('.')) {
try {
const moduleDirectory = getPackageRootDirectory(request);
if (!moduleDirectory) {
return false;
}
return moduleDirectory.includes(mod);
} catch (err) {
return false;
}
}
// Otherwise, for relative imports
return path.resolve(context, request).includes(mod);
});
};
const configuration = {
node: {
global: true,
},
env: {
ENV: process.env.NODE_ENV,
},
// optimizeFonts: false,
// target: 'serverless',
// bs-platform
// pageExtensions: ['jsx', 'js', 'bs.js'],
// options: { buildId, dev, isServer, defaultLoaders, webpack }
webpack: (config, options) => {
// config.experimental.forceSwcTransforms = true;
// console.log('fallback', config.resolve.fallback);
if (!options.isServer) {
// We shim fs for things like the blog slugs component
// where we need fs access in the server-side part
config.resolve.fallback.fs = false;
} else {
// SSR
// provide plugin
config.plugins.push(
new options.webpack.ProvidePlugin({
requestAnimationFrame: path.resolve(__dirname, './polyfills/raf.js'),
}),
);
}
// react-native-web
config.resolve.alias = {
...(config.resolve.alias || {}),
// Transform all direct `react-native` imports to `react-native-web`
'react-native$': 'react-native-web',
'react-native-linear-gradient': 'react-native-web-linear-gradient',
};
config.resolve.extensions = [
'.web.js',
'.web.ts',
'.web.tsx',
...config.resolve.extensions,
];
config.externals = config.externals.map(external => {
if (typeof external !== 'function') {
return external;
}
return async ({ context, request, getResolve }) => {
if (hasInclude(context, request)) {
return;
}
return external({ context, request, getResolve });
};
});
const babelLoaderConfiguration = {
test: /\.jsx?$/,
use: options.defaultLoaders.babel,
include: modulesPaths,
// exclude: /node_modules[/\\](?!react-native-vector-icons)/,
};
babelLoaderConfiguration.use.options = {
...babelLoaderConfiguration.use.options,
cacheDirectory: false,
// For Next JS transpile
presets: ['next/babel'],
plugins: [
['react-native-web', { commonjs: true }],
['#babel/plugin-proposal-class-properties'],
// ['#babel/plugin-proposal-object-rest-spread'],
],
};
config.module.rules.push(babelLoaderConfiguration);
return config;
},
};
// module.exports = withTM(config);
module.exports = configuration;
SSR will fail to build when missing some functions at server side. The most popular with React Native is requestAnimationFrame. I added it add a Webpack Plugin to mimic it. It can be an empty function or Polyfill:
The file 'polyfills/raf.js(I just put it assetImmediate`):
const polys = { requestAnimationFrame: setImmediate };
module.exports = polys.requestAnimationFrame;
The Babel config is necessary for the last part of it, couldn't work directly in next config. babel.config.js :
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [['module:react-native-dotenv'], 'react-native-reanimated/plugin'],
};
And finally, my list of packages in package.json:
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"android": "react-native run-android",
"android:dev": "adb reverse tcp:8081 tcp:8081 && react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint .",
"web": "webpack serve -d source-map --mode development --config \"./web/webpack.config.js\" --inline --color --hot",
"build:web": "webpack --mode production --config \"./web/webpack.config.js\" --hot",
"next:dev": "next",
"next:build": "next build",
"next:start": "next start",
"next:analyze": "ANALYZE=true next build"
},
"dependencies": {
"#material-ui/core": "^4.12.4",
"#react-native-async-storage/async-storage": "^1.17.3",
"#react-navigation/drawer": "^6.4.1",
"#react-navigation/native": "^6.0.10",
"#react-navigation/stack": "^6.2.1",
"#reduxjs/toolkit": "^1.8.1",
"axios": "^0.21.1",
"local-storage": "^2.0.0",
"lottie-ios": "^3.2.3",
"lottie-react-native": "^5.1.3",
"lottie-web": "^5.9.4",
"moment": "^2.29.1",
"next": "^12.1.6",
"nookies": "^2.5.2",
"numeral": "^2.0.6",
"raf": "^3.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-native": "0.68.1",
"react-native-dotenv": "^2.5.5",
"react-native-gesture-handler": "^2.4.2",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-linear-gradient": "^2.5.6",
"react-native-media-query": "^1.0.9",
"react-native-paper": "^4.12.1",
"react-native-progress": "^5.0.0",
"react-native-read-more-text": "^1.1.2",
"react-native-reanimated": "^2.8.0",
"react-native-safe-area-context": "^4.2.5",
"react-native-screens": "^3.13.1",
"react-native-share-menu": "^6.0.0",
"react-native-svg": "^12.3.0",
"react-native-svg-transformer": "^1.0.0",
"react-native-vector-icons": "^9.1.0",
"react-native-view-more-text": "^2.1.0",
"react-native-web": "^0.17.7",
"react-native-web-linear-gradient": "^1.1.2",
"react-redux": "^8.0.1"
},
"devDependencies": {
"#babel/plugin-proposal-class-properties": "^7.14.5",
"#next/bundle-analyzer": "^12.2.2",
"#react-native-community/eslint-config": "^2.0.0",
"#swc/cli": "^0.1.57",
"#swc/core": "^1.2.179",
"eslint": "^7.28.0",
"metro-react-native-babel-preset": "^0.66.0",
"url-loader": "^4.1.1",
"webpack": "^5.39.1",
"webpack-cli": "^4.7.2"
},
"jest": {
"preset": "react-native-web"
},
"sideEffects": false
}
NB: only React-Native packages used also in Web has to be transpiled. Some React-Native packages can be used ONLY in Native, so transpiling them for Web will add up unnecessary chunks of heavy codes in the Web, which is not good. React-Native-Web/React-Native is already more heavy for Web than normal packages made directly for Web.
TIPS to keep it cool with NextJS
Avoid writing conditional Platform.OS === 'web' on small components where you plan to use either a React-Native module or a Web module, which can cause all of them to load unnecessary Native-Only package on web codes. If size is not important, then you can ignore it. Add extension .web.js and .native.js at the end and separate the small codes. For example I write separate Functions and Components for : Storage.web.js, Storage.native.js, CustomLink.web.js, CustomLink.native.js, and hooks useCustomNavigation.web.js, useCustomNavigation.native.js, so that I call CustomLink in place of NextJS Link/router and React-Navigation Link/navigation.
I use react-native-media-query package as life saver for advanced media queries for all SSR/CSR Web and Native responsive display. The App can be restructured on big screen like normal Desktop Web, and be shrunk to Mobile View on the go, EXACTLY LIKE Material-UI on NextJS.

Webpack compile sass into css file problem

Firstly I'd like to say that I've seen same question but I cannot upvote nor comment to say that I'm in the same need. So it's clearly a duplicate of How to use Webpack 4 to compile Sass files properly?
I'm just trying to compile some sass file into css file using webpack. But webpack creates some useless js files with the same name as the css files.
The folder Structure:
--dist
--src
|--a
|--editor.scss
|--style.scss
|--b
|--editor.scss
|--style.scss
and my dependencies (package.json)
"devDependencies": {
"webpack": "latest",
"webpack-cli": "latest",
"css-loader": "latest",
"sass-loader": "latest",
"node-sass": "latest",
"mini-css-extract-plugin": "latest"
}
here the config I've set up already (webpack.config.js):
const path = require("path");
const context = path.resolve(__dirname);
const output = context + "/dist";
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const entries = {
"/dist/a/editor": "./src/a/editor.scss",
"/dist/a/style": "./src/a/style.scss",
"/dist/b/editor": "./src/b/editor.scss",
"/dist/b/style": "./src/b/style.scss"
};
module.exports = {
context: context,
entry: entries,
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].css"
})
],
output: {
path: output
// // publicPath: distBlocks,
// // filename: "[name].js"
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: "css-loader"
},
{
loader: "sass-loader"
}
]
}
]
}
};
Actual result:
--dist
|--a
|--editor.css
|--editor.js
|--style.css
|--style.js
|--b
|--editor.css
|--editor.js
|--style.css
|--style.js
--src
|--a
|--editor.scss
|--style.scss
|--b
|--editor.scss
|--style.scss
Expected result is the same without those useless js files...
So How webpack is suppose to be configured to get the css file only and no js files from the scss??
UPDATE:
Here is a workaround : webpack-fix-style-only-entries
webpack.config.js
...
const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries");
...
Module.exports = {
...
plugins: [
new FixStyleOnlyEntriesPlugin(),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].css"
})
],
...
There is a fix but only for the future version of webpack (^5) if understood correctly webpack git

Laravel + VueJs + Webpack + Karma = world of pain

Is it possible to write unit tests for VueJs if you are using Laravel's Elixir for your webpack configuration?
VueJs 2x has a very simple example for a component test: Vue Guide Unit testing
<template>
<span>{{ message }}</span>
</template>
<script>
export default {
data () {
return {
message: 'hello!'
}
},
created () {
this.message = 'bye!'
}
}
</script>
and then...
// Import Vue and the component being tested
import Vue from 'vue'
import MyComponent from 'path/to/MyComponent.vue'
describe('MyComponent', () => {
it('has a created hook', () => {
expect(typeof MyComponent.created).toBe('function')
})
it ...etc
})
and gives an example of a karma conf file here: https://github.com/vuejs-templates
But the Karma configuration file requires a webpack configuration file
webpack: webpackConfig,
The only problem is the Laravel's Elixir is creating the webpack configuration so it can't be included.
I have tried creating another webpack configuration file based on the example from https://github.com/vuejs-templates/webpack.
Something like this:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// Since sass-loader (weirdly) has SCSS as its default parse mode, we map
// the "scss" and "sass" values for the lang attribute to the right configs here.
// other preprocessors should work out of the box, no loader config like this necessary.
'scss': 'vue-style-loader!css-loader!sass-loader',
'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
and included it like...
// Karma configuration
// Generated on Wed Mar 15 2017 09:47:48 GMT-0500 (CDT)
var webpackConf = require('./karma.webpack.config.js');
delete webpackConf.entry;
module.exports = function(config) {
config.set({
webpack: webpackConf, // Pass your webpack.config.js file's content
webpackMiddleware: {
noInfo: true,
stats: 'errors-only'
},
But I am getting errors that seem to indicate that webpack isn't doing anything.
ERROR in ./resources/assets/js/components/test.vue
Module parse failed: /var/www/test/resources/assets/js/components/test.vue Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| <template>
| <span >{{test}}</span>
| </template>
Ok, I got this to work. Couple of things that might help.
I was originally running gulp, and trying to run tests in my vagrant box, to try to match the server configuration. I think that makes it much harder to find examples and answers on the internet.
Ok, so the main problem I was having is that webpack wasn't processing my components included in my test files. I copied the webpack config out of the laravel-elixir-vue-2/index.js node module directly into the Karma configuration file and it started working.
The key is that karma-webpack plugin needs both the resolve and module loader configuration settings (resolve with alias and extensions) for it to work.
Hope this helps someone.
karma.conf.js:
module.exports = function (config) {
config.set({
// to run in additional browsers:
// 1. install corresponding karma launcher
// http://karma-runner.github.io/0.13/config/browsers.html
// 2. add it to the `browsers` array below.
browsers: ['Chrome'],
frameworks: ['jasmine'],
files: ['./index.js'],
preprocessors: {
'./index.js': ['webpack']
},
webpack: {
resolve: {
alias: {
vue: 'vue/dist/vue.common.js'
},
extensions: ['.js', '.vue']
},
vue: {
buble: {
objectAssign: 'Object.assign'
}
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'file-loader',
query: {
limit: 10000,
name: '../img/[name].[hash:7].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
query: {
limit: 10000,
name: '../fonts/[name].[hash:7].[ext]'
}
}
]
}
},
webpackMiddleware: {
noInfo: true,
},
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' },
]
},
});
};
I ran into the exact same problem. The accepted answer did not fully work for me. The following solved my issue:
Install relevant loaders for webpack:
npm install --save-dev vue-loader file-loader url-loader
Create webpack config file (note the format). The accepted answer produced errors citing invalid format of the webpack.config.js file. At least with me it did.
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: [
{ loader: 'vue-loader' }
]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: [
{
loader: 'file-loader',
query: {
limit: 10000,
name: '../img/[name].[hash:7].[ext]'
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: [
{
loader: 'url-loader',
query: {
limit: 10000,
name: '../fonts/[name].[hash:7].[ext]'
}
}
]
}
]
}
}
karma.conf.js
// Karma configuration
var webpackConf = require('./webpack.config.js');
delete webpackConf.entry
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
port: 9876, // web server port
colors: true,
logLevel: config.LOG_INFO,
reporters: ['progress'], // dots, progress
autoWatch: true, // enable / disable watching files & then run tests
browsers: ['Chrome'], //'PhantomJS', 'Firefox',
singleRun: true, // if true, Karma captures browsers, runs the tests and exits
concurrency: Infinity, // how many browser should be started simultaneous
webpack: webpackConf, // Pass your webpack.config.js file's content
webpackMiddleware: {
noInfo: true,
stats: 'errors-only'
},
/**
* base path that will be used to resolve all patterns (eg. files, exclude)
* This should be your JS Folder where all source javascript
* files are located.
*/
basePath: './resources/assets/js/',
/**
* list of files / patterns to load in the browser
* The pattern just says load all files within a
* tests directory including subdirectories
**/
files: [
{pattern: 'tests/*.js', watched: false},
{pattern: 'tests/**/*.js', watched: false}
],
// list of files to exclude
exclude: [
],
/**
* pre-process matching files before serving them to the browser
* Add your App entry point as well as your Tests files which should be
* stored under the tests directory in your basePath also this expects
* you to save your tests with a .spec.js file extension. This assumes we
* are writing in ES6 and would run our file through babel before webpack.
*/
preprocessors: {
'app.js': ['webpack', 'babel'],
'tests/**/*.spec.js': ['babel', 'webpack']
},
})
}
Then run karma start and everything should work.

Webpack, sass being overwriten

I'll start by saying I have a bit of a bizarre webpack build. I have 2 projects, the main project and the shared project (like a common library).
Whenever I refer to my shared project from the main project, it get some bizarre IE only/react/webpack build error where I get an error when trying to refer to my shared library. To solve this, I aliased my shared library references to the actual source on my computer in a different folder. That looks something like this:
var alias.shared-library = 'c:\\shared-library\\index.js';
This fixed my bizarre IE problem. However, another issue raised in my webpack production build. On the production server, the shared library exists within the node_modules folder, so I tried something like this:
var alias.shared-library = 'node_modules\\shared-library\\index.js';
It mostly works, except all my scss from the main project is overwritten by scss from the shared project (the output css only contains scss from the shared lib and not main). My full setup looks something like this (I removed parts I thought weren't important):
var SHARED_PATH = path.resolve(__dirname, 'node_modules/shared-library');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractCss = new ExtractTextPlugin('css.styles.css');
const extractScss = new ExtractTextPlugin('scss.styles.css');
const plugs = [];
plugs.push(
new webpack.DefinePlugin({
__DEVELOPMENT__: false
})
);
plugs.push(
new HtmlWebpackPlugin({
template: './index.html',
inject: 'body',
chunks: ['app', 'vendor']
})
);
plugs.push(new webpack.NoErrorsPlugin());
plugs.push(
new CopyWebpackPlugin([
{ from: 'src/assets', to: 'assets' },
])
);
plugs.push(
new SplitByPathPlugin([
{ name: 'vendor', path: [path.join(__dirname, 'node_modules')] },
])
);
plugs.push(extractCss);
plugs.push(extractScss);
plugs.push(
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
})
);
var alias = {
'shared-library': path.resolve(SHARED_PATH, '\\src\\index.js')
};
var loaderDirs = ['node_modules', 'webpack_loaders'];
var moduleDirs = ['node_modules', 'src'];
loaderDirs.push(SHARED_PATH + '\\node_modules');
moduleDirs.push(SHARED_PATH + '\\node_modules');
moduleDirs.push(SHARED_PATH + '\\src');
module.exports = {
entry: {
app: ['./src/index.js']
},
resolve: {
extensions: ['', '.js', '.scss', '.css'],
modulesDirectories: moduleDirs,
alias
},
resolveLoader: {
modulesDirectories: loaderDirs
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[hash].js',
publicPath: '/main/',
sourceMapFilename: '[name].[hash].js.map',
chunkFilename: '[id].chunk.js',
},
plugins: plugs,
module: {
loaders: [
{
test: /\.js?$/,
loader: 'babel?sourceMaps=true',
include: [/src/, path.resolve(SHARED_PATH, '\\src\\index.js')]
},
{
test: /\.css$/,
loader: extractCss.extract('style', 'css')
},
{
test: /\.scss$/,
loader: extractScss.extract('style', 'css!sass'),
include: /src/ // Trying to exclude scss from shared-library, doesn't work though
},
loaders.json,
loaders.image,
loaders.svg,
loaders.font,
loaders.locale,
loaders.html
]
},
postcss: (webpack) => {
return [
autoprefixer({
browsers: ['last 2 versions'],
}),
postcssImport({
addDependencyTo: webpack,
})
];
},
externals: {
'react/lib/ReactContext': 'window',
'react/lib/ExecutionEnvironment': 'window',
'react/addons': true
}
};
I've tried things like different permutations of exclude/include on my loaders. I thought the allChunks option on ExtractTextPlugin might work:
const extractScss = new ExtractTextPlugin({filename: 'scss.styles.css', allChunks: true});
but it just gives me this error:
TypeError: path.replace is not a function
at Template.replacePathVariables (c:\r2\RCA\rca-ui\node_modules\webpack\lib\
TemplatedPathPlugin.js:70:4)
at Template.applyPlugins [as applyPluginsWaterfall] (c:\r2\RCA\rca-ui\node_m
odules\webpack\node_modules\tapable\lib\Tapable.js:37:47)
at Compilation.getPath (c:\r2\RCA\rca-ui\node_modules\webpack\lib\Compilatio
n.js:882:27)
at ExtractTextPlugin.<anonymous> (c:\r2\RCA\rca-ui\node_modules\extract-text
-webpack-plugin\index.js:300:29)
at Array.forEach (native)
at ExtractTextPlugin.<anonymous> (c:\r2\RCA\rca-ui\node_modules\extract-text
-webpack-plugin\index.js:289:20)
at Compilation.next (c:\r2\RCA\rca-ui\node_modules\webpack\node_modules\tapa
ble\lib\Tapable.js:69:14)
at ExtractTextPlugin.<anonymous> (c:\r2\RCA\rca-ui\node_modules\extract-text
-webpack-plugin\index.js:309:4)
at Compilation.applyPluginsAsync (c:\r2\RCA\rca-ui\node_modules\webpack\node
_modules\tapable\lib\Tapable.js:71:13)
at Compilation.<anonymous> (c:\r2\RCA\rca-ui\node_modules\webpack\lib\Compil
ation.js:563:8)
at Compilation.next (c:\r2\RCA\rca-ui\node_modules\webpack\node_modules\tapa
ble\lib\Tapable.js:67:11)
at ExtractTextPlugin.<anonymous> (c:\r2\RCA\rca-ui\node_modules\extract-text
-webpack-plugin\index.js:285:5)
at c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\node_modules\as
ync\lib\async.js:52:16
at done (c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\node_modu
les\async\lib\async.js:246:17)
at c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\node_modules\as
ync\lib\async.js:44:16
at c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\index.js:269:6
at c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\node_modules\as
ync\lib\async.js:52:16
at done (c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\node_modu
les\async\lib\async.js:246:17)
at c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\node_modules\as
ync\lib\async.js:44:16
at c:\r2\RCA\rca-ui\node_modules\extract-text-webpack-plugin\index.js:259:9
at c:\r2\RCA\rca-ui\node_modules\webpack\lib\Compilation.js:469:4
at Array.forEach (native)
at callback (c:\r2\RCA\rca-ui\node_modules\webpack\lib\Compilation.js:468:14
)
at Compilation.<anonymous> (c:\r2\RCA\rca-ui\node_modules\webpack\lib\Compil
ation.js:489:4)
at c:\r2\RCA\rca-ui\node_modules\webpack\lib\Compilation.js:332:10
at c:\r2\RCA\rca-ui\node_modules\webpack\node_modules\async\lib\async.js:52:
16
at Object.async.forEachOf.async.eachOf (c:\r2\RCA\rca-ui\node_modules\webpac
k\node_modules\async\lib\async.js:236:30)
at Object.async.forEach.async.each (c:\r2\RCA\rca-ui\node_modules\webpack\no
de_modules\async\lib\async.js:209:22)
at Compilation.addModuleDependencies (c:\r2\RCA\rca-ui\node_modules\webpack\
lib\Compilation.js:185:8)
at Compilation.processModuleDependencies (c:\r2\RCA\rca-ui\node_modules\webp
ack\lib\Compilation.js:170:7)
Any ideas on the proper approach to solve this problem? Would the allChunks option help me here? Do I need to tighten up my include/excludes? Is there something else I'm missing? Should I give up and try and fix the IE problem another way? Is there a way I can debug what is happening? Here's the versions of the libraries I am using:
"extract-text-webpack-plugin": "1.0.1",
"style-loader": "0.13.0",
"sass-loader": "3.1.2",
"webpack": "1.13.2"
Thanks

Using Slick-Carousel and NPM

Still trying to master NPM and hit some roadblocks.
My slick-carousel works great when I use the CDN process. But I'm doing everything in NPM so figured I should do the same with this plug-in, but can't seem to get it off the ground.
Ran the install:
npm install slick-carousel --save
Which adds to my package.json file:
"devDependencies": {
"copy-webpack-plugin": "^3.0.1",
"css-loader": "^0.23.1",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"gh-pages": "^0.11.0",
"html-webpack-plugin": "^2.21.0",
"img-loader": "^1.3.1",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.1"
},
"dependencies": {
"font-awesome-webpack": "0.0.4",
"jquery": "^3.0.0",
"slick-carousel": "^1.6.0"
}
I'm smart enough to know that I need to require the file in my index.js file:
var $ = require('jquery');
require("../css/style.css");
require("font-awesome-webpack");
require("slick-carousel");
I can see that I now have all the jQuery for slick-carousel, but none of the css.
Now I figure I should require the two .css files living in the node_modules folder:
require("slick-carousel/slick/slick.css");
require("slick-carousel/slick/slick-theme.css");
And this is where it all breaks. The slick.css file loads and the basic slick-carousel is now working in my html output. But the slick-theme file breaks everything by pushing this error:
./~/slick-carousel/slick/ajax-loader.gif
Module parse failed: /Users/ryanbuchholtz/Documents/thinkful/haventower/node_modules/slick-carousel/slick/ajax-loader.gif Unexpected character '' (1:7)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '' (1:7)
This makes me think something is broken in my webpack.config.js:
var path = require('path');
var packageData = require('./package.json');
var filename = [packageData.name, packageData.version, 'js'];
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var CopyWebpackPlugin = require('copy-webpack-plugin');
var plugins = [
new HtmlWebpackPlugin({
inject: 'head',
template: 'index.html',
minify: {
"collapseWhitespace": true,
"removeComments": true,
"removeRedundantAttributes": true,
"removeScriptTypeAttributes": true,
"removeStyleLinkTypeAttributes": true
}
}),
new ExtractTextPlugin('style.css')
];
module.exports = {
entry: {
main: [
path.resolve(__dirname, packageData.main)
]
},
output: {
path: path.resolve(__dirname, 'build'),
filename: filename.join('.'),
},
devtool: 'source-map',
plugins: plugins,
module: {
loaders: [
{
test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader", "file-loader")
},
{ test: /\.(jpe?g|png|gif|svg)$/, loader: "file-loader"
},
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff"
},
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader"
}
]
}
};
I could really use some assistance in the best way to use slick-carousel while building in NPM and with webpack. So many moving pieces and, when I kinda think I get it, this comes along and I spend 7 hours trying to fix it before asking for help.
Any help is deeply appreciated.
I had the same issue, but I didn't want to change slick-carousel in my project, so one month late but here is how I solved it:
First install Webpack image-loader:
$ npm install image-webpack-loader --save-dev
Then change these lines (in your webpack configuration):
loaders: [{
test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader", "file-loader")
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
'file?hash=sha512&digest=hex&name=[hash].[ext]',
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
]
},
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader"
}]
This will change file-loader (what you are using for loading images) for image-loader, it will know how to compile .gif files and other formats.
For aditional information about this, you can check the github page
Also, if you are using ReactJS, don't use slick-carousel directly, because it uses direct DOM manipulation thanks to JQuery dependency, right now I'm using react-slick is very stable and has cool options like settings based on responsive layout custom prev and next arrows and more.
I hope it help you
require("slick-carousel/slick/slick.css");
require("slick-carousel/slick/slick-theme.css");
From what I understand you do not need both of these - only one.
Slick-theme is the default CSS, while Slick you integrate.
I could be wrong (I'm really new to development) but I've had no problems with the initial configuration!

Resources