Angular: Wrong imports written by tree-shakable providers under Bazel

Created on 15 May 2018  ·  1Comment  ·  Source: angular/angular

Tree-shakable providers trigger the Angular compiler to introduce import statements in user's code, which we didn't do before. It turns out we have places where we conflate module paths on disk with module names (earlier versions of the compiler didn't have this distinction)
So we end up writing imports like import {} from 'wksp/packages/path/to/thing' that should have been import {} from '@pkg_name/path/to/thing'

As far as we know, this issue only affects Bazel users who compile with ngc and use tree-shakable providers. So far we have only seen this issue internally in Angular.

This was found as part of #20030

To repro, add a tree-shakable provider e.g. paste into packages/common/src/common_module.ts

import {Injectable, PLATFORM_ID} from '@angular/core';
import {DOCUMENT, isPlatformBrowser} from '@angular/common';

export function viewportScrollerFactory(platformId: string, document: any) {
  return isPlatformBrowser(platformId) ? null: null;
}

@Injectable(
    {providedIn: 'root', useFactory: viewportScrollerFactory, deps: [PLATFORM_ID, DOCUMENT]})
export abstract class ViewportScroller {}

then build the npm package

bazel build //packages/common:npm_package

And look for the string angular/packages in the output:

$ export bin=$(bazel info bazel-bin)
$ find $bin -type f -exec fgrep -l angular/packages {} \; | grep -v ngsummary | grep -v ngfactory

we remove ngsummary files because these always have had paths on disk as module identifiers.
ngfactory files are not shipped with the package so these are probably harmless too.

Interestingly, we find the bad import in the ES2015 output:

bin/packages/common/src/common_module.closure.js

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes} checked by tsc
 */
/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import { NgModule } from '@angular/core';
import { COMMON_DIRECTIVES } from './directives/index';
import { DEPRECATED_PLURAL_FN, NgLocaleLocalization, NgLocalization, getPluralCase } from './i18n/localization';
import { COMMON_DEPRECATED_I18N_PIPES } from './pipes/deprecated/index';
import { COMMON_PIPES } from './pipes/index';
import { Injectable, PLATFORM_ID } from '@angular/core';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
import * as i0 from "@angular/core";
import * as i1 from "@angular/core/src/application_tokens";
import * as i2 from "angular/packages/common/src/dom_tokens";  // <-------------------------

but not in the ES5 output

bin/packages/common/src/common_module.js

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define("@angular/common/src/common_module", ["require", "exports", "tslib", "@angular/core", "@angular/common/src/directives/index", "@angular/common/src/i18n/localization", "@angular/common/src/pipes/deprecated/index", "@angular/common/src/pipes/index", "@angular/core", "@angular/common", "@angular/core", "@angular/core/src/application_tokens", "@angular/common/src/dom_tokens"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var tslib_1 = require("tslib");
    var core_1 = require("@angular/core");
    var index_1 = require("@angular/common/src/directives/index");
    var localization_1 = require("@angular/common/src/i18n/localization");
    var index_2 = require("@angular/common/src/pipes/deprecated/index");
    var index_3 = require("@angular/common/src/pipes/index");
    var core_2 = require("@angular/core");
    var common_1 = require("@angular/common");
    var i0 = require("@angular/core");
    var i1 = require("@angular/core/src/application_tokens");
    var i2 = require("@angular/common/src/dom_tokens");
P3 bazel low bufix

Most helpful comment

We're running into this issue now too.

We call the compiler API's internally in a mono-repo, this results for us internally also in import paths like :

import * as i1 from "@our-group/our-package/src/some-service";

This happens for us in ES5 output though, but I assume the root cause is similar.
Workarounds for us is defining the factory ourselves in the @Injectable. That stops angular from generating the factory and thus generating the faulty import.

Not sure if I need to provide more info / reproducible:

  • it would be very difficult for me to do so since this is a mix of proprietary code.
  • I believe you know the cause already but currently, understandably, don't prioritise it based on the assumption it only happens internally at Angular.

>All comments

We're running into this issue now too.

We call the compiler API's internally in a mono-repo, this results for us internally also in import paths like :

import * as i1 from "@our-group/our-package/src/some-service";

This happens for us in ES5 output though, but I assume the root cause is similar.
Workarounds for us is defining the factory ourselves in the @Injectable. That stops angular from generating the factory and thus generating the faulty import.

Not sure if I need to provide more info / reproducible:

  • it would be very difficult for me to do so since this is a mix of proprietary code.
  • I believe you know the cause already but currently, understandably, don't prioritise it based on the assumption it only happens internally at Angular.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

escardin picture escardin  ·  3Comments

wfalkwallace picture wfalkwallace  ·  3Comments

robwormald picture robwormald  ·  3Comments

alippai picture alippai  ·  3Comments

pkozlowski-opensource picture pkozlowski-opensource  ·  3Comments