Use index file to export .tsx modules - javascript

It's a common thing to create a index.js file in an React application with the only purpose to export several modules, in order to avoid having too many import statements on other components. This can be done by:
index.js
export { Credits } from './Credits.js';
export { SocialMedia } from './SocialMedia.js';
any module that might use those exports:
import * as var_name from index.js
And this is very nice. It wraps exports into a single file. However, when I changed my project to React with typescript, I found that .tsx files cannot be exported like that. The image below is the error I got after changing the project to typescript and the extensions became .tsx
Is there a way of 'bundle' export React .tsx files with the structure shown above? If not, what is the simplest way of centralizing .tsx files export?
My webpack.config.js:
module.exports = {
module: {
rules: [{
test: /\.scss$/,
use: ["sass-loader"]
}]
}
};

You can definitely use the same style of having an index file to group up exports for a whole folder. The simplest way around your problem would be to omit the file extension (assuming you only have one "index" file in the folder).
For example, let's say you have a component in 'common/Example.tsx':
import React from 'react'
export const Example = () => (<div>I'm an example component</div>)
You can then export it in an index file 'common/index.tsx':
export { Example } from './Example'
And import it from somewhere else, e.g. 'App.tsx':
import { Example } from './common'

Related

Storybook doesn't work with renamed exports

I am writing a component library in/for react and using Rollup to bundle it. As part of the build process I am using Terser to make sure the bundle is as small as possible. As part of the minification process, Terser renames the variables and then renames then back at export. Like so:
export{_ as BtnResponsive,l as Button};
This works fine when importing my library into other ES modules. Like so:
import { BtnResponsive } from '#namespace/component-lib';
But when I create a Storybook story Button.stories.tsx and import my component like so:
import { ComponentStory, ComponentMeta } from '#storybook/react';
import { BtnResponsive } from '#namespace/component-lib';
export default {
title: 'Components/BtnResponsive',
component: BtnResponsive,
} as ComponentMeta<typeof BtnResponsive>;
const Template: ComponentStory<typeof BtnResponsive> = (args) => <BtnResponsive {...args} />;
export const Primary = Template.bind({});
Primary.args = { children: 'Primary', theme: 'primary' };
Storybook then renders the component in my story like
<_ theme="primary">
Primary
</_>
Storybook somehow doesn't respect the renamed export. Does anyone know how to either make storybook respect the component name? or know how I can adjust Terser so Storybook understands the name correctly?
Thanks in advance!
Terser apparently has options that you can use to prevent it from mangling function names. In my rollup.config.js I had to add the following:
terser({
keep_fnames: true,
})

How to to change the default file from index to a custom in React Components?

Structure of the project generally looks like this:
components
- my-component
- - my-component.ts
- - index.ts
Where index.(ts/js) always consists of
import MyComponent from './my-component';
export default MyComponent;
I want to remove index.(js/ts) and still import/require my components using the path
import MyComponent from './components/my-component' // not having the index file!
// Please, do not suggest importing them like this
import MyComponent from './components/my-component/my-component'
I use Webpack 5. I know there was a module for Webpack 4, but it does not work with 5.
I want NodeJS/Webpack to look for a custom filename rather than index.
Some other solutions:
You could move components/my-component/my-component.ts to components/my-component.ts. I assume you have a reason not to do this (presumably, other files in that component directory).
You could use resolve.alias and list all mappings you want:
module.exports = {
//...
resolve: {
alias: {
"./components/my-component": path.resolve(__dirname, 'components/my-component/my-component.js'),
//...
},
},
};
You could set resolve.mainFiles to list all of your .js filenames. This seems pretty ugly though, as it's a global setting.
Are you sure that Webpack 5 doesn't support DirectoryNamedWebpackPlugin? It's explicitly mentioned in the documentation.

NPM package: best practices and exposing multiple import paths

I created an NPM package that uses Webpack and Babel for transpiling/bundling.
In my package.json, I've got main set to "main": "build/index.js". And in my Webpack config, I have entry set to entry: { app: './src/index.js' }. My entry file is shown below.
Everything works fine when the package is installed. However, with this setup, two import paths are exposed for every helper:
This is a problem for editors that support auto imports, since they will sometimes auto import from 'my-package/build/utils/helper1' rather than the preferred path of 'my-package'.
So, two questions:
Is there any way to prevent the longer import path from being exposed?
What is considered best practice when creating NPM packages. Is my setup acceptable, or should I be doing something different?
Entry File:
import helper1 from './utils/helper1';
import helper2 from './utils/helper2';
export {
helper1,
helper2,
};
const myPackage = {
helper1,
helper2,
};
export default myPackage;
you can utilize Webpack resolve
I often use the first way:
export {
helper1,
helper2,
};
Recently, I found that we can use Object.freeze() to export. This is a good article.
I would suggest probably merge your helper1 and helper2 in one file and name it helpers, then you can put them in the class myPackage so that you then export them as a module like this
import myPackage from './utils/helper';
// OR import {helper1, helper2} from './utils/helpers';
export default class myPackage {
helper1,
helper2,
};
OR
import {myPackage} from './utils/helpers';
// OR import {helper1, helper2} from './utils/helpers';
module.exports.myPackage = (helper1, helper2) => {
this.helper1 = helper1;
this.helper2 = helper2;
};
I hope this helps.

Global Import In ES6

I have a large third party library that I need to share between two projects. The project has multiple folders with multiple files that contain multiple exports. Instead of importing these modules like this
import {BaseContainer} from '#company/customproject/src/containers/BaseContainer.js'
I would like to do this
import { BaseContainer } from '#company/customproject'
I know I can manually import all the modules into a single index.js file in the base directory but i am wondering if there is an easier way to do not have import them all explicitly
I know I can manually import all the modules into a single index.js file in the base directory but i am wondering if there is an easier way to do not have import them all explicitly
You should really just create an index.js file and import into that whatever you want to export so that you can control what APIs get exported and to not export private APIs.
That said there is an automated tool that generates an index.js automatically for you:
> npm install -g create-index
> create-index ./src
Which will generate an index.js with all the exports.
As the other answer suggests, you should create an index.js within each directory and explicitly export contents
#company/customproject/index.js
import {BaseContainer, SomeOtherContainer} from './src/containers'
export {
BaseContainer,
SomeOtherContainer
}
#company/customproject/src/containers/index.js
import BaseContainer from './BaseContainer'
import SomeOtherContainer from './SomeOtherContainer'
export {
BaseContainer,
SomeOtherContainer
}
Another option to autoload an entire directory is using require and module.exports to export every scanned file, however. You would likely run into conflicts using both ES6 import/export along with module.exports and default export statements.
#company/customproject/index.js
const fs = require('fs')
const modules = {}
fs.readdirSync(__dirname+'/src/containers').forEach(file => {
file = file.replace('.js', '')
modules[file] = require('./src/containers/'+file)
// map default export statement
if (modules[file].default) {
modules[file] = modules[file].default
}
})
module.exports = modules
Then simply use it in any ES5 or ES6 module
const {BaseContainer} = require('#company/customproject')
or
import {BaseContainer} from '#company/customproject'

Properly export/import parts of a JS project

I'm building an npm package (ES6 + Babel) for the first time and I'm having trouble connecting it all together so it can be imported by the end user.
My build (output) folder structure is the same as src:
build
- index.js
- BaseClass.js
sublclasses
- SubClassA.js
- SubClassB.js
SubClassA and SubClassB import and extend BaseClass and are both exported using the module.exports. The entry point, index.js, has only two lines:
import SubClassA from './subclasses/SubClassA'
import SubClassB from './subclasses/SubClassB'
package.json has the main field set to ./build/index.js.
When installing the project (or npm linking) into a test project, I write:
import SubClassA, SubClassB from 'my-package'
Import works, but imported classes are undefined. I've tried a couple more ways to do it, but it didn't work.
How should I do it properly?
EDIT: after changing index.js to:
import SubClassA from './subclasses/SubClassA'
import SubClassB from './subclasses/SubClassB'
module.exports = SubClassA
module.exports = SubClassB
it kind of works. 'Kind of' means that if I import both classes in the test project like so:
import SubClassA, SubClassB from 'my-package'
and then do:
let sca = new SubClassA()
it turns out to be SubClassB. If I ommit SubClassB from import, it works normally.
EDIT 2 - SOLUTION:
Per instructions in the comments below, I've changed the index.js file like so:
export { default as SubClassA } from './subclasses/SubClassA'
export { default as SubClassB } from './subclasses/SubClassB'
and I imported it in the test project like so:
import { SubClassA, SubClassB } from 'my-project' and it worked.
The problem is you're not exporting anything from your main file,
using es6 import/export syntax you can directly export it with:
export {default as SubclassA} from './subclasses/SubClassA'
export {default as SubclassB} from './subclasses/SubClassB'
then to you can use the named imports :
{SubClassA, SubClassB} from 'my-package'

Categories