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

1.0.1 -> 1.1.3 broke DI -- Uncaught Error: Can't resolve all parameters for providesGuard(?) #6788

Closed
btgoodwin opened this issue Jun 23, 2017 · 7 comments
Assignees
Labels
needs: repro steps We cannot reproduce the issue with the information given

Comments

@btgoodwin
Copy link

btgoodwin commented Jun 23, 2017

Bug Report or Feature Request (mark with an x)

- [x] bug report -> please search issues before submitting
- [ ] feature request

Versions.

macOS Sierra:

{ 'test-app': '0.0.0',
  npm: '3.10.10',
  ares: '1.10.1-DEV',
  http_parser: '2.7.0',
  icu: '58.2',
  modules: '48',
  node: '6.10.3',
  openssl: '1.0.2k',
  uv: '1.9.1',
  v8: '5.1.281.101',
  zlib: '1.2.11' }

Repro steps.

  1. Upgrade from version 1.0.1 to 1.1.3
  2. npm run start (which is ng serve)

The log given by the failure.

compiler.es5.js:1689 Uncaught Error: Can't resolve all parameters for providesGuard: (?).
    at syntaxError (http://localhost:4200/vendor.bundle.js:18052:34)
    at CompileMetadataResolver.webpackJsonp.../../../compiler/@angular/compiler.es5.js.CompileMetadataResolver._getDependenciesMetadata (http://localhost:4200/vendor.bundle.js:32119:35)
    at CompileMetadataResolver.webpackJsonp.../../../compiler/@angular/compiler.es5.js.CompileMetadataResolver._getFactoryMetadata (http://localhost:4200/vendor.bundle.js:31999:51)
    at CompileMetadataResolver.webpackJsonp.../../../compiler/@angular/compiler.es5.js.CompileMetadataResolver.getProviderMetadata (http://localhost:4200/vendor.bundle.js:32272:43)
    at http://localhost:4200/vendor.bundle.js:32193:49
    at Array.forEach (native)
    at CompileMetadataResolver.webpackJsonp.../../../compiler/@angular/compiler.es5.js.CompileMetadataResolver._getProvidersMetadata (http://localhost:4200/vendor.bundle.js:32153:19)
    at http://localhost:4200/vendor.bundle.js:31728:63
    at Array.forEach (native)
    at CompileMetadataResolver.webpackJsonp.../../../compiler/@angular/compiler.es5.js.CompileMetadataResolver.getNgModuleMetadata (http://localhost:4200/vendor.bundle.js:31719:49)

Desired functionality.

Would like to know how to correct the ModuleWithProviders guard so that it's compatible going forward if this change in behavior between 1.0.1 and 1.1.3 is permanent. If it's a bug in 1.1.3, then a fix would be good. The work-around right now is to use 1.0.1.

Mention any other details that might be useful.

Here is a sanitized version of the module that reproduces the issue:

import {
    NgModule,
    ModuleWithProviders,
    Inject,
    InjectionToken,
    SkipSelf,
    Optional
} from '@angular/core';

// REST Service and Configuration
// IRestConfig is: 
//    export interface IRestConfig { port?: number; host?: string; apiUrl?: string; }
// REST_CONFIG is: 
//    export const REST_CONFIG = new InjectionToken<IRestConfig>('REST_CONFIG');
export { IRestConfig } from './shared/rest.config';
import { IRestConfig } from './shared/rest.config';
import { REST_CONFIG } from './shared/rest.service';

// Guard for providing RP
export const GUARD = new InjectionToken<void>('GUARD');

@NgModule({ })
export class TestModule {
    static forRoot(config?: IRestConfig): ModuleWithProviders {
        return {
            ngModule:  TestModule,
            providers: [
                { provide: REST_CONFIG, useValue: config },
                {
                    provide: GUARD,
                    useFactory: providesGuard,
                    deps: [
                        [ REST_CONFIG, new Optional(), new SkipSelf() ]
                    ]
                }
            ]
        }
    }
    constructor(@Inject(GUARD) guard: any) { /** */ }
}

export function providesGuard(config: IRestConfig): any {
    if (config) {
        throw new Error('TestModule.forRoot() called twice.');
    }
    return 'guarded';
}

At the app module, one would add TestModule.forRoot({ port: 1234 }), for example, into the imports.

Changing from 1.0.1 to 1.1.3 of @angular/cli results in the error (at the browser) that the argument to providesGuard couldn't be resolved.

EDIT: Was missing code ticks ahead of the last block of code.

@filipesilva
Copy link
Contributor

I think this isn't as much a CLI build problem as it is a runtime Angular problem. It sounds like a very similar situation to #2034.

You're using an interface as a DI token, which is an approach that has some problems. See #2034 (comment) for a more detailed answer about this.

Can you tell me if making IRestConfig a class instead of an interface helps?

@filipesilva filipesilva self-assigned this Jun 27, 2017
@filipesilva filipesilva added the needs: more info Reporter must clarify the issue label Jun 27, 2017
@btgoodwin
Copy link
Author

btgoodwin commented Jun 27, 2017 via email

@filipesilva
Copy link
Contributor

I can take a look if you can make a simple repro for this issue.

@filipesilva filipesilva added needs: repro steps We cannot reproduce the issue with the information given and removed needs: more info Reporter must clarify the issue labels Jun 28, 2017
@btgoodwin
Copy link
Author

I'm pretty deep into the rabbit hole at this point.

The interface being defined in a different file and imported was ultimately the main issue even though using a class did nothing to rectify the problem (the other module is where the related service was configured, so this was originally an attempt at avoiding a circular dependency on the interface). I’ve since flipped that paradigm to the more standard approach of having the service be part of the ModuleWithProviders forRoot routine. That resolved the error and left me with a different one about [object Object] was imported by the app module, etc. at runtime.

This error is related to my development library having been linked into the app’s node_modules (using npm link), and then, for whatever reason, this version of server pulling in node_modules/my_library -> /my_workspace/my_library/dist/../node_modules.

One suggested "fix" is to delete the linked-in library's node_modules, which means having a development environment where for any npm linked library, it would require npm install && npm run build && rm -rf node_modules every time I wanted to make a change to that library. An actual more tenable fix modifying the CLI’s generated tsconfig.app.json to include:

    “baseUrl”: “”,
    “paths”: {
        “@angular/*”: [ “../node_modules/@angular/*” ]
    }

However, now at runtime my library's use of rxjs/add/operator/map gets flagged as not a function. For example, in the library:

import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

import { Item } from './item';

export class SomeClass {
    constructor(public http: Http) {}

    someFunction(): Observable<Item> {
        return this.http
            .get(url)
            .map(res => new Item().deserialize(res.json());
    }
}

The above would work fine before v1.1.3. In fact re-installing @angular/cli@1.0.1 all these modifications all work as expected. So as stated in my opening issue, the repro steps are install 1.1.3, watch lots of things break when running ng serve. Reinstall 1.0.1, watch everything work as expected.

So while I agree that it's not necessarily a direct problem of running the transpiler, it certainly has something to do with these two versions of the CLI package. And even though I've now mitigated the original issue, it's pretty irrelevant because everything I fix to make this library to work with this version of the CLI-generated and served app just uncovers one more thing that is now broken, specifically for this version of the CLI package.

@filipesilva
Copy link
Contributor

Mind you, when I asked for repro steps I meant repro steps for me, not for you... when you say repro steps are install 1.1.3, watch lots of things break when running ng serve. that kinda doesn't help me at all, because I don't have your projects nor your setup to see things break.

There's some guidance on how to work with linked libraries here: https://github.com/angular/angular-cli/wiki/stories-linked-library. I'm not sure what your workflow is but you do need to build your library and not rely on the CLI to compile it for you. It maybe might work if you let the CLI compile it, but there's loads of scenarios where that won't work.

I think the PR where something changed that allowed your scenario to work was #6276, where we fixed some some more serious problems and then provided the linked lib guidance I linked above.

@btgoodwin
Copy link
Author

I apologize for my earlier terseness. I've used my above steps to generate a library and application from @angular/cli, which is now at 1.4.2 and also having the same problem. You can find the generated app and library here.

Your repro steps should be:

  1. Run npm install in each
  2. In the testlib/dist run npm link
  3. In testapp, run npm link testlib
  4. In testapp, npm run start.

The resulting directory structure looks like this:

testapp/node_modules/testlib --> intermediate path --> ../../testlib/dist
testlib/node_modules
testlib/dist

Note that node_modules and dist are at the same level in the testlib.

When you open a browser to the server, Angular will exception out with:

compiler.es5.js:1694 Uncaught Error: Unexpected value '[object Object]' imported by the module 'AppModule'. Please add a @NgModule annotation.
    at syntaxError (compiler.es5.js:1694)
    at compiler.es5.js:15542
    at Array.forEach (<anonymous>)
    at CompileMetadataResolver.webpackJsonp.../../../compiler/@angular/compiler.es5.js.CompileMetadataResolver.getNgModuleMetadata (compiler.es5.js:15525)
    at JitCompiler.webpackJsonp.../../../compiler/@angular/compiler.es5.js.JitCompiler._loadModules (compiler.es5.js:26958)
    at JitCompiler.webpackJsonp.../../../compiler/@angular/compiler.es5.js.JitCompiler._compileModuleAndComponents (compiler.es5.js:26931)
    at JitCompiler.webpackJsonp.../../../compiler/@angular/compiler.es5.js.JitCompiler.compileModuleAsync (compiler.es5.js:26860)
    at PlatformRef_.webpackJsonp.../../../core/@angular/core.es5.js.PlatformRef_._bootstrapModuleWithZone (core.es5.js:4536)
    at PlatformRef_.webpackJsonp.../../../core/@angular/core.es5.js.PlatformRef_.bootstrapModule (core.es5.js:4522)
    at Object.../../../../../src/main.ts (main.ts:11)

The only solution, I've found, to work in a development environment with @angular/cli >1.0.1 is to modify the gulpfile of the library so that it is generated outside of the repository's top level directory (mimicking the deployment behavior).

  1. Comment out this line, because the current version of Gulp will complain about deleting files outside the top-level directory. This of course has the downside that you can't automate a "clean" prior to rebuilding your development library.
  2. Update this line with ../testlib-dist so that the library builds outside its own project directory, away from the project's node_modules.
  3. Repeat the 4 steps from above setup from above (no need to npm install at the testapp).

The resulting directory structure looks like this:

testapp/node_modules/testlib --> intermediate path --> ../../testlib-dist
testlib/node_modules
testlib-dist/

This prevents the server from traversing down into testapp/node_modules/testlib/dist/../node_modules when looking for installed node modules.

From my perspective, all I "did" was upgrade from 1.0.1 to something newer of @angular/cli, and that's when the directory traversal behavior changed, breaking the previously-working method of linking a development library's distribution directory into an application. And the break occurred because the server (in my opinion, erroneously) starts looking in subdirectories of the app's node_modules for more node_modules paths.

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
needs: repro steps We cannot reproduce the issue with the information given
Projects
None yet
Development

No branches or pull requests

2 participants