cli: Cannot add glyphicons to bootstrap resources

I’m submitting a bug report

  • Library Version: 0.17.0

Please tell us about your environment:

  • Operating System: Windows 10

  • Node Version: 6.3.0

  • NPM Version: 3.10.5

  • Browser: all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView

  • Language: all

Current behavior: Adding bootstrap’s glyphicons file(s) causes the CLI to fail while bundling.

This configuration for bootstrap fails:

{
  "name": "bootstrap",
  "path": "../node_modules/bootstrap/dist",
  "main": "js/bootstrap.min",
  "deps": [
    "jquery"
  ],
  "exports": "$",
  "resources": [
    "css/bootstrap.css",
    "fonts/glyphicons-halflings-regular.woff2"
  ]
}

Here is the error messaged displayed:

Tracing bootstrap/css/bootstrap...
Tracing bootstrap/fonts/glyphicons-halflings-regular...
{ uid: 9,
  name: 'writeBundles',
  branch: false,
  error:
   { Error: ENOENT: no such file or directory, open 'C:\Users\Ashley\temp\demo\node_modules\bootstrap\dist\fonts\glyphicons-halflings-regular.js'
       at Error (native)
       at Object.fs.openSync (fs.js:640:18)
       at Object.fs.readFileSync (fs.js:508:33)
       at Object.exports.readFileSync (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\file-system.js:38:13)
       at amodroTrace.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\bundled-source.js:83:31)
       at Object.context.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:176:18)
       at Object.context.load (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:357:30)
       at Object.Module.load (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:832:29)
       at Object.Module.fetch (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:822:66)
       at Object.Module.check (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:854:30)
     errno: -4058,
     code: 'ENOENT',
     syscall: 'open',
     path: 'C:\\Users\\Ashley\\temp\\demo\\node_modules\\bootstrap\\dist\\fonts\\glyphicons-halflings-regular.js' },
  duration: [ 3, 162240291 ],
  time: 1469800371889 }
{ uid: 1,
  name: '<series>',
  branch: true,
  error:
   { Error: ENOENT: no such file or directory, open 'C:\Users\Ashley\temp\demo\node_modules\bootstrap\dist\fonts\glyphicons-halflings-regular.js'
       at Error (native)
       at Object.fs.openSync (fs.js:640:18)
       at Object.fs.readFileSync (fs.js:508:33)
       at Object.exports.readFileSync (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\file-system.js:38:13)
       at amodroTrace.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\bundled-source.js:83:31)
       at Object.context.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:176:18)
       at Object.context.load (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:357:30)
       at Object.Module.load (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:832:29)
       at Object.Module.fetch (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:822:66)
       at Object.Module.check (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:854:30)
     errno: -4058,
     code: 'ENOENT',
     syscall: 'open',
     path: 'C:\\Users\\Ashley\\temp\\demo\\node_modules\\bootstrap\\dist\\fonts\\glyphicons-halflings-regular.js' },
  duration: [ 3, 343545006 ],
  time: 1469800371900 }
{ uid: 0,
  name: '<series>',
  branch: true,
  error:
   { Error: ENOENT: no such file or directory, open 'C:\Users\Ashley\temp\demo\node_modules\bootstrap\dist\fonts\glyphicons-halflings-regular.js'
       at Error (native)
       at Object.fs.openSync (fs.js:640:18)
       at Object.fs.readFileSync (fs.js:508:33)
       at Object.exports.readFileSync (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\file-system.js:38:13)
       at amodroTrace.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\bundled-source.js:83:31)
       at Object.context.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:176:18)
       at Object.context.load (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:357:30)
       at Object.Module.load (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:832:29)
       at Object.Module.fetch (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:822:66)
       at Object.Module.check (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:854:30)
     errno: -4058,
     code: 'ENOENT',
     syscall: 'open',
     path: 'C:\\Users\\Ashley\\temp\\demo\\node_modules\\bootstrap\\dist\\fonts\\glyphicons-halflings-regular.js' },
  duration: [ 3, 345732955 ],
  time: 1469800371901 }
{ Error: ENOENT: no such file or directory, open 'C:\Users\Ashley\temp\demo\node_modules\bootstrap\dist\fonts\glyphicons-halflings-regular.js'
    at Error (native)
    at Object.fs.openSync (fs.js:640:18)
    at Object.fs.readFileSync (fs.js:508:33)
    at Object.exports.readFileSync (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\file-system.js:38:13)
    at amodroTrace.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\bundled-source.js:83:31)
    at Object.context.fileRead (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:176:18)
    at Object.context.load (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:357:30)
    at Object.Module.load (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:832:29)
    at Object.Module.fetch (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:822:66)
    at Object.Module.check (eval at <anonymous> (C:\Users\Ashley\temp\demo\node_modules\aurelia-cli\lib\build\amodro-trace\lib\loader\Loader.js:14:9), <anonymous>:854:30)
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: 'C:\\Users\\Ashley\\temp\\demo\\node_modules\\bootstrap\\dist\\fonts\\glyphicons-halflings-regular.js' }

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 6
  • Comments: 37 (13 by maintainers)

Most upvoted comments

Here’s my workaround, it adds one function and one import to the buid task. It copies the other resources (which are not .js , .css and .html) to a folder with the same name as the dependency name. IMO - this is what aurelia-cli should do by default, no runtime support necessary.

Edit: added @patriktornros fixes, this is in TypeScript, if you are translating this to JS - be careful with the closure scope inside the .pipe( rename(

aurelia_project/tasks/build.ts

import * as gulp from 'gulp';
import transpile from './transpile';
import processMarkup from './process-markup';
import processCSS from './process-css';
import {build} from 'aurelia-cli';
import * as project from '../aurelia.json';
import * as rename from 'gulp-rename';

export default gulp.series(
  copyOtherResources,
  readProjectConfiguration,
  gulp.parallel(
    transpile,
    processMarkup,
    processCSS
  ),
  writeBundles
);

function copyOtherResources(done){
  let stream,
    bundle = project.build.bundles.find(function(bundle){return bundle.name==="vendor-bundle.js";});
  for( let i=0; i<bundle.dependencies.length; i++ ){
    let dependency = bundle.dependencies[i],
      collectedResources = [];
    if( dependency.path && dependency.resources ){
      let path = dependency.path.replace("../","./");
      for( let n=0; n<dependency.resources.length; n++ ){
        let resource = dependency.resources[n],
          ext = resource.substr(resource.lastIndexOf('.') + 1);
        if( ext!=='js' && ext!='css' && ext!='html' && ext!=='less' && ext!='scss'){
          collectedResources.push(path+resource)
          dependency.resources.splice(n, 1);
          n--;
        }
      }
      if( collectedResources.length ){
        let s = gulp.src( collectedResources, {passthrough: true, base: path} )
          .pipe( rename( function(path){path.dirname = dependency.name +'/'+ path.dirname;return path;} ));
        stream = stream?stream.pipe(s):s;
      }
    }
  }
  if( stream )
    stream.pipe(gulp.dest('.'));
  done();
}

function readProjectConfiguration() {
  return build.src(project);
}

function writeBundles() {
  return build.dest();
}

The required dependencies are specified in aurelia.json like this:

"prepend": ["node_modules/jquery/dist/jquery.js"],
"dependencies": [
      {
        "name":"lib/semantic",
        "path":"../semantic/dist/",
        "main":"semantic",
        "exports": "$",
        "resources":["semantic.css","themes/default/assets/fonts/icons.woff2"]
      }
]

, I use prepend as a workaround for issue #257

The above code would have been much more simple and easy to implement if the aurelia-cli build code did not rename the extension for non-html/css/js resources to .js Also it would be nice to have a destination property for the dependency, where the files will be copied, the code above is using the name for this purpose.

I also find this one very crucial. I’m quite new to all this Javascript building and bundling and often struggeling even with simple tasks like including a css framework. This can get really annoying - especially because the actual programming with aurelia is so easy and you are just held up from doing the funny stuff 😉

To make life easier I adapted @geleto’s approach so I can continue building my app a bit more conveniently. Although this is definitely just a very hacky workaround, others might benefit from it as well, so I added it to the end of this post. It adds the copied resource folders to .gitignore in order to avoid committing them unintendedly. I also rewrote the copying part, because sometimes things went wrong an folders where nested incorrectly.

For the future there should be a dedicated location for these additional resources that could be excluded from the repository and deployed to the server, but I have not been able to figure that out, because the modules expect their resources to be located in a specific path. The artistry for people more versatile than me would be to come up with a build-in but flexible system to manage resources of all types.

My aurelia.json (note this is also Typescript):

import * as gulp from 'gulp';
import transpile from './transpile';
import processMarkup from './process-markup';
import processCSS from './process-css';
import { build } from 'aurelia-cli';
import * as project from '../aurelia.json';
import * as fs from 'fs';
import * as readline from 'readline';
import * as os from 'os';

export default gulp.series(
  copyAdditionalResources,
  readProjectConfiguration,
  gulp.parallel(
    transpile,
    processMarkup,
    processCSS
  ),
  writeBundles
);

function copyAdditionalResources(done){
  readGitIgnore();
  done();
}

function readGitIgnore() {
  let lineReader = readline.createInterface({
    input: fs.createReadStream('./.gitignore')
  });
  let gitignore: Array<String> = [];

  lineReader.on('line', (line) => {
    gitignore.push(line);
  });

  lineReader.on('close', (err) => {
    copyFiles(gitignore);
  })
}

function copyFiles(gitignore: Array<String>) {
  let stream,
    bundle = project.build.bundles.find(function (bundle) {
      return bundle.name === "vendor-bundle.js";
    });

  // iterate over all dependencies specified in aurelia.json
  for (let i = 0; i < bundle.dependencies.length; i++) {
    let dependency = bundle.dependencies[i];
    let collectedResources = [];
    if (dependency.path && dependency.resources) {
      // run over resources array of each dependency
      for (let n = 0; n < dependency.resources.length; n++) {
        let resource = dependency.resources[n];
        let ext = resource.substr(resource.lastIndexOf('.') + 1);
        // only copy resources that are not managed by aurelia-cli
        if (ext !== 'js' && ext != 'css' && ext != 'html' && ext !== 'less' && ext != 'scss') {
          collectedResources.push(resource);
          dependency.resources.splice(n, 1);
          n--;
        }
      }
      if (collectedResources.length) {
        if (gitignore.indexOf(dependency.name)< 0) {
          console.log('Adding line to .gitignore:', dependency.name);
          fs.appendFile('./.gitignore', os.EOL + dependency.name, (err) => { if (err) { console.log(err) } });
        }

        for (let m = 0; m < collectedResources.length; m++) {
          let currentResource = collectedResources[m];
          if (currentResource.charAt(0) != '/') {
            currentResource = '/' + currentResource;
          }
          let path = dependency.path.replace("../", "./");
          let sourceFile = path + currentResource;
          let destPath = './' + dependency.name + currentResource.slice(0, currentResource.lastIndexOf('/'));
          console.log('Copying resource', sourceFile, 'to', destPath);
          // copy files
          gulp.src(sourceFile)
            .pipe(gulp.dest(destPath));
        }
      }
    }
  }
}


function readProjectConfiguration() {
  return build.src(project);
}

function writeBundles() {
  return build.dest();
}

+1 for getting this addressed.

Solution example: https://github.com/aurelia-ui-toolkits/aurelia-materialize-bridge/issues/284#issuecomment-250112393 (and link to SO).

Download files:

Copy files:

  • css to “src\css\font-glyphicons.css”
  • fonts to “src\fonts*”

Add new prepare-font-glyphicons.js in tasks folder with code:

import gulp from 'gulp';
import merge from 'merge-stream';
import changedInPlace from 'gulp-changed-in-place';
import project from '../aurelia.json';

export default function prepareFontGlyphicons() {

  let source = 'src';

  let taskCss = gulp.src(`${source}\\css\\font-glyphicons.css`)
    .pipe(changedInPlace({firstPass:true}))
    .pipe(gulp.dest(`${project.platform.output}\\css`));

  let taskFonts = gulp.src(`${source}\\fonts\\*`)
    .pipe(changedInPlace({firstPass:true}))
    .pipe(gulp.dest(`${project.platform.output}\\fonts`));

  return merge(taskCss, taskFonts);
}

Edit \tasks\build.js and add following lines:

import prepareFontGlyphicons from './prepare-font-glyphicons'; // <<<<<<<<<<

export default gulp.series(
  readProjectConfiguration,
  gulp.parallel(
    transpile,
    processMarkup,
    processCSS,
    // my tasks
    prepareFontGlyphicons // <<<<<<<<<<
  ),
  writeBundles
);

Add in index.html line: <link rel="stylesheet" href="scripts/css/font-glyphicons.css">

This issue can now be solved by adding a copy instruction in the aurelia.json.

aurelia.json - valid if the project was created by aurelia-cli 0.25.0 or greater

"bundles": [ ... ], 
 "copyFiles": {
    "node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2": "bootstrap/fonts"
    "node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.woff": "bootstrap/fonts",
    "node_modules/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf": "bootstrap/fonts"
  }

If the project was created by an older CLI version, you will have to create the copy task inside the tasks folder -> https://github.com/aurelia/cli/blob/master/lib/resources/tasks/copy-files.js. After that, call the copy task in the build.js/ts task -> https://github.com/aurelia/cli/blob/master/lib/resources/tasks/build.js#L15

@EisenbergEffect I think you can close this issue.

Thanks for the workaround @MannikJ Here is the ES6 version of MannikJ’s fix.

import gulp from 'gulp';
import transpile from './transpile';
import processMarkup from './process-markup';
import processCSS from './process-css';
import { build } from 'aurelia-cli';
import project from '../aurelia.json';
import fs from 'fs';
import readline from 'readline';
import os from 'os';

export default gulp.series(
  copyAdditionalResources,
  readProjectConfiguration,
  gulp.parallel(
    transpile,
    processMarkup,
    processCSS
  ),
  writeBundles
);

function copyAdditionalResources(done){
  readGitIgnore();
  done();
}

function readGitIgnore() {
  let lineReader = readline.createInterface({
    input: fs.createReadStream('./.gitignore')
  });
  let gitignore = [];

  lineReader.on('line', (line) => {
    gitignore.push(line);
  });

  lineReader.on('close', (err) => {
    copyFiles(gitignore);
  })
}

function copyFiles(gitignore) {
  let stream,
    bundle = project.build.bundles.find(function (bundle) {
      return bundle.name === "vendor-bundle.js";
    });

  // iterate over all dependencies specified in aurelia.json
  for (let i = 0; i < bundle.dependencies.length; i++) {
    let dependency = bundle.dependencies[i];
    let collectedResources = [];
    if (dependency.path && dependency.resources) {
      // run over resources array of each dependency
      for (let n = 0; n < dependency.resources.length; n++) {
        let resource = dependency.resources[n];
        let ext = resource.substr(resource.lastIndexOf('.') + 1);
        // only copy resources that are not managed by aurelia-cli
        if (ext !== 'js' && ext != 'css' && ext != 'html' && ext !== 'less' && ext != 'scss') {
          collectedResources.push(resource);
          dependency.resources.splice(n, 1);
          n--;
        }
      }
      if (collectedResources.length) {
        if (gitignore.indexOf(dependency.name)< 0) {
          console.log('Adding line to .gitignore:', dependency.name);
          fs.appendFile('./.gitignore', os.EOL + dependency.name, (err) => { if (err) { console.log(err) } });
        }

        for (let m = 0; m < collectedResources.length; m++) {
          let currentResource = collectedResources[m];
          if (currentResource.charAt(0) != '/') {
            currentResource = '/' + currentResource;
          }
          let path = dependency.path.replace("../", "./");
          let sourceFile = path + currentResource;
          let destPath = './' + dependency.name + currentResource.slice(0, currentResource.lastIndexOf('/'));
          console.log('Copying resource', sourceFile, 'to', destPath);
          // copy files
          gulp.src(sourceFile)
            .pipe(gulp.dest(destPath));
        }
      }
    }
  }
}


function readProjectConfiguration() {
  return build.src(project);
}

function writeBundles() {
  return build.dest();
}

This code runs successfully with the fowling config in aurlia.json

...
{
    "name": "font-awesome",
    "main":"",
    "path": "../node_modules/font-awesome",
    "resources": [
        "css/font-awesome.css",
        "/fonts/fontawesome-webfont.woff2",
        "/fonts/FontAwesome.otf",
        "/fonts/fontawesome-webfont.eot",
        "/fonts/fontawesome-webfont.svg",
        "/fonts/fontawesome-webfont.ttf"
    ]
}
...

http://stackoverflow.com/questions/39271458/how-can-i-add-font-awesome-to-my-aurelia-project-using-npm/41236851#41236851

The best way to handle CSS frameworks is usually to do the same thing you’ve always done before. Just add a link tag to the page. That’s the way they are designed to be used and sometimes you need them loaded before the app anyway.

or just copy the fonts folder to ‘/bootstrap/fonts’, that’s where bootstrap is looking for them. you can create a task to automate that and add it to the build task.