0

I have a package written in typescript. Main module is located in src and is called Popover.ts. Main index file is called index.js and main field in package.json is pointed to that file. It has such content:

exports.Popover = require('./src/js/Popover');

Then in another package I try to include this plugin. I installed it with npm. And here issues come. In javascript everything works well:

const Popover = require('popover');

But when I try to import it in typescript file (demo.ts) it does not work:

import Popover from 'popover';

First, PhpStorm highlights popover with a red line and says Can not find module 'popover'. Second, when I build demo with webpack, I got no errors, however build file does not contain the contents of Popover.ts.

I do not understand why it happens and how I can fix it.

Update: I managed to get rid of red highlighting in PhpStorm by setting "moduleResolution": "node" in tsconfig.json (thanks to @user254694). However then I got a different problem: build failed. Webpack generated such an error:

Error: TypeScript emitted no output for 
F:\dev\js\plugins\popover\demo\npm\node_modules\popover\src\js\Popover.ts. 
By default, ts-loader will not compile .ts files in node_modules.

I googled this error and come to the following solution. Add allowTsInNodeModules option to ts-loader:

loader: 'ts-loader',
options: {
   allowTsInNodeModules: true
}

Then I added such lines to tsconfig.js:

"include": [
    "node_modules/popover/src/**/*",
    "node_modules/popover/index.js",
    "node_modules/popover/typings/**/*"
  ]

While this eliminated the webpack error and it compiled successfully, I got red highlight again.

PS: I generated declaration types in typings directory and now my package.json looks like this:

"main": "index.js",
"types": "typings/index.d.ts",

Still wonder how to make it working.

FreeLightman
  • 2,224
  • 2
  • 27
  • 42
  • I'm supposing that when you import it expects a structure in popover.ts similar to the following "export const Popover ...rest of function or whatever" but it sounds like you have require commonjs format - you might want to read this https://stackoverflow.com/questions/46677752/the-difference-between-requirex-and-import-x/46677972 – user254694 Sep 18 '19 at 12:43
  • @user254694 See my update – FreeLightman Sep 19 '19 at 06:43

1 Answers1

2

Your package should also contain index.d.ts. and your package.json should have types field which points to that file. This will allow importing the package from node_modules in typescript.

index.d.ts is generated by TypeScript compiler along with index.js when --declaration compiler option is on. It's off by default, you should add it in tsconfig.json file (or command line) if you are going to publish your package with npm.

when I build demo with webpack, I got no errors, however build file does not contain the contents of Popover.ts

This is likely a separate problem. If you are not actually using imported Popover in your typescript file, the compiler will not generate require call for that module as an optimization, so the Popover.js will not be included in the bundle. If you are using imported value, it compiles with TypeScript without errors, but generated javascript still does not work, but using that module directly from javascript works - that would be unusual and require more information to solve.

artem
  • 46,476
  • 8
  • 74
  • 78
  • See my update. I added generated types and added them to package.json – FreeLightman Sep 19 '19 at 06:44
  • webpack should use compiled `.js` files from `node_modules` for your `popover` package, just like it does for every other package from `node_modules`. Those `.js` files should be compiled separately as a build step in `popover` package, before you publish it on npm. You have to figure out why webpack is trying to load and compile typescript files from `node_modules` - this is not the normal way it's supposed work. – artem Sep 19 '19 at 12:33
  • Why not the normal way. I require it - that's why it requires it. The whole question is around this problem. I require `popover` package which has `main` field as index.js, but index.js just exports imported Popover.ts. Read my question again. – FreeLightman Sep 19 '19 at 12:49
  • I mean, the require should make webpack to add `.js` file which is already compiled. When you consume `popover` as external module, it's not normal to compile `.ts` files from that module. So, `index.js` should export compiled `Popover.js`, not `.ts`. – artem Sep 19 '19 at 20:47
  • I do not have `Popover.js`, I have only `Popover.ts`. That is the trick. My package is written in typescript. – FreeLightman Sep 20 '19 at 10:16
  • When you publish on npm, and your source language is [something](https://stackoverflow.com/questions/30928253/writing-npm-modules-in-typescript) [other](https://stackoverflow.com/questions/13645824/can-i-write-npm-package-in-coffeescript) than javascript, [the consensus seems to be](https://www.google.com/search?q=how+to+publish+npm+package+written+in+typescript) that source code should be compiled to javascript first, and it's compiled javascript files that should be published. Publishing source code is optional, but for typescript, generated `.d.ts` files should be included too. – artem Sep 20 '19 at 14:09
  • If you want to publish typescript source code only, and make everyone who uses the package to compile from source themselves - you are basically on your own, I don't think anyone has figured out an easy and convenient way to do that. – artem Sep 20 '19 at 14:12
  • I am not aware of it. My goal it to have package with typings where you require my package and have autocomplete etc. You suggest to create `js` folder and compile typescript files there? – FreeLightman Sep 20 '19 at 16:59
  • Yes there should be a folder named `js` or `dist` or anything like that, then you have separate `tsconfig.json` in that package with `outDir` set to that folder, `declarations: true`, `module: commonjs`, and compile all sources in the package before publishing. In `package.json`, `main` should point to `js/index.js` which should require `.js` files from that folder. `types` should point to `js/index.d.ts`. Then when using that package, webpack will bundle compiled `.js` files, and IDEs should follow `types` field in `package.json`, and use `.d.ts` for autocompletion, and type-checking. – artem Sep 20 '19 at 18:01
  • Thanks for such an extensive response. Now I am getting an understanding of how it works. – FreeLightman Sep 21 '19 at 13:21