├── .gitignore
├── .travis.yml
├── README.md
├── demo-library
├── .angular-cli.json
├── .editorconfig
├── build.js
├── e2e
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.e2e.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
│ ├── app
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── libex
│ │ │ ├── hello.service.ts
│ │ │ ├── hello
│ │ │ ├── hello.component.html
│ │ │ ├── hello.component.scss
│ │ │ └── hello.component.ts
│ │ │ ├── index.ts
│ │ │ ├── libex.module.ts
│ │ │ ├── package.json
│ │ │ └── theming.scss
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── typings.d.ts
├── tsconfig.json
└── tslint.json
├── example-consumer
├── .angular-cli.json
├── .editorconfig
├── README.md
├── e2e
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.e2e.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
│ ├── app
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ └── app.module.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── typings.d.ts
├── tsconfig.json
└── tslint.json
├── lerna.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | dist
5 | tmp
6 | out-tsc
7 | dist-lib
8 |
9 | # dependencies
10 | node_modules
11 |
12 | # IDEs and editors
13 | .idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # IDE - VSCode
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 |
28 | # misc
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | testem.log
35 | /typings
36 |
37 | # e2e
38 | /e2e/*.js
39 | /e2e/*.map
40 |
41 | # System Files
42 | .DS_Store
43 | Thumbs.db
44 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: trusty
3 | addons:
4 | apt:
5 | sources:
6 | - google-chrome
7 | packages:
8 | - google-chrome-stable
9 | language: node_js
10 | node_js:
11 | - 8
12 | before_script:
13 | - export DISPLAY=:99.0
14 | - sh -e /etc/init.d/xvfb start
15 | script:
16 | - cd demo-library
17 | - npm install
18 | - npm run ci
19 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular Library Example
2 |
3 | This repo consists of two parts:
4 |
5 | - demo-library: Demonstrates a way how to write a (probably company internal) Angular library. For a way how to write a public library, please visit: https://github.com/dherges/ng-packagr
6 | - example-consumer: A small demo of how to use the library
7 |
8 | It is also important to understand how the demo-library is setup itself.
9 | It's basically a regular Angular application:
10 |
11 | ```
12 | src/app
13 | libex (libex is the module name)
14 | ...
15 | app.component.ts
16 | app.module.ts
17 | build.js
18 | ```
19 |
20 | **Only what's inside of libex will get published to npm.** That makes `app.module.ts` just a **playground** for you to test out your library. It's ideal to develop against, as all the components from your library are imported directly and you can work with hot reload and don't have to publish to npm (or run npm link) each time you want to implement or test a feature.
21 |
22 | The only difference between the app.module.ts in `example-consumer/src/app` and `demo-library/src/app` is that `example-consumer` actually gets the published library from npm. This just serves the purpose for everyone reading the tutorial to see how exactly to use the published library. **Notice the modified tsconfig.json of the example-consumer!** If you want to test auto-import in IDE, I suggest opening `exmaple-consumer` as a separate project, otherwise the IDE will import the TypeScript classes from the demo-library.
23 |
24 | ## How are things published?
25 | All the library-building and -publishing logic is in `demo-library/build.js`. It's a lot less complex than in other library-generators, since it **just publishes TypeScript source files**. However, this requires an extra step on the side of the consumer, which is to add `node_modules/libex/index.ts` as sources to the tsconfig.json. You can run the script with `node build.js` from the demo-library folder.
26 |
27 | ## More cool things (optional)
28 |
29 | To build real world libraries, a bit more than the minimal example can be helpful.
30 |
31 |
32 | ### Theming
33 |
34 | Additionally to what's minimally required to build a library, I've added a way to "theme" your library. The implementation is done in an equivalent way to the theming of Angular's Material Design library.
35 |
36 | ### Lerna
37 |
38 | The `example-consumer` and the `demo-library` are in one repository. This is commonly referred to as a "Monorepo". This is quite useful to speed up development, as you can simply run `lerna bootstrap` (once you've installed `lerna`: https://github.com/lerna/lerna), which will symlink everything. Then you'll have the demo library as a **symlink** in the `node_modules` of `example-consumer`. If you now update something in `demo-library`, the project auto-refreshes even when started from `example-consumer`.
39 |
--------------------------------------------------------------------------------
/demo-library/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "libex-project"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "libex",
21 | "styles": [
22 | "styles.scss"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "e2e": {
33 | "protractor": {
34 | "config": "./protractor.conf.js"
35 | }
36 | },
37 | "lint": [
38 | {
39 | "project": "src/tsconfig.app.json"
40 | },
41 | {
42 | "project": "src/tsconfig.spec.json"
43 | },
44 | {
45 | "project": "e2e/tsconfig.e2e.json"
46 | }
47 | ],
48 | "test": {
49 | "karma": {
50 | "config": "./karma.conf.js"
51 | }
52 | },
53 | "defaults": {
54 | "styleExt": "scss",
55 | "component": {}
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/demo-library/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/demo-library/build.js:
--------------------------------------------------------------------------------
1 | const fsextra = require('fs-extra');
2 | const { exec } = require('child_process');
3 |
4 | fsextra.copy('./src/app/libex', './dist-lib', err => {
5 | if (err) return console.error(err);
6 | console.log('Copied files');
7 | createDeclarations();
8 | });
9 |
10 | function createDeclarations() {
11 | exec('cd dist-lib && tsc index.ts --declaration', () => {
12 | console.log('Generated declarations (and some JS files...)');
13 | createPackageJson();
14 | });
15 | }
16 |
17 | function createPackageJson() {
18 | const packageJSON = {
19 | "name": "libex",
20 | "version": "2.0.0",
21 | "description": "How to build libraries with Angular (2, 4, 5...)",
22 | "main": "index.js",
23 | "scripts": {
24 | "test": "echo \"Error: no test specified\" && exit 1"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "git+https://github.com/bersling/angular-library-example"
29 | },
30 | "keywords": [
31 | "Angular",
32 | "Angular2",
33 | "Library",
34 | "Example"
35 | ],
36 | "author": "bersling@gmail.com",
37 | "license": "MIT",
38 | "bugs": {
39 | "url": "https://github.com/bersling/angular-library-example/issues"
40 | },
41 | "homepage": "https://github.com/bersling/angular-library-example#readme",
42 | "types": "index.d.ts"
43 | };
44 | fsextra.writeJson('./dist-lib/package.json', packageJSON, {spaces: 2}, err => {
45 | if (err) return console.error(err);
46 | console.log('Created package.json');
47 | });
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/demo-library/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import {browser} from 'protractor';
2 | import {LibexProjectPage} from './app.po';
3 |
4 | describe('libex-project App', () => {
5 | let page: LibexProjectPage;
6 |
7 | beforeEach(() => {
8 | page = new LibexProjectPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | page.getHelloButton().click();
14 | const alertDialog = browser.switchTo().alert();
15 | expect(alertDialog.getText()).toEqual('hello!');
16 | expect(alertDialog.accept).toBeDefined();
17 | alertDialog.accept();
18 | browser.sleep(200);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/demo-library/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class LibexProjectPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getHelloButton() {
9 | return element(by.id('hello-button'));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demo-library/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/demo-library/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/demo-library/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "libex-project",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e",
12 | "test:once": "ng test --watch=false",
13 | "ci": "npm run lint && npm run test:once && npm run e2e"
14 | },
15 | "private": true,
16 | "dependencies": {
17 | "@angular/animations": "^5.0.0",
18 | "@angular/common": "^5.0.0",
19 | "@angular/compiler": "^5.0.0",
20 | "@angular/core": "^5.0.0",
21 | "@angular/forms": "^5.0.0",
22 | "@angular/http": "^5.0.0",
23 | "@angular/platform-browser": "^5.0.0",
24 | "@angular/platform-browser-dynamic": "^5.0.0",
25 | "@angular/router": "^5.0.0",
26 | "core-js": "^2.4.1",
27 | "fs-extra": "^5.0.0",
28 | "rxjs": "^5.1.0",
29 | "zone.js": "^0.8.4"
30 | },
31 | "devDependencies": {
32 | "@angular/cli": "^1.7.2",
33 | "@angular/compiler-cli": "^5.0.0",
34 | "@angular/language-service": "^5.0.0",
35 | "@types/jasmine": "2.5.45",
36 | "@types/node": "~6.0.60",
37 | "codelyzer": "^4.1.0",
38 | "jasmine-core": "~2.6.2",
39 | "jasmine-spec-reporter": "~4.1.0",
40 | "karma": "~1.7.0",
41 | "karma-chrome-launcher": "~2.1.1",
42 | "karma-cli": "~1.0.1",
43 | "karma-coverage-istanbul-reporter": "^1.2.1",
44 | "karma-jasmine": "~1.1.0",
45 | "karma-jasmine-html-reporter": "^0.2.2",
46 | "protractor": "~5.1.2",
47 | "ts-node": "^5.0.1",
48 | "tslint": "^5.9.1",
49 | "typescript": "~2.6.2"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/demo-library/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 100000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 100000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | },
28 | getPageTimeout: 100000
29 | };
30 |
--------------------------------------------------------------------------------
/demo-library/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
Example App
2 |
3 | The cool thing about developing a pure-typescript Angular library is, that it's like developing a regular app!
4 | This helps to develop libraries at a high pace, since you can use all the tools you're familiar with (angular cli etc.)
5 | and you'll have an example library-client to test things out. Here's an example from the library:
6 |
7 |
8 |
9 | And here we'll call the service 'Hello Service' from the library:
10 |
11 |
12 |
13 | I'm outside of hello... but still I get styled... :(
14 |