angular/angular-cli

Feature Request: Copy assets outside of src folder (e.g. node_modules)

Closed this issue ยท 31 comments

So this is a feature request for ability to copy assets from different folders of the project, e.g. node_modules etc. Currently it doesn't seem to be supported even if you'll specify this:

angular-cli.json

    "assets": [
      "assets",
      "../logo.png",
      "../node_modules/third-party/cool.svg"
    ],

It's actually common use case, when you need to copy assets from third-party modules from node_modules

Below you can see additional information about cli and steps to reproduce:

OS?

Windows 7, 8 or 10. Linux (which distribution). Mac OSX (Yosemite? El Capitan?)
macOS Sierra 10.12.1

Versions.

Please run ng --version. If there's nothing outputted, please run in a Terminal: node --version and paste the result here:

angular-cli: 1.0.0-beta.21
node: 7.2.1
os: darwin x64

Repro steps.

Was this an app that wasn't created using the CLI? What change did you do on your code? etc.
To reproduce this issue just try to copy file one level up of src folder or explicitly from node_modules

Stack trace

  1. If you'll specify asset as
   "assets": [
      "assets",
      "../node_modules/bootstrap-theme/svg/sprite-symbol.svg"
    ]

you won't see any stack trace, all good, but file won't be copied

  1. If you'll specify asset as
   "assets": [
      "assets",
      "node_modules/bootstrap-theme/svg/sprite-symbol.svg"
    ]
ENOENT: no such file or directory, stat '/Users/serhiisol/Development/test-app/src/node_modules/bootstrap-theme/svg/sprite-symbol.svg'
Error: ENOENT: no such file or directory, stat  #'/Users/serhiisol/Development/test-app/src/node_modules/bootstrap-theme/svg/sprite-symbol.svg'
    at Object.fs.statSync (fs.js:906:18)

Mention any other details that might be useful.

What if you want to include assets from node_modules, which seems like a fairly common use case

We also need this to copy a file from node_modules to output folder.

Same situation here. It would be awesome if angular-cli could merge several sources into joined the public foulders eg.:

"assets":[
"source1/css",
"source1/js",
"source2/css",
"source2/js"
]

output is merged in public folders /css and /js.

It also would be really handy do specify different targets to avoid naming conflicts eg.

assets: [
  { "source":"../node_modules/awsomse/style.css", "to":"css/awesome.css"},
  { "source":"../node_modules/other/style.css", "to":"css/other.css"}
]

There was something similar to @manuelfink's suggestion added for scripts/styles in #3402.

Something similar could be added for assets.

@filipesilva is it available in beta.24 ? if so I can test

@serhiisol it doesn't exist for assets, just for scripts/styles.

@filipesilva right, ok, I can try to help with it

@serhiisol I'd prefer to take this one myself since it requires a few changes throughout parts of the app that I already had to do for other PRs. I might end up consolidating that mechanism.

What's the latest on this? This would be an amazing feature.

Any update on this? It's really unfortunate that you manually have to copy the files from npm deps into your own assets directory. Especially given that there is no way to add any custom scripts into the build that could do this automatically.

@mcgraphix I agree that it's unfortunate that we can't currently configure external assets in angular.cli.json, but you can easily add a custom script to package.json to copy assets. I have this one added to copy the svg file from Octicons:

"copy:assets": "cp node_modules/octicons/build/sprite.octicons.svg src/assets",

(Use a module like eg. copyfiles if you want cross-platform copy.)

If you launch your project with npm start, you might want to append npm run copy:assets && in front of whatever is there from before.

I currently only use it for that one file, but you could do the same for all of your external assets until a better solution is implemented.

Bnaya commented

Looks like you can use explicit file-loader calls!

// this will include static files from budgetkey-ng2-components to our dist
const t = '';
console.log(require('file-loader?name=assets/[path]/[name].[ext]!budgetkey-ng2-components/assets/' + t));

I'm still hacking on it

I just wanted to thank @filipesilva for getting this feature in. Thanks man!

@jeffaxial glad you're finding it useful!

Thanks, @filipesilva!

If I understand the documentation right, I have to configure something like

{ "glob": "**/*", "input": "../node_modules/octicons/build/svg", "output": ["./assets", "../src/assets/"] }

for my own use-case if I want to have the Octicons svg files available both during development and in the production build. Is that right?

@fleskesvor that sounds mostly right, except output shouldn't be an array. It should be the location where you want them to be on a build.

@filipesilva Yes, you're absolutely right, of course. The reason why that appeared to be working must have been that I had already built my project using the correct syntax. On a new project with angular-cli up to date (v. 1.0.0-rc.1), this configuration works:

{ "glob": "**/*", "input": "../node_modules/octicons/build/svg", "output": "./assets/" }

Both during development and an AoT build.

I tried that option '{ "glob": "**/*", "input": "../../foo", "output": "_artifacts/foo"' then ran "ng build" and it doesn't do anything.

@fletchsod-developer It's working for me in version 1.0.0-rc.1. Which version are you on, and are you 100% sure of that input path?

@filipesilva I am using @angular/cli: 1.0.4
and trying like
"assets": [
{ "glob": "/*", "input": "../node_modules/styles/lib/modern/assets/images/", "output": "./assets/" },
{ "glob": "
/", "input": "../node_modules/styles/lib/modern/assets/images/", "output": "../src/assets/" },
{ "glob": "**/
", "input": "../node_modules/styles/lib/modern/assets/fonts/", "output": "./assets/" },
{ "glob": "**/*", "input": "../node_modules/styles/lib/modern/assets/fonts/", "output": "../src/assets/" }
]

But it does not copy anything.

The idea of this feature is great, but it seems that this solution doesn't work for the development environment at all. I would like to use this feature to include CSS from a node_modules directory, but if I'm unable to access the files while developing the app this doesn't seem to be the way to solve that problem. So am I missing something here or is this feature just for show?

Honestly, it's a little bit rude to assume they've spent time on a feature that only looks like it works.

I'm using it locally while developing my app as well in a production environment, and it works great.

Here is how I am using it:
I have a folder with font files in it I want to get from a node_module directory to my assets folder.
Input folder: node_modules/DIRECTORY/fonts
Output folder: assets/fonts

In .angular-cli.json:
"assets": [ "assets", { "glob": "**/*", "input": "../node_modules/DIRECTORY/fonts", "output": "./assets/fonts" } ]

Do note that you need to restart ng serve after adding the line. All I can say is that I have this working on several projects, and it works just as advertised.

Yep, as noted by me just a few comments up, I also have it working both in development and production.

But, I am facing the problem.
I have few external stylesheets which I am pulling from ../node_modules/dir/stylesheet . I am also using stylePreprocessorOptions to include few paths for pre processing otherwise it fails here itself.
These styles use some fonts and images which are available under ../node_modules/dir/stylesheets/fonts and ../node_modules/dir/stylesheets/images . The failure shows it is looking for assets under src/assets .

Error is like the following:
Module not found: Error: Can't resolve './assets/images/loading-icon.svg' in 'project-path/src' . It is repeated for each image/fonts referenced in stylesheet.

I provided the option using glob to copy assets but it does not copy it. It seems styles processing happens before copy and it tries to reference the fonts and images so it just fails and could not copy (Just a guess).

@filipesilva Work like a charm! Thanks for putting in the work. I almost pull in Gulp to copy a few files from node_modules.

@rajramo61 for stylesheets, if you use SASS/LESS, you can use the import statement without having to copy them over. Also, can you post your "apps" section in .angular-cli.json for further troubleshooting?

@hieunt88
Currently, my work around is to run a copy script to copy fonts or images from node styles folder to src/assets to mitigate this issue as this does not work for me.

Here is the app section.
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
{ "glob": "/*", "input": "../node_modules/styleg/lib/modern/assets/fonts/", "output": "../src/assets/fonts" },
{ "glob": "
/*", "input": "../node_modules/style/lib/modern/assets/images/", "output": "../src/assets/images" }
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.scss"
],
"stylePreprocessorOptions": {
"includePaths": [
"../node_modules",
"../node_modules/bourbon/app/assets/stylesheets",
"../node_modules/bourbon-neat/app/assets/stylesheets"
]
},
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}

style.scss has
@import "~style/modern"; // this is importing styles from node module.

This modern is again combination of multiple scss files which are pre-build and I need to use them.
It breaks mostly, as in one of the scss file has a variable
$font-path: 'assets/fonts/';

This variable is utilized in many places for the path for fonts. Like
@at-root {
@font-face {
font-family: "demo-icons";
src: url("#{$font-path}demo-icons.svg");
url("#{$font-path}demo-icons.woff") format("woff"),
font-weight: normal;
font-style: normal;
}
}
I see errors like following for all the files referred from font or image directories:
ERROR in ./src/assets/fonts/demo-icons.svg
Module build failed: Error: ENOENT: no such file or directory, open '//src/assets/fonts/demo-icons.svg'

I tried to override this variable $font-path but could not make it work.

@rajramo61 this is my script to copy external fonts. glob is different and will match all sub file/folder paths. This should work.

{ "glob": "**/*", "input": "../node_modules/font-awesome/fonts", "output": "./assets/fonts/" }

For $font-path, I ran into the same issue. Angular CLI would try to resolve asset paths, in your case ./src/assets/fonts/demo-icons.svg, during SCSS compiling process and fail if they don't exist. I have yet to find a way to change this behaviour. My work around is to copy the assets to the assets folder using another pre-build script e.g gulp.

Also, you need to drop src from your $font-path like so.

$font-path: 'assets/fonts/'

For SASS, I believe the problem is not with Angular CLI referencing external .scss. The build most likely broke because of the asset path issue above. Try the setting below.

"stylePreprocessorOptions": {
   "includePaths": [
     "../node_modules/path/to/modern/scss"
  ]
}

Then, in your styles.scss.

$font-path: 'assets/fonts/'
@import 'modern';

That doesn't seem to work with JSON files (to use with ngx-translate), I tried multiple ways and it doesn't do anything. This is what I tried so far

"assets": [
        "assets",
        "favicon.ico",
        { "glob": "**/*", "input": "../node_modules/angular-slickgrid/i18n", "output": "./assets/i18n/" },
        { "glob": "*", "input": "../node_modules/angular-slickgrid/i18n", "output": "./assets/i18n/" },
        { "glob": "*/", "input": "../node_modules/angular-slickgrid/i18n", "output": "./assets/i18n/" },
        { "glob": "**/*", "input": "../node_modules/angular-slickgrid/i18n", "output": "./assets/i18n" }
      ],

Would that work with JSON files or is that a restricted file type?

As @ghiscoding mentioned above, looks like it doesn't work with JSON files.
Angular 5.0.3
It will be pretty useful to allow copying of JSON files too, this way we can include different lists and other stuff from external libraries.

@ghiscoding Have you tried with:

{ "glob": "**/*.json", "input": "node_modules/angular-slickgrid/i18n", "output": "/assets/i18n/" }

This does not appear to work with ng serve in Angular 8:

"assets":[
    {"glob": "jquery/dist/jquery.js", "input": "node_modules", "output": "third_party"}
]

It will work fine with ng build, but specifically not with ng serve.

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.