Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to lookup source #359

Closed
chiel opened this issue Aug 18, 2016 · 11 comments
Closed

Unable to lookup source #359

chiel opened this issue Aug 18, 2016 · 11 comments
Labels

Comments

@chiel
Copy link

chiel commented Aug 18, 2016

Hi, I'm having an issue where nyc's html report doesn't properly compute/lookup the files' paths, resulting in the source not being viewable within the report.

Expected Behavior

nyc should determine the path correctly

Observed Behavior

nyc doesn't calculate the file path from a common base directory

Forensic Information

I first compile my source code with gulp-babel, in my package.json I have the following section

{
  "babel": {
    "presets": [
      "es2015",
      "react",
      "stage-1"
    ],
    "sourceMaps": "inline"
  }
}

This generates files which have the source maps embedded at the end of the file.

Next, I run my unit tests with AVA nyc ava. AVA has the following configuration:

{
  "ava": {
    "babel": "inherit"
  }
}

nyc has no additional configuration. Once that has completed I run nyc report --reporter=html, this then dumps the coverage report in coverage/. Everything looks great until I drill down into the source of any file, which gives me an error such as the following:

Unable to lookup source: /Users/chiel/dev/poki-cms/dist/app/actions/app/actions/gameThumbnail.js(ENOENT: no such file or directory, open '/Users/chiel/dev/poki-cms/dist/app/actions/app/actions/gameThumbnail.js')
Error: Unable to lookup source: /Users/chiel/dev/poki-cms/dist/app/actions/app/actions/gameThumbnail.js(ENOENT: no such file or directory, open '/Users/chiel/dev/poki-cms/dist/app/actions/app/actions/gameThumbnail.js')
    at Context.defaultSourceLookup [as sourceFinder] (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/context.js:15:15)
    at Context.getSource (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/context.js:74:17)
    at Object.annotateSourceCode (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-reports/lib/html/annotator.js:175:38)
    at HtmlReport.onDetail (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-reports/lib/html/index.js:217:39)
    at Visitor.(anonymous function) [as onDetail] (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/tree.js:34:30)
    at ReportNode.Node.visit (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/tree.js:123:17)
    at /Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/tree.js:116:23
    at Array.forEach (native)
    at visitChildren (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/tree.js:115:32)
    at ReportNode.Node.visit (/Users/chiel/dev/poki-cms/node_modules/nyc/node_modules/istanbul-lib-report/lib/tree.js:126:5)

As you can see, the /app/actions part of the path is duplicated in the url, so it does indeed end up with a file it cannot find. Is there any way to influence this somehow?

Operating System: OS X 10.11.4 (El Capitan)
Environment Information: https://gist.github.com/chielkunkels/49dd3a5ed82067d42baddd6ab514c89b

@chiel
Copy link
Author

chiel commented Aug 18, 2016

In the interest of full disclosure, here's the part of my gulp-babel task that does the magic:

gulp.src(src, { base: 'src' })
    .pipe(require('gulp-babel')())
    .on('error', function handleError(err) {
        gutil.log(`${c.cyan('babel')}: ${c.red('an error occured')}`);
        console.error(err.stack);
        hasError = true;
        this.emit('end');
    })
    .pipe(gulp.dest('dist'))
    .on('finish', () => {
        if (ev && !hasError) {
            gutil.log(`${c.cyan('babel')}: wrote ${c.blue(src.replace(/^src/, 'dist'))}`);
        }
    });

Maybe there's something wrong with doing { base: 'src' } which throws the sourcemap off or so?

@chiel
Copy link
Author

chiel commented Aug 18, 2016

Okay so, if I don't use gulp-babel, but instead use the babel cli (babel -d dist src), then run tests and generate coverage - the coverage report is correct, but the sourcemaps in my actual application no longer work.

@chiel
Copy link
Author

chiel commented Aug 18, 2016

I know also tried using gulp-sourcemaps instead of babel's sourceMap: 'inline' option. My task now looks like this:

const sourcemaps = require('gulp-sourcemaps');

let hasError = false;

return gulp.src(src, { base: 'src' })
    .pipe(sourcemaps.init())
    .pipe(require('gulp-babel')())
    .on('error', function handleError(err) {
        gutil.log(`${c.cyan('babel')}: ${c.red('an error occured')}`);
        console.error(err.stack);
        hasError = true;
        this.emit('end');
    })
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist'))
    .on('finish', () => {
        if (ev && !hasError) {
            gutil.log(`${c.cyan('babel')}: wrote ${c.blue(src.replace(/^src/, 'dist'))}`);
        }
    });

Then ran nyc ava && nyc report --reporter=html again. While this does change the output slightly, now I get the following error:

Unable to lookup source: /source/app/actions/gameThumbnail.js(ENOENT: no such file or directory, open '/source/app/actions/gameThumbnail.js')

So there's definitely something fishy going on here.

@bcoe
Copy link
Member

bcoe commented Aug 23, 2016

@chielkunkels hey, sorry that it took me a little while to file this bug; I was on a brief OSS vacation 🚶

Don't suppose you could point us at a snippet of code, or the underlying repo that's causing this bug?

@chiel
Copy link
Author

chiel commented Aug 23, 2016

@bcoe no problem! Hope you had a good one.

Here's the repository at least with my gulp tasks: https://github.com/PokiBV/gulp-tasks-poki

The repo that has the issue is for work so I can't really show you there, but I'll whip up a quick repo that reproduces the bug, I'll get back to you with that soon.

@chiel
Copy link
Author

chiel commented Aug 23, 2016

Alright here we are: https://github.com/chielkunkels/nyc-issue

So simply clone, npm install, gulp, npm test and npm run coverage, this will generate the coverage directory, if you open that and check out add.js, you'll see the error I described above.

Hope this helps, please do let me know if you have more information or if you want me to try certain stuff, I'm happy to help get to the bottom of this.

@gustavohenke
Copy link

I have been suffering of the same bug, however I'm compiling with TypeScript.
This has been holding me back on using sourcemaps in my app, therefore decreasing my coverage numbers 😢

@geekflyer
Copy link

same error here. My code is in TypeScript. I transpile via gulp-typescript to es6 and then via gulp-babel to es5. Sourcemaps are created using gulp-sourcemaps and inlined in dev mode.

@geekflyer
Copy link

geekflyer commented Nov 6, 2016

Hi guys, after hours (or was it days?) of try and error and looking at different workarounds (I even tried to use blanket.js haha) it surprisingly now kinda works for me. The html reporter shows me the coverage of the source ts files. I think the key point was setting the rootDir and outDir in tsconfig.json / gulp-typescript AND destPath in the write function of gulp.sourceMaps correctly. I mainly experimented with those settings because I wanted to get the file links in stacktraces in the webstorm mocha runner to work correctly, but as a side effect this seems to have fixed also the nyc configuration. Interestingly the whole thing works even though I'm not creating inline sourcemaps, which is adverse to the nyc documentation. Actually with my former setup with inline sourcemaps it didn't work.

I don't have much time to describe my setup in detail at the moment, but I can copy and paste my config files and give a very short explanation:

  • it's a node.js module written in typescript, it's being transpiled from ts to es6 via tsc and then from es6 to es5 via babel.
  • The directory structure looks like that:
/src/subdir/foo.ts
/test/subdir/foo.test.ts

For testing etc. I transpile everything to a dev directory. This is also where nyc and mocha are reading from.

/dev/src/subdir/foo.js
/dev/src/subdir/foo.js.map
/dev/test/subdir/foo.test.js
/dev/test/subdir/foo.test.js.map

package.json (the actual script task which works is named coverage. Ignore the others starting with __):

{
  "name": "hdbext-promise",
  "version": "0.0.2",
  "main": "lib/module.js",
  "types": "lib/module.d.ts",
  "scripts": {
    "test": "npm run build:dev && NODE_ENV=development DEBUG=hdbext-promise:* npm run mocha",
    "coverage": "npm run build:dev && node_modules/.bin/nyc --reporter html --report-dir testresults/coverage npm run mocha -- --reporter mochawesome --reporter-options reportDir=testresults/test",
    "mocha": "NODE_ENV=development node_modules/.bin/mocha --timeout 10000 --colors ./dev/test/**/*.js",
    "__coverage_old": "npm run build && node_modules/.bin/istanbul cover node_modules/.bin/_mocha --colors ./dev/test/**/*.js",
    "build": "NODE_ENV=production node scripts/build.js",
    "build:dev": "node scripts/build.js",
    "__build:watch": "node scripts/build.js --watch",
    "__coverage_new": "NODE_ENV=test ./node_modules/.bin/nyc --reporter=lcov --reporter=html ./node_modules/.bin/mocha ./dev/test/**/*.js"
  },
  "nyc": {
    "include": [
      "dev/src"
    ],
    "sourceMap": true
  },
  "author": "",
  "license": "ISC",
  "peerDependencies": {},
  "dependencies": {
    "@types/bluebird": "=3.0.35",
    "@types/is-plain-object": "=0.0.2",
    "bluebird": "3.4.6",
    "source-map-support": "=0.4.6"
  },
  "devDependencies": {
    "@types/chai": "=3.4.34",
    "@types/chai-as-promised": "=0.0.29",
    "@types/lodash": "=4.14.38",
    "@types/mocha": "=2.2.32",
    "@types/node": "=6.0.46",
    "@types/username": "=1.0.28",
    "babel-core": "6.18.2",
    "babel-polyfill": "=6.16.0",
    "babel-preset-es2015-node4": "=2.1.0",
    "chai": "3.5.0",
    "chai-as-promised": "6.0.0",
    "del": "=2.2.2",
    "gulp": "3.9.1",
    "gulp-babel": "6.1.2",
    "gulp-filter": "=4.0.0",
    "gulp-sourcemaps": "2.2.0",
    "gulp-typescript": "3.1.2",
    "lodash": "=4.16.6",
    "merge-stream": "=1.0.0",
    "minimist": "=1.2.0",
    "mocha": "=3.1.2",
    "mochawesome": "=1.5.3",
    "nyc": "=8.4.0",
    "stream-to-promise": "=2.2.0",
    "ts-babel-node": "=1.1.1",
    "ts-node": "=1.6.1",
    "typescript": "=2.0.7",
    "username": "=2.2.2"
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "outDir": "lib",
    "sourceMap": true,
    "strictNullChecks": true
  },
  "include": [
    "adhoc-typings/**/*",
    "src/**/*",
    "test/**/*"
  ]
}

build script build.js:
(I'm using lots of gulp plugins but not the gulp task system because I don't like how the tasks are sequenced and chained etc.. So I'm just turning all gulp vinyl streams into promises and use .then etc. to chain tasks together. Below is only the subset of the tasks in build.js which is relevant):

'use strict';

const gulp = require('gulp');
const ts = require('gulp-typescript');
const babel = require('gulp-babel');
const sourcemaps = require('gulp-sourcemaps');
const filter = require('gulp-filter');
const del = require('del');
const streamToPromise = require('stream-to-promise');
const merge = require('merge-stream');
const argv = require('minimist')(process.argv.slice(2));

const isProductionBuild = process.env.NODE_ENV === 'production';
const outDir = isProductionBuild ? 'lib' : 'dev';

const config = isProductionBuild ? {declaration: true, rootDir: 'src', outDir} : {rootDir: './', outDir};

const tsProject = ts.createProject('tsconfig.json', config);

argv.watch ? watch() : build();

function build() {

  const streamMerger = merge();

  let tsSources = tsProject.src();

  if (isProductionBuild) {
    del.sync(outDir);
    const noTestFilesFilter = filter(['**/*', '!test/**/*']);
    if (isProductionBuild) tsSources = tsSources.pipe(noTestFilesFilter);
  } else {
    const otherStuff = gulp.src('test/**/*.json', {base: './'})
      .pipe(gulp.dest(outDir));

    streamMerger.add(otherStuff);
  }

  const tsResult = tsSources
    .pipe(sourcemaps.init())
    .pipe(tsProject());

  const typings = tsResult.dts.pipe(gulp.dest(outDir));

  const jsFiles = tsResult.js
    .pipe(babel())
    .pipe(sourcemaps.write('./', {destPath: outDir}))
    .pipe(gulp.dest(outDir));

  streamMerger.add(typings);
  streamMerger.add(jsFiles);

  return streamToPromise(streamMerger);
}

.babelrc:

{
  "presets": ["es2015-node4"]
}

Let me know if this helps anyone or if you have specific questions.

@chenzhutian
Copy link

This bug may be triggered by powershell.
I also work on Typescript and I meet this bug will I use powershell to run npm run test.
However, when I use cmd to run npm run test, everything goes well.

@bcoe
Copy link
Member

bcoe commented May 14, 2017

@chielkunkels @gustavohenke Hey! I'm going around cleaning up some old issues -- sorry that this one fell by the wayside.

It looks like the underlying issue is that whatever if generating the ES6 source-maps in nyc-issue is not populating the field sourceRoot -- this makes it possible for nyc to map the source-map back to the un-transpiled code.

I think it might be worth opening an issue with gulp-babel; it might also (at this point) be worth exploring babel-plugin-istanbul (which wasn't an option when this issue was opened) I've found it works much better than relying on source-maps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants