I use custom webpack for my app with Next.js:
const path = require('path');
const Webpack = require('webpack');
module.exports = {
webpack: (config, { dev }) => {
config.plugins.push(
new Webpack.DefinePlugin([{
__BROWSER__: "typeof window !== 'undefined'",
__NETLIFY_BRANCH__: JSON.stringify(process.env.BRANCH),
__CONFIG__: JSON.stringify({
GRAPHQL_URL: process.env.GRAPHQL_URL,
SEGMENT_WRITEKEY: process.env.SEGMENT_WRITEKEY,
}),
}])
)
config.module.rules.push(
{
test: /\.png$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
name: 'graphics/[name].[ext]',
},
},
},
{
test: /\.svg$/,
loader: [
{
loader: 'babel-loader',
query: {
presets: ['es2015']
}
},
{
loader: 'svg-react-loader',
},
{
loader: 'string-replace-loader',
options: {
search: ' xmlns="http://www.w3.org/2000/svg"',
replace: '',
},
},
],
},
);
return config;
},
};
But when I try to add image to my component, my localhost launched, but I receive the error:
Cannot find module '../assets/logo.svg'
I import my image to the component as usual, and without an image, my app is running. Maybe I should add something to my webpack?
All images set in dir assets.
import logo from '../assets/logo.svg';
export default class Layout extends React.Component {
render () {
return (
<img src={logo} alt="logo" />
)
}
}
The issue (from what I understand from this: https://github.com/zeit/next.js/issues/544) is that Next.js doesn't use Webpack for server side rendering, only for client side, so you can't import an SVG like you do when it's purely a client side React project (i.e. by adding a rule for .svg files and run those files through a loader chain).
A possible solution is mentioned here, where the SVG import is not done by a Webpack loader, but by Babel (which is used on the server side code in Next.js).
It seems to work for me, like this:
npm i babel-plugin-inline-react-svg --save
add/merge the following to your Babel config (more info on how to set up a custom Babel configuration here):
"plugins": [
"inline-react-svg"
]
in your component/page, use this (the name of the variable, LogoSVG, is important and shouldn't be changed):
import LogoSVG from '../assets/logo.svg';
and use it as a React component:
return <LogoSVG alt="logo" />
Related
Is it possible to get the CSS for a Vue component, and attach it as a string to a component when building a library for use at runtime?
vue-cli-service build --mode production --target lib --name components src/index.ts
I currently achieve this for some custom js using a custom block:
vue.config.js:
...
rules: [
{
resourceQuery: /blockType=client-script/,
use: './client-script-block',
},
],
},
...
client-script-block.js:
module.exports = async function () {
return `export default function (Component) {
Component.options.__client_script = ${JSON.stringify(this.resourcePath)};
}`;
};
which then exposed the string in the Vue app that uses the library. But achieving the same thing with CSS doesn't seem to play ball.
You could take a look at this CSS Extraction modules from VueLoader, that extracts the CSS from specific file or files, and stores it in a custom file, that you could then load dynamically in runtime, like:
Install:
npm install -D mini-css-extract-plugin
// webpack.config.js
var MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// other options...
module: {
rules: [
// ... other rules omitted
{
test: /\.css$/,
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader'
]
}
]
},
plugins: [
// ... Vue Loader plugin omitted
new MiniCssExtractPlugin({
filename: 'style.css'
})
]
}
Reference: https://vue-loader.vuejs.org/guide/extract-css.html#webpack-4:
Another approach:
// webpack.config.js
var ExtractTextPlugin = require("extract-text-webpack-plugin")
module.exports = {
// other options...
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
extractCSS: true
}
}
]
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
Reference: https://vue-loader-v14.vuejs.org/en/configurations/extract-css.html
Also here you have a complete guide for extracting the CSS from a SSR (Server Side Rendered) apps: https://ssr.vuejs.org/guide/css.html#enabling-css-extraction
Ok, so I've been reading article after article after article and have not found a good solution to this, what should be a very simple process in developing a web app...
I have my setup for webpack, babel, react and in my react app, I have a very basic css import. I've imported the image, which lives in src/static/assets/images/bg.png and I can embed it using inline css in my react component without troubles. However, I want to include my image from an included css file. The css file is parsed, but then I get the error 'Module build failed' and Can't resolve './bg.png' or a very similar path error when I mess around with the path inside the included css. I have file-loader, and url-loader installed and the file is moved into the dist/ folder route (though honestly, I would rather have an images/ folder in the dist where this goes, but that's another task.
So the quest: what needs to change to have the image included from the css. I've read https://create-react-app.dev/docs/adding-images-fonts-and-files/ and that alludes that my code should work, yet it doesn't.
Code is below, or from my repo if you want the whole code base: https://github.com/abendago/reactimages
REACT Code
import React from "react";
import './css/style.css'
import bg from './static/assets/images/bg.png'
function App() {
return (
<h1 style={{backgroundImage: "url(" + bg + ")"}}>In THe App Here</h1>
);
}
export default App;
src/css/style.css
body { background-image: url(./bg.png)}
webpack config
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
},
],
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
new MiniCssExtractPlugin()
],
devServer: {
contentBase: path.join(__dirname, 'public')
}
};
You have to set the relative path with your image to solve your issue since current your path doesn't relate to anything:
body { background-image: url('../static/assets/images/bg.png')}
You might want to install file loader and image-webpack-loader and update your webpack config to reflect the following.
{
test: /\.(png|gif|jpe?g)$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
mozjpege: {
progressive: true,
quality: 80,
},
optipng: {
enabled: false,
},
pngquant:{
quality: '65-90',
speed: 4,
},
}
}
]
}
Although I think your image doesn't load when referenced in css, because you don't have a quote around it. Replace body { background-image: url(./bg.png)} with body { background-image: URL('./bg.png')} - https://developer.mozilla.org/en-US/docs/Web/CSS/background-image
Let's say that I have a node.js application, which does NOT go through my webpack bundling:
Node App
const Html = require('./build/ssr-bundle.js');
let result = Html.ssrbundle.render();
console.log(result);
Here is my ES6/JSX file, which is getting processed by webpack and I want to be able to access that render function in my node app (you guessed right, I am trying to SSR react stuff ;) )
src/Html.js -(webpack)-> build/ssr-bundle.js
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import CustomComponent from './custom-component.js';
module.exports = {
render : function () {
return ReactDOMServer.renderToString(<CustomComponent />);
} };
And here is my Webpack config
webpack.config.js
var path = require('path');
module.exports = {
entry: {
ssr: './src/Html.js',
//frontend: './src/frontend-Html.js'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: 'ssr-bundle.js',
library: 'ssrbundle'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['env','react'],
plugins: ["transform-es2015-destructuring", "transform-object-rest-spread"]
}
},
{
test:/\.css$/,
use:['style-loader','css-loader']
}
]
},
stats: {
colors: true
},
devtool: 'source-map'
};
Whatever I do, I cannot figure out how to properly use that exported variable "ssrbundle" and subsequently the render function. If I had my node app included in the bundle, everything would be all right, but this is not what I want to do.
As apokryfos suggested, I played around with the libraryTarget Webpack setting. You can find more info on using Webpack to author a library (what I was really trying to achieve) here:
https://webpack.js.org/guides/author-libraries/
and here are code examples:
https://github.com/kalcifer/webpack-library-example/blob/master/webpack.config.babel.js.
What did the trick for me, was to set the libraryTarget to "umd" , which is different than the "var" setting which is set by default and is suitable i.e. for including the script in an HTML file
I'm using webpack, and I want to load scss file in my JavaScript. (Or if it can be separate, it also fine).
This is my webpack config:
"use strict";
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');
module.exports = {
context: __dirname + '/src',
entry: './js/index.js',
output: {
path: './build',
filename: 'js/app.bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}
]
},
resolve: {
root: [
path.resolve('./src/js'),
path.resolve('./src/scss')
],
extensions: ['', '.js']
},
plugins: [
new CopyWebpackPlugin([
{ from: 'html/**', to: `${__dirname}/build/html`, flatten: true },
{ from: 'images/**', to: `${__dirname}/build/image`, flatten: true }
])
]
};
this is my files list:
src/html/index.html -> build/html/index.html (WORKED)
src/images/** -> build/images/** (WORKED)
src/js/index.js -> build/js/app.bundle.js (WORKED)
src/scss/** -> build/css/** (NOT WORKED)
This is my JavaScript code. I just started project, so not much codes:
import "babel-polyfill";
import React from 'react';
import ReactDOM from 'react-dom';
import moduleA from 'moduleA';
import "view/startup.scss";
ReactDOM.render(
<div>
<h1>Helloworld!</h1>
</div>,
document.getElementById('entry')
);
You can see this: import "view/startup.scss";
I want to load scss file into my JavaScript, but when I run webpack command, it says:
ERROR in Loader /Users/.../Desktop/work/my-project/app/node_modules/css/index.js didn't return a function
# ./scss/view/startup.scss 4:14-123
in "resolve" property of webpack config, you can see that I added another root directory for scss, also I loaded sass-loader too, but it doesn't work and I don't know why.
And as I know, with Webpack, including css/scss automatically injects into destination file, so it doesn't matter it needs to be extract as separate file, I just want that this works.
Any help will be very appreciated :)
* UPDATED *
code of ./scss/view/startup.scss
#startup {
background-color: #7087d7;
}
The error points towards the reason (I highlighted the relevant part):
ERROR in Loader /Users/.../Desktop/work/my-project/app/node_modules/css/index.js didn't return a function # ./scss/view/startup.scss 4:14-123
When you declare a loader in Webpack, you can leave off the -loader suffix (so css-loader becomes css) provided that you don't have other modules that may match the suffixless loader name.
This is where it fails in your case, because you also use the css package, which Webpack tries to use as a loader (and fails, because it's not).
To fix this, use the full loader package name:
loaders : [ "style-loader", "css-loader", "sass-loader" ]
I want to use the css-loader with the 'modules' option of webpack in a React application written in Typescript. This example was my starting point (they are using Babel, webpack and React).
webpack config
var webpack=require('webpack');
var path=require('path');
var ExtractTextPlugin=require("extract-text-webpack-plugin");
module.exports={
entry: ['./src/main.tsx'],
output: {
path: path.resolve(__dirname, "target"),
publicPath: "/assets/",
filename: 'bundle.js'
},
debug: true,
devtool: 'eval-source-map',
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({minimize: true})
],
resolve: {
extensions: ['', '.jsx', '.ts', '.js', '.tsx', '.css', '.less']
},
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader'
},
{
test: /\.tsx$/,
loader: 'react-hot!ts-loader'
}, {
test: /\.jsx$/,
exclude: /(node_modules|bower_components)/,
loader: "react-hot!babel-loader"
},
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader"
}, {
test: /\.css/,
exclude: /(node_modules|bower_components)/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader')
}
]
},
plugins: [
new ExtractTextPlugin("styles.css", {allChunks: true})
],
postcss: function() {
return [require("postcss-cssnext")()]
}
}
This is a React component I want to style with an accompanying CSS file:
import React = require('react');
import styles = require('../../../css/tree.css')
class Tree extends React.Component<{}, TreeState> {
...
render() {
var components = this.state.components
return (
<div>
<h3 className={styles.h3} >Components</h3>
<div id="tree" className="list-group">
...
</div>
</div>
)
}
}
export = Tree
tree.css
.h3{
color: red;
}
No matter what I'm doing (tried changing the import syntax, tried declaring the 'require' for ts-loader, described here, I always get:
Uncaught Error: Cannot find module "../../../css/tree.css"
at runtime and
error TS2307: Cannot find module '../../../css/tree.css'.
by the TS compiler. Whats happening? Seems to me that css-loader is not even emitting ICSS? Or is it ts-loader behaving wrong?
import has special meaning to TypeScript. It means that TypeScript will attempt to load and understand the thing being imported. The right way is to define require like you mentioned but then var instead of import:
var styles = require('../../../css/tree.css')`
Declare 'require' as per ts-loader documentation.
Use 'require' as generic with < any > type: require< any >("../../../css/tree.css").
*.d.ts file
declare var require: {
<T>(path: string): T;
(paths: string[], callback: (...modules: any[]) => void): void;
ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void;
};
*.tsx file with component
const styles = require<any>("../../../css/tree.css");
...
<h3 className={styles.h3}>Components</h3>
I know it was already answered, but I was struggling with it for a while before I realized I need to use generic type specification, without that I wasn't able to access content of CSS file. (I was getting error: Property 'h3' does not exists on type '{}'.)
I had similar problem.
For me, works import:
import '../../../css/tree.css';
Webpack change this like any other normal imports. It change it to
__webpack_require__(id)
One drawback is that you lost control on style variable.
You can use https://github.com/Quramy/typed-css-modules, which creates .d.ts files from CSS Modules .css files. Please see also https://github.com/css-modules/css-modules/issues/61#issuecomment-220684795
A bit late to game but you can create a file called tree.css.d.ts in the same folder as tree.css that has this line:
export const h3: string;
and still use the import statement import * as styles from ... and you will still getcode completion and compile time checking.
You can either manage these definition files manually or you could integrate typed-css-modules into your build pipeline (https://github.com/Quramy/typed-css-modules)