question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Bug]: jest-preset-angular does not work well with monorepos with mixed hoisted dependencies

See original GitHub issue

Version

12.2.0

Steps to reproduce

Setup a monorepo with an angular project with several dependencies, some dependencies are hoisted to the root of the monorepo and others are not.

When you run jest with this preset, you will be getting errors like:

Unexpected value 'TranslateModule' imported by the module 'DynamicTestModule'. Please add an @NgModule annotation

TypeError: Cannot read properties of undefined (reading 'Tag')

Type AgGridModule does not have 'ɵmod' property


Some tests will pass and others will fail.

As it turns out, jest-preset-angular uses this code to call ngcc to compile the dependencies, but it only accounts for the node_modules directory where the ngcc was found in https://github.com/thymikee/jest-preset-angular/blob/main/src/utils/ngcc-jest-processor.ts#L17-L19

This has the unintended effect that depending on how the hoisting happened (moving the ngcc to the root node modules or the angular project node modules), some dependencies in the other node_modules directory would not get compiled.

Took a bit of digging through the code to identify this as the source of the problem.

I fixed it in my case by copying ngcc-jest-processor and modifying it to run the compiler on both node_modules directories.

It would be great if this issue could be solved directly in this project as well.

Expected behavior

I expect all the tests to pass

Actual behavior

Some tests pass, and others fail

Additional context

For my use case, I fixed it by copying the ngcc-jest-processor into my project and modifying it to call ngcc twice (one per node modules directory) and that resolved the issue for me.

/**
 * Mainly copied from https://github.com/angular/angular-cli/blob/master/packages/ngtools/webpack/src/ngcc_processor.ts
 * and adjusted to work with Jest
 */
// const { spawnSync } = require('child_process');
// const path = require('path');
import { spawnSync } from 'child_process';
import path from 'path';

const ANGULAR_COMPILER_CLI_PKG_NAME = `@angular${path.sep}compiler-cli`;
let ngccPath = '';

try {
  ngccPath = require.resolve('@angular/compiler-cli/ngcc/main-ngcc.js');
} catch {
  const compilerCliNgccPath = require.resolve('@angular/compiler-cli/ngcc');
  ngccPath = path.resolve(
    compilerCliNgccPath.substring(0, compilerCliNgccPath.lastIndexOf(path.sep)),
    'main-ngcc.js'
  );
}
function findNodeModulesDirectory() {
  return ngccPath.substring(0, ngccPath.indexOf(ANGULAR_COMPILER_CLI_PKG_NAME));
}

const nodeModuleDirPath = findNodeModulesDirectory();

const runNgccJestProcessorForNodeModule = (tsconfigPath, nodeModuleDir) => {
  if (nodeModuleDir) {
    process.stdout.write(`\nngcc-jest-processor..: running ngcc for ${nodeModuleDir}\n`);

    const ngccBaseArgs = [
      ngccPath,
      '--source' /** basePath */,
      nodeModuleDir,
      '--properties' /** propertiesToConsider */,
      /**
       * There are various properties: fesm2015, fesm5, es2015, esm2015, esm5, main, module, browser to choose from.
       * Normally, Jest requires `umd`. If running Jest in ESM mode, Jest will require both `umd` + `esm2015`.
       */
      ...['es2015', 'main'],
      '--first-only' /** compileAllFormats */,
      'false', // make sure that `ngcc` runs on subfolders as well
      '--async',
    ];
    if (tsconfigPath) {
      ngccBaseArgs.push(...['--tsconfig', tsconfigPath]);
    }
    // We spawn instead of using the API because:
    // - NGCC Async uses clustering which is problematic when used via the API which means
    // that we cannot setup multiple cluster masters with different options.
    // - We will not be able to have concurrent builds otherwise Ex: App-Shell,
    // as NGCC will create a lock file for both builds and it will cause builds to fails.
    const { status, error } = spawnSync(process.execPath, ngccBaseArgs, {
      stdio: ['inherit', process.stderr, process.stderr],
    });
    if (status !== 0) {
      const errorMessage = error?.message ?? '';

      throw new Error(
        `${errorMessage} NGCC failed ${errorMessage ? ', see above' : ''}.`
      );
    }
  } else {
    console.log(
      `Warning: Could not locate '@angular/compiler-cli' to run 'ngcc' automatically.` +
        `Please make sure you are running 'ngcc-jest-processor.js' from root level of your project.` +
        `'ngcc' must be run before running Jest`
    );
  }
};
export const runNgccJestProcessor = (
  tsconfigPath
) => {
  runNgccJestProcessorForNodeModule(tsconfigPath, path.resolve(__dirname, 'node_modules'));
  runNgccJestProcessorForNodeModule(tsconfigPath, path.resolve(__dirname, '../../node_modules'));
};

Environment

System:
    OS: macOS 12.2.1
    CPU: (8) arm64 Apple M1 Pro
  Binaries:
    Node: 16.15.1 - ~/.nvm/versions/node/v16.15.1/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 8.11.0 - ~/.nvm/versions/node/v16.15.1/bin/npm

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:6

github_iconTop GitHub Comments

1reaction
ahnpnlcommented, Aug 12, 2022

I get it now, one thing I want to clear up is: by default this preset doesn’t provide ngcc to run but you have to opt in, please check our preset https://github.com/thymikee/jest-preset-angular/blob/main/src/presets/index.ts

Our ngcc script is to stimulate what angular cli does, but if it doesn’t fit your need, you shouldn’t use this preset’s ngcc script but should use the one from Angular itself. I think that would help you to move forward. There is an ngcc bin file which is shipped with angular cli.

Please also take a look at our example https://github.com/thymikee/jest-preset-angular/tree/main/examples/example-app-yarn-workspace which might help.

0reactions
ahnpnlcommented, Aug 12, 2022

Regarding to fix the ngcc script, feel free to submit your PR 😃

Read more comments on GitHub >

github_iconTop Results From Across the Web

Troubleshooting | jest-preset-angular - GitHub Pages
Problems may arise if you're using custom builds (this preset is tailored for angular-cli as ... This issue is not related to Jest,...
Read more >
The Hoisting Madness in Monorepos - enrq.me
We're working on a monorepo basis: each package is self-contained with its dependencies set. yarn manages the shared stuff through the ...
Read more >
Monorepos, mindsets, and perspectives | by Damian Cyrus
No more duplicated dependencies with different versions (me: Lerna will fail installation if you use hoisting on different dependency versions.) ...
Read more >
Monorepo vs Polyrepo - Earthly Blog
The decision of whether to use a monorepo or a polyrepo structure for your source code can be a very emotional (maybe even...
Read more >
Angular + Jest: Can't resolve all parameters for AppComponent
The tests run just fine when using Jasmine + Karma. It seems like something went wrong with the dependency injection when using jest...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found