I'm building a documentation site in which I want to display demo code snippets.
With the styles, it was really straight forward, as my app is only including the main.scss, so I'm processing that file with all the necessary loaders to compile it, and any other scss file is loaded using the raw-loader to get the file as plain text.
The problem I'm having is at the moment of doing the same with my components. Unlike my styles, I need to include my components both when I want to render them using babel-loader, but I also want to import them as plain text for the demo showcase.
At first I thought of compromising using an inline loader at the require level
const componentCode = require('raw-loader!./path/to/component');
The problem with this approach is that by the time I try to do this import, the file has already been ran through babel-loader so I get the compiled version of the file instead of the original. I tried passing ?enforce=pre as a query parameter to the raw-loader inline, but this had no effect.
I was wondering if there is a way of defining a rule to override an import/require statement.
According to the webpack documentation
It's possible to overwrite any loaders in the configuration by prefixing the entire rule with !.
However, I can not find any examples of this. I tried the following, which compiled but crashed right after the first ! prefixed require without any errors
webpack.coonfig.js
{
test: /\.(js|jsx)$/,
exclude: [/node_modules/],
use: [
{
loader: 'babel-loader',
options: {
presets: ['env', 'react'],
},
},
],
},
{
test: /!*\.(js|jsx)$/,
enforce: "pre",
use: [
{
loader: 'raw-loader',
},
],
},
file.jsx
const componentCode = require('raw-loader!./path/to/component');
I also thought about reading the file using the fs but not sure if this will work. In the end the code will be compiled in its entirety by webpack and only the bundle will be published. Would this be the right approach?
Found my answer at Webpack - ignore loaders in require()?, Basically I need !! before the require to ignore the pre loaders and apply mine
Related
I'm trying to configure my first project with Nx.
My client explicitly request the use of React with Typescript, Nx and the Ant design UI Kit.
I've successfully created a new project with the required specification, at least using the CSS version of the Ant design kit.
Of course, I need to customize the UI kit with colors and other modification. According to the Ant documentation, I need to use the less styles in order to modify them.
I choose to enable the less style in Nx (for each react component I have a component-name.module.less file).
Now, if I try to import the antd less style, the build process fails with the following error:
> nx run react-ui-kit:build
Bundling react-ui-kit...
Error during bundle: SyntaxError: Inline JavaScript is not enabled. Is it set in your options? in /Users/luca/Jellyfish/Customers/client/libs/react-ui-kit/node_modules/antd/lib/style/color/bezierEasing.less on line 110, column 1:
109 // https://github.com/ant-design/ant-motion/issues/44
110 .bezierEasingMixin();
111
I understand that I should edit the less-build script in order to enable the javascript inlining, something like:
module: { rules: [{
test: /\.less$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" },
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true,
}
}
}
]
}]}
Unfortunately, I can't understand where to put this configuration inside my nx project.
Did someone have a similar problem? Any suggestion?
Problem
create-react-app v2+ supports TypeScript and CSS Modules out of the box... separately. The problem arises when you try to use the two together. Facebook had an extensive discussion about this issue and ultimately closed it off on GitHub. So developers have to use hacks and other workarounds to get these two technologies to play nicely together alongside CRA.
Existing workaround:
You can manually create ComponentName.module.css.d.ts files with type definitions like this: export const identifierName: string. This allows you to take advantage of TypeScript's typing and VS Code's auto-complete when you go to import ComponentName.module.css. Unfortunately, this is extremely tedious.
Solution (?):
The folks over at Dropbox created typed-css-modules-webpack-plugin to address this issue; it auto-genertes those *.d.ts files for you. They show how to install it with yarn or npm and then give this minimal code example:
const path = require('path');
const {TypedCssModulesPlugin} = require('typed-css-modules-webpack-plugin');
module.exports = {
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
},
{
test: /\.css$/,
use: [
'style-loader',
// Use CSS Modules
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
],
},
// Generate typing declarations for all CSS files under `src/` directory.
plugins: [
new TypedCssModulesPlugin({
globPattern: 'src/**/*.css',
}),
],
};
Unfortunately, it's not immediately clear how I can use this with create-react-app. I'm not very knowledgeable when it comes to Webpack, and I'm using customize-cra to avoid ejecting out of create-react-app so I can customize the Webpack configs for some things I need. For example, Ant Design lets you import components on demand by using babel-plugin-import as detailed here:
https://ant.design/docs/react/use-in-typescript#Use-babel-plugin-import
Question: How can I convert the above Webpack configuration code to a customize-cra equivalent so that I don't have to eject out of CRA?
Okay, so I eventually did figure this out, and I wrote a blog post on the subject for anyone who runs into a similar issue:
https://aleksandrhovhannisyan.github.io/blog/dev/how-to-set-up-react-typescript-ant-design-less-css-modules-and-eslint/#3-create-react-app-css-modules-and-typescript-
The solution uses the typescript-plugin-css-modules plugin. Here are the relevant bits from my blog post:
yarn add -D typescript-plugin-css-modules
After it’s installed, add the plugin to your tsconfig.json:
{
"compilerOptions": {
"plugins": [{ "name": "typescript-plugin-css-modules" }]
}
}
Next, create a file named global.d.ts under your src directory. You don’t have to name it global, by the way; you can name the file whatever you want, as long as it has the .d.ts extension. Enter these contents:
declare module '*.module.less' {
const classes: { [key: string]: string };
export default classes;
}
If you want to also use SASS or CSS, simply add more module declarations and change the .less extension.
We’re almost done! Per the plugin’s usage instructions, if you want intellisense to work in VS Code, you’ll need to force VS Code to use your workspace version of TypeScript instead of the globally installed version. Remember when we installed TypeScript via CRA at the very beginning? That’s our workspace version of TypeScript.
Here’s how to use the workspace version of TypeScript in VS Code:
Open any TypeScript file.
Click the version number on the blue status bar at the bottom of VS Code.
Select Use Workspace Version (3.7.3 as of this writing).
Here’s a screenshot to make that clearer:
Once you do that, VS Code will create a .vscode directory in your project for workspace settings.
At this point, you're all set to use CSS Modules with TypeScript.
UPDATE 2022
Note: If you're using react-scripts#2.1.x or higher you don't need to use custom definitions like
declare module '*.module.less' {
const classes: { [key: string]: string };
export default classes;
}
Custom definitions
Note: Create React App users can skip this section if you're using react-scripts#2.1.x or higher.
Also you can add this VS code setting to you local JSON settings file:
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
This will ensure that VS Code will use the project’s version of Typescript instead of the VS Code version and will prompt you to do so if you aren’t already.
Well, everything is correct as said AlexH.
1 in tsconfig.ts.
{
"compilerOptions": {
"plugins": [{ "name": "typescript-plugin-css-modules" }]
}
}
2 in global.d.ts
declare module '*.module.less' {
const classes: { [key: string]: string };
export default classes;
}
But Also in tsconfig you should write
"include": [
"global.d.ts",
...
]
I have a project in React.js with SASS and using Webpack 1.
I have images folders with all the images and other assets' folders.
I do not want webpack to do anything with images, I want them to be loaded at the runtime by url.
I solved it in JSX by using image urls inline in the jsx code (rather then importing them) and it's working great.
But when I'm trying to do the same in SASS, it's different:
I used to refer to them in absolute pass /images/abc.png and it worked.
I didn't even had a webpack loader for them, it just worked.
But I had to change it to the relative path ../../www/images/abc.png and it's all broke down:
It refuses to work without webpack loader - just gives errors for each image.
I've tried using file-loader, but it copies all sass's used images into build folder.
I've tried using url-loader, but it just included the images into resulting css file making it enormously bloated.
So, what could I use to ignore the images from sass and just address them by url in the runtime?
Thanks
One possibility is to set the url option of the css-loader to false, so webpack won't touch the urls. Your rule for .scss could look something likes this (assuming you use extract-text-webpack-plugin):
Webpack 1:
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader?url=false!sass-loader')
},
Webpack 2:
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { url: false } },
{ loader: 'sass-loader' }
]
})
}
But that ignores all urls not just your images. If that is a problem you could use file-loader with the option emitFile: false so it won't copy your files. But you'll need some workaround to get the correct path. When using [path] in the name it will use the relative path from the context. Because you're building it to a directory (e.g. build/) you'll need to go up one directory (../) by setting the publicPath option. Which would give you the following rule:
Webpack 1:
{
test: /\.png/,
loader: 'file-loader?emitFile=false&name=[path][name].[ext]&publicPath=../'
},
Webpack 2:
{
test: /\.png/,
loader: 'file-loader',
options: {
emitFile: false,
name: '[path][name].[ext]',
publicPath: '../'
}
}
If you've set context in your webpack config or your output CSS file is more than one level deep in the build directory, you'll need to tweak the publicPath option.
I'm transitioning a site to use Webpack, and I need a little help with configuration. I have a few pages written in Pug/Jade that are large and infrequently accessed (think Terms of Service or Privacy Policy). Most of my .jade files are Angular templates, so those are inlined in their components and it works well. These few files, however, I would like Webpack to compile into static HTML files served separately from the rest of the app. However, I would still like their file names to include a hash.
The basic idea I've come up with is like this:
In routes.ts:
$routeProvider.when('/_tos', templateUrl: require('./resources/terms-of-service.jade'))
In webpack.config.js's list of loaders:
{
test: /resources.*\.jade$/,
loaders: ['file?name=[name].[hash].html', 'pug-html']
}
I've tried that with various combinations of pug-loader, pug-html-loader (with and without the ?exports=false option), html-loader, extract-loader, extract-text-webpack-plugin, and file-loader, but everything I try has extra artifacts in the resulting .html file. E.g. it might start with module.exports =, or it might put \" everywhere in the file that should just have ".
Can anyone help?
Gah! I finally figured it out. I fundamentally misunderstood the way the list of loader works. I assumed only the first loader in the array that matched was used, but no, all loaders that match are used. (Though I'm still fuzzy on the details.) Here is a working configuration, where resources is the path to my "resources" directory:
loaders: [
{
test: /\.jade$/,
include: [resources],
loaders: ['file?name=[name].[hash].html', 'pug-html?exports=false']
},
{
test: /\.jade$/,
exclude: [resources],
loaders: ['pug-html?doctype=html']
}
]
I'm having trouble getting a quite basic setup with webpack and babel working. I need babel to compile JSX, i don't use ES2015 features.
There are quite a few questions here on SO about this exact problem, but they all seem to be resolved by installing babel-preset-react or adding the preset option to babel in webpack.config.js all of which i have done already (I think).
I'm sure it's a simple thing I'm missing, but I just can't see it.
I extracted just the files needed to demonstrate my problem into this gist (i used dashes in the filenames to indicate subfolders since you can't have folders in gists). My webpack.config.js looks like this:
module.exports = {
entry: [
'./app/js/app.js'
],
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
include: __dirname + '/app/js/',
loader: 'babel-loader?presets[]=react',
}
]
},
output: {
filename: "bundle.js",
path: __dirname + '/public'
},
};
Note: in package.json everything is listed under 'dependencies', since i'm hosting this on heroku which does not install devdependencies (at least not by default)
For what it is worth, I am currently using webpack with latest babel with the following config line
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}
Since you don't use es2015, you should be able to just leave the react preset in the list. Maybe babel-loader?presets[]=react is not working the way you expect it to.
I banged my head against this for a long time as well. It turned out that the include wasn't finding my directories. The easiest way to fix, as pointed out in the comment to the marked answer above, is to use an exclude, e.g. exclude: /node_modules/, or whatever you need to exclude, and it'll start working.
If you're new to webpack, you'll find the error you get from the above kind of cryptic (will look like a parse error on your js, even though you thought babel should have transformed the JSX; the deal is that it didn't).