未加星标

Making an Addable Angular Package Using Schematics

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二05 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

“ Angular CLI is the best thing which happened to Angular” - that’s how I was thinking for a long time. Simply, it lets you do a lot of stuff with no effort.

But then, Angular announced a new idea, called Angular Labs, which has a purpose of initiating new explorations. Basically, that’s the place that Angular Elements and Schematics came from. In this way, the Angular team made my heart skip a beat once again. :heart_eyes:


Making an Addable Angular Package Using Schematics
Awesome projects by the Angular team

So, how about integrating these awesome things and taking it for a test drive?

The Goal

What we are going to do is create an Angular workspace that contains a library - which will register an Angular Element as a custom element.

But that’s not all, our library will install the necessary dependencies, including polyfills, and will inject the exported module into the root module of a host application. In other words, we’re going to implement a basic example for ng add schematic.

In the end, we’ll get an Angular Package which consists of the library and that schematic we just mentioned.

Along with that - this article assumes you’re familiar withAngular Workspace andAngular Elements. Moreover, we’re not going to explain how to serve an Angular Element for non-Angular application. In case you’re curious and want to see a workaround which demonstrates this process, check outa previous article I’ve written.

Initializing a Workspace

Let’s not waste any time and take the Angular Element example from an article I referred above - with some little modifications, obviously.

This is the project in question:

It’s easy to perceive that:

There is a single component ( MadeWithLoveComponent ) which receives couple of inputs. The component is declared in AppModule and registered as entry component. A custom element is created from the component by AppModule and then registers inside CustomElementRegistry . We don’t have a bootstrap component, therefore, we trigger the bootstrapping process manually.

Let’s start with the fun!

At first, we create a new workspace:

ng new made-with-love-workspace --style=scss

Notice that we choose to define SCSS as our CSS preprocessor - for the sake of comfort only.

This workspace will include the library which we’re going to implement in the next step, beside the main application which will import the module that’s exported by the library and use the custom element we saw above.

Internal Library Creating the Library

With Angular CLI v6, it’s so easy to create a library that transpiles to the Angular Package Format . :wink:

We just have to run the following schematic:

ng generate library made-with-love

Then will be added a new internal project under projects :


Making an Addable Angular Package Using Schematics
A new library is created as an internal project

Well, we get a new directory made-with-love that contains src . A little deeper, there is lib , which is the place we’re going to shed our code.

Let’s create the made-with-love.component.ts file:

To simplify, we use an inline template and styles. Beyond that, Angular v6.1.0 arrives with a new view encapsulation for ShadowDOM v1, instead of the deprecated ViewEncapsulation.Native - so let’s take advantage of it. :sunglasses:

Next step is to create afeature module file, which called made-with-love.module.ts :

Notice we don’t export the component - which defeats the purpose. Hence, we’ll have to apply CUSTOM_ELEMENTS_SCHEMA on our host module. It makes Angular accept non-Angular elements.

It’s clear that we want to export our module inside public_api.ts (the public barrel file) so it will be accessible externally using ES6 modules:

Indeed, we’re making progress! :muscle:

Using the Library

So far we created an Angular workspace with an internal library.

Intuitively, we’d like to load our library’s module inside app.module.ts :

Note:Definitely, we apply the schema for custom elements.

But, once we use ng start for running our host application - we’re supposed to get a transpilation error:

ERROR in src/app/app.module.ts(3,36): error TS2307: Cannot find module 'made-with-love'.

It’s absolutely justified. We haven’t installed our module before (or any package which depends on it) - what means there is no package under node_modules that’s called made-with-love .

To solve it, the library should be built. Therefore, we should run ng run made-with-love:build . This will bundle our library in Angular Package Format and place it within dist directory:


Making an Addable Angular Package Using Schematics
Bundling the library in Angular Package Format

Now, we can use the component we made inside app.component.ts :

Here’s the result:


Making an Addable Angular Package Using Schematics
Everything works perfectly!

Wait, we shed the library’s output into dist instead of node_modules - so how is the ES6 import resolved?

The trick here is related to the compiler’s module resolution . We used a schematic for generating the library, which also appended a path mapping configuration to the main tsconfig.json file:

The paths above make the compiler to resolve the module out of dist directory. Notice that the mapping is relative to baseUrl - so it must be specified.

Schematics Project

We’ve just reached the interesting part. We’re going to create a schematics project with a simple collection and implement the ng add schematic.

If you’re not familiar with Schematics in general, read about it here . You can watch the following lecture also:

Let’s get to work! :construction_worker:

Creating the Project

Of course, there’s an automatic way to generate a schematics project:

schematics blank --name=schematics

Note:Assuming @angular-devkit/schematics-cli is installed globally.

However, we want to learn something today so let’s do it from scratch.

At first, we create a new directory called schematics inside projects . It’s possible to generate it on the root level, but in this article we’re going to consider it as an internal project.

Then, we install the necessary devDependencies :

npm install @angular-devkit/core @angular-devkit/schematics --save-dev

Let’s explain:

@angular-devkit/core @angular-devkit/schematics

Well, we’re talking about an internal project. So in order to make it consistent with other internal projects, we’ll create the src directory, package.json , and tsconfig.json .

Let’s start with the package.json :

The schematics property is what indicates our collection file - which describes a list of the implemented schematics in our project. Don’t worry, we’ll create a file like that later.

Next file is tsconfig.json :

Notice we can extend the tsconfig.json from the root level, but let’s keep it simple. Along with that, the configuration above is directly taken from a project which was generated using schematics-cli .

Alright. This is how our workspace is supposed to seem right now:


Making an Addable Angular Package Using Schematics
Creating an additional internal project for Schematics Adding a Schematic

Well, we’ve an empty project for schematics and it’s about time to prepare our custom ng-add schematic.

This schematic will perform the following steps:

It will add @angular/elements , @webcomponents/custom-elements and angular-made-with-love into package.json . It will run npm install . It will import MadeWithLoveModule into the root module of the host application. It will inject the polyfill’s script file into the scripts of the host application.

Note:We could use ng add @angular/elements , but here we want to learn how to implement it on our own.

First up, we have an unsolved debt to create a collection file. So, let’s create collection.json and place it within src :

As you can see, we describe only one schematic and that’s ng-add . The factory property represents a reference for a file which contains the schematic’s exported factory function. Next to collection file, we create the ng-add directory that contains an index.ts file.

Before we start to implement the steps we mentioned above - we’ll install a utility library for Schematics, which is called schematics-utilities .

To be honest with you, it’s a library I created a while ago:

:point_right: Schematics Utilities

I just collected for you some utilities from the Angular Schematics package https://t.co/qdhgrAPMpj #Angular #AngularCLI #Schematics #AngularDevKit #AngularWorkspace #javascript #TypeScript #js #ts #WebDev #WebDevelopment #FrontEnd pic.twitter.com/LNqqglvPBl

― Nitay Neeman (@nitayneeman) July 15, 2018

This library provides us several utilities that we’re going to take advantage of.

So let’s install it:

npm install schematics-utilities --save-dev

Disclaimer:Please take the following implementations with a grain of salt - these are just a couple of examples.

Spoiler:The final package will be published as angular-made-with-love so from now on we’ll adopt that package name.

Now, let’s navigate to ng-add/index.ts and start implementing the first step - so our schematic will be able to add to package.json the necessary packages.

We create a RuleFactory function which does that:

All we do in the code above is to iterate a list of packages and invoke addPackageJsonDependency for each of them. The addPackageJsonDependency function is imported out of schematics-utilities (we’ll see the imports in the final result).

In case you get confused, Rule is essentially a function that returns a Tree :

Notice that Rule function could return Observable<Tree> or void type as well.

Next step is installing the packages. This following RuleFactory function performs it simply:

Basically, NodePackageInstallTask does all the job for us. It’s boilerplate code for a schematic task which is imported out of @angular-devkit and installs the packages using a package manager.

Next step is importing MadeWithLoveModule into the root module of the host application.

Here’s an appropriate RuleFactory function:

What we can see above is - retrieving the workspace of the host application using getWorkspace . Likewise, we pass it through getProjectFromWorkspace along with a project name in order to retrieve the project’s configuration.

Then, we just invoke addModuleImportToRootModule and pass into that:

host - A Tree representation for host workspace. moduleName - A module to inject ( MadeWithLoveModule in our case). angular-made-with-love - The package which the module is imported from. project - A project’s configuration which the module will be injected.

Great, we’re almost done!

The last step is, as we declared before, to inject the polyfill’s script file into the scripts array of the host application.

This RuleFactory function is a little complicated from the previous functions:

Just for the record, the code above is inspired by ng-add schematic of @angular/elements ( check it out ). Well, what happens there is that we read and parse the angular.json file and then retrieve the project’s configuration. Right after that, we push into the scripts array a new object with the polyfill’s path, and eventually we stringify and replace the file’s content with the new configuration.

All that’s left is to do is creating a default RuleFactory function which operates the functions we’ve just created.

Here we go:

Obviously, the default exported RuleFactory function is supposed to return a Rule . In the last code snippet, we use chain exactly for that objective - returning a single combined Rule from multiple RuleFactory functions. From here, it’s pretty straightforward - we invoke these functions according to the steps we previously defined.

Note that we allow skipping the steps by providing respective CLI parameters: skipPackageJson , skipModuleImport and skipPolyfill .

Finally, here’s the final result:

Running a Schematic

Our schematics project is written in TypeScript. This means, as you guess, that we need to transpile the source code to plain JavaScript somehow before running a specific schematic.

Let’s run the following line on the root level:

tsc -p projects/schematics/tsconfig.json

Alright, new index.js and index.js.map files appear within ng-add - what clearly means that something indeed was built there. Using these files, we will run our ng-add schematic.

Note:Make sure your global installed TypeScript package is up-to-date.

Generally, in order to run any schematic - the following CLI convention should be adopted:

schematics project-name:schematic-name parameters

So, in our case, we could type (assuming that we’re navigated to projects/schematics ):

schematics .:ng-add --skipModuleImport
Making an Addable Angular Package Using Schematics
Running our schematic

As we see, . represents the current directory ( projects/schematics ), ng-add is the schematic’s name and --skipModuleImport is just a supported parameter by the schematic.

Important to note that:

In case we run the schematic without --skipModuleImport , we’d get a transpilation error: Could not find (undefined) due to the fact that our schematic isn’t executed from the root of the workspace and therefore - it couldn’t locate it. Don’t worry, this problem will be solved soon when we actually use it from another project - so let’s ignore it right now (in order to avoid redundant complexity). The default of running a schematic is dry-run mode, which prevents from the schematics tool from actually creating and changing files. Publishing Process

Let’s recap what we did so far:

ng-add

But how do we integrate these and make from them a single Angular Package which is ready to be published?

Adding npm Scripts

Until now, we used manually commands to build both internal projects. This seems like an appropriate moment to add some handy npm scripts into the package.json .

Here’s the new scripts property:

We’ve already seen the commands of lib:build and schematics:build , whereas the other commands are pretty understandable.

Great, now we can build both projects easily!

Copying Schematics to “dist”

Let’s assume we run lib:build:prod and the library is created within dist . As we remember, the package.json expects that the collection file will be placed next to it - inside a schematics directory.

However, that’s not the case here and that means we should copy the outputs of the schematics project in some way into dist/made-with-love/schematics .

There are a lot of ways to do that, though, we’re going to choose webpack for this mission.


Making an Addable Angular Package Using Schematics
Yay, another tool!

Apparently, webpack is installed indirectly, but - let’s install these packages explicitly:

npm install webpack webpack-node-externals copy-webpack-plugin --save-dev

Then, we create the webpack.config.js file on the root level:

Assuming that schematics:build was executed, webpack will copy the output of ng-add/index.ts and the collection file into the destined directory. Also, it’ll bundle some needed utilities from schematics-utilities .

Note that it’s not a best practice to include third-party code with a published package - we definitely should avoid from that. However, in our case, we’d not like that a host application would have to install schematics-utilities before running ng-add - because it misses the whole point.

In order to bundle we just have to type webpack , although, wrapping it with an npm script which builds the project before - would be much preferable:

We’re almost at the end, I promise you. :wink:

Publishing the Package

Before any publish, we must build our library and include inside it the outputs of the schematics project. Hence, we add an additional npm script:

Let’s run npm run build-package and then navigate to dist/made-with-love .

Now - it’s the moment to publish! :rocket:

Running npm publish there will do the job:


Making an Addable Angular Package Using Schematics
Publishing the package Demo Application

Let’s initialize a new project to demonstrate what we did today:

ng new angular-addable-package-example

All that we have left is to run ng add angular-made-with-love :


Making an Addable Angular Package Using Schematics
Installing our package using `ng add` Summary

Today we’d a long journey on purpose to build our addable Angular Package.

We started by initializing an Angular Workspace and an internal library. We continued with implementing a simple Angular Element as part of our library. Then, we created a Schematics project from scratch and implemented there an example of a schematic. Lastly, we built everything that’s related to the final output and published it.

Here is the source code of the projects we created:

The workspace The demo application

A few key points to keep in mind:

Creating a library that transpiles to the Angular Package Format using Angular CLI v6 is extremely easy. Angular v6.1.0 arrives with a new view encapsulation for ShadowDOM v1. There is no point in exporting an Angular Element’s component as part of the module. In case we’re using an Angular Element inside an Angular application - we should apply CUSTOM_ELEMENTS_SCHEMA on the module which uses it. Internal libraries are imported out of dist instead of node_modules by default, due to the path mapping configuration - which is added into the tsconfig.json while we generate the library using Angular CLI. The @angular-devkit/core package is what lets us to run schematics from the CLI. We must append a schematics property which points to a collection file, for our schematics set, inside the published package.json . The schematics-utilities package is what provides us a collection of shared utilities for working with Schematics. A RuleFactory is a function which returns a Rule . A Rule is a function which returns a manipulated Tree . A schematics project should be built before running it. The dry-run mode for a schematics project prevents changes of our file system. Adding npm scripts is pretty useful when we want to manipulate the various internal projects on the workspace level.

I hope this article was informative and not exhausting for you - but you must know that was written with :heart:.

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

tags: schematics,Angular,our
分页:12
转载请注明
本文标题:Making an Addable Angular Package Using Schematics
本站链接:https://www.codesec.net/view/586898.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(18)