Angular 6 building a library with assets
Asked Answered
M

5

25

Upon building & packaging an Angular 6 library, I can't seem to be able to instruct the Angular CLI to copy the library's assets into the dist/assets folder on every build.

Assuming the project's folder structure is this -

- dist
- e2e
- node_modules
- projects
  - lib1
    - src
      - lib
      - assets
        - icons
- src

When I run ng build lib1 or ng build lib1 --prod the assets/icons folder is not being copied into dist/lib1/assets/icons.

If I run ng build then src/assets (the root src/assets) is being copied but not projects/lib1/assets.

The angular.json file contains a reference to "assets": ["src/assets"] but it won't allow adding the assets key specifically to the project, only to the main root app. When adding, I get the following error:

Schema validation failed with the following errors: Data path "" should NOT have additional properties(assets).

I also tried adding the following custom copy rule to the assets to copy the assets to dist/lib instead of to dist/appname:

  "assets": [
     "src/favicon.ico",
     "src/assets",
     { "glob": "**/*", "input": "src/assets/icons", "output": "../lib1/assets/icons" }
        ],

But I get the following error:

An asset cannot be written to a location outside of the output path.

Is there a built-in way of managing library asset's copy on every build?

UPDATE 06/05/2018

I opened an issue with Angular CLI regarding this but have not heard back yet. Issue #11701

Moonshiner answered 30/5, 2018 at 3:22 Comment(0)
M
17

Currently, I still have not found an official built-in way to do so.

I opened an Angular CLI issue and hopefully will get the CLI team response.

In the meantime my workaround is using command line tools:

In package.json I added:

"scripts": {
    ...
    "build": "ng build lib1 --prod && scss-bundle -c scss-bundle.config.json && cp -R projects/lib1/src/assets/ dist/lib1/assets/",
}

To copy the SASS files I use scss-bundle with config file scss-bundle.config.json that contains:

{
  "entry": "./projects/lib1/src/assets/style/main.scss",
  "dest": "./dist/lib1/assets/style/styles.scss"
}

This will build the SASS files of the project into 1 file and copy it into the dist folder. My SASS file structure is something like:

-- projects/lib1/src/assets/
                  -- style
                     -- main.scss
                     -- partials
                        -- _variables.scss
                        -- _styles.scss
                        __ _rtl.scss

So as you can see I don't want to ship all the raw sass, just one final file. Of course, you can also compile it into a .css file instead.

To make sure all other assets are copied, I use a simple Mac OS/Linux command cp -R or rsync.

And, of course, instead of running ng build I run npm run build.

Hope this helps, and if you have a better solution please let me know.

Moonshiner answered 5/6, 2018 at 18:28 Comment(2)
but this wouldn't work if you are developing multiple libraries, as you always build lib1 – Damali
@Damali when developing multiple I always build them separately anyway – Moonshiner
P
5

Looks like in the future this can all be automated with the CLI, however, for now, there are a few solutions out there. Some involved writing a post install script, which is a pretty good one if you have a ton of stuff going on. One involves manually moving them over, but that's just way too open for error IMO. I also have seen a couple of npm packages that you can install that seem to extend what ng-packagr does (ng-packagr builds your libraries and webpack builds your apps).

Some of these are good and some are bad, IMO, I won't go into what I think about them individually, instead I'll just share what I do.

I work on an enterprise Angular Application and I'm extracting our features and functionality into Libraries so we can start code sharing with mini-apps in the near future. Because of our processes and build protocols, we are already not using the ng cli directly to build our projects, instead we are using npm scripts.

If you are already familiar with NPM scripts, skip down below, otherwise, this quick note will be super helpful.

With the Angular CLI, you run something like these...

ng build myProject --configuration=production to run a prod build of your project.

ng build myLibrary to run a prod build of your library and you probably run ng build myLibrary --watch=true to run a dev build of your library and watch for changes while you are working on the library.

For me, while I work on the project, I use the ng CLI, just as you do and run ng build myLibrary --watch=true

This works perfectly. I have an assets folder for my libraries that contain assets and I store them in myProject/src/lib/assets. All is well. My assets aren't in my dist/myLibrary though...but hey it's cool, because during development, when I use a relative path in my image tags <img> webpack is pulling from my library project, not my dist folder anyhow. So how do I solve this with an NPM script? Well, when you read the next line, you're going to smack your forehead and go "crap, I knew that"...here goes...

IF SKIPPING, START HERE...

"myLibrary:prod": "ng build myLibrary && mkdir dist/myLibrary/lib/assets && cp -R projects/myLibrary/src/lib/assets/ dist/myLibrary/lib/assets/ && npm run msgAssetsCopied",

Yup, that's it, just some basic bash :)

I'll break it down for those that are new to the command line though.

myLibrary:prod This is the name of the npm script aka npm run script. You call it in the command line with npm run myLibrary:prod and let your terminal do the rest. The "rest" are simply commands your computer terminal can read, interpret and execute accordingly.

ng build myLibrary this triggers the standard ng CLI build command, thus building your library

&& this says "hey after you do the thing to the left of me [&&], do the thing to the right of me"

mkdir dist/myLibrary/lib/assets this one is a basic bash command that creates a directory that you will be copying your assets over to. mkdir makes a directory and the path designates where and what I want that directory to be. If I was in the folder I wanted a directory made in, say "foo", I would do mkdir bar which would give me foo/bar, if I was in "foo" and wanted the directory "tacos" to be in the "bar" directory, I would do mkdir bar/tacos and it would create "tacos" in the "bar" directory like foo/bar/tacos.

I prefer to make a folder and move assets from a -> b, rather than trying to cp a folder and it's assets.

cp -R projects/myLibrary/src/lib/assets/ dist/myLibrary/lib/assets/ this one is broken up into 4 parts for those new to bash.

  1. cp is "copy"
  2. -R is for "recursive", meaning, take all files and folders and keep them the same structure once copied.
  3. projects/myLibrary/src/lib/assets/ this is where my assets are that I want to move over to that new directory I made with the previous mkdir command earlier.
  4. dist/myLibrary/lib/assets/ is the destination for the copy command.

So with this one you have...

  1. Command cp -R
  2. Target path/to/assets/in/library/project/
  3. Destination path/to/desired/directory/in/build

The last step is npm run msgAssetsCopied which is just another npm script in my package.json that tells the person banging on the keyboard that the assets have been copied. I usually have messages throughout my scripts with emojis to make it easier for a dev to see exactly where a script is at any point in time by identifying emojis on the screen. For example...

"msgAssetsCopied": "echo 'πŸ“ Assets Copied to Library Dist Folder πŸ“'",

So πŸ“ Assets Copied to Library Dist Folder πŸ“ gets printed in terminal when we are done.

Still new? No worries, now I'll show you where they go in your package.json.

{
  "name": "YourWorkspace",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    }
}

As you can see, this is the top of your package.json file. You can add as many scripts as you need, below we'll drop in the one I just shared...

{
  "name": "YourWorkspace",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "myLibrary:prod": "ng build myLibrary && mkdir dist/myLibrary/lib/assets && cp 
    -R projects/myLibrary/src/lib/assets/ dist/myLibrary/lib/assets/ && npm run 
    msgAssetsCopied",
    }
}

Boom goes the dynamite!

Pforzheim answered 30/7, 2019 at 19:30 Comment(4)
Appreciate the detailed response! Got all that working but now not sure how to reference said asset (a JSON file in my case) from the library code. I've tried this.http.get('assets/data.json') and this.http.get('dist/myLib/assets/data.json') Any ideas? TIA – Exfoliate
I think "how" you want to bring that JSON code in, depends on "where" you are trying to bring it in from as well as your intentions. What is this 'data.json'? What is its purpose? Is it a static object that you want to make available to your app that is consuming the library? Is the data even best as json for your purpose? Maybe you could share a bit more, and hopefully I'll be able to help you more. – Pforzheim
But --watch? How can I watch files when they changed? – Victoriavictorian
@Victoriavictorian yes you can use the --watch flat to watch for file changes in your library during development. ...which will trigger a partial rebuild of the library, which in turn will trigger a re-serve if you are serving your app locally. – Pforzheim
C
4

When you asked the question it wasn't able to include the css files on the dist folder unless you create a js script that handles the logic.

Now it is possible because ng-packagr released a new version that allows you to include the assets along with the build files:

You can copy these assets by using the assets option.

{
  "ngPackage": {
    "assets": [
      "CHANGELOG.md",
      "./styles/**/*.theme.scss"
    ],
    "lib": {
      ...
    }
  }
}

More information here: https://github.com/ng-packagr/ng-packagr/blob/master/docs/copy-assets.md

Colyer answered 30/3, 2020 at 12:46 Comment(1)
Note this "assets" section should be included in ng-package.json file of your Angular Library – Telium
Y
3

Now you can do this with latest version of packager (version 9.0.0-rc.3). Please take look at example provided here

Yachting answered 10/12, 2019 at 11:7 Comment(0)
S
-1

I simply manually copied the images I needed to /dist/assets and as URL in the library I gave /assets/image.jpg

Spectroscopy answered 19/11, 2020 at 15:19 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.