ImmutableJS List type not defined in React - javascript

I'm getting the Typescript error:
'List' refers to a value, but is being used as a type here.
But the immutable.d.ts does have the export interface List type. So I'm unsure of what is going on.
Here is the file I'm trying to reference it from:
import * as React from 'react';
import {IProduct} from "./Product";
const { List } = require('immutable');
interface GantryFootProps {
orders: List<IOrder>;
}
export interface IOrder {
product: IProduct
quantity: number;
}
export const GantryFoot: React.FunctionComponent<GantryFootProps> = (props) => {
return (<div>
<h2>Gantry Foot</h2>
</div>)
}
my tsconfig
{
"compilerOptions": {
"outDir": "./dist/",
"noImplicitAny": true,
"module": "es6",
"target": "es2015",
"jsx": "react",
"allowJs": true,
"lib": ["es2015", "dom"]
},
"exclude": [
"node_modules"
]
}
and my webpack.config
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.scss/,
use: [
MiniCssExtractPlugin.loader,
'css-loader','sass-loader'],
}
],
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
externals: {
"react": 'React'
},
devServer: {
contentBase: './dist'
},
plugins: [new MiniCssExtractPlugin({
filename: 'main.css',
chunkFilename: '[name].css'
})]
};
Any other clues as to the reason for the error would be greatly appreciated. Thanks!

Assuming you are running TypeScript 3.8+ you can use import type { List } from "immutable"(Playground),
for lower TypeScript versions you can use import { List } from "immutable"(Playground) and have webpack remove immutable from your chunk.
Documentation for import type

Related

"Parsing error: invalid character" when bundling images with Webpack's loaders

When I'm trying to bundle my React project, I get the following error regarding an image I'm about to load:
Oddly enough, when I hide the error overlay, I can see in a browser that my picture has been loaded correctly, despite Webpack's complaints. I've already been carefully following other working config examples, where that problem doesn't occur, but I can't easily identify the source of the issue, what leaves me here with no idea how to approach it or where to look for a hint. I've also tried different loaders: url-loader, file-loader and image-webpack-loader, but to no avail. Maybe that's Typescript what causes these troubles?
App.tsx where I'm importing my image:
import "./app.d";
import React from "react";
import SampleImage from "./assets/images/sample-image.jpg";
const App: React.FC = () => (
<div>
<img src={SampleImage} />
</div>
);
export default App;
app.d.ts with modules declarations to allow importing assets in typescripts files:
declare module "*.jpeg";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.png";
webpack.common.ts - my main Webpack config file, where I placed both url-loader and file-loader as it is in ejected create-react-app config, but it doesn't really change anything:
import path from "path";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
module.exports = {
entry: path.resolve(__dirname, "..", "./src/index.tsx"),
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
},
},
],
},
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.(ts|tsx)$/,
/\.css$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
],
loader: require.resolve("file-loader"),
options: {
name: "static/media/[name].[hash:8].[ext]",
},
include: path.resolve(__dirname, "..", "./src/assets"),
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"),
options: {
name: "static/media/[name].[hash:8].[ext]",
},
include: path.resolve(__dirname, "..", "./src/assets"),
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx"],
},
output: {
path: path.resolve(__dirname, "..", "./dist"),
filename: "bundle.js",
},
devServer: {
contentBase: path.resolve(__dirname, "..", "./dist"),
hot: true,
compress: true,
open: true,
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: "React App",
template: path.resolve(__dirname, "..", "./src/index.html"),
}),
new ForkTsCheckerWebpackPlugin({
async: false,
eslint: {
files: path.resolve(__dirname, "..", "./src/**/*"),
},
}),
],
};
webpack.dev.ts
import webpack from "webpack";
import ReactRefreshWebpackPlugin from "#pmmmwh/react-refresh-webpack-plugin";
module.exports = {
mode: "development",
devtool: "eval-source-map",
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
plugins: [require.resolve("react-refresh/babel")],
},
},
],
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ReactRefreshWebpackPlugin({
overlay: {
sockIntegration: "wds",
},
}),
],
};
webpack.prod.ts
import webpack from "webpack";
module.exports = {
mode: "production",
devtool: "source-map",
};
webpack.config.ts
import merge from "webpack-merge";
import devConfig = require("./webpack.dev");
import prodConfig = require("./webpack.prod");
import commonConfig = require("./webpack.common");
module.exports = ({ env }: { env: "dev" | "prod" }) => {
const envConfig = env === "dev" ? devConfig : prodConfig;
return merge(commonConfig, envConfig);
};
Additionaly, my .babelrc file:
{
"presets": [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript"
],
"plugins": [
"react-refresh/babel",
[
"#babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}
and tsconfig.json:
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": ["src"]
}
Scripts in package.json:
"scripts": {
"start": "webpack serve --config config/webpack.config.ts --env env=dev --hot",
"build": "webpack --config config/webpack.config.ts --env env=prod",
},
I'd be really grateful for any hint how to solve the issue.
Thanks to help received here: https://github.com/webpack/webpack/issues/12276#event-4152056913, I've managed to surpass the error overlay by changing the options passed to the ForkTsCheckerWebpackPlugin in my webpack.common.ts file.
Before:
new ForkTsCheckerWebpackPlugin({
async: false,
eslint: {
files: path.resolve(__dirname, "..", "./src/**/*"),
},
}),
Now:
new ForkTsCheckerWebpackPlugin({
async: false,
eslint: {
files: path.resolve(__dirname, "..", "./src/**/*.{ts,tsx,js,jsx}"),
},
}),

Webpack + Typscript library import is undefined in React project

I am trying to make a React library using Typescript, Webpack and Babel however I am running into a problem. If I build then import the library into a React project then my import is 'undefined' (See the below error). I think this would be because in the bundle.js there is no module.exports for the variable that would represent my class there is only a __webpack_exports__["default"] = (ExampleComponent); (However I am unsure of what this does in practice so I could be wrong.)
I specifically got this error:
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
What I have tried:
Changing the tsconfig target to es6 and module to commonjs
Changing the tsconfig target to es6 and module to esnext
Changing the tsconfig target to es5 and module to esnext
Changing the tsconfig target to esnext and module to esnext
Changing the tsconfig target to es6 and module to es6
Changing the tsconfig target to commonjs and module to es6
Importing the library as 'import * as ExampleComponent from ...' and 'import {ExampleComponent} from ...' (As intended) both times 'ExampleComponent' was undefined.
Versions:
babel-loader: ^8.1.0
Webpack: ^4.43.0
typescript: ^3.8.3
Code:
React Project:
import React from "react";
import { ExampleComponent } from "test-lib";
// This is always undefined
console.log(ExampleComponent);
function App() {
return <ExampleComponent />;
}
export default App;
Library Project:
index.ts:
import ExampleComponent from './ExampleComponent'
export { ExampleComponent }
ExampleComponent.tsx
import * as React from 'react'
import './ExampleComponent.css'
interface Props {
text: string
}
// prettier-ignore
const ExampleComponent: React.FC<Props> = ({ text }) => (
<h1 className="example-text">{text}</h1>
)
export default ExampleComponent
Library Configs:
tsconfig.json:
{
"compilerOptions": {
"outDir": "dist",
"module": "esnext",
"lib": ["dom", "esnext"],
"moduleResolution": "node",
"jsx": "react",
"sourceMap": true,
"declaration": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowSyntheticDefaultImports": true,
"target": "es5",
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src", "tests"],
"exclude": ["node_modules", "dist", "example"]
}
.babelrc:
{
"presets": [
[
"#babel/preset-env",
{
"debug": true,
"useBuiltIns": "usage",
"corejs": 3
}
],
"#babel/preset-react",
"#babel/preset-typescript"
]
}
Webpack Config:
const path = require('path')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
module.exports = {
entry: {
bundle: './src/index.ts',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.json'],
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [{ loader: 'babel-loader' }, { loader: 'ts-loader' }],
},
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
{
test: /\.(gif|png|jpe?g|svg)$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
disable: true,
},
},
],
},
{
test: /\.js$/,
enforce: 'pre',
loader: 'source-map-loader',
},
],
},
plugins: [new ForkTsCheckerWebpackPlugin()],
}
If you want to see the full code here is a link to the Github Repo.
Based on the comment from Scovy I was able to get this working by using the output.libraryTarget and output.globalObject output options.
Now my output entry in my webpack.base.config.js looks like this:
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
libraryTarget: 'umd',
globalObject: 'this',
},
Update:
The above change did not seam to work 100% of the time so I found a library called esm-webpack-plugin which ended up working perfectly.
So the final code for the output entry in the webpack config is:
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: 'LIB',
libraryTarget: 'var',
},
and I also added the plugin:
plugins: [new ForkTsCheckerWebpackPlugin(), new EsmWebpackPlugin()],

Why is webpack not generating chunks from my dynamic imports?

I had ended up doing some refactoring around routes to allow for code splitting but following the react/webpack instructions I've still got just the 2 entry bundles being generated.
Index.tsx
import React from "react"
import { render } from "react-dom"
import { Provider } from "react-redux"
import { store } from "services/configureStore"
import { ConnectedApp } from "src/App"
import { ConnectedFeatureToggleProvider } from "./components/AppWrapper/FeatureToggleProvider"
const renderApp = () => {
render(
<Provider store={store}>
<ConnectedFeatureToggleProvider>
<ConnectedApp />
</ConnectedFeatureToggleProvider>
</Provider>,
document.querySelector("#app"),
)
}
// run app when FIT Core functions are ready
window.onFitCoreReady = () => {
renderApp()
}
App.tsx
import React, { useEffect, Suspense } from "react"
import { hot } from "react-hot-loader/root"
import { connect } from "react-redux"
import { Switch, Redirect, Route, Router } from "react-router-dom"
// import { ConnectedEconomyManager } from "modules/economyManager/EconomyManager"
import { ConnectedPlayerAccounts } from "modules/playerAccountDataManager/PlayerAccounts"
import { HealthDashboard } from "modules/healthDashboard/HealthDashboard"
import { PackSimulator } from "modules/packSimulator/PackSimulator"
const mapStateToProps = (state: GlobalState) => ({
...
})
const mapDispatchToProps = (dispatch: Dispatch) => ({
...
})
type Props = {
}
const ConnectedEconomyManager = React.lazy(() => import("modules/economyManager/EconomyManager"))
export const App = ({
}: Props) => {
return (
<Router history={history}>
<Suspense fallback={<span>LOADING LOADING LOADING</span>}>
<Switch>
<Redirect
exact
from="/marketplace/"
to={{
pathname: "/marketplace/economy",
search: window.location.search,
}}
/>
<Route path="/marketplace/economy" component={ConnectedEconomyManager} />
<Route path="/marketplace/playerAccounts" component={ConnectedPlayerAccounts} />
<Route path="/marketplace/health" component={HealthDashboard} />
<Route path="/marketplace/packSimulator" component={PackSimulator} />
</Switch>
</Suspense>
</Router>
)
}
export const ConnectedApp = hot(connect(mapStateToProps, mapDispatchToProps)(App))
webpack/local.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const _ = require('lodash');
const common = require('./common');
const path = require('path');
const open = process.env.npm_package_config_WEBPACK_OPEN_WINDOW === 'true';
const host = process.env.npm_package_config_WEBPACK_LOCAL_HOST;
const port = process.env.npm_package_config_WEBPACK_PORT;
const ROOT_DIR = path.resolve(__dirname, '../');
const APP_DIR = path.resolve(ROOT_DIR, 'src');
module.exports = env => {
if (!env) {
// Prevent references to 'undefined'
env = {};
}
return merge.smart(common, {
mode: 'development',
devServer: {
disableHostCheck: true,
port: '443',
historyApiFallback: true,
open: open ? 'Google Chrome' : open, // auto-open in browser
openPage: 'marketplace/economy?project=' + projectName,
},
devtool: 'eval-source-map',
module: {
rules: [
_.merge(
_.find(common.module.rules, rule => rule.use && rule.use.loader === 'babel-loader'),
{ use: { options: { plugins: ['#babel/plugin-syntax-dynamic-import', 'babel-plugin-styled-components', '#babel/plugin-proposal-class-properties'] } } }
),
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
],
},
plugins: [
// copies the index.html file to the build directory:
new HtmlWebpackPlugin({
template: `${APP_DIR}/index.html`,
templateParameters: {
...define
}
}),
],
});
}
webpack/common.js
const path = require('path');
const webpack = require('webpack');
const ROOT_DIR = path.resolve(__dirname, '../');
const BUILD_DIR = path.resolve(ROOT_DIR, 'dist');
const APP_DIR = path.resolve(ROOT_DIR, 'src');
module.exports = {
entry: {
main: [
`${APP_DIR}/index.tsx`, // main entry point to the application
],
semantic: path.resolve(ROOT_DIR, 'semantic-theme', 'semantic.less'),
},
module: {
rules: [
{
test: /\.[j|t]sx?$/,
use: {
loader: 'babel-loader',
options: {
presets: [['#babel/preset-env', { useBuiltIns: 'entry', corejs: '3.0.0' }], '#babel/preset-react'],
overrides: [
{
test: /\.tsx?$/,
presets: [['#babel/preset-env', { useBuiltIns: 'entry', corejs: '3.0.0' }], '#babel/preset-react', '#babel/preset-typescript'],
},
],
},
},
include: APP_DIR,
},names
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: 'url-loader?limit=10000&mimetype=application/font-woff',
},
{
test: /\.(ttf|otf|eot|svg|png|jpe?g|gif)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: 'file-loader',
},
],
},
output: {
path: `${BUILD_DIR}`,
filename: '[name].[hash].js',
chunkFilename: '[name].[hash].js',
publicPath: '/',
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
modules: [
ROOT_DIR,
APP_DIR,
'node_modules',
],
alias: {
// tell semantic-ui-less to use our theme config
'../../theme.config$': path.resolve(ROOT_DIR, 'semantic-theme', 'theme.config'),
'react-dom': '#hot-loader/react-dom',
},
},
stats: { colors: true },
};
tsconfig.json
{
"compilerOptions": {
"plugins": [
{
"name": "typescript-styled-plugin"
}
],
"noEmit": true,
"strict": true,
"sourceMap": true,
"noImplicitAny": false,
"noUnusedLocals": true,
"module": "esnext",
"target": "esnext",
"lib": [
"esnext",
"dom"
],
"moduleResolution": "node",
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"components/*": ["src/components/*"],
"modules/*": ["src/modules/*"],
"services/*": ["src/services/*"],
},
"types": [
"react",
"jest",
]
},
"include": [
"./src/**/*",
"./#types/**/*",
],
}
I expected a new chunk to be generated for the EconomyManager lazy import but the build only generates a main.[hash].js and semantic.[hash].js. Where am I going wrong here?
I checked and EconomyManager exports are not being referenced anywhere else in the application as I thought that might be it.
#babel/preset-env might be transpiling your dynamic imports to deferred requires, this will prevent Webpack from knowing where to code split.
We will need to exclude the plugin #babel/plugin-proposal-dynamic-import so your import() statements are preserved. Try adding the exclude field to your #babel/preset-env options in your Webpack config.
presets: [['#babel/preset-env', { useBuiltIns: 'entry', corejs: '3.0.0', exclude: ['proposal-dynamic-import'] }]
This is described in similar GitHub Issues:
https://github.com/babel/babel/issues/11204
https://github.com/babel/babel/issues/10194
If you set the 'moduleResolution' property to 'NodeNext' in tsconfig, it creates.
"compilerOptions": {
"moduleResolution": "NodeNext",
"module": "commonjs",
}

Cannot access prototype after compiling with Webpack in Typescritp

I Converted an old Javascript library for the company I work with, to a brand new Typescript Library. I use WebPack since I heard i need a module management system in order to make my library works in the browser. Problem is, whenever i'm trying to initiate my library ( in the JQuery function $(document).ready() , i cannot access my object prototype, I got a typeError :
Uncaught TypeError: Cannot read property 'loadPage' of undefined
Here is my code :
webpack.config.js
const path = require('path');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var webpack = require("webpack");
var fs = require("fs");
module.exports = {
entry: './src/libraryLoader.ts',
module: {
rules: [
{
test: /\.(tsx?)$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.json$/,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]",
publicPath: "config/"
}
},
exclude: /node_modules/
}
]
},
plugins: [
new CopyWebpackPlugin([
{ from: 'src/settings/config', to: "config" }
]),
new UglifyJsPlugin({
uglifyOptions: {
ie8: true,
//compress: true
}
}),
new webpack.BannerPlugin({
banner: fs.readFileSync('./copyright', 'utf8'),
raw: true
})
],
resolve: {
extensions: [ '.tsx', '.ts', '.js' ],
modules: [
"node_modules"
]
},
// Enable sourcemaps for debugging webpack's output.
devtool: "eval",
output: {
filename: 'library.min.js',
path: path.resolve(__dirname, 'dist'),
},
watch: false,
watchOptions: {
ignored: /node_modules/
},
externals: {
// require("jquery") is external and available
// on the global var jQuery
"jquery": "jQuery"
}
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"moduleResolution": "node",
"module": "commonjs",
"outDir": "build",
"strict": true,
"noImplicitAny": true,
"allowJs": true,
"sourceMap": true,
"typeRoots": [
"./node_modules/#types"
],
"project": "src",
"lib": [ "es2015", "dom"]
},
"include": [
"src/**/*",
"typings/**/*"
]
}
Where I load my library :
$(document).ready(function () {
let loader = new LibraryLoader();
loader.library().loadPage(); // <- Error here
/*...*/
});
My Loader class
export class LibraryLoader{
private _library: Library;
public LibraryLoader() {
this.setLibrary(new Library(/* ... unrelated options ... */));
}
public library(): Library{
return this._library;
}
private setLibrary(value: Library) : void {
this._library = value;
}
}
P.S. There is a loadPage function in the library, but the function library() return undefined anyways,

import node modules in electron angular webpack app

I have a electron application using Angular (2) and I pack it using Webpack. I want to use my node modules in my Angular components
import { Injectable } from '#angular/core';
import {Bonjour} from 'bonjour';
Update:
I set the target to electron-renderer in my webpack configuration. Now I get the error error TS2693: 'Bonjour' only refers to a type, but is being used as a value here
Bonjour remains undefined.
Here is my webpack.config.js:
var path = require('path');
var webpack = require('webpack');
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
module.exports = {
devtool: 'source-map',
target: 'electron-renderer',
entry: {
'angular': [
'rxjs',
'reflect-metadata',
'#angular/core',
'#angular/router',
'#angular/http'
],
'app': './app/main'
},
output: {
path: __dirname + '/build/',
publicPath: 'build/',
filename: '[name].js',
sourceMapFilename: '[name].js.map',
chunkFilename: '[id].chunk.js'
},
resolve: {
extensions: ['.ts', '.js', '.json', '.css', '.html']
},
module: {
loaders: [{
test: /\.ts$/,
loader: 'ts-loader',
exclude: [/node_modules/]
}]
},
plugins: [
new CommonsChunkPlugin({ name: 'angular', filename: 'angular.js', minChunks: Infinity }),
new CommonsChunkPlugin({ name: 'common', filename: 'common.js' })
]
};
How can I setup my Electron/Angular application in such a way that I can import node modules as mentioned above?
added tsconfig:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"moduleResolution": "node",
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"typeRoots": [
"../node_modules/#types"
]
},
"files": [
"app/main.ts"
]
}
This plugin in question exported a prototype function in its module.exports. Thus the plugin needed to be instantiated like this:
var Bonjour = require('bonjour')
var bonjour = new Bonjour();

Categories

Resources