How to put import type always as last one with eslint-plugin-import - javascript

I have a react native project in which I use absolute paths starting with
components/ features/ or infrastructure/
I wanted them to be separated from the node modules imports but I want to
import type {xxx} from 'features|components|infrastructure|';
to always go last within a group, or even better to all the import type to always go last in the entire imports section and preferably sorted alphabetically.
So far I came up with such a config
module.exports = {
root: true,
extends: ['#react-native-community'],
plugins: ['import'],
rules: {
'import/order': [
'error',
{
groups: [
['builtin', 'external'],
'internal',
'parent',
['sibling', 'index'],
'object',
'type',
],
pathGroups: [
{
pattern: '#(react|react-native)',
group: 'external',
position: 'before',
},
{
pattern: 'components/**',
group: 'internal',
},
{
pattern: 'features/**',
group: 'internal',
},
{
pattern: 'infrastructure/**',
group: 'internal',
},
],
pathGroupsExcludedImportTypes: ['react'],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
},
};
but the problem here is that it does not separate import and import type and it puts imports like so
import React from 'react';
import { View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import RegistrationScreen from 'features/account/screens/Registration';
import type { Test } from 'features/account/types';
import { colors } from 'infrastructure/theme';
Thanks.

Related

How to import SVG's in nextjs

I'm migrating my react website to nextjs. I have one assets folder at the root which contains lots of svgs and I have one constants.js file where I'm importing all the SVG's as ReactComponent. I would like to know how to convert this part of code in nextjs.
Error message:
Module not found: Can't resolve '../../../assets/skills/html5.svg'
> 1 | import { ReactComponent as HTML } from "../../../assets/skills/html5.svg";
2 | import { ReactComponent as CSS } from "../../../assets/skills/css.svg";
constants.js
import { ReactComponent as HTML } from "../../../assets/skills/html5.svg";
import { ReactComponent as CSS } from "../../../assets/skills/css.svg";
export const SKILLS = [
{
name: "html",
image: HTML, // SVG Component
},
{
name: "css",
image: CSS,
},
]
Folder Structure:
You can setup SVGR into your project and add them as regular react components. Your code could end up looking something like this
export const SKILLS = [
{
name: "html",
image: <Html className="html"/>, // SVG Component
},
{
name: "css",
image: <Css className="css"/>,
},

How to include Tailwind CSS styles from React component library into the app CSS bundle?

G'day,
I am building a React component library with TailwindCSS and ViteJS. The library outputs each component as a separate JS file, for example:
// #components-library/src/ComponentA.jsx
export default function ComponentA() {
return <div className="flex justify-center">Component A</div>;
}
// Bundle Output: #components-library/dist/ComponentA.js
import { j as jsx } from "./jsx-runtime.js";
import "react";
function ComponentA() {
return /* #__PURE__ */ jsx("div", {
className: "flex justify-center",
children: "Component A",
});
}
export { ComponentA as default };
The app that is consuming this components library is also using Tailwind. However, the styles are not applied to the imported component. App-specific styles are working fine.
/* app/src/index.css */
#tailwind base;
#tailwind components;
#tailwind utilities;
// app/src/App.jsx
import CompoenntA from "#component-library/ComponentA";
export default function App() {
return (
<div className="p-10">
<ComponentA />
</div>
);
}
Awaiting expert advice.
Thank you!
Currently, I am also working on a similar project as like as you. I have built my component library with rollup. It generated a CSS file then imported that CSS file to my main package.
My Rollup Config
import resolve from "#rollup/plugin-node-resolve";
import babel from "#rollup/plugin-babel";
import commonjs from "#rollup/plugin-commonjs";
import typescript from "#rollup/plugin-typescript";
import { terser } from "rollup-plugin-terser";
import external from "rollup-plugin-peer-deps-external";
import postcss from "rollup-plugin-postcss";
import swc from "rollup-plugin-swc";
import filesize from "rollup-plugin-filesize";
const packageJson = require("./package.json");
export default [
{
input: "index.ts",
output: [
{
file: packageJson.main,
format: "cjs",
sourcemap: false,
name: "#ht/components",
},
{
file: packageJson.module,
format: "esm",
sourcemap: false,
},
],
plugins: [
babel({
// this is needed because we're using TypeScript
babelHelpers: "bundled",
extensions: [".ts", ".tsx"],
}),
external(),
resolve(),
commonjs(),
typescript({ tsconfig: "./tsconfig.json" }),
postcss({
config: {
path: "./postcss.config.js",
},
extensions: [".css"],
minimize: true,
extract: "lib.css",
}),
swc({
jsc: {
parser: {
syntax: "typescript",
},
target: "es2018",
},
}),
terser(),
filesize(),
],
},
];
Main Package's index
import "#ht/components/dist/cjs/lib.css";
import "./assets/css/tailwind.css";
You should add the path of the component to the tailwind.config.js.
In my case, my component library is symlinked to the current project using yarn link and the same problem was happening. Here is what I added in tailwind.config.js:
module.exports = {
// use content instead of purge if using tailiwind v3
purge: [
"./src/**/*.{js,jsx,ts,tsx}",
"./public/index.html",
// this line fixed the issue for me
"./node_modules/{component-library-name}/src/**/*.{js,jsx,ts,tsx}"
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {}
},
variants: {
extend: {}
},
plugins: []
};

Individual component exports with Rollup instead of one massive index.js file?

I’m working on a proprietary component library built with React and using Rollup to bundle everything. Currently, it’s bundling everything into these files:
dist
├ cjs
│ └ index.js (1.7mb)
└ esm
└ index.js (1.7mb)
My hope is that I could instead have every component be bundled individually so the consuming app doesn’t have to download a huge index.js file, but this may be where my inexperience with Rollup kicks in.
I currently have a single entrypoint for Rollup:
input: [
'src/index.js',
],
My index.js file looks something like this (but with many more components):
import { Badge } from './components/Badge';
import { Button } from './components/Button';
import { CardFooter } from './components/CardFooter';
import { CardHeader } from './components/CardHeader';
import { CardTagList } from './components/CardTagList';
import { CardToolbar } from './components/CardToolbar';
import { CartAnimation } from './components/CartAnimation';
export {
Badge,
BasePrice,
Button,
CardFooter,
CardHeader,
CardTagList,
CardToolbar,
CartAnimation,
};
What do I have to do to ensure that components are each bundled separately and can still be imported in the apps that use the library with:
import { Button } from '#company/component-library';
Here is my full config as it stands today:
import { babel } from '#rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
import commonjs from '#rollup/plugin-commonjs';
import dynamicImportVars from '#rollup/plugin-dynamic-import-vars';
import eslint from '#rollup/plugin-eslint';
import json from '#rollup/plugin-json';
import resolve from '#rollup/plugin-node-resolve';
import stylelint from 'rollup-plugin-stylelint';
import styles from 'rollup-plugin-styles';
require('dotenv').config();
export default {
external: [
'react',
'react-dom',
'styled-components',
],
input: [
'src/index.js',
],
output: [
{
// Bundle into ESM for modern consumers.
// Only ESM build can currently be tree-shaken.
dir: 'dist/esm',
format: 'esm',
},
{
// Bundle into CJS for other consumers.
dir: 'dist/cjs',
format: 'cjs',
},
],
plugins: [
eslint({
include: '**/*.js',
throwOnError: true,
}),
stylelint(),
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
}),
resolve({
browser: true,
}),
styles(),
commonjs(),
json(),
dynamicImportVars({}),
terser(),
],
};
Note: Probably not important, but this project is published to npm as a private repo, but currently the app that uses it installs it using a commit hash.
You could try adding the preserveModule
export default {
preserveModules: true,
...
}
I have done some tweek to the rollup config. My problem statement was to export custom elements seperately. Find my custom solution below:
function generateComponentConfig() {
const dir = fs.readdirSync("./src/web-components");
return dir.map(folderName => {
return {
input: [`src/web-components/${folderName}/index.js`],
output: [
{ file: `public/build/wc/${folderName}.mjs`, 'format': 'es' },
{ file: `public/build/wc/${folderName}.js`, 'format': 'umd', name: folderName }
],
plugins: [
svelte({ compilerOptions:{customElement: true}, emitCss: false, include: /\.wc\.svelte$/ }),
svelte({ compilerOptions: {customElement: false}, emitCss: false, exclude: /\.wc\.svelte$/ }),
resolve()
]
};
})};
Use the above function in your export as below
export default [
...generateComponentConfig(), {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js',
},
plugins: plugins(),
watch: {
clearScreen: false,
}}];

Modifying an existing file with Plop?

I’m using plop to generate components for a React-based component library. I have a directory full of Handlebars templates that are used to create new component directories, and all is working well. I also have this index.js file that acts as the entrypoint for my bundler. It’s just full of lots of imports and an export. It looks like this:
import { Badge } from './components/Badge';
import { Button, ButtonMemo } from './components/Button';
import { Column } from './components/Column';
import { ErrorBoundary } from './components/ErrorBoundary';
import { FormBasicInfo } from './components/FormBasicInfo';
import { FormLogin } from './components/FormLogin';
import { FormSignup } from './components/FormSignUp';
import { Icon } from './components/Icon';
import { Input } from './components/Input';
import { Link } from './components/Link';
import { Loader } from './components/Loader';
import { Logo } from './components/Logo';
import { Row } from './components/Row';
import { Select } from './components/Select';
import { SimpleMenu } from './components/SimpleMenu';
import { Slideshow } from './components/Slideshow';
import { Spinner } from './components/Spinner';
export {
Badge,
Button,
Column,
ErrorBoundary,
FormBasicInfo,
FormLogin,
FormSignup,
Icon,
Input,
Link,
Loader,
Logo,
Row,
Select,
SimpleMenu,
Slideshow,
Spinner,
};
When generating a new component, I would love to also add the new component to this entrypoint file. That would require adding an import line to the top block, and an export line to the bottom. Ideally it would still be alphabetically sorted, but that isn’t a hard requirement.
Is this possible?
This is how I ended up pulling it off:
In my index.js file (the one I want modified), I added two comments in the places where the inserts should take place:
…
import { PointAccountCard } from './components/PointAccountCard';
import { Card } from './components/Card';
import { SettingsPanelFooter } from './components/SettingsPanelFooter';
// COMPONENT IMPORTS
export {
…
PointAccountCard,
Card,
SettingsPanelFooter,
// COMPONENT EXPORTS
};
Then in my plopfile.js config, I added these two steps to my actions config:
module.exports = function (plop) {
plop.setGenerator('component', {
actions: [
…
{
path: '../../src/index.js',
pattern: /(\/\/ COMPONENT IMPORTS)/g,
template: 'import { {{name}} } from \'./components/{{name}}\';\n$1',
type: 'modify',
},
{
path: '../../src/index.js',
pattern: /(\/\/ COMPONENT EXPORTS)/g,
template: '\t{{name}},\n$1',
type: 'modify',
},
],
description: 'New React Component',
prompts: [
…
],
})
};
All new components are automatically added to the index.js file.

ERROR ReferenceError: React is not defined | Using React Components in Angular

So basically I want to render one single React component (a notification component) into my Angular project. I created Notification.tsx
import * as React from 'react';
import { FunctionComponent, useEffect, useRef, useState } from 'react';
import { IconButton, Badge, Menu, List, ListItem, ListItemIcon, ListItemText, ListItemSecondaryAction, Avatar } from '#material-ui/core';
import { Notifications, Delete, Event, HourglassEmpty, Alarm } from '#material-ui/icons';
import { makeStyles } from '#material-ui/core/styles';
import { grey } from '#material-ui/core/colors';
export interface NotificationProps { }
export const NotificationComponent: FunctionComponent<NotificationProps> = (props: NotificationProps) => {
return <div className={"row"}>
<IconButton disableRipple={true} aria-label="notification" className="py-3 my-auto"
onClick={handleNotificationMenuClick} aria-controls="notification-drop-down-menu"
aria-haspopup="true">
<Badge badgeContent={4} max={99} color="secondary">
<Notifications />
</Badge>
</IconButton>
</div>
};
A wrapper component NotificationWrapper.tsx
import { AfterViewInit, Component, ElementRef, OnChanges, OnDestroy, SimpleChanges, ViewChild, ViewEncapsulation } from '#angular/core';
import { NotificationComponent } from './Notification';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
const containerElementName = 'notificationComponentTemplate';
#Component({
selector: 'react-notification-component',
template: `<span #${containerElementName}></span>`,
encapsulation: ViewEncapsulation.None,
})
export class NotificationWrapper implements OnChanges, OnDestroy, AfterViewInit {
#ViewChild(containerElementName, { static: false }) containerRef: ElementRef;
constructor() { }
ngOnChanges(changes: SimpleChanges): void {
this.render();
}
ngAfterViewInit() {
this.render();
}
ngOnDestroy() {
ReactDOM.unmountComponentAtNode(this.containerRef.nativeElement);
}
private render() {
ReactDOM.render(<div className={'notification-wrapper'}>
<NotificationComponent />
</div>, this.containerRef.nativeElement);
}
}
Added this wrapper to app.module.ts's #NgModule
import { NotificationWrapper } from "../react-components/notification/NotificationWrapper";
#NgModule({
declarations: [
NotificationWrapper,
],
})
Used the notification wrapper selector as follows:
<div class="col-sm-6">
<react-notification-component></react-notification-component>
</div>
Everything works fine when served locally and as I went through other similar questions on this site: I've added "jsx": "react" to tsconfig.json,
plugins: [
new webpack.ProvidePlugin({
"React": "react",
})
]
and
externals: {
'react': 'React'
},
to webpack.config.js. Here's the whole file for reference.
// Work around for https://github.com/angular/angular-cli/issues/7200
const path = require('path');
const webpack = require('webpack');
// change the regex to include the packages you want to exclude
const regex = /firebase\/(app|firestore)/;
module.exports = {
mode: 'production',
entry: {
// This is our Express server for Dynamic universal
server: './server.ts'
},
externals: {
'./dist/server/main': 'require("./server/main")',
'react': 'React'
},
target: 'node',
node: {
__dirname: false,
__filename: false,
},
resolve: { extensions: ['.ts', '.js'] },
target: 'node',
mode: 'none',
// this makes sure we include node_modules and other 3rd party libraries
externals: [/node_modules/, function (context, request, callback) {
// exclude firebase products from being bundled, so they will be loaded using require() at runtime.
if (regex.test(request)) {
return callback(null, 'commonjs ' + request);
}
callback();
}],
optimization: {
minimize: false
},
output: {
// Puts the output at the root of the dist folder
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
noParse: /polyfills-.*\.js/,
rules: [
{ test: /\.ts$/, loader: 'ts-loader' },
{
// Mark files inside `#angular/core` as using SystemJS style dynamic imports.
// Removing this will cause deprecation warnings to appear.
test: /(\\|\/)#angular(\\|\/)core(\\|\/).+\.js$/,
parser: { system: true },
},
]
},
plugins: [
new webpack.ProvidePlugin({
"React": "react",
}),
new webpack.ContextReplacementPlugin(
// fixes WARNING Critical dependency: the request of a dependency is an expression
/(.+)?angular(\\|\/)core(.+)?/,
path.join(__dirname, 'src'), // location of your src
{} // a map of your routes
),
new webpack.ContextReplacementPlugin(
// fixes WARNING Critical dependency: the request of a dependency is an expression
/(.+)?express(\\|\/)(.+)?/,
path.join(__dirname, 'src'),
{}
)
]
};
My problem is, when I build my app, the notification component is not rendered and I get this error in console ERROR ReferenceError: React is not defined. Is there anything that I missed? Thanks in advance.
Add the following to tsconfig.json
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"jsx": "react",
}
Set aot and buildOptimizer to false in angular.json
Replace
import * as React from 'react';
import * as ReactDOM from 'react-dom'
with
import React from 'react';
import ReactDOM from 'react-dom';
I think it's a typical unwanted optimization that remove React in the final js (React isn't directly used)
Try to call a method or an attribute on React like React.version :
import {
OnChanges,
ViewChild
} from '#angular/core';
import { MyComponent } from './react/MyComponent';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
const containerElementName = 'myReactComponentContainer';
#Component({
...
})
export class MyComponent implements OnChanges {
#ViewChild(containerElementName) containerRef: ElementRef;
ngOnChanges(changes: SimpleChanges): void {
this.render();
}
private render() {
React.version;
ReactDOM.render(<MyReactComponent />, this.containerRef.nativeElement);
}
}

Categories