Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to publish angular 2 typescript library on npm

I have created a typescript library for angular 2 which facilitates the to access my backend service.

So far it's a private repo, but I want to upload it as an open source library to github and register it on npm.

I'm not really sure what to do now, since documentation on this subject is not easy to find.

the folder structure looks like this:

src
|--sdk.ts // entry point
|--services
   |--auth.ts
   |--database.ts
   |--5 more ts files
|--utils
   |--utils.ts
   |--interfaces.ts
|--tests (8 ..spec.ts files)

my entry point (sdk.ts) looks like this

import { NgModule, ModuleWithProviders } from '@angular/core';
import { Injectable } from '@angular/core';
import { SelfbitsDatabase } from './services/database';
import { SelfbitsAuth } from './services/auth';
import { SelfbitsAppConfig } from './utils/interfaces';
import { SelfbitsFile } from './services/file';
import { SelfbitsUser } from './services/user';
import { SelfbitsDevice } from './services/device';
import { SelfbitsPush } from './services/push';
import { HttpModule } from '@angular/http';

@Injectable()
export class SelfbitsAngular {
    constructor(
        public auth : SelfbitsAuth,
        public database : SelfbitsDatabase,
        public file : SelfbitsFile,
        public user : SelfbitsUser,
        public device: SelfbitsDevice,
        public push : SelfbitsPush
    ){}
}

export const SELFBITS_PROVIDERS:any[] = [
    SelfbitsAngular,
    SelfbitsAuth,
    SelfbitsDatabase,
    SelfbitsFile,
    SelfbitsUser,
    SelfbitsDevice,
    SelfbitsPush
];

@NgModule({
    providers:SELFBITS_PROVIDERS,
    imports:[ HttpModule ]
})

export class SelfbitsAngularModule{
    static initializeApp(config:SelfbitsAppConfig):ModuleWithProviders{
        return {
            ngModule:SelfbitsAngularModule,
            providers:[
                { provide: 'SelfbitsConfig', useValue: config }
            ]
        }
    }
}

and here's the webpack.config.js which doesn't really do what I want...

module.exports = {
    entry:'./src/sdk.ts',
    output:{
        path: helpers.root('dist'),
        filename:'selfbitsangular2sdk.js'
    },
    resolve: {
        extensions: ['', '.js', '.ts']
    },

    module: {
        loaders: [
            {
                test: /\.ts$/,
                exclude:'/test/',
                loaders: ['ts-loader']
            }
        ]
    }
};

I'm not sure if webpack is even the right choice..or wether is should be bundled and minified or not. Any hints and tipps are welcome!

Cheers

like image 910
Han Che Avatar asked Sep 15 '16 17:09

Han Che


3 Answers

The publishing process itself is pretty easy:

# Login with your existing npm user
npm login

# Bump library version
npm version --patch

# Publish your library
npm publish

The hardest part is to correctly prepare the content of your package.json and the folder that will hold you build library (one of the common way to name it is dist).

Your dist folder should contain files that are ready to be consumed by Angular JIT projects as well as by Angular AOT projects. It is also should contain your library bundle that is ready to be consumed by the browser directly (let's say via SystemJS).

In a result your dist folder should contain the following files:

  • index.umd.js - UMD bundle that is ready for SystemJS consumption
  • index.umd.min.js - minified version of the bundle to save user's traffic
  • index.umd.js.map, index.umd.min.js.map - for debugging purpose
  • *.js — [produced by tsc, remember that ngc is a wrapper] compiled JavaScript representation of the component or service *.ts files of your library.
  • *.d.ts — [produced by tsc, remember that ngc is a wrapper] declaration files. Since *.ts files with types are being transpiled to *.js files that don’t support typings the TypeScript Compiler needs to put all types information to separate *.d.ts files in order to be able to use those *.js files in TypeScript projects later. By the way, there is a DefinitelyTyped project with a lot of type definitions that have been already contributed for a plenty of JS non-TypeScript libraries.
  • *.metadata.json — metadata associated with the current component (or NgModule). It is kind of JSON representation of the objects we pass to the @Component, @NgModule decorators. This file contains the information that project’s (not library’s) NGC will need that was in the original library *.ts files but was not included in the *.d.ts files.

In this case your package.json should be notified about your build files via main, module and typings fields:

 {
   ...
   "main": "./dist/index.umd.js",
   "module": "./dist/index.js",
   "typings": "./dist/index.d.ts",
   ...
 }

You may find more detailed description of what files should be in dist folder and how to compile them in How to create AOT/JIT compatible Angular 4 library with external SCSS/HTML templates article

like image 85
Oleksii Trekhleb Avatar answered Oct 20 '22 10:10

Oleksii Trekhleb


Angular University has a good, step by step, tutorial on publishing an Angular 2 library to npm that addresses your concerns/questions.

You can provide a bundled and unbundled version if you wish, I would always provide an unbundled version though. In my libraries I do not provide a bundled version and leave it up to the consumer to bundle and minify.

http://blog.angular-university.io/how-to-create-an-angular-2-library-and-how-to-consume-it-jspm-vs-webpack/


Updated answer

The following steps will go through the process of creating, testing and publishing an unbundled angular module for consumption by a consumer using a bundler (webpack, angular cli, etc). For a more complete answer which includes bundling, see @OleksiiTrekhleb's answer.

Publishing an angular 2 library can be intimidating, but when it comes down it it's really no different than publish any other package on NPM. The following information will use the folder structure:

  • package root
    • src
    • dist

1. Setting up tsconfig.json

As with any typescript library you want to have the declaration option set to true in your tsconfig.json under compilerOptions to ensure our consumers can take advantage of types within our package:

"declaration": true

In compilerOptions we also want to specify our outDir to keep transpiled code separate from the source:

"outDir": "./dist"

We want the include option to point to our source folder (note include is a sibling in relation to compilerOptions):

"include": [
  "./src"
]

Enable the experimental decorator options under compilerOptions:

"experimentalDecorators": true,
"emitDecoratorMetadata": true 

To avoid some errors when transpiling, you will need to enable skipLibCheck as well:

"skipLibCheck": true

Result

{
  "compilerOptions": {
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "skipLibCheck": true,
    "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    "outDir": "./dist",                        /* Redirect output structure to the directory. */
    "strict": true,                            /* Enable all strict type-checking options. */
    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    "emitDecoratorMetadata": true         /* Enables experimental support for emitting type metadata for decorators. */
  },
  "include": [
    "./src"
  ]
}

2. The Module

This example will use a module with a single component. The component is fairly straight forward:

./src/helloWorld/helloWorld.component.ts

import { Component } from "@angular/core";

@Component({
    selector: 'hello-world',
    template: '<div>Hello, world</div>'
})
export class HelloWorldComponent {

}

The module should add consumable components to declarations and exports. We need to add to exports so when consumers import our module, they can use our components as well.

./src/helloWorld/helloWorld.module.ts

import { NgModule } from '@angular/core'
import { HelloWorldComponent } from './helloWorld.component';

const components: any[] = [
    HelloWorldComponent
];

@NgModule({
    declarations: components,
    exports: components // Don't forget to export!
})
export class HelloWorldModule {

}

3. The barrel

To simplify importing our modules we use a barrel, which file that exports everything to be consumed by our consumers.

./src/index.ts

export { HelloWorldModule } from './helloWorld/helloWorld.module';
export { HelloWorldComponent } from './helloWorld/helloWorld.component';

4. Setting up NPM

Package.json

In package.json, change the main property to point to our transpiled barrel, ./dist/index.js. Also add the typings property to point to our barrels definition file ./dist/index.d.ts.

Add the property prepublish under scripts in your package.json.

"scripts": {
    "prepublish": "tsc"
}

Also note that your angular and related dependencies should be under peerDependencies and not dependencies

NPM Ignore

Create a .npmignore file in the root of your package and ignore the src folder and any other files you do not want published with your package

src/
*.log

5. Testing

You can easily test your npm packages locally with npm link. In your module package folder, run the command npm link.

Then in your test project, run the command npm link <my package name>

Now you can import your module and add it to your test projects imports without having to publish.

6. Publishing

Your package can now be published with a simple npm publish

like image 22
3 revs Avatar answered Oct 20 '22 08:10

3 revs


For those who are still trying to figure out how to do it:

  1. Create your library using @jvandemo's Angular Lib Yeoman generator

  2. After that you just run: npm adduser and npm publish

like image 2
Juan Herrera Avatar answered Oct 20 '22 09:10

Juan Herrera