{"id":2379,"date":"2018-04-03T15:10:27","date_gmt":"2018-04-03T13:10:27","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=2379"},"modified":"2018-04-03T15:10:27","modified_gmt":"2018-04-03T13:10:27","slug":"generating-custom-angular-code-with-the-cli-and-schematics-part-iii","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/","title":{"rendered":"Generating Custom Angular Code With The CLI And Schematics, Part III:"},"content":{"rendered":"<div class=\"article\">\n<hr>\n<p><b>Table of Contents<\/b><\/p>\n<p>This blog post is part of an article series.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/10\/29\/generating-custom-code-with-the-angular-cli-and-schematics.aspx\" rel=\"nofollow\">Part I: Generating Custom Code With The Angular CLI And Schematics<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/01\/generating-angular-code-with-schematics-part-ii-modifying-ngmodules.aspx\" rel=\"nofollow\">Part II: Automatically Updating Angular Modules With Schematics And The CLI<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/01\/02\/angular-cli-and-schematics-part-iii-typescripts-compiler-api.aspx\" rel=\"nofollow\">Part III: Extending Existing Code With The TypeScript Compiler API<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/03\/20\/custom-schematics-part-iv-frictionless-library-setup-with-the-angular-cli-and-schematics.aspx\" target=\"_blank\" rel=\"noopener\">Part IV:&nbsp;Frictionless Library Setup with the Angular CLI and Schematics<\/a><\/li>\n<li><a href=\"https:\/\/www.angulararchitects.io\/post\/2018\/04\/17\/seamlessly-updating-your-angular-libraries-with-ng-update.aspx\">Part V: Seamlessly Updating your Angular Libraries with ng update<\/a><\/li>\n<\/ul>\n<hr>\n<p>In my two previous blog posts, I've shown how to leverage Schematics to <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/10\/29\/generating-custom-code-with-the-angular-cli-and-schematics.aspx\">generate custom code with the Angular CLI<\/a> as well as to <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/01\/generating-angular-code-with-schematics-part-ii-modifying-ngmodules.aspx\">update an existing NgModules<\/a> with declarations for generated components. The latter one was not that difficult because this is a task the CLI performs too and hence there are already helper functions we can use.<\/p>\n<p>But, as one can imagine, we are not always that lucky and find existing helper functions. In these cases we need to do the heavy lifting by ourselves and this is what this post is about: Showing how to directly modify existing source code in a safe way.<\/p>\n<p>When we look into the helper functions used in the previous article, we see that they are using the <a href=\"https:\/\/github.com\/Microsoft\/TypeScript\/wiki\/Using-the-Compiler-API\">TypeScript Compiler API<\/a> which e. g. gives us a syntax tree for TypeScript files. By traversing this tree and looking at its nodes we can analyse existing code and find out where a modification is needed.<\/p>\n<p>Using this approach, this post extends the schematic from the last article so that the generated Service is injected into the <code>AppComponent<\/code> where it can be configured:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-keyword\">import<\/span> { SideMenuService } from <span class=\"hljs-string\">'.\/core\/side-menu\/side-menu.service'<\/span>;\n\n@Component({ [...] })\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppComponent {\n\n  <span class=\"hljs-keyword\">constructor<\/span>(\n    private sideMenuService: SideMenuService) {\n        <span class=\"hljs-comment\">\/\/ sideMenuService.show = true;<\/span>\n  }\n}\n<\/div><\/code><\/pre>\n<p>I think, providing boilerplate for configuring a library that way can lower the barrier for getting started with it. However, please note that this simple example represents a lot of situations where modifying existing code provides more convenience.<\/p>\n<p>The <a href=\"https:\/\/github.com\/manfredsteyer\/schematics-sample\">source code<\/a> for the examples used for this can be found <a href=\"https:\/\/github.com\/manfredsteyer\/schematics-sample\">here in my GitHub repository<\/a>.<\/p>\n<blockquote><p>\nSchematics is currently an Angular Labs project. Its public API is experimental and can change in future.<br \/>\n<img decoding=\"async\" width=\"300\" src=\"https:\/\/i.imgur.com\/y8LIiVg.png\" alt=\"Angular Labs\">\n<\/p><\/blockquote>\n<h2 id=\"walking-a-syntax-tree-with-the-typescript-compiler-api\">Walking a Syntax Tree with the TypeScript Compiler API<\/h2>\n<p>To get familiar with the TypeScript Compiler API, let's start with a simple NodeJS example that demonstrates its fundamental usage. All we need for this is TypeScript itself. As I'm going to use it within an simple NodeJS application, let's also install the typings for it. For this, we can use the following commands in a new folder:<\/p>\n<pre><code>npm init\nnpm install typescript --save\nnpm install @types\/node --save-dev\n<\/code><\/pre>\n<p>In addition to that, we need a <code>tsconfig.json<\/code> with respective compiler settings:<\/p>\n<pre class=\"hljs\"><code><div>{\n  <span class=\"hljs-attr\">\"compilerOptions\"<\/span>: {\n    <span class=\"hljs-attr\">\"target\"<\/span>: <span class=\"hljs-string\">\"es6\"<\/span>,\n    <span class=\"hljs-attr\">\"module\"<\/span>: <span class=\"hljs-string\">\"commonjs\"<\/span>,\n    <span class=\"hljs-attr\">\"lib\"<\/span>: [<span class=\"hljs-string\">\"dom\"<\/span>, <span class=\"hljs-string\">\"es2017\"<\/span>],\n    <span class=\"hljs-attr\">\"moduleResolution\"<\/span>: <span class=\"hljs-string\">\"node\"<\/span>\n  }\n}\n<\/div><\/code><\/pre>\n<p>Now we have everything in place for our first experiment with the Compiler CLI. Let's create a new file <code>index.ts<\/code>:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> * as ts from <span class=\"hljs-string\">'typescript'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> * as fs from <span class=\"hljs-string\">'fs'<\/span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">showTree<\/span>(<span class=\"hljs-params\">node: ts.Node, indent: <span class=\"hljs-built_in\">string<\/span> = '    '<\/span>): <span class=\"hljs-title\">void<\/span> <\/span>{\n\n    <span class=\"hljs-built_in\">console<\/span>.log(indent + ts.SyntaxKind[node.kind]);\n\n    <span class=\"hljs-keyword\">if<\/span> (node.getChildCount() === <span class=\"hljs-number\">0<\/span>) {\n        <span class=\"hljs-built_in\">console<\/span>.log(indent + <span class=\"hljs-string\">'    Text: '<\/span> + node.getText());\n    }\n\n    <span class=\"hljs-keyword\">for<\/span>(<span class=\"hljs-keyword\">let<\/span> child of node.getChildren()) {\n        showTree(child, indent + <span class=\"hljs-string\">'    '<\/span>);\n    }\n}\n\n<span class=\"hljs-keyword\">let<\/span> buffer = fs.readFileSync(<span class=\"hljs-string\">'demo.ts'<\/span>);\n<span class=\"hljs-keyword\">let<\/span> content = buffer.toString(<span class=\"hljs-string\">'utf-8'<\/span>);\n<span class=\"hljs-keyword\">let<\/span> node = ts.createSourceFile(<span class=\"hljs-string\">'demo.ts'<\/span>, content, ts.ScriptTarget.Latest, <span class=\"hljs-literal\">true<\/span>);\n\nshowTree(node);\n<\/div><\/code><\/pre>\n<p>The <code>showTree<\/code> function recursively traverses the syntax tree beginning with the passed node. For this it logs the node's <code>kind<\/code> to the console. This property tells us whether the node represents for instance a class name, a constructor or a parameter list. If the node doesn't have any children, the program is also printing out the node's textual content, e. g. the represented class name. The function repeats this for each child node with an increased indent.<\/p>\n<p>At the end, the program is reading a TypeScript file and constructing a new <code>SourceFile<\/code> object with it's content. As the type <code>SourceFile<\/code> is also a node, we can pass it to <code>showTree<\/code>.<\/p>\n<p>In addition to this, we also need the <code>demo.ts<\/code> file the application is loading. For the sake of simplicity, let's go with the following simple class:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">class<\/span> Demo {\n    <span class=\"hljs-keyword\">constructor<\/span>(otherDemo: Demo) {}\n}\n<\/div><\/code><\/pre>\n<p>To compile and run the application, we can use the following commands:<\/p>\n<pre><code>tsc index.ts\nnode index.js\n<\/code><\/pre>\n<p>Of course, it would make sense to create a npm script for this.<\/p>\n<p>When running, the application should show the following syntax tree:<\/p>\n<pre><code>SourceFile\n    SyntaxList\n        ClassDeclaration\n            ClassKeyword\n                Text: class\n            Identifier\n                Text: Demo\n            FirstPunctuation\n                Text: {\n            SyntaxList\n                Constructor\n                    ConstructorKeyword\n                        Text: constructor\n                    OpenParenToken\n                        Text: (\n                    SyntaxList\n                        Parameter\n                            Identifier\n                                Text: otherDemo\n                            ColonToken\n                                Text: :\n                            TypeReference\n                                Identifier\n                                    Text: Demo\n                    CloseParenToken\n                        Text: )\n                    Block\n                        FirstPunctuation\n                            Text: {\n                        SyntaxList\n                            Text: \n                        CloseBraceToken\n                            Text: }\n            CloseBraceToken\n                Text: }\n    EndOfFileToken\n        Text: \n<\/code><\/pre>\n<p>Take some time to look at this tree. As you see, it contains a node for every aspect of our <code>demo.ts<\/code>. For instance, there is a node with of the kind <code>ClassDeclaration<\/code> for our class and it contains a <code>ClassKeyword<\/code> and an <code>Identifier<\/code> with the text <code>Demo<\/code>. You also see a <code>Constructor<\/code> with nodes that represent all the pieces a constructor consists of. It contains a <code>SyntaxList<\/code> with a sub tree for the constructor argument <code>otherDemo<\/code>.<\/p>\n<p>When we combine what we've learned when writing this example with the things we already know about Schematics from the previous articles, we have everything to implement the initially described endeavor. The next sections describe the necessary steps.<\/p>\n<h2 id=\"providing-key-data\">Providing Key Data<\/h2>\n<p>When writing a Schematics rule, a first good step is thinking about all the data it needs and creating a class for it. In our case, this class looks like this:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">interface<\/span> AddInjectionContext {\n    appComponentFileName: <span class=\"hljs-built_in\">string<\/span>;       \n        <span class=\"hljs-comment\">\/\/ e. g. \/src\/app\/app.component.ts<\/span>\n\n    relativeServiceFileName: <span class=\"hljs-built_in\">string<\/span>;    \n        <span class=\"hljs-comment\">\/\/ e. g. .\/core\/side-menu\/side-menu.service<\/span>\n\n    serviceName: <span class=\"hljs-built_in\">string<\/span>;\n        <span class=\"hljs-comment\">\/\/ e. g. SideMenuService<\/span>\n}\n<\/div><\/code><\/pre>\n<p>To get this data, let's create a function <code>createAddInjectionContext<\/code>:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createAddInjectionContext<\/span>(<span class=\"hljs-params\">options: ModuleOptions<\/span>): <span class=\"hljs-title\">AddInjectionContext<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">let<\/span> appComponentFileName = findFileByName(<span class=\"hljs-string\">'app.component.ts'<\/span>, options.path || <span class=\"hljs-string\">'\/'<\/span>, host);\n    <span class=\"hljs-keyword\">let<\/span> destinationPath = constructDestinationPath(options);\n    <span class=\"hljs-keyword\">let<\/span> serviceName = classify(<span class=\"hljs-string\"><code>&lt;span class=&quot;hljs-subst&quot;&gt;${options.name}&lt;\/span&gt;Service<\/code><\/span>);\n    <span class=\"hljs-keyword\">let<\/span> serviceFileName = join(normalize(destinationPath), <span class=\"hljs-string\"><code>&lt;span class=&quot;hljs-subst&quot;&gt;${dasherize(options.name)}&lt;\/span&gt;.service<\/code><\/span>);\n    <span class=\"hljs-keyword\">let<\/span> relativeServiceFileName = buildRelativePath(appComponentFileName, serviceFileName);\n\n    <span class=\"hljs-keyword\">return<\/span> {\n        appComponentFileName,\n        relativeServiceFileName,\n        serviceName\n    }\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">findFileByName<\/span>(<span class=\"hljs-params\">file: <span class=\"hljs-built_in\">string<\/span>, path: <span class=\"hljs-built_in\">string<\/span>, host: Tree<\/span>): <span class=\"hljs-title\">string<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">let<\/span> dir: DirEntry | <span class=\"hljs-literal\">null<\/span> = host.getDir(path);\n\n    <span class=\"hljs-keyword\">while<\/span>(dir) {\n        <span class=\"hljs-keyword\">let<\/span> appComponentFileName = dir.path + <span class=\"hljs-string\">'\/'<\/span> + file;\n        <span class=\"hljs-keyword\">if<\/span> (host.exists(appComponentFileName)) {\n            <span class=\"hljs-keyword\">return<\/span> appComponentFileName;\n        }\n        dir = dir.parent;\n    }\n    <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>File &lt;span class=&quot;hljs-subst&quot;&gt;${file}&lt;\/span&gt; not found in &lt;span class=&quot;hljs-subst&quot;&gt;${path}&lt;\/span&gt; or one of its anchestors<\/code><\/span>);\n}\n<\/div><\/code><\/pre>\n<p>As this listing shows, <code>createAddInjectionContext<\/code> takes an instance of the class <code>ModuleOptions<\/code>. It is part of the utils Schematics contains and represents the parameters the CLI passes. The three needed fields are inferred from those instance. To find out in which folder the generated files are placed, it uses the custom helper <code>constructDestinationPath<\/code>:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">constructDestinationPath<\/span>(<span class=\"hljs-params\">options: ModuleOptions<\/span>): <span class=\"hljs-title\">string<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">'\/'<\/span> + (options.sourceDir? options.sourceDir + <span class=\"hljs-string\">'\/'<\/span> : <span class=\"hljs-string\">''<\/span>) + (options.path || <span class=\"hljs-string\">''<\/span>)\n                + (options.flat ? <span class=\"hljs-string\">''<\/span> : <span class=\"hljs-string\">'\/'<\/span> + dasherize(options.name));\n}\n<\/div><\/code><\/pre>\n<p>In addition to this, it uses further helper functions Schematics provides us:<\/p>\n<ul>\n<li><code>classify<\/code>: Creates a class name, e. g. <code>SideMenu<\/code> when passing <code>side-menu<\/code>.<\/li>\n<li><code>normalize<\/code>: Normalizes a path in order to compensate for platform specific characters like \\ under Windows.<\/li>\n<li><code>dasherize<\/code>: Converts to Kebab case, e. g. it returns <code>side-menu<\/code> for <code>SideMenu<\/code>.<\/li>\n<li><code>join<\/code>: Combines two paths.<\/li>\n<li><code>buildRelativePath<\/code>: Builds a relative path that points from the first passed absolute path to the second one.<\/li>\n<\/ul>\n<p>Please note, that some of the helper functions used here are not part of the public API. To prevent breaking changes I've copied the respective files. <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/01\/generating-angular-code-with-schematics-part-ii-modifying-ngmodules.aspx\">More about this wrinkle<\/a> can be found in my <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/12\/01\/generating-angular-code-with-schematics-part-ii-modifying-ngmodules.aspx\">previous article<\/a> about this topic.<\/p>\n<h2 id=\"adding-a-new-constructor\">Adding a new constructor<\/h2>\n<p>In cases where the <code>AppComponent<\/code> does not have a constructor, we have to create one. The Schematics way of doing this is creating a <code>Change<\/code>-Object that describes this modification. For this task, I've created a function <code>createConstructorForInjection<\/code>. Although it is a bit long because we have to include several null\/undefined checks, it is quite straight:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createConstructorForInjection<\/span>(<span class=\"hljs-params\">context: AddInjectionContext, nodes: ts.Node[], options: ModuleOptions<\/span>): <span class=\"hljs-title\">Change<\/span> <\/span>{\n    <span class=\"hljs-keyword\">let<\/span> classNode = nodes.find(n =&gt; n.kind === ts.SyntaxKind.ClassKeyword);\n\n    <span class=\"hljs-keyword\">if<\/span> (!classNode) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>expected class in &lt;span class=&quot;hljs-subst&quot;&gt;${context.appComponentFileName}&lt;\/span&gt;<\/code><\/span>);\n    }\n\n    <span class=\"hljs-keyword\">if<\/span> (!classNode.parent) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>expected constructor in &lt;span class=&quot;hljs-subst&quot;&gt;${context.appComponentFileName}&lt;\/span&gt; to have a parent node<\/code><\/span>);\n    }\n\n    <span class=\"hljs-keyword\">let<\/span> siblings = classNode.parent.getChildren();\n    <span class=\"hljs-keyword\">let<\/span> classIndex = siblings.indexOf(classNode);\n\n    siblings = siblings.slice(classIndex);\n\n    <span class=\"hljs-keyword\">let<\/span> classIdentifierNode = siblings.find(n =&gt; n.kind === ts.SyntaxKind.Identifier);\n\n    <span class=\"hljs-keyword\">if<\/span> (!classIdentifierNode) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>expected class in &lt;span class=&quot;hljs-subst&quot;&gt;${context.appComponentFileName}&lt;\/span&gt; to have an identifier<\/code><\/span>);\n    }\n\n    <span class=\"hljs-keyword\">if<\/span> (classIdentifierNode.getText() !== <span class=\"hljs-string\">'AppComponent'<\/span>) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>expected first class in &lt;span class=&quot;hljs-subst&quot;&gt;${context.appComponentFileName}&lt;\/span&gt; to have the name AppComponent<\/code><\/span>);\n    }\n\n    <span class=\"hljs-comment\">\/\/ Find opening cury braces (FirstPunctuation means '{' here).<\/span>\n    <span class=\"hljs-keyword\">let<\/span> curlyNodeIndex = siblings.findIndex(n =&gt; n.kind === ts.SyntaxKind.FirstPunctuation); \n\n    siblings = siblings.slice(curlyNodeIndex);\n\n    <span class=\"hljs-keyword\">let<\/span> listNode = siblings.find(n =&gt; n.kind === ts.SyntaxKind.SyntaxList);\n\n    <span class=\"hljs-keyword\">if<\/span> (!listNode) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>expected first class in &lt;span class=&quot;hljs-subst&quot;&gt;${context.appComponentFileName}&lt;\/span&gt; to have a body<\/code><\/span>);\n    }\n\n    <span class=\"hljs-keyword\">let<\/span> toAdd = <span class=\"hljs-string\">`\n  constructor(private <span class=\"hljs-subst\">${camelize(context.serviceName)}<\/span>: <span class=\"hljs-subst\">${classify(context.serviceName)}<\/span>) {\n    \/\/ <span class=\"hljs-subst\">${camelize(context.serviceName)}<\/span>.show = true;\n  }\n`<\/span>;\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> InsertChange(context.appComponentFileName, listNode.pos+<span class=\"hljs-number\">1<\/span>, toAdd);\n\n}\n<\/div><\/code><\/pre>\n<p>The parameter <code>nodes<\/code> contains all nodes of the syntax tree in a flat way. This structure is also used by some default rules Schematics comes with and allows to easily search the tree with Array methods. The function looks for the first node of the kind <code>ClassKeyword<\/code> which contains the <code>class<\/code> keyword. Compare this with the syntax tree above which was displayed by the first example.<\/p>\n<p>After this it gets an array with the <code>ClassKeyword<\/code>'s siblings (=its parent's children) and searches it from left to right in order to find a position for the new constructor. To search from left to right, it truncates everything that is on the left of the current position using <code>slice<\/code> several times. To be honest, this is not the best decision in view of performance, but it should be fast enough and I think that it makes the code more readable.<\/p>\n<p>Using this approach, the functions walks to the right until it finds a <code>SyntaxList<\/code> (= class body) that follows a <code>FirstPunctuation<\/code> node (= the character '{' in this case) which in turn follows an <code>Identifier<\/code> (= the class name). Then it uses the position of this <code>SyntaxList<\/code> to create an <code>InsertChange<\/code> object that describes that a constructor should be inserted there.<\/p>\n<p>Of course, we could also search the body of the class to find a more fitting place for the constructor -- e. g. between the property declarations and the method declarations -- but for the sake of simplicity and demonstration, I've dropped this idea.<\/p>\n<h2 id=\"adding-a-constructor-argument\">Adding a constructor argument<\/h2>\n<p>If there already is a constructor, we have to add another argument for our service. The following function is taking care about this task. Among other parameters, it takes the node that represents the constructor. You can also compare this with the syntax tree of our first example at the beginning.<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">addConstructorArgument<\/span>(<span class=\"hljs-params\">context: AddInjectionContext, ctorNode: ts.Node, options: ModuleOptions<\/span>): <span class=\"hljs-title\">Change<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">let<\/span> siblings = ctorNode.getChildren();\n\n    <span class=\"hljs-keyword\">let<\/span> parameterListNode = siblings.find(n =&gt; n.kind === ts.SyntaxKind.SyntaxList);\n\n    <span class=\"hljs-keyword\">if<\/span> (!parameterListNode) {\n        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>expected constructor in &lt;span class=&quot;hljs-subst&quot;&gt;${context.appComponentFileName}&lt;\/span&gt; to have a parameter list<\/code><\/span>);\n    }\n\n    <span class=\"hljs-keyword\">let<\/span> parameterNodes = parameterListNode.getChildren();\n\n    <span class=\"hljs-keyword\">let<\/span> paramNode = parameterNodes.find(p =&gt; {\n        <span class=\"hljs-keyword\">let<\/span> typeNode = findSuccessor(p, [ts.SyntaxKind.TypeReference, ts.SyntaxKind.Identifier]);\n        <span class=\"hljs-keyword\">if<\/span> (!typeNode) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">false<\/span>;\n        <span class=\"hljs-keyword\">return<\/span> typeNode.getText() === context.serviceName;\n    });\n\n    <span class=\"hljs-comment\">\/\/ There is already a respective constructor argument --&gt; nothing to do for us here ...<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (paramNode) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> NoopChange();\n\n    <span class=\"hljs-comment\">\/\/ Is the new argument the first one?<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (!paramNode &amp;&amp; parameterNodes.length == <span class=\"hljs-number\">0<\/span>) {\n        <span class=\"hljs-keyword\">let<\/span> toAdd = <span class=\"hljs-string\"><code>private &lt;span class=&quot;hljs-subst&quot;&gt;${camelize(context.serviceName)}&lt;\/span&gt;: &lt;span class=&quot;hljs-subst&quot;&gt;${classify(context.serviceName)}&lt;\/span&gt;<\/code><\/span>;\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> InsertChange(context.appComponentFileName, parameterListNode.pos, toAdd);\n    }\n    <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">if<\/span> (!paramNode &amp;&amp; parameterNodes.length &gt; <span class=\"hljs-number\">0<\/span>) {\n        <span class=\"hljs-keyword\">let<\/span> toAdd = <span class=\"hljs-string\">`,\n    private <span class=\"hljs-subst\">${camelize(context.serviceName)}<\/span>: <span class=\"hljs-subst\">${classify(context.serviceName)}<\/span>`<\/span>;\n        <span class=\"hljs-keyword\">let<\/span> lastParameter = parameterNodes[parameterNodes.length<span class=\"hljs-number\">-1<\/span>];\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> InsertChange(context.appComponentFileName, lastParameter.end, toAdd);\n    }\n\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> NoopChange();\n}\n<\/div><\/code><\/pre>\n<p>This function retrieves all child nodes of the constructor and searches for a <code>SyntaxList<\/code> (=the parameter list) node having a <code>TypeReference<\/code> child which in turn has a <code>Identifier<\/code> child. For this, it uses the helper function <code>findSuccessor<\/code> displayed below. The found identifier holds the type of the argument in question. If there is already an argument that points to the type of our service, we don't need to do anything. Otherwise the function checks wether we are inserting the first argument or a subsequent one. In each case, the correct position for the new argument is located and then the function returns a respective <code>InsertChange<\/code>-Object for the needed modification.<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">findSuccessor<\/span>(<span class=\"hljs-params\">node: ts.Node, searchPath: ts.SyntaxKind[] <\/span>) <\/span>{\n    <span class=\"hljs-keyword\">let<\/span> children = node.getChildren();\n    <span class=\"hljs-keyword\">let<\/span> next: ts.Node | <span class=\"hljs-literal\">undefined<\/span> = <span class=\"hljs-literal\">undefined<\/span>;\n\n    <span class=\"hljs-keyword\">for<\/span>(<span class=\"hljs-keyword\">let<\/span> syntaxKind of searchPath) {\n        next = children.find(n =&gt; n.kind == syntaxKind);\n        <span class=\"hljs-keyword\">if<\/span> (!next) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">null<\/span>;\n        children = next.getChildren();\n    }\n    <span class=\"hljs-keyword\">return<\/span> next;\n}\n<\/div><\/code><\/pre>\n<h2 id=\"deciding-whether-to-create-or-modify-a-constructor\">Deciding whether to create or modify a Constructor<\/h2>\n<p>The good message first: We've done the heavy work. What we need now is a function that decides which of the two possible changes -- adding a constructor or modifying it -- needs to be done:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildInjectionChanges<\/span>(<span class=\"hljs-params\">context: AddInjectionContext, host: Tree, options: ModuleOptions<\/span>): <span class=\"hljs-title\">Change<\/span>[] <\/span>{\n\n    <span class=\"hljs-keyword\">let<\/span> text = host.read(context.appComponentFileName);\n    <span class=\"hljs-keyword\">if<\/span> (!text) <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> SchematicsException(<span class=\"hljs-string\"><code>File &lt;span class=&quot;hljs-subst&quot;&gt;${options.module}&lt;\/span&gt; does not exist.<\/code><\/span>);\n    <span class=\"hljs-keyword\">let<\/span> sourceText = text.toString(<span class=\"hljs-string\">'utf-8'<\/span>);\n\n    <span class=\"hljs-keyword\">let<\/span> sourceFile = ts.createSourceFile(context.appComponentFileName, sourceText, ts.ScriptTarget.Latest, <span class=\"hljs-literal\">true<\/span>);\n\n    <span class=\"hljs-keyword\">let<\/span> nodes = getSourceNodes(sourceFile);\n    <span class=\"hljs-keyword\">let<\/span> ctorNode = nodes.find(n =&gt; n.kind == ts.SyntaxKind.Constructor);\n\n    <span class=\"hljs-keyword\">let<\/span> constructorChange: Change;\n\n    <span class=\"hljs-keyword\">if<\/span> (!ctorNode) {\n        <span class=\"hljs-comment\">\/\/ No constructor found<\/span>\n        constructorChange = createConstructorForInjection(context, nodes, options);\n    } \n    <span class=\"hljs-keyword\">else<\/span> { \n        constructorChange = addConstructorArgument(context, ctorNode, options);\n    }\n\n    <span class=\"hljs-keyword\">return<\/span> [\n        constructorChange,\n        insertImport(sourceFile, context.appComponentFileName, context.serviceName, context.relativeServiceFileName) \n    ];\n\n}\n<\/div><\/code><\/pre>\n<p>As the first sample in this post, it uses the TypeScript Compiler API to create a <code>SourceFile<\/code> object for the file containing the <code>AppComponent<\/code>. Then it uses the function <code>getSourceNodes<\/code> which is part of Schematics to traverse the whole tree and creates a flat array with all nodes. These nodes are searched for a constructor. If there is none, we are using our function <code>createConstructorForInjection<\/code> to create a <code>Change<\/code> object; otherwise we are going with <code>addConstructorArgument<\/code>. At the end, the function returns this <code>Change<\/code> together with another <code>Change<\/code> created by <code>insertImport<\/code> which also comes with Schematics and creates the needed <code>import<\/code> statement at the beginning of the TypeScript file.<\/p>\n<p>Please note that the order of these two changes is vital because they are adding lines to the source file which is forging the position information within the node objects.<\/p>\n<h2 id=\"putting-all-together\">Putting all together<\/h2>\n<p>Now, we just need a factory function for a rule that is calling <code>buildInjectionChanges<\/code> and applying the returned changes:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">injectServiceIntoAppComponent<\/span>(<span class=\"hljs-params\">options: ModuleOptions<\/span>): <span class=\"hljs-title\">Rule<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> (host: Tree) =&gt; {\n\n        <span class=\"hljs-keyword\">let<\/span> context = createAddInjectionContext(options);\n        <span class=\"hljs-keyword\">let<\/span> changes = buildInjectionChanges(context, host, options);\n\n        <span class=\"hljs-keyword\">const<\/span> declarationRecorder = host.beginUpdate(context.appComponentFileName);\n        <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> change of changes) {\n            <span class=\"hljs-keyword\">if<\/span> (change <span class=\"hljs-keyword\">instanceof<\/span> InsertChange) {\n                declarationRecorder.insertLeft(change.pos, change.toAdd);\n            }\n        }\n        host.commitUpdate(declarationRecorder);\n\n        <span class=\"hljs-keyword\">return<\/span> host;\n    };\n};\n<\/div><\/code><\/pre>\n<p>This function takes the <code>ModuleOptions<\/code> holding the parameters the CLI passes and returns a <code>Rule<\/code> function. It creates the context object with the key data and delegates to <code>buildInjectionChanges<\/code>. The received rules are iterated and applied.<\/p>\n<h2 id=\"adding-rule-to-schematic\">Adding Rule to Schematic<\/h2>\n<p>To get our new <code>injectServiceIntoAppComponent<\/code> rule called, we have to call it in its <code>index.ts<\/code>:<\/p>\n<pre class=\"hljs\"><code><div>[...]\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">options: MenuOptions<\/span>): <span class=\"hljs-title\">Rule<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">return<\/span> (host: Tree, context: SchematicContext) =&gt; {\n\n      [...]\n\n      <span class=\"hljs-keyword\">const<\/span> rule = chain([\n        branchAndMerge(chain([\n          mergeWith(templateSource),\n          addDeclarationToNgModule(options, options.export),\n          injectServiceIntoAppComponent(options)\n        ]))\n      ]);\n\n      <span class=\"hljs-keyword\">return<\/span> rule(host, context);\n    }\n}\n<\/div><\/code><\/pre>\n<h2 id=\"testing-the-extended-schematic\">Testing the extended Schematic<\/h2>\n<p>To try the modified Schematic out, compile it and copy everything to the <code>node_modules<\/code> folder of an example application. As in the <a href=\"https:\/\/www.angulararchitects.io\/post\/2017\/10\/29\/generating-custom-code-with-the-angular-cli-and-schematics.aspx\">former blog article<\/a>, I've decided to copy it to <code>node_modules\/nav<\/code>. Please make sure to exclude the Schematic Collection's <code>node_modules<\/code> folder, so that there is no folder <code>node_modules\/nav\/node_modules<\/code>.<\/p>\n<p>After this, switch to the example application's root and call the Schematic:<\/p>\n<pre style=\"line-height: 1.42857;\"><code>ng g nav:menu side-menu --menu-service --export<\/code><\/pre>\n<p>This not only created the <code>SideMenu<\/code> but also injects its service into the <code>AppComponent<\/code>:<\/p>\n<pre class=\"hljs\"><code><div><span class=\"hljs-keyword\">import<\/span> { Component } from <span class=\"hljs-string\">'@angular\/core'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { OnChanges, OnInit } from <span class=\"hljs-string\">'@angular\/core'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { SideMenuService } from <span class=\"hljs-string\">'.\/core\/side-menu\/side-menu.service'<\/span>;\n\n@Component({\n  selector: <span class=\"hljs-string\">'app-root'<\/span>,\n  templateUrl: <span class=\"hljs-string\">'.\/app.component.html'<\/span>,\n  styleUrls: [<span class=\"hljs-string\">'.\/app.component.css'<\/span>]\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">class<\/span> AppComponent {\n\n  <span class=\"hljs-keyword\">constructor<\/span>(private sideMenuService: SideMenuService) {\n      <span class=\"hljs-comment\">\/\/ sideMenuService.show = true;<\/span>\n  }\n\n  title = <span class=\"hljs-string\">'app'<\/span>;\n}\n<\/div><\/code><\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Extending Existing Code With The TypeScript Compiler API<\/p>\n","protected":false},"author":9,"featured_media":2997,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2379","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-unkategorisiert"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Generating Custom Angular Code With The CLI And Schematics, Part III: - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Generating Custom Angular Code With The CLI And Schematics, Part III: - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Extending Existing Code With The TypeScript Compiler API\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2018-04-03T13:10:27+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"853\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manfred Steyer, GDE\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daniel\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer, GDE\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\"},\"author\":{\"name\":\"Manfred Steyer, GDE\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\"},\"headline\":\"Generating Custom Angular Code With The CLI And Schematics, Part III:\",\"datePublished\":\"2018-04-03T13:10:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\"},\"wordCount\":1694,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"articleSection\":[\"Unkategorisiert\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\",\"name\":\"Generating Custom Angular Code With The CLI And Schematics, Part III: - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"datePublished\":\"2018-04-03T13:10:27+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"width\":1280,\"height\":853},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Generating Custom Angular Code With The CLI And Schematics, Part III:\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\",\"name\":\"Manfred Steyer, GDE\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer, GDE\"},\"sameAs\":[\"https:\/\/x.com\/daniel\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Generating Custom Angular Code With The CLI And Schematics, Part III: - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/","og_locale":"en_US","og_type":"article","og_title":"Generating Custom Angular Code With The CLI And Schematics, Part III: - ANGULARarchitects","og_description":"Extending Existing Code With The TypeScript Compiler API","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/","og_site_name":"ANGULARarchitects","article_published_time":"2018-04-03T13:10:27+00:00","og_image":[{"width":1280,"height":853,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","type":"image\/jpeg"}],"author":"Manfred Steyer, GDE","twitter_card":"summary_large_image","twitter_creator":"@daniel","twitter_misc":{"Written by":"Manfred Steyer, GDE","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/"},"author":{"name":"Manfred Steyer, GDE","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951"},"headline":"Generating Custom Angular Code With The CLI And Schematics, Part III:","datePublished":"2018-04-03T13:10:27+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/"},"wordCount":1694,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","articleSection":["Unkategorisiert"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/","name":"Generating Custom Angular Code With The CLI And Schematics, Part III: - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","datePublished":"2018-04-03T13:10:27+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","width":1280,"height":853},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/generating-custom-angular-code-with-the-cli-and-schematics-part-iii\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Generating Custom Angular Code With The CLI And Schematics, Part III:"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951","name":"Manfred Steyer, GDE","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","caption":"Manfred Steyer, GDE"},"sameAs":["https:\/\/x.com\/daniel"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2379","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=2379"}],"version-history":[{"count":0,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2379\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/2997"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=2379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=2379"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=2379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}