I have an NPM module that uses another local NPM module containing shared code. Neither of them are public, this is all local.
I import the shared module in my package.json like so:
"my-shared": "file:../my-shared-code"
When I npm install, my-shared-code gets imported correctly, and I can import code like:
import Blah from 'my-shared/src/sharedTypes';
Problem
I have to use the word "src" in the import. However, when I build I create a build directory, which breaks all these imports!
I was wondering if I could use NPM to map the imports somehow?
Can I make it so I don't have to use the word "src" at all?
Could I just do:
import Blah from 'my-shared/sharedTypes';
and then it magically figures out whether to use the "src" or "build" dirs?
What I tried
I looked into the options for package.json, and there is a "files" property that I thought might help. However I think that just whitelists files.
I also looked into the "main" property, however I'm not "exporting a module". I just have a load of utility files that I wanna be able to import into multiple other projects.
What I'm actually trying to achieve
I'm using typescript, and I've got a front-end and a backend that both share types for certain models, but also share some utility functions.
I want my typescript/react front-end and my typescript backend to be able to import typescript files from another node_package, however it needs to know to use "src" for development and "build" when built for production.
While it's not exactly what you're asking for, you might be looking for npm link. It's not intuitive, but it's what I use for your above situation.
npm-link is a way of registering local projects so that others can reference them. Just beware that reading the docs is important -- using it may impact your local environment in non-obvious ways (especially if you build and publish directly from your machine--versus CI/CD).
In the consumer project you can use path mapping --> https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
As for the package, you can re-export using export * from './my-file' syntax, in a index.ts file (you may need one at each directory). But this is tedious if you have a lot of files. (imperfect example)
Another solution seems to make a script to copy the package.json into the dist folder and make the build there.
There is a similar discussion there: How to npm publish specific folder but as package root
Related
I am a relative newbie to node.js and typescript and am looking for tutorials and examples for building custom libraries for typescript. I am currently working on a project(switched to WebStorm GUI) that requires I build several microservices and several of the microservices will need to share certain code, i.e., base repository functionality, etc. My though would be to move the code they have in common to a series of separate libraries(projects) and make them into typings and have the projects that need them to install them. Following the example I found here, and using grunt: What is the story for creating and consuming TypeScript libraries?, I was able to generate the .js file and the .d.ts files in a dist folder on the project. There are a few areas that I am confused about:
1) The example makes reference to a "main": "./dist/my.service.js" section in the jsconfig file, how necessary is this file and what should go into it?
2) If all of the files are transpiled and added to the dist folder, will the dependent code still be able to access it or do I have to transpile all of the files to root?
3) After I have have all of the file successfully transpiled and moved to the proper location, how do I install them to the dependent project if they are local and not up on the npm or Definitely Typed, etc.?
Well if you would like to see an example project that I am currently working on that at the moment can be installed with npm is binary-type-tree. There are some things I need to fix but overall the project is working great. You can see my setup for Jest in my package.json. Along with how to set up the types and the main.
Depending on what your library will be focused on you will want to choose the appropriate module system. Right now commonJS is the most common for npm packages. Although you can only compile to one file with system or amd.
As for compiling you do not need gulp/grunt you can use Webstorms IDE to compile your files. Simply open up the Webstorm settings "Languages and Frameworks" and select "Typescript" there you will see enable Typescript compiler.
The example given is very old. Typescript had a major update to how typings work back in October of 2016, they moved to Typescript 2 I think, or maybe it was November. Anyway when you google search Typescript I would set the filter to not before that time.
1.
The main in the package.json of the Typescript project should point to the entry point of your library, or your executable. Since mine is a library it points to the compiled folder's index file. The file does not exist in my repo but it is there upon compilation.
2.
This I believe is answered depending on your build. In my build as an example I use module system es6 as you can see in my tsconfig.json file. This uses the ES6 module system.
if you would like to use commonjs the structure of my project will still work properly except you will need something like Systemjs or Babel on the front end.
3.
This one is a bit unknown to me but what I have done for only testing is copy my directory into the node modules of another project I am working on. Now to require the project you do not need a #types since I have "declaration": true in my tsconfig.json file. The package.json of my project has a "types": section which points to the compiled .d.ts file. The project that is requiring this new TS package, if also written in TS, will have to have the typeRoots and types section in the tsconfig.json file. Once this is all set up you should be able to require it just fine.
Make sure that the src of all your TS files is declared in the includes section of the tsconfig if your files are not compiled to root. Otherwise your project will have to require in files in an odd format like import * as BTT from "lib/basic-node/avl-tree";. Which is not what I wanted, once I added this it became import * as BTT from 'binary-type-tree';.
The "main" field in your package.json tells Node's module system what file to require when calling require(), so you will definitely need it. You point that field to a transpiled file, which will also contained transpiled references to your other transpiled TypeScript files, so you shouldn't have to add anything to root.
If you're trying to use the package from another project, you simply reference it as a dependency using NPM the same way you would with any other JS project.
{
"name": "dependent-project",
"dependencies": {
"bar": "file:../typescript-project"
}
}
Again, Node will know how to load the TypeScript project because you've specified the transpiled entry point in the main field.
There are several things you must consider:
In package.json, you must set some things up (example):
main property to point to your UMD compatible bundle
module property for ES5 module. Then modern workflows can benefit from it, for example to apply Tree Shaking
typings pointing to your .d.ts file (which should be generated)
That counts on a build process, which can be made with a module bundler, such as RollupJS or Webpack. They can generate source maps and so on.
As per question 3: you can install packages from local or even from Git repos. In your package.json, for example:
"your-library": "git+https://github.com/alexjoverm/typescript-library-starter.git"
I'd suggest you to take a look at TypeScript Library Starter. You can find there everything you need. It has configured out of the box:
Automatic releases
Package.json configuration
Universal module bundles
Source Maps
Typings (.d.ts) auto generated
Docs using TypeDoc
Tests and coverage
I'm trying to fully understand how Typescript works in a Node.js project. To accomplish this I have created my own custom_modules folder with a separate #types folder underneath for my declarations while the actual implementation is under the custom_modules attempting to mimic the structure of node_modules. My goal is to make this module usable in the project non-relatively with separate declaration and implementation. I have been able to setup a project that compiles with this setup, but running it errors with:
Cannot find module 'foo'
The source is available here:
https://github.com/anorborg/learn-ts
The node_modules folder is a somewhat special case in how typings get handeled. This is a result of how nodejs works. Take a look at the [module-resolution] doucmentation (https://www.typescriptlang.org/docs/handbook/module-resolution.html), it describes more in depth how module resolution work in typescript.
But in short to answer your question: you can not use non-relative module paths in this way. Node will look for the file in node_modules at runtime, and will not find it there. The paths property in the tsconfig.json is there to solve problems that can occur in other cases, as when targeting RequireJS or SystemJS for example, but not when targeting node.
Project A contains a few functions and data models I use in diff't repos, all tied to the same product. I'd like to turn them into an npm module, but without extracting the code from project A.
When I see other modules on npm, they generally tie to a github repo that contains all the source code, as well as a full stack to run/modify the module.
Does this mean I have to extract the code from project A into its own repository, build/configure a stack to allow it to run in isolation from project A, and then import it back into project A & other projects?
Or is it possible to just export the functions w/o a full stack, and without moving the code from my main project?
an attempt to pre-empt 'duplicate' comments:
this Q talks about working with an existing module, which doesn't answer my concern, as it has to do w/ worrying about pull requests being merged on time
npm link, discussed here, looks like it'd do the trick if I'd already extracted the code from the project, but I'd like to avoid that.
If you really want to share a snippet through npm but still use the code at the same place in your project, you could extract the code into its own repo, but still use it inside your project as a git sub-module.
Create a submodule repository from a folder and keep its git commit history
Do you know if it's standard for npm modules in their own repos to include the full stack for running them?
Ideally, it's to test them and ease the development, but it's totally optional. You could only put a JavaScript file and the package.json and it would work.
I have a repo which consists of several "micro-services" which I upload to AWS's Lambda. In addition I have a few shared libraries that I'd like to package up when sending to AWS.
Therefore my directory structure looks like:
/micro-service-1
/dist
package.json
index.js
/micro-service-2
/dist
package.json
index.js
/shared-component-1
/dist
package.json
component-name-1.js
/shared-component-2
/dist
package.json
component-name-2.js
The basic deployment leverages the handy node-lambda npm module but when I reference a local shared component with a statement like:
var sharedService = require('../../shared-component-1/dist/index');
This works just fine with the node-lambda run command but node-lambda deploy drops this local dependency. Probably makes sense because I'm going below the "root" directory in my dependency so I thought maybe I'd leverage gulp to make this work but I'm pretty darn new to it so I may be doing something dumb. My strategy was to:
Have gulp deploy depend on a local-deps task
the local-deps task would:
npm build --production to a directory
then pipe this directory over to the micro-service under the /local directory
clean up the install in the shared
I would then refer to all shared components like so:
var sharedService = require('local/component-name-1');
Hopefully this makes what I'm trying to achieve. Does this strategy make sense? Is there a simpler way I should be considering? Does anyone have any examples of anything like this in "gulp speak"?
I have an answer to this! :D
TL;DR - Use npm link to link create a symbolic link between your common component and the dependent component.
So, I have a a project with only two modules:
- main-module
- referenced-module
Each of these is a node module. If I cd into referenced-module and run npm link, then cd into main-module and npm link referenced-module, npm will 'install' my referenced-module into my main-module and store it in my node_modules folder. NOTE: When running the second npm link, the name of the project is the one you find in your package.json, not the name of the directory (see npm link documentation, previously linked).
Now, in my main-module all I need to do is var test = require('referenced-module') and I can use that to my hearts content. Be sure to module.exports your code from your referenced-module!
Now, when you zip up main-module to deploy it to AWS Lambda, the links are resolved and the real modules are put in their place! I've tested this and it works, though not with node-lambda yet, though I don't see why this should be a problem (unless it does something different with the package restores).
What's nice about this approach as well is that any changes I make to my referenced-module are automatically picked up by my main-module during development, so I don't have to run any gulp tasks or anything to sync them.
I find this is quite a nice, clean solution and I was able to get it working within a few minutes. If anything I've described above doesn't make any sense (as I've only just discovered this solution myself!), please leave a comment and I'll try and clarify for you.
UPDATE FEB 2016
Depending on your requirements and how large your application is, there may be an interesting alternative that solves this problem even more elegantly than using symlinking. Take a look at Serverless. It's quite a neat way of structuring serverless applications and includes useful features like being able to assign API Gateway endpoints that trigger the Lambda function you are writing. It even allows you to script CloudFormation configurations, so if you have other resources to deploy then you could do so here. Need a 'beta' or 'prod' stage? This can do it for you too. I've been using it for just over a week and while there is a bit of setup to do and things aren't always as clear as you'd like, it is quite flexible and the support community is good!
While using serverless we faced a similar issue, when having the need to share code between AWS Lambdas. Initially we used to duplication the code, across each microservice, but later as always it became difficult to manage.
Since the development done in Windows Environment, using symbolic links was not an option for us.
Then we came up with a solution to use a shared folder to keep the local dependencies and use a custom written gulp task to copy these dependencies across each of the microservice endpoints so that the dependency can be required similar to npm package.
One of the decisions we made is not to keep two places to define the dependencies for microservices, so we used the same package.json to define the local shared dependencies, where gulp task passes this file and copy the shared dependencies accordingly also installing the npm dependencies with a single command.
Later we made the code open source as npm modules serverless-dependency-install and gulp-dependency-install.
I used the command npm install firebase --save from the root of my project folder and a firebase folder was successfully added to my node_modules folder and the packages.json was also updated.
I also use typescript so I have a app.ts file where I try and do import Firebase = require("firebase");
However it doesn't work. I get red squiggles and error saying cannot find external module "firebase".
I have installed express and importing it in app.ts as well and that works fine, no errors.
I have tried to look through the firebase source but its minified and impossible to try and do any finding there, about what is exported and such.
Any ideas about what might be wrong and how I can fix it?
Three things:
You should download the Firebase type definition from Definitely Typed using NuGet, TSD, or some other method.
The Firebase definition on Definitely Typed doesn't seem to be written in an external module form. You may need to add the line export = Firebase; at the bottom of it.
TypeScript currently has odd default behavior with definition files (d.ts) that describe external modules written in JavaScript; it will search in each directory from the directory where the file is back to the root of the current drive looking for a .ts or .d.ts file with a name that matches the name of the thing you've imported. So your best bet is to move the Firebase.d.ts file to the root of the project. If you're interested in a bit more discussion on this, check out https://github.com/Microsoft/TypeScript/issues/2338 . They're working on it.
If you do those three things, it should work.