How to add typescript paths to storybook - reactjs

I have a react application with a custom Webpack configuration.
After adding Webpack aliases that matches tsconfig.json file compilerOptions->paths field the aliases were recognized by webpack.
Since storybook comes with a built in Webpack configuration, my aliases are not read by Storybook and I'm getting the following error:
Module not found: Error: Can't resolve <path with typescript alias> in <some folder path>

In Storybook main.js file, add the following:
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
...,
webpackFinal: async (config, { configType }) => {
config.resolve.plugins = [new TsconfigPathsPlugin()];<-- this line
return config;
}
};
You can install tsconfig-paths-webpack-plugin using the following command from the folder in which your application's package.json file resides:
npm i tsconfig-paths-webpack-plugin -D
Solution was derived from this discussion:
https://github.com/storybookjs/storybook/issues/6316

For future vistors of this question, since 15th July of 2022 storybooks can use Vite instead Webpack.
In that case I recommend using vite-tsconfig-paths instead of tsconfig-paths-webpack-plugin. If you are using TS paths in Vite, you probably already have this package installed.
Add this to your .storybook/main.js
const { mergeConfig } = require("vite")
const { default: tsconfigPaths } = require('vite-tsconfig-paths')
module.exports = {
// your previous configs and more...
viteFinal(config, { configType }) {
return mergeConfig(config, {
plugins: [
tsconfigPaths()
]
})
}
}

An alternative to accepted solution:
If you prefer not to install an external library such as tsconfig-paths-webpack-plugin, you can create a custom file, say:
tsconfig-webpack-utils.js
and do something similar to the following:
const { compilerOptions } = require('../tsconfig.json');
function getAliases() {
const baseUrl = getTSBaseUrl();
return Object.fromEntries(Object.entries(compilerOptions.paths).map(([key, value]) => {
return [
key.replace(/\/\*\*?$/,''),
value.map(entryPath => path.resolve(__dirname, baseUrl, entryPath.replace(/\/\*\*?$/,'/')))
]
}));
}
function getTSBaseUrl() {
return path.resolve(__dirname, `../${compilerOptions.baseUrl}`);
}
exports.addTsDefinitionsToWebpack = function(webpackConfig) {
if (!webpackConfig.resolve.modules) {
webpackConfig.resolve.modules = ['node_modules'];
}
webpackConfig.resolve.modules.push(getTSBaseUrl());
webpackConfig.resolve.alias = {
...webpackConfig.resolve.alias,
...getAliases()
};
}
This solution only works for very simple aliases. It is recommended to use an appropriate library or to expand this solution according to your needs.
You can then use it as follows in every webpack config you require it:
addTsDefinitionsToWebpack(webpackConfig);

Related

Webpack obfuscator not working with craco, maps disabled

today I have a very large problem using react & craco, I can't seem to get my webpack-obfuscator to do anything. I have disabled source maps, but to no avail.
This is my craco config:
const path = require("path");
const WebpackObfuscator = require('webpack-obfuscator');
module.exports = {
webpack: {
configure: (webpackConfig) => {
// Because CEF has issues with loading source maps properly atm,
// lets use the best we can get in line with `eval-source-map`
if (webpackConfig.mode === 'development' && process.env.IN_GAME_DEV) {
webpackConfig.devtool = 'eval-source-map'
webpackConfig.output.path = path.join(__dirname, 'build')
}
return webpackConfig
},
plugins: {
add: [
new WebpackObfuscator ({
rotateStringArray: true
}),
],
},
},
devServer: (devServerConfig) => {
if (process.env.IN_GAME_DEV) {
// Used for in-game dev mode
devServerConfig.writeToDisk = true
}
return devServerConfig
}
}
I get no visible maps files when building, and I've put "GENERATE_SOURCEMAP=false" in my .env file that's located where the package.json is.
Hopefully someone has the answer as to why this is happening.
Kind regards, and thanks for reading.
To upgrade a short config, you can use a construct that, if the condition is met, updates the configuration without using WebpackObfuscator:
module.exports = {
webpack: {
configure: {
...(process.env.IN_GAME_DEV && process.env.NODE_ENV === 'development' && {devtool: 'eval-source-map'})
}
}
}
Also, if you need additional properties for the configuration, in addition to the dvttool, you can add them

Cannot use material-ui with storybook "Can't resolve '#emotion/react'"

I am using webpack#5 for storybook of my project. The problem is when I use my component in storybook (which uses material-ui components) I get error:
ModuleNotFoundError: Module not found: Error: Can't resolve '#emotion/react' in '/Users/USER/Dev/PROJECT/front/packages/components/node_modules/#mui/styled-engine/GlobalStyles'
I have tried to install this package, add it to addons, add alias for this module to storybook config, install other strange material-ui modules. Nothing works for me, still the same error.
Can you help and suggest what I can try to solve this?
So I found some solution on a page and it works for me:
//main.js
webpackFinal: async (config) => {
return merge(config, {
resolve: {
alias: {
'#emotion/react': getPackageDir('#emotion/react'),
},
},
});
},
//main.js
function getPackageDir(filepath) {
let currDir = path.dirname(require.resolve(filepath));
while (true) {
if (fs.existsSync(path.join(currDir, 'package.json'))) {
return currDir;
}
const { dir, root } = path.parse(currDir);
if (dir === root) {
throw new Error(`Could not find package.json in the parent directories starting from ${filepath}.`);
}
currDir = dir;
}
}

How to import minified js file on react create app

So I have this Create react app (I don't really understand webpack), and I wanted to use EaselJS on this one, However the NPM counterpart of EselJS is their version 2 (BETA) and is quite unstable and undocumented - That's why I wanted to use the minified version.
I have a easeljs.min.js on my project but I don't know how to "import it".
doing `import './easeljs.min.js' seems to also generate a lot of linting issues and seems to nor work.
EDIT:
I tried using react-helmet and append it as a script tag, but it seems that react is doing something with the minified version and causes it to error. (unexpected token <)
So I was able to fix it:
I installed react-app-rewired created config-overrides.js and added this code:
module.exports = function override(config, env) {
if (!config.resolve || Object.keys(config.resolve).length === 0) {
config.resolve = {};
}
if (!config.module || Object.keys(config.module).length === 0) {
config.module = {};
}
if (!config.module.rules || config.module.rules.length === 0) {
config.module.rules = [];
}
const resolve = {
alias: {
createjs: "createjs/builds/1.0.0/createjs.js"
}
};
config.resolve = {
...config.resolve,
...resolve
};
config.module.rules.push({
test: /node_modules[/\\]createjs/,
loaders: ["imports-loader?this=>window", "exports-loader?window.createjs"]
});
return config;
};
It seems that it is an issue with createjs itself https://github.com/CreateJS/Combined/issues/12
I also ended up using this repo for createjs.

Setting up babel-plugin-styled-components + Typescript + create-react-app

We are currently trying to integrate the babel-plugin-styled-components into our typescript and create-react-app based setup for a better debugging experience and we are having difficulties doing so.
We are reluctant to eject the app, which is why we are trying to set it up using react-app-rewired and we also managed to get our typescript code to compile using react-app-rewire-typescript along with react-app-rewire-styled-components.
For some reason however, the displayName is not applied, which makes me think the babel plugin is not applied.
We are using "start": "react-app-rewired start" as our dev server script and the config-overrides.js looks like this:
const rewireTypescript = require('react-app-rewire-typescript');
const rewireStyledComponents = require('react-app-rewire-styled-components');
module.exports = function override(config, env) {
return rewireTypescript(rewireStyledComponents(config, env), env);
}
I have no idea what we are missing. Swapping the encapsulation of the rewire... functions also didn't help.
Does anyone here have experience with that or can point me in the right direction?
If you are using webpack, I found a solution that did not require using babel.
https://github.com/Igorbek/typescript-plugin-styled-components
To use it, you add a custom transform to your typescript loader with webpack:
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
options: {
getCustomTransformers: () => ({ before: [styledComponentsTransformer] })
}
}
This solves the displayName not showing when using styled-components and typescript.
I solved the issue by add something like this in config-overrides.js using react-app-rewired:
const createStyledComponentsTransformer = require('typescript-plugin-styled-components').default;
const styledComponentsTransformer = createStyledComponentsTransformer();
module.exports = function override(config, env) {
const rule = config.module.rules.filter(l => l.oneOf)[0];
const tsLoader = rule.oneOf.filter(l => String(l.test) === String(/\.(ts|tsx)$/))[0];
tsLoader.use[0].options.getCustomTransformers = () => ({
before: [styledComponentsTransformer]
});
return config;
}

webpack separate build files

I have a nested directory structure with jsx modules, like
app/js/header/index.jsx
app/js/task/runner.jsx
and so on
is it possible to have webpack transpile each one of them and output the result in the same directory as the jsx file?
Regards
If I understand you correctly, you want to put resulting module next to each source module. It seems that you can achieve this with a plugin:
var fs = require('fs');
function MyPlugin() {}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
compilation.modules.forEach(m => {
if (/filename/.test(m.resource)) { // test for filename to exclude node_modules
fs.writeFileSync(m.resource + '.transpiled', m._source._value);
}
});
callback();
});
};
and in the webpack config:
{
...
plugins: [ MyPlugin() ],
...
}
Is it what you are trying to do?

Resources