I am in the process of extracting the core components of our React application into a separate component library for use in other client applications. These components use SVG icons, which already works in the original application by using babel-loader.
However, since the components are written in Typescript, as far as I have understood it, I need ts-loader for the library to work properly, even though I could not test this, because I'm not even able to compile the libary. To be able to export SVGs at all, I additionally included babel-loader with the babel-plugin-named-asset-import.
This is my project structure:
.
├── config
│ └── webpack.config.js
├── global.d.ts
├── index.ts
├── package.json
├── package-lock.json
├── src
│ ├── assets
│ │ └── icons
│ │ ├── index.d.ts
│ │ ├── index.ts
│ │ └── upload.svg
│ ├── bar
│ │ └── index.ts
│ └── foo
│ ├── index.ts
│ └── Stuff.ts
└── tsconfig.json
webpack.config.json:
const path = require("path");
module.exports = {
entry: "./index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: "ts-loader",
},
exclude: /node_modules/,
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
loader: require.resolve("babel-loader"),
options: {
customize: require.resolve(
"babel-preset-react-app/webpack-overrides"
),
plugins: [
[
require.resolve("babel-plugin-named-asset-import"),
{
loaderMap: {
svg: {
ReactComponent: "@svgr/webpack?-svgo,+ref![path]",
},
},
},
],
],
},
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
};
tsconfig.json:
{
"include": ["./global.d.ts", "./index.ts", "src/**/*"],
"compilerOptions": {
"allowJs": false,
"jsx": "react",
"esModuleInterop": true,
"downlevelIteration": true,
"declaration": true,
"noEmit": false,
"outDir": "dist",
"moduleResolution": "node",
"module": "commonjs",
"target": "es5",
"lib": ["es5", "es6", "es7", "es2017", "dom"],
"sourceMap": true,
"typeRoots": ["./node_modules/@types", "./@types"],
"resolveJsonModule": true
}
}
global.d.ts - I found out that the declare module part here has exactly no effect, I get the same result with or without it.
import 'jest-dom/extend-expect';
declare module '*.svg' {
const content: string;
export default content;
}
./src/assets/icons/index.ts should be responsible for exporting all the available icons for other files to import them.
export { ReactComponent as Add } from "./add.svg";
The ./src/demo/index.ts contains code that "uses" this SVG:
import { Add } from "../assets/icons";
export default {
Add,
};
The index.ts in the root directory contains the same content to demonstrate the inconsistency of my problem:
import { Add } from "../assets/icons";
export default {
Add,
};
So far so good, I thought, so I ran webpack with npx webpack --config ./config/webpack.config.js, only for the whole thing to fail with this error message:
ERROR in C:\work\tstest\src\demo\index.ts
[tsl] ERROR in C:\work\tstest\src\demo\index.ts(1,21)
TS2306: File 'C:/work/tstest/src/assets/icons/index.ts' is not a module.
This leaves me completely confused with several questions:
- How is the file in question not a module? Would be great to know what's preventing ts-loader from recognizing it correctly
./src/demo/index.tsand./index.tsare 100% identical in content. Still, compilation works for the one file, but not the other. Why is that and where is this behavior documented?- What am I doing wrong?
- What is the correct way to pull this off?
I suppose this is happening because after babel-loader is done doing its thing, the ./src/assets/icons/index.ts does not look the way ts-loader wants it, but that doesn't quite explain how two files with the same content are processed differently.