I am quite newbie with React and Webpack, so I have a (maybe trivial) question:
I have recently created the react-scroll2top-button component.
What I don't like, though is the fact that my users have to provide the following configuration:
.jsx
import Scroll2TopButton from 'react-scroll2top-button/Scroll2TopButton';
webpack.config.js
module: {
loaders: [
{
...,
include: [path.resolve(__dirname, 'node_modules/react-scroll2top-button/Scroll2TopButton')],
...
}
]
}
Is there any optimal way to get rid of this ugly configuration?
I would like my users to be able to use my component by just importing it, like:
import Scroll2TopButton from 'react-scroll2top-button';
and nothing more.
I used the term "ugly", since I am almost sure that there is a way to fix this, after having seen that this is the way the rest of react components work. Maybe I am missing something really crucial, but that's why I came here, to also learn the proper way of doing this, since I have already done it twice and I think it is not that handy.
Thodoris
Your package is missing an entry point.
Add an index.js file and export Scroll2TopButton as default:
import Scroll2TopButton from './Scroll2TopButton.jsx';
export default Scroll2TopButton;
Or simply rename Scroll2TopButton.jsx to index.js.
Related
I'm moving forward with the project and I want to start writing tests.
But I came across a problem that I don't know how to solve.
I'm using path mapping in the project, but when I write the tests the page (or component) is not found.
I care like this
import Dashboard from '#pages/dashboard';
and the error is
Cannot find module '#pages/dashboard' from 'src/__tests__/pages/dashboard.test.tsx'
Does anyone know why?
After taking another look at the Jest documentation I realized that the jest.config.ts settings were wrong.
I changed it to this and it worked.
moduleNameMapper: { '^#pages/(.*)$': '<rootDir>/src/pages/$1' }
Just for the record, my tsconfig looks like this:
"paths": { "#pages/*": ["./src/pages/*"] }
I have a setting style file like:
GlobalStyles.js
export const GlobalStyles={
ViewContainer:{flex:1, justifyContent:'center', alignItems:'center'},
Center:{justifyContent:'center', alignItems:'center'},
FontNormal:15,
FontMedium:18,
FontLarge:28,
FontHeader:38
}
module.export={GlobalStyles}
and when i used it in another JS file, let say Home.js. i want to vscode know every Key:Value i've defined,
with those export in GlobalStyles.js vscode suggesting an import style like this:
import { GlobalStyles } from '../Component/GlobalStyles';
and my expected result is something like:
import { ViewContainer, Center, FontMedium, [and so on]} from '../Component/GlobalStyles';
how to let vscode suggesting me Auto Import foo from path/to/GlobalStyles when i'm typing foo? where foo is like ViewContainer, Center, FontMedium, [and so on].
The feature you're talking about is called Auto Imports. To answer your question, this feature exists and works by default in VSCode.
Here is a sample of how i've used it in a React project. Here I have a component inside a folder with the same name. Outside of that folder is a file I called ModuleExports.js and it currently has the following code.
import Navbar from "./Navbar/Navbar";
export { Navbar };
For reference, export is an alias of module.export so feel free to use them interchangeably. This is a component so when I try to use it as such you can see from the screenshot I am suggested an import to use.
The same can be done without being a component. I'll declare a testObject inside the same file ModuleExports.js and export it.
Then let's see if intellisense will pick it up.
There it is. I hope this helps and do ask if you want more clarification or are running into issues.
In this case, the mistake seems to be using dynamic export. That's old CommonJs style. VSCode uses typescript tools for static analysis. To take advantage of that, you have to use ES6 export.
CommonJs: module.export={GlobalStyles}
ES6-modules: export GlobalStyles
The significant difference between two is that CommonJs variant is simply variable which is defined at runtime. Ie. there is no way to know what you are exporting without executing the code. ES6 export is reserved word. It's construct which cant be changed after definition which also means, it's type definition can be found without actually executing the code.
Typescript, Babel etc provide interoperability between two module systems but they are two very different things by spec.
I see that this answer suggests the syntax for importing images as shown below (commented out). In my case, it didn't work out (complaining there's no modules to find in that file) and I had to switch to the syntax that's currently active.
// import Author from "../assets/author.png";
var Author = require("../assets/author.png");
The difference I can imagine is that I'm using TypeScript (transpiling my TSX by awesome-typescript-loader and loading my PNG file-loader) and they seem to use JSX. But as far my understanding goes, it all transpiles to plain JS and require in the end.
Being a noob on React, I'm not sure what the reason of this discrepancy is but also I'm not sure what to google for to investigate myself.
This is more of a problem with typescript than webpack itself, you might need to declare modules on a declaration file.
Create a declarations.d.ts
Update your tsconfig.json
"include": [
"./declarations.d.ts",
],
Put this on that file:
declare module '*.png';
Error might be gone.
You can declare a module for your images like this:
declare module "*.png" {
const value: any;
export default value;
}
Then, you will be able to import your image like this:
import AuthorSrc from "../assets/author.png";
This is happening because webpack doesn't support image import out of the box. So you need to add a rule for that in the webpack config file. When you add a new rule, TypeScript doesn't automatically know that, so you need to declare a new module to resolve this. Without the module, you will be able to import images, but TypeScript will throw an error because you didn't tell to it is possible.
This issue has nothing to do with webpack or any bundler and is not quite a problem with typescript.
Typescript has stated that `require("path") is a way to include modules to the scope of your current module, whilst it can be also used to read some random files (such as json files, for example).
As Vincent and Playma256 specified, you can declare a module wildcard to match certain file types, so you can import it as an import statement. But you don't really need to do this. Typescript won't give you an error if you are trying to import a png or a json file (tslint might, but that depends on your configuration).
By the way, if your declaration is within the source folder of your project as defined in tsconfig.json, you don't need to include it as specified by Playma256.
I've created a sample project in node for you to test:
https://github.com/rodrigoelp/typescript-declare-files
I think you can solve this problem with Webpack&&typescript.The official webpage of webpack has introduced something about this in
https://webpack.js.org/guides/typescript/
And I have try this myself in
https://github.com/reactpersopnal/webpack-root/tree/feature/typescript
The reason is that you would like to use non-code assets with TypeScript, so we need to defer the type for these imports for webpack.
Your could simply add custom.d.ts.
declare module "*.jpg" {
const content: any;
export default content;
}
I am using React with FuseBox as bundler. The issue I am having at the moment is that aliasing isn't working so I can help deal with relative path hell.
My structure of the project:
stores folder has my MobX stores and an index.ts file that exports all the stores.
services has a bunch of service classes all exported in there respective files (no index.ts)
So in my fuse.ts I have:
alias: {
"services": "~/services",
"stores": "~/stores"
},
Then in my ui folder for example I am importing like so:
import AccountStore from "stores";
I get [ts] Cannot find module 'stores' error on that line at "stores".
Not sure have I got the alias section wrong? My homeDir in fuse.ts is set to "src/". I don't have any paths or baseUrl set in tsconfig like I did have when we were using webpack to setup absolute paths. Not sure if those are needed again or if it is something I am doing wrong with alias.
Any tips would be great :)
I have looked at the alias documentation on the fusebox site and followed it and tried a few different combinations but not getting any closer to it working. Would love some examples from people who have got this working.
Edit:
I have additionally done the following while trying to figure this out:
remove .fusebox folder
restarted vscode
have checked the bundle and it is adding a tilde there so fusebox must be recognising it?
will continue to add more things I try..
My solution to the problem I was having was to put the tsconfig.json file inside my src folder and the project/app inside ~.
Inside tsconfig.json i set baseUrl to root.
{
"compilerOptions": {
"baseUrl": ".",
// ...
}
}
This then allowed me to do absolute imports that would be the same across all files so it would be easy to reuse imports and quickly copy if needed.
import { service1, service2, service3 } from '~/services';
import { store1, store2 } from '~/stores';
The tilde usually represents home or base on a lot of systems so a few devs agreed it would be appropriate to use it in our folder structure. Though did have to remember to remove ~ from .gitignore if you have something that is autogenerated and its automatically in there.
I present you the following code:
https://plnkr.co/edit/xxNW1xAIPoGtTK84OxGq
NOTE: This does not work because of SystemJS which I just can not configure. Whoever wants can edit if freely to make it work. I have been using the angular-cli and default webpack config and this works.
My question is about the "AlertService" which is needed to display an "alert". It has been extracted in the core module. However when I need to use it I have import it like so import { AlertService } from '../core/alert/alert.service' as present in dashboard.component.ts in order to inject it.
Doesn't this break the modular approach since I have to give it the path to the class ? If I change the location of the AlertService within the CoreModule I still have to go and change the string in the DashboardComponent. Also in this example if AlertService is not present DashboardComponent will not fire ... but DashboardComponent is part of the DashboardModule which should be able to start on its own - otherwise what is the point of the modules if they are coupled statically I could just put everything in one place.
What I want is to create a general alert component which I only need to include and once in the whole app and be able to use it everywhere.
But I think I am misunderstanding the concept of modules and/or how to use them. I have read the Modules' section in Angular.io multiple times and gone through multiple tutorials.
Best regards
If having to change lots of file paths bothers you a lot, one possible solution is to add another layer - create a services.ts file, then have the canonical imports there rather than in each component. That way, you only have to update one file if you change the path:
services.ts:
import { AlertService } from './alert.service';
// ES2015 shorthand property initializer
export default {
AlertService
};
yourComponent.ts:
import { AlertService } from "../services";
...
If you want a component to work even if a service is not present, you can make it an optional dependency (returning null if it doesn't exist), but TypeScript forces you to still have to import the service into the file to get code completion/typings. That's always felt a little janky to me.