Less integration with a Nx React project - reactjs

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?

Related

A fool-proof tsup config for a React component library

I've never published an NPM package before. All these details to generate a package seem way too complicated to my level. The only tool, that was beginner friendly, that I could find is create-react-library which recommended to switch to tsup instead.
I'm asking here to know if there's a batteries-included, most-cases-met, setup for tsup or any other tool of your recommendation for this kind of project (and I think this is a common scenario):
A React Project
Typed with Typescript
Tested with Jest
No dependencies
Exports React components
Should be public on NPM
If you want most-cases-met, batteries-included way to make a library like that then take a look at dts-cli. It will work but it will definitely be slower than tsup. I myself am in the process of switching a library with about 60 react components from dts-cli to tsup because the build time started taking too long (about a minute) on a MacBook Air M1.
Here is an example setup.
First you need to bundle each component separately. You can use a glob as an entry point in Tsup.
Keep in mind that options that works for Esbuild works for Tsup most of the time.
// tsup.config.ts
defineConfig([
{
clean: true,
sourcemap: true,
tsconfig: path.resolve(__dirname, "./tsconfig.build.json"),
entry: ["./components/core/!(index).ts?(x)"],
format: ["esm"],
outDir: "dist/",
esbuildOptions(options, context) {
// the directory structure will be the same as the source
options.outbase = "./";
},
},
Then you'll want to have a index.ts, for convenience, that expose named exports. This index is sometimes referred as a "barrel" file.
// index.ts
// the actual file is "Button.tsx" but we still want a ".js" here
export { Button } from "./components/core/Button.js";
Notice the .js extension. ESM expects explicit extensions so it's needed in the final build.
Adding the .js doesn't seem to bother TypeScript, which stills correctly recognize the type of "Button" from Button.tsx. At this point I am not sure why it works, but it does.
Transpile this index, without bundling.
// tsup.config.ts
{
clean: true,
sourcemap: true,
tsconfig: path.resolve(__dirname, "./tsconfig.build.json"),
entry: ["index.ts", "./components/core/index.ts"],
bundle: false,
format: ["esm"],
outDir: "dist",
esbuildOptions(options, context) {
options.outbase = "./";
},
},
])
Finally define your package.json as usual:
"sideEffects": false,
"type": "module",
"exports": {
".": "./dist/index.js"
},
sideEffects is a non-standard property targeting application bundlers like Webpack and Rollup.
Setting it to false tells them that the package is safe for tree-shaking.
Now import { Button } from "my-package" should work as you expect, and tree-shaking and dynamic loading at app-level become possible because "Button" is bundled as its own ES module Button.js, and the package is marked as being side-effect free.
This is confirmed by my Webpack Bundle Analyzer in a Next app:
Before (a single bundled index.js):
After (separate files means I can select more precisely my imports):
Final config available here (might be improved in the future)

Webpack 5 - Asset Modules - png|jpg - Webpack Error

I am using the latest version of webpack for my react project.
I am attempting to load png or jpgs. I was trying with the old "loader"s however I was still getting the "unexpected token" when trying to load an imported image.
I found from googling that Webpack 5 now uses Asset Modules to load assets now.
I've used the recommend module rule below:
module: {
rules: [
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource'
},
However when I attempt to build, I get this error:
Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.module.rules[0].type should be one of these:
"javascript/auto" | "javascript/dynamic" | "javascript/esm" | "json" | "webassembly/experimental"
-> Module type to use for the module
I have been on this for the last 7hr hours with no luck. Any assistance would be appreciated. :)
You can try this to match images of different types.
{
test: [/\.svg$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: "file-loader",
options: {
name: "asset/resource/[name].[ext]"
}
}
make sure the path exists.
Tip: Double check whether both installed packages: webpack and webpack-cli are up-to-date.

Create-react-app + TypeScript + CSS Modules: Auto-generating type definitions without ejecting from CRA

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",
...
]

How to override a webpack loader based on specific require needs?

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

Webpack 1 + sass (in react) - leave my images alone

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.

Resources