Jekyll2023-01-26T16:07:22+00:00https://www.imimick.com/feed.xmlimimickimimick is a technology blog about software and the internet.Trevor KarjanisHow to Upgrade Angular2022-10-13T00:00:00+00:002022-10-13T00:00:00+00:00https://www.imimick.com/2022/10/13/1665680733792<p>The Angular team puts a reasonable amount of effort into ensuring major versions have a stable semi-automated migration path and reduced number of breaking changes. However, upgrading involves more than just Angular. <!-- excerpt --> An upgrade typically includes upgrades of dependencies like webpack and TypeScript which can have breaking changes of their own. Additionally, new versions can break peer dependencies of packages not depended on by Angular, like TypeScript ESLint. This guide supplements the official update guide with more detailed steps defining the overall process and recommendations for troubleshooting. It is tested as of Angular 14.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>1. Read the documentation on the <a href="https://angular.io/guide/releases">“Angular versioning and releases”</a>.</p>
<ul>
<li>Releases use semantic versioning. Breaking changes will only be introduced in major versions. <sup><a href="https://angular.io/guide/releases#angular-versioning">[i]</a></sup></li>
<li>When updating multiple major versions, perform each update one major version at a time. <sup><a href="https://angular.io/guide/releases#supported-update-paths">[i]</a></sup></li>
<li>A new major version is released every six months. <sup><a href="https://angular.io/guide/releases#release-frequency">[i]</a></sup></li>
<li>Major releases are under active development for six months. After six months, major versions are supported for 12 months. <sup><a href="https://angular.io/guide/releases#support-window">[i]</a></sup></li>
<li>Deprecations are enforced three major releases after they are announced. <sup><a href="https://angular.io/guide/releases#deprecation-practices">[i]</a></sup></li>
<li>Do not extend public API’s that are not explicitly documented as extendable. Do not depend on private API’s. <sup><a href="https://angular.io/guide/releases#public-api-surface">[i]</a></sup></li>
</ul>
<p>2. The most common point of failure when performing an upgrade is npm peer dependency resolution failure. Note which packages that have been added to a workspace have peer dependencies on Angular, Angular Material, TypeScript, RxJS, etc. Common examples include <code class="language-plaintext highlighter-rouge">@typescript-eslint</code>, <code class="language-plaintext highlighter-rouge">@angular-eslint</code>, and NgRx. Also, note any additional peer dependencies of these packages, e.g. <code class="language-plaintext highlighter-rouge">@typescript-eslint/eslint-plugin</code> depends on <code class="language-plaintext highlighter-rouge">eslint</code>.</p>
<ul>
<li>TypeScript ESlint does not have an explicit dependency on TypeScript. That is defined in its <a href="https://github.com/typescript-eslint/typescript-eslint#supported-typescript-version">documentation</a>.</li>
<li>Angular ESLint <a href="https://github.com/angular-eslint/angular-eslint#supported-angular-cli-versions">aligns its major version</a> with Angular.</li>
<li>NgRx has <a href="https://ngrx.io/guide/migration/v14">update guides</a>.</li>
</ul>
<h3 id="upgrading-multiple-versions">Upgrading Multiple Versions</h3>
<p>When upgrading across multiple major versions, upgrade in lockstep but all at once so that builds and tests are only addressed once - with the lastest version.</p>
<h3 id="upgrade-steps">Upgrade Steps</h3>
<p>The <code class="language-plaintext highlighter-rouge">ng update</code> commands in steps 4 and 6 are required to perform automatic migrations. Migrations can be run independently with the <a href="https://angular.io/cli/update#options">–migrations-only option</a>.</p>
<ol>
<li>(Optional) Identify compatible versions for packages from prerequisite 2, and update them in <code class="language-plaintext highlighter-rouge">package.json</code>. See npm error 2 for other solutions.
<ul>
<li>This is not always required. Angular is generally <a href="https://github.com/angular/angular/issues/46398#issuecomment-1158275250">forward compatible</a> for two major versions. However, that is not always the case, so updating packages with peer dependencies is sometimes required.</li>
</ul>
</li>
<li>(Recommended) Delete <code class="language-plaintext highlighter-rouge">package-lock.json</code>. See <a href="#npm-error-1">npm error 1</a>.</li>
<li>If the workspace has libraries, update the peer dependency versions, including Angular but not Material, in each library’s <code class="language-plaintext highlighter-rouge">package.json</code>. See <a href="#npm-error-3">npm error 3</a>.</li>
<li>Perform step one of the <a href="https://update.angular.io">update guide</a>.
<ul>
<li>By default, this step requires a clean branch. Use the <code class="language-plaintext highlighter-rouge">--allow-dirty</code> option to allow updates when the repository contains modified files.</li>
<li>If this step fails with an npm error, find a solution in the <a href="#npm-errors">npm Errors</a> section.</li>
</ul>
</li>
<li>If the workspace has libraries that use Angular Material, update the peer dependency versions in each library’s <code class="language-plaintext highlighter-rouge">package.json</code>. See <a href="#npm-error-4">npm error 4</a>.</li>
<li>If the workspace uses Angular Material, perform step two of the <a href="https://update.angular.io/">update guide</a>. Check “I use Angular Material”.
<ul>
<li>If this step fails with an npm error, find a solution in the <a href="#npm-errors">npm Errors</a> section.</li>
</ul>
</li>
<li>Enumerate the remaining items in the <a href="https://update.angular.io">update guide</a>. Evaluate if each applies to the workspace.</li>
<li>If appropriate, update the <a href="https://www.typescriptlang.org/tsconfig#lib">lib option</a> in <code class="language-plaintext highlighter-rouge">tsconfig.json</code> to enable new features.</li>
<li>If a command in step 4 or 6 has been forced, delete <code class="language-plaintext highlighter-rouge">package-lock.json</code>, and run <code class="language-plaintext highlighter-rouge">npm install</code> to generate a proper lock file.</li>
</ol>
<h4 id="npm-errors">npm Errors</h4>
<p>When a peer dependency error occurs in step 4 or 6 npm will have deleted <code class="language-plaintext highlighter-rouge">node_modules</code>. Revert any changes to <code class="language-plaintext highlighter-rouge">package.json</code>, and run <code class="language-plaintext highlighter-rouge">npm install</code>.</p>
<p><a id="npm-error-1"></a>
1. Peer resolution fails to resolve satisfiable <code class="language-plaintext highlighter-rouge">@angular-devkit</code> dependencies.</p>
<p>Resolution:
A. Delete <code class="language-plaintext highlighter-rouge">package-lock.json</code>.
B. Delete <code class="language-plaintext highlighter-rouge">node_modules</code>.</p>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
npm ERR! While resolving: ni-systemlink-shared@1.0.0
npm ERR! Found: @angular-devkit/build-angular@12.2.17
npm ERR! node_modules/@angular-devkit/build-angular
npm ERR! dev @angular-devkit/build-angular@"^13.3.9" from the root project
npm ERR! peer @angular-devkit/build-angular@">=0.8.9 || >= 12.0.0" from @storybook/angular@6.5.9
npm ERR! node_modules/@storybook/angular
npm ERR! dev @storybook/angular@"^6.4.19" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! dev @angular-devkit/build-angular@"^13.3.9" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: @angular/compiler-cli@13.3.11
npm ERR! node_modules/@angular/compiler-cli
npm ERR! peer @angular/compiler-cli@"^13.0.0 || ^13.3.0-rc.0" from @angular-devkit/build-angular@13.3.9
npm ERR! node_modules/@angular-devkit/build-angular
npm ERR! dev @angular-devkit/build-angular@"^13.3.9" from the root project
</code>
</pre>
</details>
<p>2. Peer resolution fails with a legitimate unsatisfiable error. This will be caused by packages from prerequisite 2.</p>
<p>Resolution:<br />
A. Update the dependency in <code class="language-plaintext highlighter-rouge">package.json</code> with a compatible version before performing step 4.<br />
B. Include the package and a compatible version in the command in step 4.<br />
C. Install the package with a compatible version using the <a href="https://docs.npmjs.com/cli/v6/commands/npm-install">force option</a> option before performing step 4.<br />
D. Use the <a href="https://angular.io/cli/update#options">force option</a> with the command in step 4.</p>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
npm ERR! While resolving: ni-systemlink-shared@1.0.0
npm ERR! Found: @angular/common@13.3.11
npm ERR! node_modules/@angular/common
npm ERR! @angular/common@"^13.3.11" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/common@"^12.1.0" from @ni/nimble-angular@10.0.0
npm ERR! node_modules/@ni/nimble-angular
npm ERR! @ni/nimble-angular@"^10.0.0" from the root project
</code>
</pre>
</details>
<p><a id="npm-error-3"></a>
3. Peer resolution fails to resolve a peer dependency of a library in the workspace. The workspace has a library <code class="language-plaintext highlighter-rouge">package.json</code> that declares the old version of a peer dependency.</p>
<p>Resolution: Complete step 3.</p>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
npm ERR! While resolving: @ni-kismet/file-manager-lib@1.8.2
npm ERR! Found: @angular/common@13.3.11
npm ERR! node_modules/@angular/common
npm ERR! peer @angular/common@"^13.3.11" from @ni-kismet/file-manager-lib@1.8.2
npm ERR! projects/file-manager-lib
npm ERR! @ni-kismet/file-manager-lib@1.8.2
npm ERR! node_modules/@ni-kismet/file-manager-lib
npm ERR! workspace projects\file-manager-lib from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/common@"^12.1.0" from @ni/nimble-angular@10.0.0
npm ERR! node_modules/@ni/nimble-angular
npm ERR! peer @ni/nimble-angular@"^10.0.0" from @ni-kismet/file-manager-lib@1.8.2
npm ERR! projects/file-manager-lib
npm ERR! @ni-kismet/file-manager-lib@1.8.2
npm ERR! node_modules/@ni-kismet/file-manager-lib
npm ERR! workspace projects\file-manager-lib from the root project
```
</code>
</pre>
</details>
<p><a id="npm-error-4"></a>
4. Peer resolution fails to resolve an Angular Material peer of a library in the workspace. Material was incorrectly updated in step 3, or the library’s <code class="language-plaintext highlighter-rouge">package.json</code> was not updated in step 4.</p>
<p>Resolution:<br /></p>
<ol>
<li>In step 3, don’t update Material peers.<br /></li>
<li>Complete step 4.</li>
</ol>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
npm WARN While resolving: @ni-kismet/file-manager-lib@1.8.2
npm WARN Found: @angular/cdk@12.2.13
npm WARN node_modules/@angular/cdk
npm WARN @angular/cdk@"^12.2.13" from the root project
npm WARN 2 more (@angular/material, smart-webcomponents-angular)
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer @angular/cdk@"12.2.13" from @angular/material@12.2.13
npm WARN node_modules/@angular/material
npm WARN @angular/material@"^12.2.13" from the root project
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @ni-kismet/file-manager-lib@1.8.2
npm WARN Found: @angular/cdk@undefined
npm WARN node_modules/@angular/cdk
npm WARN @angular/cdk@"^12.2.13" from the root project
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer @angular/cdk@"^13.3.11" from @ni-kismet/file-manager-lib@1.8.2
npm WARN projects/file-manager-lib
npm WARN @ni-kismet/file-manager-lib@1.8.2
npm WARN node_modules/@ni-kismet/file-manager-lib
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @ni-kismet/file-manager-lib@1.8.2
npm WARN Found: @angular/material@undefined
npm WARN node_modules/@angular/material
npm WARN @angular/material@"^12.2.13" from the root project
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer @angular/material@"^13.3.11" from @ni-kismet/file-manager-lib@1.8.2
npm WARN projects/file-manager-lib
npm WARN @ni-kismet/file-manager-lib@1.8.2
npm WARN node_modules/@ni-kismet/file-manager-lib
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: @ni-kismet/systemlink-lib-angular@34.0.0
npm WARN Found: @angular/cdk@undefined
npm WARN node_modules/@angular/cdk
npm WARN @angular/cdk@"^12.2.13" from the root project
</code>
</pre>
</details>
<p>5. Peer resolution fails because a dependency declares multiple compatible versions for a peer, and npm resolves to the wrong version.</p>
<p>Resolution:<br />
A. Download and modify the package’s <code class="language-plaintext highlighter-rouge">package.json</code> to declare the correct version. Install by file path, and then replace with the correct version.<br />
B. Use the <a href="https://angular.io/cli/update#options">force option</a> with the command in the failing step.</p>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
npm ERR! While resolving: ni-systemlink-shared@1.0.0
npm ERR! Found: @angular/cli@13.3.9
npm ERR! node_modules/@angular/cli
npm ERR! dev @angular/cli@"^13.3.9" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/cli@">= 14.0.0 < 15.0.0" from @angular-eslint/schematics@14.1.2
npm ERR! node_modules/@angular-eslint/schematics
npm ERR! peer @angular-eslint/schematics@"^13.5.0 || ^14.1.2" from @ni/eslint-config-angular@3.3.1
npm ERR! node_modules/@ni/eslint-config-angular
npm ERR! dev @ni/eslint-config-angular@"file:./packages/ni-eslint-config-angular-3.3.1.tgz" from the root project
</code>
</pre>
</details>
<p>6. Package installation fails stating that a package is not a dependency. The package has been deleted from <code class="language-plaintext highlighter-rouge">node_modules</code>.</p>
<p>Resolution: Reinstall the package by running <code class="language-plaintext highlighter-rouge">npm install --no-save <package-name>@<old-version></code>.</p>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
Package '@angular/core' is not a dependency.
</code>
</pre>
</details>
<p>7. Package installation fails stating that an Angular package is already up-to-date. The new version is already installed.</p>
<p>Resolution: Install the old version by running <code class="language-plaintext highlighter-rouge">npm install --no-save <package-name>@<old-version></code>.</p>
<details>
<summary>Example</summary>
<pre class="language-bash highlighter-rouge">
<code>
Package '@angular/core' is already up to date.
</code>
</pre>
</details>
<p>8. An npm error continues to occur with a dependency declared by file reference. Dependencies declared by file reference fail to install properly if the package is updated, but its version hasn’t changed.</p>
<p>Resolution: Delete <code class="language-plaintext highlighter-rouge">package-lock.json</code>, and run <code class="language-plaintext highlighter-rouge">npm install</code>.</p>
<h4 id="deleting-node_modules">Deleting <code class="language-plaintext highlighter-rouge">node_modules</code></h4>
<p>Deleting the <code class="language-plaintext highlighter-rouge">node_modules</code> directory with the right-click menu on Windows is terribly slow. Create a <code class="language-plaintext highlighter-rouge">deldir.bat</code> script with the following to delete directories ~3 times faster.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>del /f/s/q %1 <span class="o">></span> nul
<span class="nb">rmdir</span> /s/q %1
</code></pre></div></div>
<h3 id="nodejs-and-typescript-support">Node.js and TypeScript Support</h3>
<p>Angular supports LTS and maintenance versions of Node.js and will upgrade TypeScript. As of Angular 15, these versions are <a href="https://angular.io/guide/update-to-version-15">officially documented</a>.</p>Trevor KarjanisThe Angular team puts a reasonable amount of effort into ensuring major versions have a stable semi-automated migration path and reduced number of breaking changes. However, upgrading involves more than just Angular.Upgrading to Angular 142022-10-13T00:00:00+00:002022-10-13T00:00:00+00:00https://www.imimick.com/2022/10/13/1665680757682<p>This guide is specific to upgrading from Angular 12 to 14. <!-- excerpt --> It should be completed before building and running tests.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>Angular 14 will not work with libraries built with 12 due to changes for the standalone component feature.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error TS2314: Generic <span class="nb">type</span> <span class="s1">'ɵɵDirectiveDeclaration'</span> requires 6 <span class="nb">type </span>argument<span class="o">(</span>s<span class="o">)</span><span class="nb">.</span>
</code></pre></div></div>
<h3 id="upgrade-steps">Upgrade Steps</h3>
<ol>
<li>Complete the <a href="">general upgrade guide</a>.</li>
<li>If the workspace has libraries, configure them to compile in <a href="https://angular.io/guide/angular-package-format#partial-compilation">partial mode</a> by adding the following to <code class="language-plaintext highlighter-rouge">tsconfig.lib.json</code>.
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="nl">"angularCompilerOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"compilationMode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"partial"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div> </div>
</li>
<li>If the workspace CI pipeline supports it, enable the <a href="https://angular.io/cli/cache">build cache</a> for CI by running the following. The cache is enabled for local development by default.
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ng config cli.cache.environment all
</code></pre></div> </div>
</li>
<li>Remove <code class="language-plaintext highlighter-rouge">defaultCollection</code> from <code class="language-plaintext highlighter-rouge">angular.json</code> and, run the following. <code class="language-plaintext highlighter-rouge">defaultCollection</code> is <a href="https://github.com/angular/angular-cli/blob/main/CHANGELOG.md#angularcli-20">deprecated</a>.
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ng config cli.schematicCollections <span class="s2">"[</span><span class="se">\"</span><span class="s2"><default-schematic></span><span class="se">\"</span><span class="s2">]"</span>
</code></pre></div> </div>
</li>
<li>If the workspace has a projects directory, ensure scripts in <code class="language-plaintext highlighter-rouge">package.json</code> specify the project name. <code class="language-plaintext highlighter-rouge">defaultProject</code> has been <a href="https://github.com/angular/angular-cli/blob/main/CHANGELOG.md#angularcli-20">deprecated</a>.</li>
<li>Remove all tilde (~) prefixes from SCSS imports in style files.</li>
</ol>Trevor KarjanisThis guide is specific to upgrading from Angular 12 to 14.Angular Library Development with npm-link2022-10-13T00:00:00+00:002022-10-13T00:00:00+00:00https://www.imimick.com/2022/10/13/1665687498774<p>As of Angular 14, <a href="https://github.com/ng-packagr/ng-packagr/blob/main/CHANGELOG.md#1404-2022-07-20">ng-packagr 14.4.0</a> supports watching for changes in all of a library’s entry points, primary and secondary, with <a href="https://docs.npmjs.com/cli/v8/commands/npm-link/">npm-link</a>.</p>
<!-- excerpt -->
<ol>
<li>Run <code class="language-plaintext highlighter-rouge">ng build --watch <project-name></code> to build the library, and rebuild it when changes are made.</li>
<li>Run <code class="language-plaintext highlighter-rouge">cd dist/<project-name> && npm link</code>.</li>
<li>In a workspace to link, modify <code class="language-plaintext highlighter-rouge">tsconfig.json</code> as outlined in the section “TSConfig Paths for npm Link” below.</li>
<li>In the workspace to link, run <code class="language-plaintext highlighter-rouge">npm link <package-name></code> where <code class="language-plaintext highlighter-rouge">package-name</code> is the library’s package name.
<ul>
<li>Note, running <code class="language-plaintext highlighter-rouge">npm install</code> will replace the link.</li>
</ul>
</li>
<li>In the workspace to link, run <code class="language-plaintext highlighter-rouge">ng build --watch <application-name></code> where <code class="language-plaintext highlighter-rouge">application-name</code> is a dependent application or library.</li>
</ol>
<p>The library and then application will be rebuilt automatically when changes are made. Developers are can perform library development without copying the library after every change and build. Libraries can be linked together, and multiple libraries can be developed at once.</p>
<h4 id="tsconfig-paths-for-npm-link">TSConfig Paths for npm-link</h4>
<p>Step three will require adding <a href="https://www.typescriptlang.org/tsconfig/paths.html">TSConfig path</a> configurations to the linked workspace’s <code class="language-plaintext highlighter-rouge">tsconfig.json</code> - quite a few more than alternatives. A linked package is a symlink to a workspace with a separate <code class="language-plaintext highlighter-rouge">node_modules</code> directory. Node.js will resolve packages in the current and linked workspace resulting in various errors. Configuring <a href="https://www.typescriptlang.org/tsconfig#skipLibCheck">skipLibChecks</a> as <code class="language-plaintext highlighter-rouge">true</code> does not resolve the issue. In testing, adding the paths to an application’s <code class="language-plaintext highlighter-rouge">tsconfig.app.json</code> or library’s <code class="language-plaintext highlighter-rouge">tsconfig.lib.json</code> did not work. The root <code class="language-plaintext highlighter-rouge">tsconfig.json</code> was required.</p>
<p>The necessary paths will be those of packages installed in both workspaces and the library itself if there internal imports of entry points. The build errors are clues, but it is recommended to start with Angular. Add paths until the build succeeds. Only one wildcard can be used in each path, so multiple paths will be needed for libraries with multiple entry points and directories.</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"paths"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"@angular/*"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"node_modules/@angular/*"</span><span class="p">],</span><span class="w">
</span><span class="nl">"@angular/common/*"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"node_modules/@angular/common/*"</span><span class="p">],</span><span class="w">
</span><span class="nl">"rxjs"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"node_modules/rxjs"</span><span class="p">],</span><span class="w">
</span><span class="nl">"@third-party-scope/third-party-lib"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"node_modules/@third-party-scope/third-party-lib"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h5 id="examples">Examples</h5>
<p>The following are examples of errors solved with a path configuration.</p>
<ul>
<li>Angular localization results in a redeclared global function.
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> error TS2451: Cannot redeclare block-scoped variable <span class="s1">'$localize'</span><span class="nb">.</span>
</code></pre></div> </div>
</li>
<li>RxJS results in a type mismatch.
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> error TS2322: Type <span class="s1">'Observable<ArrayBuffer>'</span> is not assignable to <span class="nb">type</span> <span class="s1">'Observable<WorkspaceCollection>'</span><span class="nb">.</span>
</code></pre></div> </div>
</li>
<li>In certain cases, the downstream build will modify the library’s <code class="language-plaintext highlighter-rouge">node_modules</code> and result in an upstream build failure.
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> error NG6002: <span class="s1">'MyModule'</span> does not appear to be an NgModule class.
</code></pre></div> </div>
</li>
</ul>
<h3 id="peer-dependencies">Peer Dependencies</h3>
<p>npm may not install all of a library’s peer dependencies, e.g. <code class="language-plaintext highlighter-rouge">@angular/elements</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error TS2307: Cannot find module <span class="s1">'@angular/elements'</span> or its corresponding <span class="nb">type </span>declarations.
</code></pre></div></div>
<p>Take the following steps to ensure the package is installed in subsequent runs.</p>
<ol>
<li>Delete <code class="language-plaintext highlighter-rouge">package-lock.json</code>.</li>
<li>Run <code class="language-plaintext highlighter-rouge">npm install <package-name></code>.</li>
<li>Delete the newly added dependency from <code class="language-plaintext highlighter-rouge">package.json</code>.</li>
<li>Start at step three above to re-establish the link.</li>
</ol>
<h3 id="alternatives">Alternatives</h3>
<p>Linking is used for performing library development across workspaces. As outlined in <a href="https://stackoverflow.com/a/71375807/392895">SO#59356732</a>, an alternative is to add a library’s output directory to an application’s TSConfig paths which has a similar result. In testing it required fewer path configurations but only supports TypeScript resources. Linking with npm supports changes to assests and resulted in less rebuilds in the dependent workspace on Windows.</p>
<p>For a single workspace, paths to a library’s output directory are added to the TSConfig of the workspace. It is <a href="https://github.com/angular/angular-cli/issues/12000#issuecomment-418034164">not recommended</a> to import from a library’s source, because the build output is different from source.</p>
<h3 id="history">History</h3>
<p>Prior to 14, watching for changes in libraries with npm-link was limited to the primary entry point, because webpack assumes that packages in <code class="language-plaintext highlighter-rouge">node_modules</code> are only <a href="https://github.com/webpack/webpack/issues/11612#issuecomment-705790881">managed by a package manager</a>. It remains that webpack only considers the package name and version when watching for changes to packages. Secondary entry points don’t have a package file. As of <a href="https://github.com/ng-packagr/ng-packagr/pull/2379">PR#2379</a>, ng-packagr will update the primary entry point when changes are made to a secondary.</p>Trevor KarjanisAs of Angular 14, ng-packagr 14.4.0 supports watching for changes in all of a library’s entry points, primary and secondary, with npm-link.Test Teardown and Angular Elements2022-10-13T00:00:00+00:002022-10-13T00:00:00+00:00https://www.imimick.com/2022/10/13/1665687500875<p>A common pattern is to create <a href="https://angular.io/guide/elements">Angular elements</a> in the constructor or <code class="language-plaintext highlighter-rouge">ngBootstrap</code> method of its associated module. This allows elements to use the module’s <a href="https://angular.io/api/core/Injector">injector</a>, inheriting it’s scope and lifecycle. A conditional check is required, because <code class="language-plaintext highlighter-rouge">customElements.define</code> <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#exceptions">will throw</a> if the registry already has an entry.</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">@</span><span class="nd">NgModule</span><span class="p">()</span>
<span class="k">export</span> <span class="kd">class</span> <span class="nx">AppModule</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">injector</span><span class="p">:</span> <span class="nx">Injector</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">customElement</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">custom-element</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">el</span> <span class="o">=</span> <span class="nx">createCustomElement</span><span class="p">(</span><span class="nx">CustomComponent</span><span class="p">,</span> <span class="p">{</span> <span class="na">injector</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">injector</span> <span class="p">});</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">custom-element</span><span class="dl">'</span><span class="p">,</span> <span class="nx">el</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As of Angular 13, <a href="https://angular.io/api/core/testing/ModuleTeardownOptions#destroyAfterEach">ModuleTeardownOptions.destroyAfterEach</a> defaults to true. The test module passed to <code class="language-plaintext highlighter-rouge">TestBed.initTestEnvironment</code> will be destroyed and the DOM cleared after every test. This should result in “faster, less memory-intensive, and less interdependent [tests].” For tests with an Angular element defined, however, its associated injector will be destroyed after the first test - resulting in the following error.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error: NG0205: Injector has already been destroyed.
error properties: Object<span class="o">({</span> code: 205 <span class="o">})</span>
Error: NG0205: Injector has already been destroyed.
at R3Injector.assertNotDestroyed <span class="o">(</span>node_modules/@angular/core/fesm2020/core.mjs:6850:19<span class="o">)</span>
at R3Injector.get <span class="o">(</span>node_modules/@angular/core/fesm2020/core.mjs:6758:14<span class="o">)</span>
at new ComponentNgElementStrategy <span class="o">(</span>node_modules/@angular/elements/fesm2020/elements.mjs:219:37<span class="o">)</span>
at ComponentNgElementStrategyFactory.create <span class="o">(</span>node_modules/@angular/elements/fesm2020/elements.mjs:176:16<span class="o">)</span>
at NgElementImpl.ngElementStrategy <span class="o">(</span>node_modules/@angular/elements/fesm2020/elements.mjs:469:37<span class="o">)</span>
at NgElementImpl.apply <span class="o">(</span>node_modules/@angular/elements/fesm2020/elements.mjs:498:22<span class="o">)</span>
at _ZoneDelegate.invoke <span class="o">(</span>node_modules/zone.js/fesm2015/zone.js:372:26<span class="o">)</span>
at ProxyZoneSpec.onInvoke <span class="o">(</span>node_modules/zone.js/fesm2015/zone-testing.js:287:39<span class="o">)</span>
at _ZoneDelegate.invoke <span class="o">(</span>node_modules/zone.js/fesm2015/zone.js:371:52<span class="o">)</span>
at Zone.runGuarded <span class="o">(</span>node_modules/zone.js/fesm2015/zone.js:144:47<span class="o">)</span>
</code></pre></div></div>
<h3 id="debugging">Debugging</h3>
<p>To determine the problematic element, check the beginning of the above callstack. Run the tests in watch mode, and set a breakpoint where the exception is thrown (<a href="https://github.com/angular/angular/blob/a3e1303f04d4afe456f3728939b3aa54e29f9fb3/packages/core/src/di/r3_injector.ts#L303">r3_injector.ts line 303</a> as of 14.2.0). Once hit, a few steps up the stack will be the <code class="language-plaintext highlighter-rouge">ComponentNgElementStrategy</code> constructor (element.mjs line 219 corresponding to <a href="https://github.com/angular/angular/blob/4e10a7494130b9bb4772ee8f76b66675867b2145/packages/elements/src/component-factory-strategy.ts#L88">component-factory-strategy.ts line 88</a>). Inpect the <code class="language-plaintext highlighter-rouge">componentFactory</code> argument to reveal the component.</p>
<h3 id="resolution">Resolution</h3>
<p>Often the custom element isn’t necessary for the test. Write the test so that constructing it isn’t required. Alternatively, disable <code class="language-plaintext highlighter-rouge">destroyAfterEach</code> for the affected set of tests.</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">beforeEach</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">TestBed</span><span class="p">.</span><span class="nx">configureTestingModule</span><span class="p">({</span>
<span class="na">teardown</span><span class="p">:</span> <span class="p">{</span> <span class="na">destroyAfterEach</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<h4 id="developer-preview">Developer Preview</h4>
<p>As of Angular 14.2.0, standalone elements are available for preview (<a href="https://github.com/angular/angular/pull/46475">PR#46475</a>). This offers an opportunity to create an independent application environment with <a href="https://angular.io/api/platform-browser/createApplication">createApplication</a> which decouples elements from the test module.</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">beforeAll</span><span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">customElement</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">custom-element</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">application</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">createApplication</span><span class="p">({</span> <span class="na">providers</span><span class="p">:</span> <span class="p">[]</span> <span class="p">});</span>
<span class="kd">const</span> <span class="nx">el</span> <span class="o">=</span> <span class="nx">createCustomElement</span><span class="p">(</span><span class="nx">CustomComponent</span><span class="p">,</span> <span class="p">{</span> <span class="na">injector</span><span class="p">:</span> <span class="nx">appRef</span><span class="p">.</span><span class="nx">injector</span> <span class="p">});</span>
<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">custom-element</span><span class="dl">'</span><span class="p">,</span> <span class="nx">el</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<p><a href="https://blog.angular.io/angular-v13-is-now-available-cce66f7bc296#1cd7">Read more</a> on the Angular blog.</p>Trevor KarjanisAs of Angular 13, ModuleTeardownOptions.destroyAfterEach defaults to true. The test module passed to TestBed.initTestEnvironment will be destroyed and the DOM cleared after every test. For tests with an Angular element defined, its associated injector will be destroyed after the first test - resulting in an error.Lost in Cyberspace2020-10-11T00:00:00+00:002020-10-11T00:00:00+00:00https://www.imimick.com/2020/10/11/88004<p>A first blog post should always be downright boring. Compiled here is a list of notes and tips for new and experienced remote desktop (RDP) users. They’ll help you stay online through situations and mistakes particularly in times when asking for help to recover a lost machine is a lot to ask.</p>
<!-- excerpt -->
<tip>
<h5 id="enable-remote-desktop">Enable Remote Desktop</h5>
<p>It’s elementary, but for first time users and new machines it’s important to note. The remote desktop feature is likely disabled. Check your platform’s documentation for how to enable it.</p>
</tip>
<h3 id="identify-fully-qualified-domain-names">Identify Fully Qualified Domain Names</h3>
<p>Depending on the organization of the private network, hostnames may fail to resolve over VPN, and IP addresses may be subject to change. Take note and configure remote desktop connections with the fully qualified domain name of the remote computer. Note the IP address as well to ensure you have a backup address in the case of adverse complications. On Windows, see the DNS suffix and IP address provided by <code class="language-plaintext highlighter-rouge">ipconfig</code>.</p>
<h3 id="enable-wake-on-lan">Enable Wake on LAN</h3>
<p>The magic packet defined in the <a href="https://en.wikipedia.org/wiki/Wake-on-LAN">Wake on LAN</a> (WoL) standard can be encapsulated in a routable, connectionless IP packet. Therefore, many utilities do so, and enabling WoL can be beneficial in waking a remote computer from a low power sleep state. However, a computer’s IP address can change while it is powered off. The standard requires the use of a broadcast address which is blocked by most routers, including over VPN. Resolution requires configuring all intermediate routers or a WoL gateway. Alternatively, leave a device accessible on the local subnet. By the time I’ve accessed VPN and started my development machine, I’m running at least four active computers: phone, laptop, development machine, test machine, and frequently remote test devices. In consideration for excessive consumption, I leave the test machine available from which I wake others.</p>
<tip>
<p>Wake on LAN does not work for all sleep states, so take care when restarting or configuring power settings. Do not mistake the <code class="language-plaintext highlighter-rouge">shutdown /h</code> option for the shutdown help command — results may vary.</p>
</tip>
<h3 id="enable-ac-power-recovery">Enable AC Power Recovery</h3>
<p>Wake on LAN won’t work when a computer is fully powered off. Even hibernating computers will not return if power is lost. Enable AC power recovery to ensure a machine remains accessible after an ungraceful shutdown. If it’s supported, it can be found in the BIOS settings. If you’re already remote, check the device manufacture for an application that can configure it from the operating system like <a href="https://www.dell.com/support/article/en-us/sln311302/dell-command-configure">Dell Command | Configure</a>. For a guarantee and especially for computers that sleep anyway, I configure mine to power on rather than to the last sleep state after power is restored. Since it’s harmless, I also configure them to power on every morning at six just in case of an anomaly.</p>
<tip>
<p>As of version 4.0, Dell Command | Configure requires WMI-ACPI BIOS support. Check the list of <a href="https://www.dell.com/support/article/en-us/sln312336">supported platforms</a>. If the device model is not supported, try version 3.3.</p>
</tip>
<h3 id="enable-rdp-remotely">Enable RDP Remotely</h3>
<p>In the event that access is required to a new computer, check if remote desktop can be enabled remotely. <a href="https://mediarealm.com.au/articles/remotely-enable-remote-desktop-windows-10/">Anthony Eden’s article</a> on Media Realm covers how to do so with Windows 10 and <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/psexec">PsExec</a>. The remote account <a href="https://serverfault.com/a/280756">must have adminstrative privileges</a>. Also, <a href="https://stackoverflow.com/a/828630/392895">this will not work</a> when connecting from a computer on a different domain, like connecting with a local account.</p>
<ol>
<li>Start a command prompt on the remote computer.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>psexec \\<hostname> -u <domain> <username> -p <password> -h cmd
</code></pre></div> </div>
</li>
<li>Open the firewall port for remote desktop.
<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">netsh</span> <span class="kd">advfirewall</span> <span class="kd">firewall</span> <span class="kd">set</span> <span class="kd">rule</span> <span class="kd">group</span><span class="o">=</span><span class="s2">"remote desktop"</span> <span class="kd">new</span> <span class="kd">enable</span><span class="o">=</span><span class="kd">Yes</span>
</code></pre></div> </div>
</li>
<li>Configure the registry to enable remote desktop.
<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">reg</span> <span class="kd">add</span> <span class="s2">"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server"</span> <span class="na">/v </span><span class="kd">fDenyTSConnections</span> <span class="na">/t </span><span class="kd">REG_DWORD</span> <span class="na">/d </span><span class="m">0</span> <span class="na">/f
</span></code></pre></div> </div>
</li>
<li>Start the remote desktop service.
<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">net</span> <span class="nb">start</span> <span class="s2">"remote desktop services"</span>
</code></pre></div> </div>
</li>
</ol>
<h3 id="remote-recovery">Remote Recovery</h3>
<p>The remote desktop service can become unresponsive. I found a machine inaccessible following an IT scheduled update, likely due to issues shutting down processes before restarting. Updates can also disable the feature. In which cases, it may be necessary to recover the system by executing remote commands with protocols like <a href="https://en.wikipedia.org/wiki/Secure_Shell">Secure Shell (SSH)</a>. <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/psexec">PSExec</a> is a light-weight utility for Windows that can launch interactive command prompts. Windows system commands like <a href="https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/shutdown">shutdown</a>, services, and regedit also have remote support. If AC power recovery is not enabled, care should be taken with the shutdown command. Also of note, mind the target when using services.msc. When restarted, it uses the previously connected computer.</p>
<h3 id="lookup-and-test-an-address">Lookup and Test an Address</h3>
<p>Use the <a href="https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ping">ping</a> command to test address resolution and connection speed. Provided with a hostname, ping will resolve the IP address. Remember, the FQDN may be required. On Windows, the <code class="language-plaintext highlighter-rouge">ping -a <ip address></code> command will reverse lookup the hostname and return the FQDN if successful. Providing an IP address to <code class="language-plaintext highlighter-rouge">nslookup</code> will do the same.</p>
<p>It is possible to retrieve a MAC address with which to use with WoL, however, it will only work to lookup addresses on a local subnet. First, ping the address. This will populate the arp cache tables which are then accessible with <code class="language-plaintext highlighter-rouge">arp -a <ip address></code>.</p>
<h3 id="dont-use-remote-desktop">Don’t Use Remote Desktop</h3>
<p>Enough internal resources may be accessible with just VPN that RDP may not be necessary. This alternative likely requires replicating the desktop work environment locally but avoids many of the common pitfalls of remote access. Issues with bandwidth and latency can result in grainy or incorrect visuals, laggy cursors, skips, freezes, and unusable video and sound. A native desktop experience may provide an easier and more productive solution in general. It’s important to know and comply with organizational rules and requirements as well as national laws and regulations. Your organization may restrict certain resources on personal devices or may have additional restrictions for certain environments like public networks. At the least, they likely require anti-virus software, and your government likely imposes export controls that need to be carefully considered.</p>Trevor KarjanisA first blog post should always be downright boring. Compiled here is a list of notes and tips for new and experienced remote desktop (RDP) users. They’ll help you stay online through situations and mistakes particularly in times when asking for help to recover a lost machine is a lot to ask.