12

I followed several tutorials on how to build and test an angular libary.

E.g. https://www.youtube.com/watch?v=lvjt9rBHWjo

It's working fine except that each time I'm doing a ng build mylibary, it's erasing the mylibrary folder in the dist folder. And before it has finished to build, the server (launched with npm start) detect the change (folder erased) and re compiles. And of course, since the library folder is not present anymore, there is a compilation error with no other thing to do than ctrl-c and again npm start ...

What did I missed ?

3
  • why dont u stop npm server while building with ng build command Commented Dec 16, 2019 at 12:38
  • 1
    why would I do that ? In the video it's working like that. I'm simply want to do the same... Commented Dec 16, 2019 at 12:41
  • Maybe this: github.com/angular/angular-cli/issues/2502 can help you out Commented Dec 16, 2019 at 14:21

5 Answers 5

16

Here is a super handy way to make the app reload automatically whether changes are made to the host app or to the library source code, all while keeping the original structure ready to build and publish (no need to revert any changes made to the code prior building and publishing the lib).

Having a library called my-lib, the following steps are needed:

  • Go to projects/my-lib/src/lib directory and create index.ts file which exports lib components that are meant to be publicly available

  • Edit the projects/my-lib/src/public-api.ts file in a way it exports all from the previously created index.ts file, e.g.:

    export * from './lib/index';

  • Finally, update the generated TS paths for the lib in tsconfig.json file (the root one) to point to the index.ts file created previously (instead of pointing to the dist folder)

Here is the git commit showing the changes described in the steps above.

For a more detailed info visit: Setting up live reload for Angular CLI libraries.

No need for external dependencies.

Sign up to request clarification or add additional context in comments.

8 Comments

This is brilliant and should be the correct answer! I've applied this simple update to my Angular 11 workspace and working flawlessly. Library updates trigger and live reload my app fast and smoothly (live reload). So happy, this improves dev time like I was hoping. Thank you.
I've gone back and forth on this idea, but I'm not sure that it's necessarily safe based on comments by the Angular team. See their discussion, specifically this: the really big problem is that you're not actually building the library properly. When you eventually try to build and consume the library, any number of problems can come up that you didn't see while developing.
@MarkBrodziak I've built 5-6 libraries this way and didn't notice any problems (wondering what they might be). The way it's described in the answer, dev-server is watching raw files for a change. Once you're ready to publish, you're going to use the standard production build process which will compile the library to the dist folder (to be published to npm from there).
The whole process is similar to the standard app development, watch raw files while developing, and build for production once ready.
I've ended up settling on this approach, and it has worked just fine for our use-case, so it may have been an overabundance of caution. In our case we're not publishing our libraries as NPM modules before integration, so as far as I can see it should be wholly equivalent.
|
11

As of Angular 12, a single workspace, monolithic repository is not required but is the approach Angular takes and sometimes recommends. I've outlined and tested single and multiple workspace solutions below. The Angular team has recommended against referencing an unbuilt library many times, because the application builds are different than the libraries' [1][2].

Single Workspace

When a library and application are in the same workspace, add the watched library's output directory to the application's tsconfig paths. If the library has multiple entry points, create separate paths for the main and secondaries.

/* tsconfig.app.json */
"compilerOptions": {
  "paths": {
    "@my-scope/my-lib": ["dist/my-lib"],
    "@my-scope/my-lib/*": ["dist/my-lib/*"]
  }
}

Run the library's watch command.

ng build my-lib --configuration development --watch

Run the application's serve command.

ng serve app --configuration development

Multiple Workspaces

There are three solutions for developing a library with an application in a different workspace.

TSConfig Paths

Adding the library's output directory to the application's tsconfig paths, as demonstrated in the previous section, is the easiest solution. However, the catch is that additional paths for unwatched libraries may need to be added as well, particularly those with deep imports. These sometimes produce compile errors like the following, although, I've seen other obscure cases.

Module build failed (from .../ivy/index.js)
Error TS2339: Property 'X' does not exist on type 'typeof import("Y")
Error TS2305: Module '"X"' has no exported member 'Y'.

Always include Angular and then any that fail until successful.

/* tsconfig.app.json */
"compilerOptions": {
  "paths": {
    "@angular/*": ["node_modules/@angular/*"],
    "@my-scope/my-lib": ["../MyWorkspace/dist/my-lib"],
    "@my-scope/my-lib/*": ["../MyWorkspace/dist/my-lib/*"],
    "@third-party-scope/third-party-lib": ["node_modules/@third-party-scope/third-party-lib"]
  }
}

VSCode may require the library paths in tsconfig.json.

Directory Reference

Enable the preserveSymlinks build option in angular.json.

Install the library using the output directory path to create a symlink. npm does not install peer dependencies when installing symlinks. Install these with install-peerdeps or manually. The following is a useful npm script.

"link:my-lib": "npm install ../MyWorkspace/dist/my-lib && install-peerdeps --only-peers --silent @my-scope/my-lib",

Run the library's watch and application's serve commands.

npm Link

Detailed Explanation

Use npm link to create a symlink to the output directory.

> (cd MyWorkspace/dist/my-lib && npm link)
> (cd AppWorkspace && npm link @my-scope/my-lib)

Add tsconfig paths to tsconfig.json. 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.

"paths": {
  "@angular/*": ["node_modules/@angular/*"],
  "@angular/common/*": ["node_modules/@angular/common/*"],
  "rxjs": ["node_modules/rxjs"],
  "@third-party-scope/third-party-lib": ["node_modules/@third-party-scope/third-party-lib"]
}

Run the library's watch and application's serve commands. Note, running npm install will replace the link.

Secondary Entry Points

Watching secondary entry points is not supported prior to Angular 14. It does work with tsconfig paths. The preferred workaround is to configure webpack to watch node_modules for the application build. Install the custom webpack builder.

npm install -D @angular-builders/custom-webpack

Configure the build architect target. Create "linked" build and serve configurations.

/* angular.json */
"projects": {
  "my-app": {
    ...
    "architect": {
      "build": {
        "builder": "@angular-builders/custom-webpack:browser",
        ...
        "configurations": {
          ...
          "linked": {
            ... a development configuration
            "customWebpackConfig": {
              "path": "./webpack.config.js"
            }
        }
      "serve": {
        "builder": "@angular-builders/custom-webpack:dev-server",
        "configurations": {
          ...
          "linked": {
            "browserTarget": "my-app:build:linked"
          }
}

Remove node_modules from the managed paths in the webpack configuration.

/* webpack.config.json */
module.exports = {
    snapshot: { managedPaths: [] }
};

Alternatively, disable the build cache.

export NG_BUILD_CACHE=0

What is a workspace?

A workspace is the root directory structure and configuration files, like angular.json and package.json, which contains and manages projects (applications and libraries). A monolithic workspace contains all projects which share a single set of dependencies. Some Angulars prefer to organize projects into separate workspaces so that they can manage dependency versions separately. For more information, check out the documentation.

3 Comments

what's a workspace?
Great question. I've added a section at the bottom to define what they are.
Thank you very much for the answer. I was having problems making my app refresh properly linking it to a custom library with multiple entry points. I didn't know that secondary entry points aren't still supported with symlinks and your workarround seems to work!
10

You can use wait-on to await the building of the library, rimraf to clean the dist directory and npm-run-all to run the watch scripts parallel with one command from one command line window. Therefore install wait-on, rimraf and run-p as development dependency:

npm install wait-on --save-dev
npm install rimraf --save-dev
npm install run-p --save-dev

And update in package.json the scripts consequently based on the example below:

  "scripts": {
    ...
    "clean": "rimraf dist",
    "start:app": "wait-on dist/your-library-name/fesm5 && ng serve --poll 2000",
    "watch:lib": "ng build your-library-name --watch",
    "watch:all": "npm run clean && run-p watch:lib start:app",
    ...
  },

The library and the application together can be watched using npm run watch:all command.

This is how the scripts work:

"clean": "rimraf dist"

Removes the dist folder.

"start:app": "wait-on dist/your-library-name/fesm5 && ng serve --poll 2000"

Waits on the fesm5 folder in the dist directory, ng serve --poll 2000 starts the app and extends the file watch polling time to 2000 ms. In my case the last one was necessary because after a library modification the app was able to reload in the browser with the same content as previously, I could only see the new build after pressing F5.

"watch:lib": "ng build your-library-name --watch"

Builds the library in watch mode.

"watch:all": "npm run clean && run-p watch:lib start:app"

Cleans the dist folder, after that it serves the application and watches the library parallel.

6 Comments

I'm using Angular 10 and its solution works great! I just changed 2 things: instead of installing npm install run-p --save-dev I installed npm install npm-run-all --save-dev and instead of waiting on dist/lib/fesm5 I waited on dist/lib/fesm2015. Thank you!
Thanks for sharing the idea about npm-run-all! Yes, in case of Angular 10 waiting on dist/lib/fesm2015 is perfect.
@MilanTenk I need to manually reload the URL once I change anything. Can't we make it auto reload URL with this?
@K.Raj: I did not solve that problem yet, so I can't help you with this question. :\
Were you successful in getting library sourceMaps to work properly with this method?
|
1

Use the following steps If you would like to include the built version of the library.

  1. Build the library in watch mode

    ng build my-lib --watch

  2. Add the path to the built library in tsconfig.json under paths

   "paths": {
      "my-lib": [
        "dist/my-lib/bundles/my-lib.umd.js"
      ]
    }
  1. Launch your main app from a new terminal window

    ng serve --open

The downside of this approach compared to the one recommended by seidme is a slightly longer build time needed to assemble the library.

To avoid opening multiple terminals you can add the following command under scripts in package.json. npm install wait-on before running the command.

"dev": "ng build my-lib --watch & (wait-on ./dist/my-lib/bundles/my-lib.umd.js --delay 2000 && ng serve --open)"

Comments

0

I had the same issue this is how I sorted.

Open tsConfig.json.

add paths to the project files inside the sub-project array as below. Add this line "projects/my-test-sub-project/src/public-api.ts"

  "my-test-sub-project": [
    "dist/my-test-sub-project",
    "dist/my-test-sub-project/*",
    "projects/my-test-sub-project/src/public-api.ts"
  ],

Then remove the my-test-sub-project dist file from the dist folder.

Finally, build the main project in build mode.

ng build my-lib --watch

Here is what happens when building the project, if the dist file is not found then the compiler checks for the library source for the files inside the project folder and builds the project when changing the library, the project automatically rebuilds the project due to the watch build.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.