Search code examples
angularangular5npm-link

Angular 5 - How to generate definition files


I have been trying several ways but I am not able to understand how to generate definition files for my project. I have two angular-cli generated applications A and B, I want A to consume B as a package (with npm link). As far as I understand I need to create an index.ts file in project B with all the modules I want to export and run 'ng build' command. I have seen that only creates the bundles but not the definition files, how can I generate definition files? Is this the correct approach?.

I have tried some other options like 'rollup' and 'ngmakelib' but it seems to be pretty hard to do that simple task. Do we have an easy way to generate Angular 5 projects as libraries and consume that libraries from another projects?


Solution

  • At a high level, here are the steps you would need to do to create a reusable Angular module (all done without webpack):

    1. Create a public_api.ts file within your src folder. This should contain all your exported symbols, so that users of your library can do: import { symbol } from 'your-library'

    2. Copy your src folder to a build/dist folder making sure to inline your templates. I use gulp and gulp-inline-ng2-template for this.

    gulpfile.js

        const gulp = require('gulp');
        const replace = require('gulp-replace');
        const inlineNg2Template = require('gulp-inline-ng2-template');
        const del = require('del');
    
        gulp.task('clean', function () {
            return del([
               "dist/**"
            ], { force: true });
        });
    
    
        gulp.task('copy-public-api', ['clean'], function () {
            return gulp.src([
                '../src/public_api.ts'
            ])
            .pipe(replace('./app', './src'))
            .pipe(gulp.dest('dist'))
    
        });
        gulp.task('copy-src', ['copy-public-api'], function () {
            return gulp.src([
               '../src/app/**/*.ts',
               '!../src/app/**/*.spec.ts'
            ])
            .pipe(inlineNg2Template({ base: '../src', useRelativePaths: true }))
            .pipe(gulp.dest('dist/src'))
        });
    

    public_api.ts

        export * from './app/app.module';
        // ... other exports ...
    
    1. Create a tsconfig file for ngc. You'll be using ngc to generate the necessary metadata files. Here are the settings that I use. The file is placed in a "build" folder (notice the relative paths for typeRoots and paths).

    build/tsconfig.json

        {
            "compilerOptions": {
                "baseUrl": ".",
                "rootDir": ".",
                "outDir": "",
                "paths": {
                    "*": [
                        "../node_modules/*"
                    ]
                },             
                "declaration": true,
                "stripInternal": true,
                "noImplicitAny": true,
                "strictNullChecks": true,
                "noFallthroughCasesInSwitch": true,
                "moduleResolution": "node",
                "module": "es2015",
                "target": "es5",
                "lib": [
                    "es2015",
                    "dom"
                ],
                "skipLibCheck": true,
                "typeRoots": [     
                    "../node_modules/@types/"  
                ], 
                "experimentalDecorators": true,
                "emitDecoratorMetadata": true,
                "sourceMap": true,
                "inlineSources": true
            },
            "files": [
                "dist/public_api.ts"
            ],
            "angularCompilerOptions": {
                "annotateForClosureCompiler": true,
                "strictMetadataEmit": false,
                "skipTemplateCodegen": true,
                "flatModuleOutFile": "index.js",
                "flatModuleId": "ng-module-template"
            }    
    
        }
    

    The angularCompilerOptions ensure that one metadata file is created (index.js).

    1. Use ngc to compile the module from the build folder. Make sure to install @angular/compiler and @angular/compiler-cli:

      ../node_modules/.bin/ngc -p tsconfig.json
      
    2. Deploy only the files that are needed. I deploy from build\dist to dist:

      gulp.task('build', [], function (done) {
          gulp.src([
              'dist/index.js',
              'dist/public_api.js',
              'dist/index.metadata.json',
              'dist/**/*.d.ts',
              '!../src/app/**/*.spec.ts'
          ], { base: 'dist' })
              .pipe(gulp.dest('../dist'))
              .on('end', function () {
                  del('dist/**', { force: true }).then(function () {
                      done();
                  });
              });
      });
      
    3. Make sure you modify your package.json to point to index.js:

      {
        "name": "ng-module-template",
        "version": "1.0.0",
        "description": "",
        "main": "dist/index.js",
        "typings": "dist/index.d.ts",
      }
      

    Optional: Creating Bundles

    Here is a gulp build target for compiling and creating bundles using rollup with treeshaking:

    gulp.task('compile', ['copy-src'], function (done) {
        gulp.src('tsconfig.json')
            .pipe(shell(['"../node_modules/.bin/ngc" -p <%= file.path %>']))
            .on('end', function () {
                del('node_modules/**', { force: true }).then(function () {
                    done();
                });
            });
    });
    
    gulp.task('bundle', ['compile'], function (done) {
        var external = [
            '@angular/core',
            '@angular/common',
            '@angular/compiler',
            '@angular/core',
            '@angular/http',
            '@angular/platform-browser',
            '@angular/platform-browser-dynamic',
            '@angular/router',
            '@angular/router-deprecated'
        ];
    
        var globals = {
            '@angular/core': 'vendor._angular_core',
            '@angular/http': 'vendor._angular_http',
            '@angular/platform-browser': 'vendor._angular_platformBrowser',
            '@angular/platform-browser-dynamic': 'vendor._angular_platformBrowserDynamic',
            '@angular/router-deprecated': 'vendor._angular_routerDeprecated'
        };
    
        rollup.rollup({
            input: 'dist/index.js',
            onwarn: function (warning) {
                if (warning.message.indexOf("treating it as an external dependency") > -1)
                    return;
    
    
                console.warn(warning.message);
            }
    
        }).then(function (bundle) {
            var umd = bundle.write({
                file: `dist/bundles/${pkg.name}.umd.js`,
                format: 'umd',
                exports: 'named',
                name: pkg.name,
                sourcemap: true,
                external: external,
                globals: globals
            });
            var cjs = bundle.write({
                file: `dist/bundles/${pkg.name}.cjs.js`,
                format: 'cjs',
                exports: 'named',
                name: pkg.name,
                sourcemap: true,
                external: external,
                globals: globals
            });
            var amd = bundle.write({
                file: `dist/bundles/${pkg.name}.amd.js`,
                format: 'amd',
                exports: 'named',
                name: pkg.name,
                sourcemap: true,
                external: external,
                globals: globals
            });
    
            var es = bundle.write({
                file: `dist/index.es5.js`,
                format: 'es',
                exports: 'named',
                name: pkg.name,
                sourcemap: true,
                external: external,
                globals: globals
    
            });
    
            return Promise.all([umd, cjs, amd, es]).then(function () {
                done();
            });
    
        });
    });
    

    Source Code Demo

    Pre-requisites

    Angular5+
    Git (installed locally if you are publishing to a local folder)

    https://github.com/angular-patterns/ng-module-template

    Build Targets

    npm run dev

    For development

    npm run build

    For production app build (outputs to dist folder)

    npm run build-module

    For a module build (outputs to dist folder)

    npm run publish

    For publishing to c:\packages using git. Alternatively, run npm publish to publish to npm.

    npm run name-module -- --(module-name)

    For naming the module. This modifies the source.

    Installing from c:\packages

    npm install c:\packages\<module-name>