How to make a declared module visible in Enzyme shallow render? - reactjs

I've been trying to figure out how to have my declared module be found when I shallow render a component using enzyme in my jest unit tests. I have a custom declared module like so:
// index.d.ts
declare module "_aphrodite" {
import {StyleDeclarationValue} from "aphrodite";
type CSSInputType = StyleDeclarationValue | false | null | void;
interface ICSSInputTypesArray extends Array<CSSInputTypes> {}
export type CSSInputTypes = CSSInputType | CSSInputType[] | ICSSInputTypesArray;
}
Which is used by a component of mine called closeButton:
// closeButton.tsx
import {CSSInputTypes} from "_aphrodite";
export interface ICloseButtonProps {
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
cssStyles?: CSSInputTypes;
}
#injectable()
#observer
#autobind
export class CloseButton extends React.Component<ICloseButtonProps> {
// implementation
}
And a simple unit test that shallow renders a component:
// closeButton.test.tsx
import {shallow} from "enzyme";
import {CloseButton} from "../../common";
import * as React from "react";
describe("Common - Close Button", () => {
it("Shallow Render", () => {
const component = shallow(<CloseButton onClick={null}/>);
console.log(component);
});
});
When I run the test, I get the following error:
Which is strange because the closeButton class doesn't throw any compilation errors and maps the module fine. Same goes with when I run my project locally, it doesn't throw any run time error about not being able to find the _aphrodite module. It seems it's just with testing that this comes up.
Now I've tried to change various settings in my jest.config.json, tsconfig.json, and webpack.config.js settings with no luck. I'm hoping someone with more experience than I would know what needs to be done in order to make my _aphrodite module found when running a shallow render on a component.
Below are the settings for the aforementioned files:
// jest.config.json
{
"verbose": true,
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"moduleDirectories": [
"node_modules",
"src"
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|svg)$": "<rootDir>/src/components/__tests__/_transformers/fileTransformer.js"
},
"transform": {
"\\.(ts|tsx)$": "ts-jest"
},
"setupFiles": [
"<rootDir>/src/components/__tests__/setup.ts"
],
"testRegex": "(/__tests__/\\.*|(\\.|/)(test))\\.tsx?$",
"testURL": "http://localhost/",
"collectCoverage": false,
"timers": "fake"
}
// tsconfig.json
{
"compileOnSave": true,
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build/",
"sourceMap": true,
"noImplicitAny": true,
"module": "esnext",
"target": "es2018",
"jsx": "react",
"watch": false,
"removeComments": true,
"preserveConstEnums": true,
"inlineSourceMap": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"lib": [
"dom",
"dom.iterable",
"es2018",
"esnext"
],
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"isolatedModules": false
},
"include": [
"./src/**/*"
],
"exclude": [
"./node_modules"
]
}
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require('webpack');
const dotenv = require('dotenv');
const fs = require('fs'); // to check if the file exists
module.exports = () => {
return {
plugins: []
};
};
/**
* DevServer
*/
const devServer = {
inline: true,
host: "localhost",
port: 3000,
stats: "errors-only",
historyApiFallback: true,
watchOptions: {
poll: true
},
};
module.exports.getEnv = () => {
// Create the fallback path (the production .env)
const basePath = __dirname + '/.env';
// We're concatenating the environment name to our filename to specify the correct env file!
const envPath = basePath + ".local";
// Check if the file exists, otherwise fall back to the production .env
const finalPath = fs.existsSync(envPath) ? envPath : basePath;
// call dotenv and it will return an Object with a parsed key
const finalEnv = dotenv.config({path: finalPath}).parsed;
// reduce it to a nice object, the same as before
const envKeys = Object.keys(finalEnv).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(finalEnv[next]);
return prev;
}, {});
return new webpack.DefinePlugin(envKeys);
};
/**
* Plugins
*/
const plugins = [
new HtmlWebpackPlugin({
template: "./index.html"
}),
module.exports.getEnv()
];
module.exports = {
entry: "./src/index.tsx",
output: {
filename: "bundle.js",
path: __dirname + "/build",
publicPath: "/"
},
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".json"]
},
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'.
{test: /\.tsx?$/, loader: "ts-loader"},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{enforce: "pre", test: /\.js$/, loader: "source-map-loader", exclude: [/node_modules/, /build/, /__test__/]},
{test:/\.css$/, use:['style-loader','css-loader'] },
{test:/\.(png|svg)$/, loader: "url-loader"},
{test:/\.mp3$/, loader: "url-loader" }
]
},
plugins: plugins,
devServer: devServer,
mode: "development",
performance: {
hints: false
}
};
And here is my project structure:
Feel free to ask if more information is needed.

Turns out I just needed to add it to the list of setup files in jest.config.json
// jest.config.json
"setupFiles": [
"<rootDir>/src/components/__tests__/setup.ts",
"<rootDir>/src/aphrodite/index.ts"
],

Related

Bundle Webpack React library as an ESM module - Object.defineProperty called on non-object

I need to publish a single package that can be imported as default (or named as last resort) by multiple projects.
After hundreds of issues trying to do this with Rollup I moved on to trying to bundle my library with Webpack. My package only exports one default.
I have another project that is importing the package as default that I have been testing with, it's intended that it should return the root React component which is default exported.
I have tried bundling with UMD, which doesn't crash my external project but doesn't return an exported default or named export anywhere.
If I set my Webpack config to bundle ESM instead I get an obscure vague error regarding Object.defineProperty.
If somebody has ESM bundling working in Webpack 5 please help, or any ideas that have worked for you
Uncaught TypeError: Object.defineProperty called on non-object
at Function.defineProperty (<anonymous>)
at Function.__webpack_require__.r (build.js:13594)
at eval (index.js:1)
at Object.../discover-v2/dist/index.js (build.js:10623)
at __webpack_require__ (build.js:13402)
at fn (build.js:13671)
at eval (index.tsx:20)
at Module../src/views/Discovers/Discover/index.tsx (build.js:5141)
at __webpack_require__ (build.js:13402)
at fn (build.js:13671)
This points to a webpack function:
/* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
webpack.config.ts:
module.exports = {
entry: './src/package.tsx',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
library: {
// name: 'discover',
type: 'module',
},
libraryTarget: 'module',
module: true,
},
experiments: {
asset: true,
outputModule: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
//TODO waiting on https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/61
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
//#ts-ignore
plugins: [new TsconfigPathsPlugin()],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
],
exclude: [/node_modules/, nodeModulesPath],
},
{
test: /\.(png|jpg|mov|mp4)$/,
loader: 'url-loader',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader'],
},
{
test: /\.svg$/,
use: ['#svgr/webpack', 'url-loader'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new webpack.ProvidePlugin({
process: 'process/browser',
}),
new webpack.DefinePlugin({
'process.env.LOCAL': JSON.stringify(process.env.LOCAL),
'process.env.STAGING': JSON.stringify(process.env.STAGING),
'process.env.API_URL': JSON.stringify(process.env.API_URL),
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.NAME': JSON.stringify(pkg.name),
'process.env.VERSION': JSON.stringify(pkg.version),
}),
new ForkTsCheckerWebpackPlugin({
eslint: {
files: './src/**/*.{ts,tsx,js,jsx}', // required - same as command `eslint ./src/**/*.{ts,tsx,js,jsx} --ext .ts,.tsx,.js,.jsx`
},
}),
new CleanWebpackPlugin(),
],
};
.babelrc
{
"presets": [
["#babel/env", { "modules": false }],
"#babel/react",
"#babel/typescript",
[
"#emotion/babel-preset-css-prop",
{
"autoLabel": "always",
"sourceMap": true,
"labelFormat": "[local]"
}
]
],
"plugins": [
"#babel/transform-runtime",
"#babel/proposal-class-properties",
"#babel/proposal-object-rest-spread"
]
}
Root component - src/package.tsx
const RootProvider: React.FC<RootProps> = ({ assets, ...props }) => (
<StoreProvider store={store}>
<ConnectedRouter history={history}>
<AssetsContext.Provider value={assets}>
<Root {...props} />
</AssetsContext.Provider>
</ConnectedRouter>
</StoreProvider>
);
export default RootProvider;
export { RootProvider as discover };
package.json main/module
{
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
...
tsconfig.json
{
"compilerOptions": {
"typeRoots": ["src/#types", "node_modules/#types"],
"sourceMap": true,
"noImplicitAny": false,
"module": "esnext",
"target": "es5",
"lib": ["esnext", "dom", "dom.iterable"],
"removeComments": true,
"allowSyntheticDefaultImports": true,
"jsx": "react-jsx",
"jsxImportSource": "#emotion/react",
"allowJs": true,
"baseUrl": "./",
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"downlevelIteration": true,
"paths": {
...
},
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"declarationDir": ".",
"preserveConstEnums": true,
"declaration": true // generates .d.ts files inside the output directory
},
"include": ["./src", "./webpack.config.ts", "./webpack.package.config.ts"],
"exclude": ["dist"]
}
Bundle dist/index.js output exports (added default and named to test)
var __webpack_exports__ = __webpack_require__("./src/package.tsx");
var __webpack_exports__default = __webpack_exports__.default;
var __webpack_exports__discover = __webpack_exports__.discover;
export { __webpack_exports__default as default, __webpack_exports__discover as discover };
My consuming project that is importing the package has a similar webpack (without the library config, including aliases to fix multiple react errors when testing packaged project with yarn link), with an exact same .babelrc and tsconfig.json (with path changes)
alias: {
...
// bypass multiple react versions issue when testing internal packages
react: path.resolve('./node_modules/react'),
'react-dom': path.resolve('./node_modules/react-dom'),
'#emotion/react': path.resolve('./node_modules/#emotion/react'),
'#material-ui/core': path.resolve('./node_modules/#material-ui/core')
}
From my research, it is related to eval-* devtool in webpack config.
https://github.com/webpack/webpack/issues/11277#issuecomment-724024303
A workaround would be to set it (for the project importing that package) to false or change it to something other than eval-* according to docs: https://webpack.js.org/configuration/devtool/

Webpack Dev Server cannot find modules with Typescript if I use custom paths in tsconfig

I'm building a project from scratch on React + TypeScript and using the Webpack Dev server.
I want to use relative paths to components, which will be not strict for a more flexible development.
In my App.tsx, I'm trying to import component:
import EntityTypes from "components/EntityTypes/EntityTypes";
and getting an error
Module not found: Error: Can't resolve 'components/EntityTypes/EntityTypes' in '/home/eugene/prj/work/crud-react/src'
File by this path exists (/src/components/EntityTypes/EntityTypes.js) and exports the class like
export default EntityTypes;
My tsconfig.json:
{
"compilerOptions": {
"sourceMap": true,
"noImplicitAny": false,
"module": "commonjs",
"target": "es5",
"lib": [
"es2015",
"es2017",
"dom"
],
"removeComments": true,
"allowSyntheticDefaultImports": false,
"jsx": "react",
"allowJs": true,
"baseUrl": ".",
"paths": {
"components/*": [
"src/components/*"
]
}
}
}
webpack.config.js:
const HtmlWebPackPlugin = require( 'html-webpack-plugin' );
const path = require( 'path' );
module.exports = {
context: __dirname,
entry: './src/index.js',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
output: {
path: path.resolve( __dirname, 'dist' ),
filename: 'main.js',
publicPath: '/',
},
devServer: {
historyApiFallback: true
},
module: {
rules: [
{
test: /\.(tsx|ts)?$/,
loader: 'awesome-typescript-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|j?g|svg|gif)?$/,
use: 'file-loader'
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: path.resolve( __dirname, 'public/index.html' ),
filename: 'index.html'
})
]
};
I've checked the documentation of typescript about "paths", the file is exists, I don't understand what the problem.
I have sorted out how to use paths for my files and make them flexible, that I can reorganize the project and manage paths easy without a lot of changes of strict relative paths.
The problem was related to configuration of the webpack.
Final configuration is next:
tsconfig.json
{
"compilerOptions": {
// ...
"paths": {
"~/components/*": [
"./src/components/*"
],
"~/services/*": [
"./src/services/*"
],
"~/interfaces/*": [
"./src/interfaces/*"
]
},
}
}
webpack.config.js
const path = require('path');
module.exports = {
// ...
resolve: {
// ...
alias: {
'~/components': path.resolve(process.cwd(), './src/components'),
'~/interfaces': path.resolve(process.cwd(), './src/interfaces'),
'~/services': path.resolve(process.cwd(), './src/services'),
}
},
}
Examples of usage
import {IEntities} from "~/interfaces/entities.interface";
// ...
import EntityForm from "~/components/EntityForm/EntityForm";
Just add tsconfig-paths-webpack-plugin into your webpack configuration:
const { cwd } = require('node:process');
const { resolve } = require('node:path');
const TsconfigPathsWebpackPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
resolve: {
plugins: [
new TsconfigPathsWebpackPlugin({
configFile: resolve(cwd(), './tsconfig.json'),
})
]
}
};
And you don't need to duplicate paths configuration as webpack aliases.
You're using absolute imports, not relative ones. Try import EntityTypes from "./components/EntityTypes/EntityTypes";, assuming the file you're importing it into is on the same level as the components directory.

Visual Studio Code Intellisense doesn't work with Opencv.JS?

I have reactjs + typescript web application which suppose to use opencv.js library for doing some vision tasks. However I can't make intellisense to work in my Visual Studio Code IDE for that library. I load library as following
var CV = require('../opencvLibs/build_js/bin/opencv_js.js');
class InnerApp extends React.Component{
componentDidMount(){
CV["onRuntimeInitialized"]= () => {
let mat = new CV.Mat();
console.log(mat.size());
mat.delete();
}
}
render(){
return(
<Provider rootStore = {new RootStore()}>
<App/>
</Provider>
)
}
}
ReactDOM.render(
<Provider>
<InnerApp />
</Provider>,
document.getElementById('root'));
As you can see I am logging into console empty mat size just to check if library is working fine and I am getting width and height logged properly which indicates that library is loaded successfully.
I tried to add jsconfig.json file as I saw that as potential solution in official Visual Studio Code docs, however that didn't work.
tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"module": "commonjs",
"target": "es6",
"noImplicitAny": false,
"esModuleInterop": true,
"removeComments": true,
"preserveConstEnums": true,
"jsx": "react",
"sourceMap": true,
"experimentalDecorators": true,
"lib": ["es6","dom"],
"typeRoots": [
"typings",
"node_modules/#types"
]
},
"files":[
"opencvLibs/build_js_wasm/bin/opencv.js"
]
}
jsconfig.json
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
},
"exclude": [
"node_modules",
"**/node_modules/*"
],
"include": [
"opencvLibs/build_js/bin/opencv.js"
],
"typeAcquisition": {
"include": [
"opencvLibs/build_js/bin/opencv.js"
]
}
}
webpack.dev.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const HOST = process.env.HOST || '127.0.0.1';
const PORT = process.env.PORT || '9000';
const config = {
mode: 'development',
entry: {
app: './src/index.tsx',
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].bundle.js'
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
devtool: 'eval-cheap-module-source-map',
module: {
rules: [{
test: /\.tsx?$/,
use: [{
loader: 'ts-loader',
options: {
onlyCompileBundledFiles: true,
}
}]
},
{
test: /\.(s?)css$/,
use: ['style-loader','css-loader','sass-loader']
},
{
test: /\.otf$/,
use: {
loader: 'file-loader',
options: {
name: 'dist/fonts/[name].[ext]'
}
}
},
{
//test: /\.(png|jpe?g|gif|svg)$/
test: /\.(eot|woff|woff2|ttf|svg|png|jpe?g|gif)(\?\S*)?$/,
use: [{
loader: 'url-loader',
//loader: 'url?limit=100000&name=[name].[ext]',
options: {
limit: 8192
}
}]
},
],
},
devServer: {
host: HOST,
port: PORT,
compress: true,
inline: true,
historyApiFallback: true,
hot: true,
overlay: true,
open: true,
},
plugins: [
new webpack.LoaderOptionsPlugin({
options:{
scripts: [
"../opencvLibs/build_js_wasm/bin/opencv.js"
]}
}),
new webpack.HotModuleReplacementPlugin({
}),
new HtmlWebpackPlugin({
template: 'src/index.html',
title: 'React Mobx Starter',
inject: 'body'
}),
],
node: {
fs: 'empty'
},
};
module.exports = config;
Expected result would be that intellisense works when I type "CV."
Thanks for your help!

Creating a React component library

I am creating a modular component library with React and TypeScript with Babel 7.
I want the user of my library to import the components by a syntax similar to this:
import SomeComponent from "my-awesome-lib/SomeComponent"
SomeComponent is a TSX module in my-awesome-lib package:
import * as React from "react";
export default function () {
return <h1>SomeComponent</h1>
}
And main field of the package.json file of my-awesome-component is:
"main": "src/index.ts"
I do not want to publish compiled version of my component library, because I am importing CSS and other assets in my components and I expect all the users of my package to use Webpack with some specific configs.
Now my problem is that `import SomeComponent from "my-awesome-lib/SomeComponent" fails with a parse error:
ERROR in ../node_modules/wtf/index.tsx 4:9
Module parse failed: Unexpected token (4:9)
You may need an appropriate loader to handle this file type.
|
| export default function () {
> return <h1>WTF</h1>
| }
It seems Webpack does not load or transform the TSX files in node_modules.
I use this tsconifg.json at the root of my user app (which imports my-awesome-lib):
{
"compilerOptions": {
"outDir": "./dist",
"module": "commonjs",
"target": "es5",
"jsx": "react",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"esModuleInterop": true,
"downlevelIteration": true,
"lib": ["es5", "es2015", "dom", "scripthost"],
"typeRoots": ["node_modules/#types", "src/#types"],
},
"include": ["src/**/*", "node_modules/**/*"],
"exclude": []
}
And the relevant configurations of Webpack are:
const tsModules = {
test: /\.(js|jsx|ts|tsx)$/,
include: [path.resolve('src')],
exclude: /node_modules/,
loader: 'babel-loader'
}
const resolve = {
alias: {
},
modules: [
'node_modules'
],
extensions: ['.tsx', '.ts', '.js']
}
module.exports = {
...
context: resolve('src'),
resolve: resolve,
module: {
rules: [
tsModules,
...
]
}
}
How can I make Webpack to load and transform TSX modules of my-awesome-lib from node_modules?
This setup assuming you are using styled-components and have no css/scss.
What's important here is that you have "module": commonJS in your tsconfig and libraryTarget: "commonJS" in your webpack config. Externals tells webpack not bundle you're library with React, or React-DOM or styled-components and instead to look for those packages within the project you're importing into.
you're also going to need to take React, react-dom and styled-components out of your package.json dependencies and put those package in your peer-dependencies
const path = require("path");
const fs = require("fs");
const TerserPlugin = require('terser-webpack-plugin');
const appIndex = path.join(__dirname, "../src/main.tsx");
const appBuild = path.join(__dirname, "../storybook-static");
const { TsConfigPathsPlugin } = require('awesome-typescript-loader');
module.exports = {
context: fs.realpathSync(process.cwd()),
mode: "production",
bail: true,
devtool: false,
entry: appIndex,
output: {
path: appBuild,
filename: "dist/Components.bundle.js",
publicPath: "/",
libraryTarget: "commonjs"
},
externals: {
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react'
},
'react-dom': {
root: 'ReactDOM',
commonjs2: 'react-dom',
commonjs: 'react-dom',
amd: 'react-dom'
},
"styled-components": {
root: "styled-components",
commonjs2: "styled-components",
commonjs: "styled-components",
amd: "styled-components"
}
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
parallel: true,
cache: true,
sourceMap: false,
})
],
},
resolve: {
extensions: [".web.js", ".mjs", ".js", ".json", ".web.jsx", ".jsx", ".ts", ".tsx"],
alias: {
"react-native": "react-native-web",
},
},
module: {
strictExportPresence: true,
rules: [
{ parser: { requireEnsure: false } },
{
test: /\.(ts|tsx)$/,
loader: require.resolve("tslint-loader"),
enforce: "pre",
},
{
oneOf: [
{
test: /\.(tsx?)$/,
loader: require.resolve('awesome-typescript-loader'),
options: {
configFileName: 'tsconfig.prod.json'
}
},
],
},
],
},
plugins: [
new TsConfigPathsPlugin()
],
node: {
dgram: "empty",
fs: "empty",
net: "empty",
tls: "empty",
child_process: "empty",
},
performance: false,
};
note: it's important that you target a point in you're application as an entry that ONLY has the components you want to export.
I.E For me it's Main.tsx and inside Main.tsx it looks like this.
export { Checkbox } from "./components/Checkbox/Checkbox";
export { ColorUtils } from "./utils/color/color";
export { DataTable } from "./components/DataTable/DataTable";
export { DatePicker } from "./components/DateTimePicker/DatePicker/DatePicker";
export { DateTimePicker } from "./components/DateTimePicker/DateTimePicker/DateTimePicker";
export { Disclosure } from "./components/Disclosure/Disclosure";
This means webpack won't bundle things you're not meaning to export. To test you're bundle works try importing something with require syntax from the bundle to get around typescript typings and turn allowJS true in tsconfig.
something like
const Button = require("../path/to/js/bundle").Button
console.log(Button);
I found create-react-library extremely helpful
You are excluding your node_modules directory (which generally is a good thing):
const tsModules = {
test: /\.(js|jsx|ts|tsx)$/,
include: [path.resolve('src')],
exclude: /node_modules/,
loader: 'babel-loader'
}
Besides explicitely excluding the node_modules folder, you also only allow the content of the src folder to be processed by babel-loader because of your include property in tsModules. So this error:
ERROR in ../node_modules/wtf/index.tsx 4:9 Module parse failed:
Unexpected token (4:9)
makes sense.
You can still exclude node_modules except for a single folder if you remove the tsModules.include property and change your regular expression in tsModules.exclude:
const tsModules = {
// ...
exclude: /node_modules\/(?!my-awesome-lib)\/*/
// ...
}
Or you could, but I haven't tested it yet, add the my-awesome-lib dir to the include array:
const tsModules = {
// ...
include: [
path.resolve(__dirname, './src'),
path.resolve(__dirname, './node-modules/my-awesome-lib')
]
// ...
}
Then files in your node_modules/my-awesome-lib directory will pass the babel-loader which will transform the typescript code.
Edit: I think you confusion is coming from your tsconfig.json file with "include": ["src/**/*", "node_modules/**/*"],. Babel is transpiling your code, not typescript. So having a tsconfig.json file in your root directory may help your IDE (especially if you are using Microsoft's VScode), but has no effect on how babel and #babel/preset-typescript transform your code.

Webpack+ tsconfig + dynamic import throwing NoEmit error for .d.ts files

I'm having a strange issue understanding how webpack, tsconfig and .d.ts files are working together.
I've the following project structure:
The ScriptsApp contains an #types folder as follows:
My tsconfig.json is as follows:
{
"compilerOptions": {
"target": "ES2018",
"module": "esnext",
"lib": [
"es6",
"dom",
"scripthost",
"es2018",
"es2018.promise"
],
"jsx": "react",
"sourceMap": true,
"outDir": "./.out/",
"noImplicitAny": true,
"strictFunctionTypes": true,
"alwaysStrict": true,
"moduleResolution": "node",
"typeRoots": ["node_modules/#types", "ScriptsApp/#types"]
},
"include": ["./ScriptsApp/**/*.tsx", "./ScriptsApp/**/*.ts", "ScriptsApp/#types"],
"exclude": ["node_modules"],
"files": ["ScriptsApp/indexApp.tsx"]
}
And this is my webpack config:
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
module.exports = {
mode: "development",
output: {
filename: "[name].bundle.[hash].js",
path: path.join(__dirname, ".out/"),
chunkFilename: "[name].chunk.js",
publicPath: "/",
hotUpdateChunkFilename: ".hot/hot-update.js",
hotUpdateMainFilename: ".hot/hot-update.json"
},
optimization: {
runtimeChunk: {
name: "manifest"
},
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
priority: -20,
chunks: "all"
}
}
}
},
target: "web",
devServer: {
contentBase: ".out/",
hot: true
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
inject: true,
template: path.join(__dirname, "./index.html")
}),
new ForkTsCheckerWebpackPlugin({
checkSyntacticErrors: true,
tslint: "./tslint.json",
tslintAutoFix: true,
tsconfig: "./tsconfig.json",
async: false,
reportFiles: ["ScriptsApp/**/*"]
})
],
module: {
rules: [
{
test: /\.(png|jpg|ico)$/,
loader: "file-loader",
options: {
name: ".img/[name].[ext]?[hash]",
publicPath: "/"
}
},
{
test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "file-loader",
options: {
name: ".fonts/[name].[ext]?[hash]",
publicPath: "/"
}
},
{
test: /\.(ts|tsx)$/,
use:"ts-loader"
},
{
test: /\.js$/,
loader: "source-map-loader"
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},
{
loader: "css-loader",
options: {
sourceMap: true
}
},
{
loader: "resolve-url-loader"
},
{
loader: "sass-loader",
options: {
sourceMap: true
}
}
]
}
]
},
resolve: {
extensions: [".js", ".ts", ".tsx", ".scss", ".css", ".png", ".ico", ".json"]
},
devtool: "source-map"
};
Now my question:
I'm trying to use dynamic imports in one of my React components as follows:
private loadComponentFromPath(path: string) {
import(`../../ScriptsApp/${path}`).then(component =>
this.setState({
component: component.default
})
);
}
As soon as I added dynamic import, my build started showing this error for all the .d.ts files inside ScriptsApp/#types folder
WARNING in ./ScriptsApp/#types/react-adal.d.ts
Module build failed (from ./node_modules/ts-loader/index.js):
Error: TypeScript emitted no output for C:\code\AzureCXP-Eng\src\Applications\AzureCxpWebSite\WebSite\FeedbackSrc\App\ScriptsApp\#types\react-adal.d.ts.
at makeSourceMapAndFinish (C:\code\AzureCXP-Eng\src\Applications\AzureCxpWebSite\WebSite\FeedbackSrc\App\node_modules\ts-loader\dist\index.js:78:15)
at successLoader (C:\code\AzureCXP-Eng\src\Applications\AzureCxpWebSite\WebSite\FeedbackSrc\App\node_modules\ts-loader\dist\index.js:68:9)
at Object.loader (C:\code\AzureCXP-Eng\src\Applications\AzureCxpWebSite\WebSite\FeedbackSrc\App\node_modules\ts-loader\dist\index.js:22:12)
# ./ScriptsApp lazy ^\.\/.*$ namespace object ./#types/react-adal.d.ts
# ./ScriptsApp/Routes/AppRoutesList.tsx
# ./ScriptsApp/Routes/Routes.tsx
# ./ScriptsApp/Components/App.tsx
# ./ScriptsApp/indexApp.tsx
# ./ScriptsApp/index.tsx
# multi webpack-hot-middleware/client?reload=true ./ScriptsApp/index.tsx
How I can currently make the error go away?
Move #types folder outside the ScriptsApp, or
Not use dynamic imports
Rename all .d.ts files under ScriptsApp/#types to .interface.ts --> most baffling to me
I'm not able to understand why though. I'm also new to the entire technology stack so sorry if I'm missing something obvious. Please explain this behavior. Also, any suggestions on improving the configs are also much appreciated. Thanks.
From another GitHub issue you can do this in your tsconfig.json file.
{
"compilerOptions": {
"target": "ES6",
"jsx": "react",
"noEmit": true
},
"exclude": [
"node_modules",
"dev_server.js"
]
}
Could you try to add the line "noEmit": false.
The following code in tsconfig.json should ensure that the errors from within node_modules will not be reported.
{
compilerOptions: {
skipLibCheck: true
}
}

Resources