Related
Im trying to build out a micro frontend for the first time so my main application can support a sub application. I have it all working when everything is just rendering App.tsx (remote) but as soon as I try and render a child within the remote application I get the Cannot find module error from webpack
Container craco.congig.js
/* eslint-disable #typescript-eslint/no-var-requires */
const CracoEsbuildPlugin = require('craco-esbuild');
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
module.exports = {
webpack: {
plugins: {
add: [
new ModuleFederationPlugin({
name: "Dashboard",
remotes: {
DigitalCanopy: "DigitalCanopy#//localhost:3001/remoteEntry.js",
},
shared: {
react: {
singleton: true,
strictVersion: true,
requiredVersion: deps['react']
},
"react-dom": {
singleton: true,
strictVersion: true,
requiredVersion: deps['react-dom']
},
},
}),
],
},
...
My remote craco.config.js
/* eslint-disable #typescript-eslint/no-var-requires */
const CracoEsbuildPlugin = require('craco-esbuild');
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
module.exports = {
devServer: {
port: 3001
},
webpack: {
plugins: {
add: [
new ModuleFederationPlugin({
name: "DigitalCanopy",
exposes: {
"./DigitalCanopy": "./src/App.tsx",
},
filename: "remoteEntry.js",
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
}),
],
},
configure: (webpackConfig) => ({
...webpackConfig,
output: {
...webpackConfig.output,
publicPath: "auto",
},
}),
},
plugins: [
{
plugin: CracoEsbuildPlugin,
options: {
esbuildLoaderOptions: {
// Optional. Defaults to auto-detect loader.
loader: 'tsx', // Set the value to 'tsx' if you use typescript
target: 'es2018',
},
esbuildMinimizerOptions: {
target: 'es2018',
css: true, // if true, OptimizeCssAssetsWebpackPlugin will also be replaced by esbuild.
},
skipEsbuildJest: false, // Optional. Set to true if you want to use babel for jest tests,
esbuildJestOptions: {
loaders: {
'.ts': 'ts',
'.tsx': 'tsx',
},
},
},
},
],
};
My remote App.tsx
import { createTheme, ThemeProvider } from '#mui/material';
import React from 'react';
import './App.css';
import App1 from './App1';
const baseTheme = createTheme({
...MUI THEME STUFF
});
function App() {
return (
<ThemeProvider theme={baseTheme}>
<div className="App">Digital Canopy App</div>;
<App1 />
</ThemeProvider>
);
}
export default App;
This works and renders fine until I try and render <App1 />
Then I get this error Module not found: Error: Can't resolve './App1' in ...
Any ideas? I feel like this should just work. Importing children components from within the remote is pretty standard doesnt seem unique but I cannot find anything similar online which makes me think Im missing something obvious.
Thanks for the help
My issue was I was missing the .tsx extension on the import. I have no idea why that is required by the sub application. My build configs are identical for typescript and my main app does not require that. I'll dig in and try and figure out why
i want to make my own ReactJS Typescript library and use it for other React JS Typescript projects. My library was built with Webpack 4 and has this structure:
|-src
| images
| | logo.svg
| button-component
| | index.tsx
| | styles.less
| container-component
| | index.tsx
| | styles.less
| index.tsx
| styes.less
My tsconfig.json file:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"jsx": "react",
"esModuleInterop": true,
"sourceMap": false,
"baseUrl": ".",
"strict": true,
"paths": {
"#/*": ["src/*"]
},
"declaration": true,
"outDir": "./dist/",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"strictNullChecks": false,
"removeComments": true,
"noLib": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noEmitOnError": false
}
}
My webpack.config.js:
const path = require("path");
const WebpackMd5Hash = require("webpack-md5-hash");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
entry: {
main: "./src/index.tsx"
},
resolve: {
extensions: [".tsx", ".js"]
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: "index.js",
libraryTarget: "umd"
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin({
parallel: true,
cache: false,
extractComments: false,
terserOptions: {
output: {
comments: false,
},
},
}
)]
},
//devtool: 'source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
},
{
test: /\.(less|css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: "css-loader"
},
{
loader: "postcss-loader"
},
{
loader: 'less-loader'
}
]
},
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
limit: 10000,
},
},
],
}
]
},
plugins: [
new CleanWebpackPlugin(path.resolve(__dirname, '../dist'), {
root: process.cwd()
}),
new MiniCssExtractPlugin({
filename: "styles.css"
}),
new WebpackMd5Hash()
]
};
index.tsx:
export { default as ButtonComponent } from './button-component';
export { default as ContainerComponent } from './container-component';
Button Component:
// #ts-nocheck
import React from 'react';
import { Button } from 'antd';
interface ButtonComponentState { }
interface ButtonComponentProps {
text: any
}
class ButtonComponent extends React.Component<ButtonComponentProps, ButtonComponentState> {
constructor(props: any) {
super(props);
}
render() {
return(<Button type={"primary"}>{this.props.text}</Button> )
}
}
export default ButtonComponent;
Container Component:
// #ts-nocheck
import React from 'react';
import { Card } from 'antd';
import { ButtonComponent } from '../button-component';
interface ContainerComponentState { }
interface ContainerComponentProps {
title: string
width: number
data: any
textBtn: any
}
class ContainerComponent extends React.Component<ContainerComponentProps, ContainerComponentState> {
constructor(props: any) {
super(props);
}
render() {
return(
<Card title={this.props.title} bordered={false} style={{ width: this.props.width }}>
{this.props.data}
<ButtonComponent text={this.props.textBtn}/>
</Card>
)
}
}
export default ContainerComponent;
After build with command: "webpack --env prod --mode production", i copies all the files in the dist folder to other ReactJS Typescript project, import them and use but i have these error messages:
Line 1:1: Expected an assignment or function call and instead saw an expression no-unused-expressions
Line 1:123: 'define' is not defined no-undef
Line 1:134: 'define' is not defined no-undef
Line 1:520: Expected an assignment or function call and instead saw an expression no-unused-expressions
...
This is the first line of built index.js file:
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define
I don't know how to correct this since Webpack built my library successfully without any error (just warning about the js file is big after build).
I also have another question: if i splitChunks those dependencies into a vendor.js file, do i need to import it in another project or just import my component from built index.js?
When trying to add a ref to my component, I am getting a linting error:
TS2339: Property 'childNavRef' does not exist on type 'Navigation'
How do I properly attach a reference for a typescript react component? Thanks, and you can see the code below for the component as well as the tsconfig and eslint.
navigation.tsx:
import * as React from 'react';
interface MyState {
show: boolean;
}
export default class Navigation extends React.Component<{}, MyState> {
public constructor() {
super({});
this.state = {
show: true,
};
this.childNavRef = React.createRef();
}
public render(): React.ReactElement {
return (
<nav>
{
this.state.show
&& (
<div ref={this.childNavRef}>
This is a component
</div>
)
}
</nav>
);
}
}
tsconfig.json:
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "es6",
"moduleResolution": "node",
"target": "es5",
"jsx": "react",
"allowJs": true,
"baseUrl": ".",
"paths": {
"actions/*": ["src/app/redux/actions/*"],
}
}
}
.estlintrc:
module.exports = {
env: {
'jest/globals': true
},
extends: [
'airbnb',
'plugin:#typescript-eslint/recommended',
],
globals: {
'document': true,
'window': true,
},
overrides: [
{
'files': ['**/*.tsx'],
'rules': {
'react/prop-types': 'off'
}
}
],
parser: '#typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
project: './tsconfig.json',
},
plugins: ['#typescript-eslint', 'jest'],
rules: {
'import/no-extraneous-dependencies': [
'error',
{
'devDependencies': [
'**/*.stories.jsx',
'**/*.stories.tsx',
'**/*.test.jsx',
'**/*.test.js',
'**/*.test.tsx',
'**/*.test.ts',
'setupTests.js',
],
},
],
'#typescript-eslint/camelcase': ['error', { 'properties': 'never' }],
'indent': 'off',
'#typescript-eslint/indent': [
'error',
2,
{
'ignoredNodes': ['JSXElement'],
'SwitchCase': 1,
},
],
'#typescript-eslint/explicit-function-return-type': ['error', {
'allowExpressions': true,
'allowTypedFunctionExpressions': true
}],
'semi': 'off',
'#typescript-eslint/semi': ['error'],
'react/destructuring-assignment': [false, 'always', { 'ignoreClassFields': true }],
'react/jsx-filename-extension': [1, { 'extensions': ['.jsx', '.tsx'] }],
},
settings: {
'import/extensions': [
'.js',
'.jsx',
'.ts',
'.tsx',
],
'import/resolver': {
webpack: {
config: 'webpack.common.js',
}
}
},
};
You have to declare members before you can use them.
Try this:
export default class Navigation extends React.Component<{}, MyState> {
// Declare the private member variable
private childNavRef: React.RefObject<HTMLDivElement>;
public constructor() {
super({});
this.state = {
show: true,
};
this.childNavRef = React.createRef();
}
...
You can also try this and let TypeScript automatically infer the variable type:
export default class Navigation extends React.Component<{}, MyState> {
// Declare AND initialize the private member variable
private childNavRef = React.createRef();
public constructor() {
super({});
this.state = {
show: true,
};
// Note that you no longer have to instantiate childNavRef here anymore,
// as TypeScript will do that automatically (it will actually compile to something
// like in the first example, where ref is set after the super call in the constructor).
}
...
Im converting React app to use Typescript. And Im getting the following error:
ERROR in [at-loader] ./src/components/Services/Services.tsx:34:29
TS7017: Element implicitly has an 'any' type because type 'typeof "/Users/path/to/project/src/components/Services/Services.css"' has no index signature.
Webpack config
const path = require("path");
module.exports = {
context: path.join(__dirname, "src"),
entry: ["./index.js"],
output: {
path: path.join(__dirname, "www"),
filename: "bundle.js"
},
devtool: "sourcemap", // has to be removed in production
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"],
modules: [path.join(__dirname, "node_modules")]
},
module: {
rules: [
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" },
{
test: /\.(jsx?)$/,
exclude: /node_modules/,
use: ["babel-loader"]
},
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "typings-for-css-modules-loader",
options: {
modules: true,
localIdentName: "[name]__[local]___[hash:base64:5]"
// namedExport: true
}
},
"postcss-loader"
]
},
{
test: /\.(jpeg|jpg|png|gif|svg)$/i,
use: [
{
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "images/"
}
}
]
}
]
}
};
And this is my tsconfig:
{
"compilerOptions": {
"outDir": "./www/",
"sourceMap": true,
"noImplicitAny": true,
"alwaysStrict": true,
"strictNullChecks": true,
"module": "es6",
"moduleResolution": "node",
"jsx": "react",
"target": "es5",
"allowJs": true,
"allowSyntheticDefaultImports": true
},
"include": ["./src/**/*"]
}
This is the Services.tsx file:
import React from "react";
import ReactDOM from "react-dom";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import queryString from "query-string";
import * as styles from "./Services.css";
import globalStyles from "../../styles/global.css";
import SubHeader from "../common/SubHeader";
class Services extends React.Component<any, any> {
constructor() {
super();
}
render() {
return (
<div className={styles["services"]}>
<SubHeader text={"MY SERVICES"} />
<div className={styles["body"]}>
//loop over props and render
</div>
</div>
);
}
}
function mapStateToProps(state: any) {
return {
services: state.services,
vehicles: state.vehicles
};
}
export default connect(mapStateToProps)(Services);
This is the Services.css file:
.services {
padding-top: 64px;
.body {
margin: 16px;
}
}
And this is the Services.css.d.ts file created by typings-for-css-modules-loader:
export interface IServicesCss {
'services': string;
'body': string;
}
export const locals: IServicesCss;
Im using typescript: "^2.7.2" and typings-for-css-modules-loader: "^1.7.0"
I cant figure out what the error means. I searched online and all the resource mention about index signature related. Since Im new to Typescript, cant figure out what it takes to fix the issue.
Try this might help article for style loader
Typescript is unable to load the css files or is unable to find the declarations for the same please do try these webpack.config.js changes of adding a style-loader in plugins might help and your related problem will be solved
The error/warning was coming because namedExport was not set as true. (I had it commented out)
Note: even with namedExport: false, the code will run. But the .css.d.ts file created will be of the following structure:
export interface IServiceDetailCardCss {
'service-detail-card': string;
'left-col': string;
'amount': string;
'symbol': string;
}
export const locals: IServiceDetailCardCss;
But, if you set namedExport: true, each classname will be an individual named export, like this:
export const serviceDetailCard: string;
export const leftCol: string;
export const amount: string;
export const symbol: string;
..and you will be able to reference them like styles.serviceDetailCard in your JSX.
Note: If you enable namedExport: true, you should also couple it with camelCase: true, to convert classnames of type some-classname to someClassname.
I'm working on a ReactJs project, I am writing code with ES7 in order to write more elegant code inside my React component, specifically static propTypes.
I use Gulp with Babel, ESlint, but I cannot fix a compilation error related to my static propTypes
This is the error message I get
[11:12:34] ESLintError in plugin 'gulp-eslint'
Message:
Parsing error: Unexpected token =
Details:
fileName: [MYFOLDER]/client/components/app/article/asset/index.js
lineNumber: 5
[11:12:36]
[MYFOLDER]/client/components/app/article/asset/index.js
5:19 error Parsing error: Unexpected token =
and it is referred to the line static propTypes = {
import React from 'react';
export default class Asset extends React.Component {
static propTypes = {
articleImageUrl: React.PropTypes.string.isRequired,
};
static defaultProps = {
articleImageUrl: 'https://placeholdit.imgix.net/~text?txtsize=60&txt=640%C3%97480&w=640&h=480'
};
constructor(props) {
super(props);
}
render() {
return (
<div className="article__asset">
<figure>
<img src={this.props.articleImageUrl} />
</figure>
</div>
);
}
}
This is my babel configuration
return browserify({
debug: true,
entries: [`${NPM_DIR}/es5-shim/es5-shim.js`, CLIENT_DIR + '/index.js']
})
.transform(babelify.configure({
sourceMapRelative: CLIENT_DIR,
presets: ['react', 'es2015', 'stage-0'],
plugins: ["transform-object-rest-spread", "transform-decorators-legacy", "transform-class-properties"]
}))
.bundle()
.on('error', function(e){
gutil.log(e);
})
.pipe(source('bundle.js'))
.pipe(rename('bundle.min.js'))
.pipe(gulp.dest(PUBLIC_DIR));
This is my eslint configuration
{
"plugins": [
"react"
],
"extends": ["eslint:recommended", "plugin:react/recommended"],
"ecmaVersion": 7,
"rules": {
// rules
},
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"jsx": true,
"blockBindings": true
}
}
}
What am I doing wrong? Thanks a lot
Use the babel-eslint parser in your ESLint configuration.
npm install babel-eslint --save
{
"parser": "babel-eslint",
"plugins": ["react"],
...
}