http://streetwill.co/posts/240-on-the-table I have an update for make library to compatible with AOT. you can look at the update in “ Make your library compatible with AOT section” In this post I create library for angular 2 but you can apply this approach to any framework with change a little bit. If you have been working in front-end field for a while, you will find one day you have to do same work as you did before. I bet you (used to) copy your old code and paste to new project (or you write it from scratch again because your old code is too mess). Don’t worry, almost everyone have done this before including me. One day I created table ui(with pagination feature) for one project. But in another project also has table ui. I had to copy and paste it to new project. The problem is when I found bug in my table,I have to fix all table component in every project that has the same table. That’s terrible and so boring. There’re many problem with copy and paste which I’m sure you already know it. So one day I think why don’t I make one table and publish it to npm? It’s really great because I just need to change my code in only one place and just update my table library in every project (with ) and I can install it easily with or change table library code with no fear it will break your old project because it doesn’t update automatically unless you update it by running . npm update mylovelytable npm install mylovelytable npm update mylovelytable The only one (and big) problem is I didn’t know how to do it :/ So this post is from what I’ve learned creating library in angular from reading many article, look other library code(eg. @angular/material ) try and error. Thanks to Olivier Combe , Minko Gechev for great posts. I’ve learn a lot from them about creating library in Angular2. section. If you understand module system in javascript already,you can skip to Let’s write library code I’ll cover some basic topic. Almost every library is module so I’ll explain module system in javascript first and then I’ll show you the example how I make library in Angular2. I’m not a professional bad ass programer so this post may be it has mistake so if you guys found some error or improvment. Please comment below,I’ll buy you a cookie :D Here’s complete souce code for this post, https://github.com/Elecweb/emptytext Done for shit. Let’s started! What’s module in javascript? https://unsplash.com/search/photos/lego?photo=jrW3-eLhWAw Whatever library you gonna make,module is fundamental. If you’re front-end developer that start from writing jquery,css,html from good old days. When you try to include some library, you download their source code or . npm install and then you just write script tag to include library file. <script src=”path/to/jquery.js”></script> or css <link rel=”stylesheet” type=”text/css” href=”path/to/theme.css”> If you include one or two that’s not a big problem, but how about 10 or 20? (These days library is small,self-contained,do one specific job so it can happen). Would it be easier if we can include library we want in javascript file and write script tag only one place? You can even include css in javascript file! So no more dozens of tag script or link. There’re also many problem from I mention above ,for example: Can you remember when there’s error tell you that there’s no jquery from other library? even you include it already and then you realize you need to include them before some library. Order dependent. Declaring every variable in global scope isn’t a good idea. you can never know if you have rewrite declared variable so your application can break easily. Name collision. It hard to maintain and reuse because without module you may need to write code for many job in single place (or 2 -3) . you never know if you remove or add some code will break existing feature. Maintainablity and Reusability. Trust me, copy your node_module folder to another sever is really hurt your feeling. You can solve this with package.json (with dependencies version compatible with your project) or copy 4–5 files produced by module bunder. Portable. npm install You need to specify path. it may not a big deal but would it be better if you don’t have to ? Module can help you solve these problem. How module looks like in javascript? Module isn’t something new in computer science. There’s module for a long time in other language. Javascript didn’t include it unfortunately. Whatever programming language is. Module is self-contained code that do one specific job and do it very well. You can include them, remove them or change them with more confident because variable or function isn’ t in global scope and you can explicitly export or hide these variable or function . You may be used to write everything in one particular file or many files but varaible or function can be accessed across over files. That’s bad you may messed up variable from (like change value) without knowing and your project just break without any compiler error. All you can do is scratch your head and blame the world why it’s so cruel. script1.js When application grow up, your code is mess because it does many job. When your client want you to change your old code or user find some bug, it’s nightmare for you. It make some(or a lot of) time you can figure out your code what they do. (If you never found this problem, trust me it’s really really nightmare). Let’s look how module works in javascript. I’ll explain Module Format first. Module Format in Javascript Module format is syntax we can use to define module. Before ES2015, JavaScript did not have any official syntax to define modules so smart developer try to define syntax for module There’re many module format in javascript. For example: Commonjs AMD UMD ES2015 They are just format. It does the same job,make your code be modular. I’ll show you example of Commonjs. //idoaboutmathcalculate.js let indexjscannotseeme = "great!";let plusTwoNumber= (a,b)=>{return a+b;}module.exports = plusTwoNumber; //index.jsconst plus2num = require('./idoaboutmathcalculate');console.log(plus2num(2,5)); //7 In the above example, there’re 2 filles. and As you can see, you can include js file from the other js. idoaboutmathcalculate.js index.js. You can choose what varaible you will hide or expose to external. can see only function because expose it only. index.js plusTwoNumber idoaboutmathcalculate.js You can divide up you code to many file and include,remove and change easily. For ES2015,it use and . For Example: import export //idoaboutmathcalculate.js export default const plusTwoNumber= (a,b)=>{return a+b;} //index.jsimport plus2num from './idoaboutmathcalculate';console.log(plus2num(2,5)); //7 I’ll not cover all of module format. There’re many post about them that explain it very well. _If you’re a newcomer to JavaScript, jargon like “module bundlers vs. module loaders,” “Webpack vs. Browserify” and “AMD…_medium.freecodecamp.com JavaScript Modules: A Beginner’s Guide _Modern JavaScript development can be overwhelming. When working on a project, you may wonder why all the modern…_www.jvandemo.com A 10 minute primer to JavaScript modules, module formats, module loaders and module bundlers I suggest you to stick with because it’s module standard(but not support yet for now). ES2015 Whatever module format is. the same purpose of them is just make module happen in Javascript. But there’re still more to make module works. What’s Module loader ,Module bundler and Module Resolution? You have module format but you still can’t use module . Why? Unfortunately browser nowadays doesn’t support it yet but it will! We need two more things to make it works, module bundler(or Module loader) and module resolution. I’ll cover detail on module loader and module bundler first. Module loader What it does? It load your entry file and dependency on another file. I’ll show example of using SystemJS which is one of moduler loaders. app.js SystemJS.import('./main.js').then((m)=>{ console.log(m.test); }); main.js var a = require(‘./sub.js’);module.exports = {test:a}; sub.js module.exports = "Hello world"; SystemJS will load all the files for you in correct order(app.js → main.js →sub.js). It’s will show “Hello world” in the console. Module bundler It will bundle your entry file and and dependency on another file(module) by building a dependency graph and use the graph to generate an optimized bundle where scripts will be loaded in the correct order. For short, when you divide your code to multiple files, module bundler will bundle your multiple files to just only one file for us. I’ll show example of using module bundler(with Rollup) in section. Let’s set config for bundler Module bundler and Module loader do similar job but not the same— they load module files to the browser. The difference is works in run time and work in compile time. load each file at a time while Module bundler will bundle all module files in compile time to single file and browser will has to load only the bundled file. Module loader Module bundler Module loader Example of module loader are: :loader for modules in AMD format. RequireJS :loader for modules in AMD,CommonJS, UMD. SystemJs Example of module bundler are: :bumdler for CommonJS module Browserify :bundler for AMD,CommonJS,ES6 modules Webpack :bundler for AMD,UMD,CommonJS,ES6 modules Rollup.js So what should I use ? module bundler or module loader? Well, it depends on you and situation. But in this post I’ll use module bundler because module bundler will emit only one file so your browser need to request just one file. Last topic in Module, Module resolution. Module resolution Look at the example: import { jquery } from ‘jquery’; Module resolution tell module bundler where to look the file that you import. It depends on your setting and module bundler. For example, webpack will look for the file in directories specified in if you don’t provide path. It’s d is folder_._ resolve.modules efault setting node_modules You can use relative path and absolute path as well. import plus2num from './idoaboutmathcalculate';//orimport plus2num from 'absolute/path/idoaboutmathcalculate'; You can notice there’s no file extension. The default is ,you can set default file extension in module bundler config file. .js Let’s write some real code! Ok, I understand some basic idea about module already but how it’s related to create library? Whatever library you make, it’s all about module — self-contained funtionality, reusable, do only one job and do it very well. If you can create your module, it’s not hard to push your code to npm or whatever. Are you still here? good. Let’s finally create our lovely library. The library I’m gonna make is called “emptytext”. This library include directive to be used for showing “empty” when there’s no text in element or component , service to change the message(which default is “empty”). First let’s setup project and some config. emptytext Here’s the folder structure. /lib — your source codeempty-text.directive.tsempty-text.module.tsempty-text.service.tsempty-text.ts /tools — node files for utility work (I’ll explain later)cleanup.jscopy-package.jsremovecode.js /dist — your module that user will get when ‘npm install’ package.jsonrollup.config.esm.jsrollup.config.umd.jstsconfig.json For code in this post,you can found here First let’s create a package.json file and follow the prompts. npm init I will use typescript for writing code in this post (it doesn’t require). npm install -g typescript and set some config for typescript compiler. I create config file named . tsconfig.json {“compilerOptions”: {“target”: “es5”,“module”: “es2015”,“sourceMap”: true,“moduleResolution”: “node”,“emitDecoratorMetadata”: true,“experimentalDecorators”: true,“declaration”: true,“outDir”: “./dist”,“lib”: [“es2015”, “dom”]},“files”: [“./lib/empty-text.ts”] } : javascript(ECMAScript) version of compiled code. target : module format of complied code. There’re several options,for example, UMD,ES2015,AMD. module : tell typescript compiler to generate . sourcemap sourcemap : how modules get resolved(where to look your dependency file from ) which there’re 2 ways,Node or Classic. . module resolution import for more detail and : you need to set it true for angular (they use it,for example, @Component and @Directive. emitDecoratorMetadata experimentalDecorators : generate corresponding .d.ts file( ). declaration definition file : built-in API declaration(difinition file) that you can choose to include in your project. lib : it’s where your typescript file that you want to compile with files tsc Now I can compile typescript code to javascript code with tsc -p tsconfig.json // or just tsc In this library, I need `angular/core` npm install @angular/core --save Don’t worry much how to compile our code for now,I’ll explain later. Let’s write library code I’ll not explain code much, it’s easy to understand and not the point of this post. Here’s the directive. lib/empty-text.directive.ts Here’s the service for setting message, lib/empty-text.service.ts Here’s the module for user to import. lib/empty-text.directive.ts Create entry file for export all other files in library, empty-text.ts export * from "./empty-text.module"; export * from "./empty-text.directive"; export * from "./empty-text.service"; That’s it. This library is small, just 4 files. Let’s set config for bundler You need to bundle code in files to a single file which format is UMD(UMD is it can be used with any module format) and ES2015. Universal Module Definition, The reasom we provide ES2015 because user can get benefit from if they use Rollup or Webpack. Tree Shaking There’re no document about how you should provide code in library for user in Angular (If anyone know about this,please comment below). So I look at _@angular/material_ how they build their file and use their format in my library. First, you need module bundler. It can be Webpack, Rollup or whatever. in this post, I’ll use Rollup. npm install --global rollup You need to bundle your code in two format,UMD and ES2015 so there’re two config file for Rollup. The different is property which set and for UMD and ES2015 respectively. format umd es Since config file for Rollup is normal javascript file,you can import and change only for property,so you don’t need to duplicate your other config properties by hand. format I’ll set config file for first. umd Here’s the config file, rollup.config.umd.js : where the entry file you want to bundle. input : config for bundled file which have following: output : module format of your bundled file. output.format : The variable name, representing your / bundle. output.name iife umd : Set to true so Rollup will provide sourcemap.(it’s not necessary in this case because I don’t minify my code for simplicity.) output.sourcemap : The file to write to. output.file : exclude dependency code in bundled code,which in this case, I assume user who use this library have installed external @angular/core : Function that will intercept warning message. I ignore two unnecessary warning( and ). onwarn THIS_IS_UNDEFINED MISSING_GLOBAL_NAME : plugin used in Rollup. I’ll explan next what plugin I use and why plugins We need to use plugins in Rollup for make magic happes. rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-angular rollup-plugin-typescript node-sass I’ll explain what these plugin do. But first, let’s install these plugins. npm install --save-dev rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-angular rollup-plugin-typescript node-sass Let’s look at section plugins excerpt from rollup.config.umd.js : If you don’t inline template and style for component, you need to parse external template and style to be included in component. angular rollup-plugin-angular : Parse sass to css before styles are included in component. we use it in plugin node-sass angular if(scss){ css = sass.renderSync({ data: scss }).css.toString(); }else{ css = ''; } return css; and config section. The Library in the example doesn’t have any component but I want to show how you can config for library that provide component. If your library doesn’t provide component, you can skip rollup-plugin-angular node-sass : Transpling Typescript to plain javascript. typescript : Locate modules using the . resolve Node resolution algorithm : Rollup can understand only module format ES2015 but some library you may install are commonjs so you need this plugin so that Rollup can understand commonjs module. commonjs For ES2015 module config file,we don’t have to duplicate by hand. just import config from and overide some config. rollup.config.umd.js import config from './rollup.config.umd.js'; import {nameLibrary,PATH_DIST} from './config-library.js' config.output.format = "es";config.output.file = PATH_DIST+nameLibrary+".esm.js" export default config; I override to “es” and for give name of bundled file. output.format output.file You may have noticed, I import some variable from for variables defined for name of bundled file and destination path so that we don’t have to duplicate it. config-library.js config-library.js export const nameLibrary = "empty-text"; export const PATH_SRC = "lib/"; export const PATH_DIST = "dist/"; Now you can bundle with defined config with command: rollup -c rollup.config.umd.js //umdrollup -c rollup.config.esm.js //es2015 Get rid of unnecessary files for user . There’re unneccessary files when we bundle like .ts file (because we will provide one bundle javascript file per module format) I’ll use node to handle deleting unnessary files and copy and paste file to folder for us (I’ll explain why soon). package.json dist In folder, I create for copy in root folder to folder. tools copy-package.js package.json dist const fs = require('fs'); let resizable = fs.readFileSync('package.json').toString(); fs.writeFileSync('dist/package.json', resizable); and which for deleting section and . cleanup-package.js script devDependencies const fs = require('fs'); const packageJson = JSON.parse(fs.readFileSync('./dist/package.json').toString()); delete packageJson.devDependencies; delete packageJson.scripts; fs.writeFileSync('./dist/package.json', JSON.stringify(packageJson, null, 2)); The last file is for remove unnecessary javscript file emitted from Typescript compiler. We just want definition typescript for IDE(autocomplete for example) to provide for user. removecode.js const del = require('del'); del(['dist/!(*.umd.js|*.esm.js|*.d.ts|*.umd.js.map|*.esm.js.map)']).then(paths => { console.log('Files and folders that would be deleted:\\n', paths.join('\\n')); }); It remove every file except for our bundled files( *.umd.js , *.esm.js), definition typscript ( *.d.ts) and sourcemap ( *.umd.js.map, *.esm.js.map). I use library for remove files . del Install from npm. del npm install --save-dev del Now you can run script we defined in this section with command.For example, if you want to copy and paste package.json to folder, just run in your favourite command line. node dist node tools/copy-package.js There’re many command we need to run. 1) We need to remove folder for clearing files. 2) bundle file with (2 commands for umd and ES2015. 3) run node scripts( 3 commands for copy package.json, clear script and devdependencies in package.json, remove unnessary code). dist rollup It’d be better if we just run one command that do these tasks for us. Set npm for easily build script Finally you can build the library ! the last task is set in so you don’t have to type many command to build our code. script package.json ... "scripts": { "copy": "node tools/copy-package.js && node tools/cleanup.js", "bundle": "rimraf dist && rollup -c rollup.config.umd.js && rollup -c rollup.config.esm.js && tsc", "postbundle": "node tools/removecode.js", "build": "npm run bundle && npm run copy" }, Now you need to just write one command npm run build Set some config for module resolution When user our library folder, it’ll look for the files where we provide in properties of . — in section import package.json For more detail Distributing the components Set the value of the property of to point to the ES5 UMD bundle. _main_ _package.json_ Set the value of the property to point to the entry file of the version of the library. is a field in where bundlers such as rollup and webpack 2 expect to find a reference to the ES2015 version of the code. Note that some older versions of the bundlers use instead of so we need to set both properties. _module_ _esm_ _module_ _package.json_ _jsnext:main_ _module_ … package.json "main": "empty-text.umd.js", "jsnext:main": "empty-text.esm.js", "module": "empty-text.esm.js", "types": "empty-text.d.ts", Make your library compatible with AOT [Updated!(26/07/2017)] Since library for angular 2 need to be . You need to run to emit along side to your package. compatible with AOT ngc .metadata.json First install : @angular/compiler-cli npm install --save-dev @angular/compiler-cli and then update in section: package.json scripts "scripts": {..., "bundle": "rimraf dist && rollup -c rollup.config.umd.js && rollup -c rollup.config.esm.js && tsc && ngc", //<--- add ngc... }, Last thing is update in for not removing files which emited from command tools/removecode.js .metadata.json ngc del(['dist/!(*.umd.js|*.esm.js|*.d.ts|*.umd.js.map|*.esm.js.map|*.metadata.json)']).then(paths => { console.log('Files and folders that would be deleted:\\n', paths.join('\\n')); }); Let’s build and publish to the world~ You can now build file with command which will run and scripts. Notice ,it’ll be run after complete. npm run build bundle copy postbundle bundle After run script,we will get files in folder build dist empty-text.d.tsempty-text.directive.d.tsempty-text.esm.jsempty-text.esm.js.mapempty-text.module.d.tsempty-text.service.d.tsempty-text.umd.jsempty-text.umd.js.map Only one task left is publishing it to npm. We can publish library code to npm with just command . npm run publish But wait! if I publish my library for now, user will get unnessesary files like our and folders. We should get rid of them. lib tools There’re 2 ways to do this. create to ignore unnessary files for publishing. .npmignore publish in folder with . dist package.json I’ll use the second way in this post. That’s why I need to copy to folder. package.json dist We don’t need to copy and paste by hand because will do the work for you. copy-package.js Navigate to folder and from there. So user that install our library will get files only from folder. dist npm publish dist PS. Navigate dist folder and run command from there is boring but I can’t find the way to publish from dist folder in root folder. If you have some suggestion,please let me know in the comment. UPDATE suggest me to include in script section. So we just need to run . Maxime Lafarie “publish”: “cd ./dist && npm publish” npm run publish Phew.. It’s a long road. You’re really good reader. Now you can publish your library to the world!. User just and to their module like this. npm install emptytext import import { EmptyTextModule } from ‘emptytext’; People now can use sweet directive (or you in the future. you will thanks yourself). Trust me If you have any question, or some suggestion(really applicated!).Please comment below. It’s really long post. Take a break and have coffee party.