Compare commits
49 Commits
5.0.0-rc.3
...
5.0.0-rc.9
Author | SHA1 | Date | |
---|---|---|---|
f5b7f2b9a5 | |||
509f392ab0 | |||
cf5fce8d5e | |||
f1248b69e6 | |||
4498dddbe3 | |||
812786f44e | |||
de24d54517 | |||
c295aeeca2 | |||
a8add78fe1 | |||
e3a16ed02d | |||
fd37f3fbab | |||
85e95cc32b | |||
de71ba74bb | |||
a01c877534 | |||
2d508a3ef0 | |||
4285b6c3e3 | |||
5542517b9c | |||
fef3539608 | |||
f4d5729cb3 | |||
d343bf7885 | |||
9ce7f0e538 | |||
4a23df3909 | |||
14016c781f | |||
47caebfe86 | |||
5cfd9c6020 | |||
47bc6f105d | |||
40fa2593a9 | |||
680bcf7b8a | |||
ef08330341 | |||
6cc042e2ba | |||
9b26455740 | |||
18bce5987c | |||
f1108fea76 | |||
64b3e3e41a | |||
a82f863e24 | |||
bde57016c6 | |||
b16f4bce98 | |||
6bed189e37 | |||
4abacb58f1 | |||
04200150d5 | |||
fc0b1d5b61 | |||
8d45fefc31 | |||
5da96c75a2 | |||
90d1423fb4 | |||
910735d732 | |||
fc86352adf | |||
441e01c568 | |||
14380ff086 | |||
820bb7bd8c |
@ -29,7 +29,7 @@ jobs:
|
||||
- restore_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
- run: yarn install --freeze-lockfile --non-interactive
|
||||
- run: yarn install --frozen-lockfile --non-interactive
|
||||
- run: ./node_modules/.bin/gulp lint
|
||||
|
||||
build:
|
||||
|
@ -7,9 +7,12 @@
|
||||
#
|
||||
# alexeagle - Alex Eagle
|
||||
# alxhub - Alex Rickabaugh
|
||||
# brocco - Mike Brocchi
|
||||
# chuckjaz - Chuck Jazdzewski
|
||||
# filipesilva - Filipe Silva
|
||||
# Foxandxss - Jesús Rodríguez
|
||||
# gkalpak - George Kalpakas
|
||||
# hansl - Hans Larsen
|
||||
# IgorMinar - Igor Minar
|
||||
# jasonaden - Jason Aden
|
||||
# juleskremer - Jules Kremer
|
||||
@ -144,11 +147,23 @@ groups:
|
||||
- mhevery
|
||||
- IgorMinar #fallback
|
||||
|
||||
compiler-cli/ngtools:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/compiler-cli/src/ngtools*"
|
||||
users:
|
||||
- hansl
|
||||
- filipesilva #fallback
|
||||
- brocco #fallback
|
||||
|
||||
compiler-cli:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/compiler-cli/*"
|
||||
- "packages/bazel/*"
|
||||
include:
|
||||
- "packages/compiler-cli/*"
|
||||
- "packages/bazel/*"
|
||||
exclude:
|
||||
- "packages/compiler-cli/src/ngtools*"
|
||||
users:
|
||||
- alexeagle
|
||||
- chuckjaz
|
||||
|
@ -1,12 +1,10 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
# force trusty as Google Chrome addon is not supported on Precise
|
||||
dist: trusty
|
||||
node_js:
|
||||
- '6.9.5'
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
|
75
CHANGELOG.md
75
CHANGELOG.md
@ -1,3 +1,78 @@
|
||||
<a name="5.0.0-rc.9"></a>
|
||||
# [5.0.0-rc.9](https://github.com/angular/angular/compare/5.0.0-rc.8...5.0.0-rc.9) (2017-10-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** always use relative paths to refer to generated code ([fd37f3f](https://github.com/angular/angular/commit/fd37f3f)), closes [#20031](https://github.com/angular/angular/issues/20031)
|
||||
* **compiler:** re-export less symbols in `.ngfactory.ts` files ([#19884](https://github.com/angular/angular/issues/19884)) ([e3a16ed](https://github.com/angular/angular/commit/e3a16ed)), closes [#19883](https://github.com/angular/angular/issues/19883)
|
||||
* **compiler:** report errors properly in G3 in certain conditions ([#20041](https://github.com/angular/angular/issues/20041)) ([de24d54](https://github.com/angular/angular/commit/de24d54))
|
||||
* **core** add missing globals from each rollup configuration ([#20028](https://github.com/angular/angular/issues/20028)) ([812786f](https://github.com/angular/angular/commit/812786f))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.8"></a>
|
||||
# [5.0.0-rc.8](https://github.com/angular/angular/compare/5.0.0-rc.7...5.0.0-rc.8) (2017-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** avoid producing source mappings for host views ([#19965](https://github.com/angular/angular/issues/19965)) ([2d508a3](https://github.com/angular/angular/commit/2d508a3))
|
||||
* **platform-server:** add missing packages to the UMD global rollup config ([4285b6c](https://github.com/angular/angular/commit/4285b6c))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.7"></a>
|
||||
# [5.0.0-rc.7](https://github.com/angular/angular/compare/5.0.0-rc.6...5.0.0-rc.7) (2017-10-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** don’t store invalid state when using `listLazyRoutes` ([#19953](https://github.com/angular/angular/issues/19953)) ([4a23df3](https://github.com/angular/angular/commit/4a23df3))
|
||||
* **compiler:** make watch mode work on windows ([#19953](https://github.com/angular/angular/issues/19953)) ([f4d5729](https://github.com/angular/angular/commit/f4d5729)), closes [#19951](https://github.com/angular/angular/issues/19951)
|
||||
* **compiler:** recover from structural errors in watch mode ([#19953](https://github.com/angular/angular/issues/19953)) ([d343bf7](https://github.com/angular/angular/commit/d343bf7))
|
||||
* **compiler:** translate emit diagnostics with `noEmitOnError: true`. ([#19953](https://github.com/angular/angular/issues/19953)) ([9ce7f0e](https://github.com/angular/angular/commit/9ce7f0e)), closes [#19935](https://github.com/angular/angular/issues/19935)
|
||||
* **service-worker:** don't block initialization on registration ([#19936](https://github.com/angular/angular/issues/19936)) ([47caebf](https://github.com/angular/angular/commit/47caebf))
|
||||
* **service-worker:** fix improper call of Observable.merge ([#19962](https://github.com/angular/angular/issues/19962)) ([14016c7](https://github.com/angular/angular/commit/14016c7))
|
||||
* **service-worker:** listen for messages on the right event source ([#19954](https://github.com/angular/angular/issues/19954)) ([5cfd9c6](https://github.com/angular/angular/commit/5cfd9c6))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.6"></a>
|
||||
# [5.0.0-rc.6](https://github.com/angular/angular/compare/5.0.0-rc.5...5.0.0-rc.6) (2017-10-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** automatically set `emitDecoratorMetadata` when `"annotationsAs": "static fields”` ([#19927](https://github.com/angular/angular/issues/19927)) ([ef08330](https://github.com/angular/angular/commit/ef08330)), closes [#19916](https://github.com/angular/angular/issues/19916)
|
||||
* **compiler:** don’t type check templates with `skipTemplateCodegen` ([#19909](https://github.com/angular/angular/issues/19909)) ([18bce59](https://github.com/angular/angular/commit/18bce59))
|
||||
* **compiler-cli:** only use error collector when needed. ([#19912](https://github.com/angular/angular/issues/19912)) ([9b26455](https://github.com/angular/angular/commit/9b26455))
|
||||
* **compiler-cli:** produce correct paths for windows output ([#19915](https://github.com/angular/angular/issues/19915)) ([6cc042e](https://github.com/angular/angular/commit/6cc042e))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.5"></a>
|
||||
# [5.0.0-rc.5](https://github.com/angular/angular/compare/5.0.0-rc.4...5.0.0-rc.5) (2017-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** report all diagnostic error messages ([#19886](https://github.com/angular/angular/issues/19886)) ([a82f863](https://github.com/angular/angular/commit/a82f863))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.4"></a>
|
||||
# [5.0.0-rc.4](https://github.com/angular/angular/compare/5.0.0-rc.3...5.0.0-rc.4) (2017-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** don't console.error from the compile helper ([#19879](https://github.com/angular/angular/issues/19879)) ([5da96c7](https://github.com/angular/angular/commit/5da96c7))
|
||||
* **compiler:** correctly calculate the outDir if it repeats a parts of the `rootDir`. ([#19836](https://github.com/angular/angular/issues/19836)) ([fc0b1d5](https://github.com/angular/angular/commit/fc0b1d5)), closes [#19718](https://github.com/angular/angular/issues/19718)
|
||||
* **service-worker:** include versionedFiles in the manifest hashTable ([#19837](https://github.com/angular/angular/issues/19837)) ([90d1423](https://github.com/angular/angular/commit/90d1423))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.3"></a>
|
||||
# [5.0.0-rc.3](https://github.com/angular/angular/compare/5.0.0-rc.2...5.0.0-rc.3) (2017-10-18)
|
||||
|
||||
|
@ -220,6 +220,7 @@ The following is the list of supported scopes:
|
||||
* **platform-webworker**
|
||||
* **platform-webworker-dynamic**
|
||||
* **router**
|
||||
* **service-worker**
|
||||
* **upgrade**
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
@ -156,7 +156,7 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
|
||||
# Set up the Node.js scripts
|
||||
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
||||
WORKDIR $AIO_SCRIPTS_JS_DIR/
|
||||
RUN yarn install --production --freeze-lockfile
|
||||
RUN yarn install --production --frozen-lockfile
|
||||
|
||||
|
||||
# Set up health check
|
||||
|
@ -9,7 +9,7 @@ readonly defaultImageNameAndTag="aio-builds:latest"
|
||||
# (Necessary, because only `scripts-js/dist/` is copied to the docker image.)
|
||||
(
|
||||
cd "$SCRIPTS_JS_DIR"
|
||||
yarn install --freeze-lockfile --non-interactive
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
yarn build
|
||||
)
|
||||
|
||||
|
@ -7,6 +7,6 @@ source "`dirname $0`/_env.sh"
|
||||
# Test `scripts-js/`
|
||||
(
|
||||
cd "$SCRIPTS_JS_DIR"
|
||||
yarn install --freeze-lockfile --non-interactive
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
yarn test
|
||||
)
|
||||
|
@ -68,30 +68,33 @@ describe('site App', function() {
|
||||
|
||||
// TODO(https://github.com/angular/angular/issues/19785): Activate this again
|
||||
// once it is no more flaky.
|
||||
xdescribe('google analytics', () => {
|
||||
beforeEach(done => page.gaReady.then(done));
|
||||
|
||||
it('should call ga', done => {
|
||||
page.ga()
|
||||
.then(calls => {
|
||||
expect(calls.length).toBeGreaterThan(2, 'ga calls');
|
||||
done();
|
||||
});
|
||||
});
|
||||
describe('google analytics', () => {
|
||||
|
||||
it('should call ga with initial URL', done => {
|
||||
let path: string;
|
||||
|
||||
page.navigateTo('api');
|
||||
page.locationPath()
|
||||
.then(p => path = p)
|
||||
.then(() => page.ga().then(calls => {
|
||||
expect(calls.length).toBeGreaterThan(2, 'ga calls');
|
||||
expect(calls[1]).toEqual(['set', 'page', path]);
|
||||
// The last call (length-1) will be the `send` command
|
||||
// The second to last call (length-2) will be the command to `set` the page url
|
||||
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
// Todo: add test to confirm tracking URL when navigate.
|
||||
it('should call ga with new URL on navigation', done => {
|
||||
let path: string;
|
||||
page.getLink('features').click();
|
||||
page.locationPath()
|
||||
.then(p => path = p)
|
||||
.then(() => page.ga().then(calls => {
|
||||
// The last call (length-1) will be the `send` command
|
||||
// The second to last call (length-2) will be the command to `set` the page url
|
||||
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
|
@ -12,7 +12,6 @@ export class SitePage {
|
||||
.all(by.css('a'))
|
||||
.filter((a: ElementFinder) => a.getAttribute('href').then(href => githubRegex.test(href)))
|
||||
.first();
|
||||
gaReady: promise.Promise<any>;
|
||||
|
||||
static setWindowWidth(newWidth: number) {
|
||||
const win = browser.driver.manage().window();
|
||||
@ -25,11 +24,14 @@ export class SitePage {
|
||||
.first();
|
||||
}
|
||||
getLink(path) { return element(by.css(`a[href="${path}"]`)); }
|
||||
ga() { return browser.executeScript('return window["gaCalls"]') as promise.Promise<any[][]>; }
|
||||
ga() { return browser.executeScript('return window["ga"].q') as promise.Promise<any[][]>; }
|
||||
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
|
||||
|
||||
navigateTo(pageUrl = '') {
|
||||
return browser.get('/' + pageUrl).then(_ => this.replaceGa(_));
|
||||
return browser.get('/' + pageUrl)
|
||||
// We need to tell the index.html not to load the real analytics library
|
||||
// See the GA snippet in index.html
|
||||
.then(() => browser.driver.executeScript('sessionStorage.setItem("__e2e__", true);'));
|
||||
}
|
||||
|
||||
getDocViewerText() {
|
||||
@ -61,31 +63,4 @@ export class SitePage {
|
||||
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the ambient Google Analytics tracker with homebrew spy
|
||||
* don't send commands to GA during e2e testing!
|
||||
* @param _ - forward's anything passed in
|
||||
*/
|
||||
private replaceGa(_: any) {
|
||||
|
||||
this.gaReady = browser.driver.executeScript(() => {
|
||||
// Give ga() a "ready" callback:
|
||||
// https://developers.google.com/analytics/devguides/collection/analyticsjs/command-queue-reference
|
||||
window['ga'](() => {
|
||||
window['gaCalls'] = [];
|
||||
window['ga'] = function() { window['gaCalls'].push(arguments); };
|
||||
});
|
||||
|
||||
})
|
||||
.then(() => {
|
||||
// wait for GaService to start using window.ga after analytics lib loads.
|
||||
const d = promise.defer();
|
||||
setTimeout(() => d.fulfill(), 1000); // GaService.initializeDelay
|
||||
return d.promise;
|
||||
});
|
||||
|
||||
return _;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,17 @@
|
||||
"source": "**/!(*.*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"source": "/",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Link",
|
||||
"value": "</generated/navigation.json>;rel=preload;as=fetch,</generated/docs/index.json>;rel=preload;as=fetch"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug --ignore-packages @angular/service-worker",
|
||||
"aio-use-npm": "node tools/ng-packages-installer restore .",
|
||||
"aio-use-npm": "node tools/ng-packages-installer restore . && yarn upgrade @angular/cli@1.3.0",
|
||||
"aio-check-local": "node tools/ng-packages-installer check .",
|
||||
"ng": "yarn check-env && ng",
|
||||
"start": "yarn check-env && ng serve",
|
||||
@ -19,11 +19,11 @@
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn ~~update-webdriver",
|
||||
"e2e": "ng e2e --no-webdriver-update",
|
||||
"presetup": "yarn install --freeze-lockfile && yarn ~~check-env && yarn boilerplate:remove",
|
||||
"presetup": "yarn install --frozen-lockfile && yarn ~~check-env && yarn boilerplate:remove",
|
||||
"setup": "yarn aio-use-npm && yarn example-use-npm",
|
||||
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn generate-plunkers && yarn generate-zips && yarn docs",
|
||||
"presetup-local": "yarn presetup",
|
||||
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
||||
"setup-local": "yarn aio-use-local && yarn upgrade @angular/cli@1.5.0-rc.2 && yarn example-use-local",
|
||||
"postsetup-local": "yarn postsetup",
|
||||
"pretest-pwa-score-localhost": "yarn build",
|
||||
"test-pwa-score-localhost": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score http://localhost:4200 90\"",
|
||||
|
@ -19,6 +19,11 @@ const config = require('lighthouse/lighthouse-core/config/default.js');
|
||||
// Constants
|
||||
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
|
||||
|
||||
// Specify the path to Chrome on Travis
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
|
||||
}
|
||||
|
||||
// Run
|
||||
_main(process.argv.slice(2));
|
||||
|
||||
|
@ -45,6 +45,7 @@ import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { SearchBoxComponent } from './search/search-box/search-box.component';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
import { WindowToken, windowProvider } from 'app/shared/window';
|
||||
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
|
||||
@ -113,7 +114,8 @@ export const svgIconProviders = [
|
||||
ScrollSpyService,
|
||||
SearchService,
|
||||
svgIconProviders,
|
||||
TocService
|
||||
TocService,
|
||||
{ provide: WindowToken, useFactory: windowProvider },
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -1,130 +1,87 @@
|
||||
import { ReflectiveInjector } from '@angular/core';
|
||||
import { fakeAsync, tick } from '@angular/core/testing';
|
||||
|
||||
import { GaService } from 'app/shared/ga.service';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { WindowToken } from 'app/shared/window';
|
||||
|
||||
describe('GaService', () => {
|
||||
let gaSpy: jasmine.Spy;
|
||||
let gaService: GaService;
|
||||
let injector: ReflectiveInjector;
|
||||
|
||||
// filter for 'send' which communicates with server
|
||||
// returns the url of the 'send pageview'
|
||||
function gaSpySendCalls() {
|
||||
let lastUrl: string;
|
||||
return gaSpy.calls.all()
|
||||
.reduce((acc, c) => {
|
||||
const args = c.args;
|
||||
if (args[0] === 'set') {
|
||||
lastUrl = args[2];
|
||||
} else if (args[0] === 'send') {
|
||||
acc.push(lastUrl);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
let gaSpy: jasmine.Spy;
|
||||
let mockWindow: any;
|
||||
|
||||
beforeEach(() => {
|
||||
injector = ReflectiveInjector.resolveAndCreate([
|
||||
GaService,
|
||||
{ provide: Logger, useClass: TestLogger }
|
||||
]);
|
||||
gaSpy = jasmine.createSpy('ga');
|
||||
mockWindow = { ga: gaSpy };
|
||||
injector = ReflectiveInjector.resolveAndCreate([GaService, { provide: WindowToken, useFactory: () => mockWindow }]);
|
||||
gaService = injector.get(GaService);
|
||||
});
|
||||
|
||||
describe('with ambient GA', () => {
|
||||
let gaService: GaService;
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
this.winGa = window['ga']; // remember current GA tracker just in case
|
||||
|
||||
// Replace Google Analytics tracker with spy after calling "ga ready" callback
|
||||
window['ga'] = (fn: Function) => {
|
||||
window['ga'] = gaSpy = jasmine.createSpy('ga');
|
||||
fn();
|
||||
tick(GaService.initializeDelay); // see GaService#initializeGa
|
||||
};
|
||||
gaService = injector.get(GaService);
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
window['ga'] = this.winGa;
|
||||
});
|
||||
|
||||
it('should initialize ga with "create" when constructed', () => {
|
||||
const first = gaSpy.calls.first().args;
|
||||
expect(first[0]).toBe('create');
|
||||
});
|
||||
|
||||
describe('#locationChanged(url)', () => {
|
||||
it('should send page to url w/ leading slash', () => {
|
||||
gaService.locationChanged('testUrl');
|
||||
let args = gaSpy.calls.all()[1].args;
|
||||
expect(args).toEqual(['set', 'page', '/testUrl']);
|
||||
args = gaSpy.calls.all()[2].args;
|
||||
expect(args).toEqual(['send', 'pageview']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#sendPage(url)', () => {
|
||||
it('should set page to url w/ leading slash', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
const args = gaSpy.calls.all()[1].args;
|
||||
expect(args).toEqual(['set', 'page', '/testUrl']);
|
||||
});
|
||||
|
||||
it('should send "pageview" ', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
const args = gaSpy.calls.all()[2].args;
|
||||
expect(args).toEqual(['send', 'pageview']);
|
||||
});
|
||||
|
||||
it('should not send twice with same URL, back-to-back', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpySendCalls()).toEqual(['/testUrl']);
|
||||
});
|
||||
|
||||
it('should send twice with same URL, back-to-back, even when the hash changes', () => {
|
||||
// Therefore it is up to caller NOT to call it when hash changes if this is unwanted.
|
||||
// See LocationService and its specs
|
||||
gaService.sendPage('testUrl#one');
|
||||
gaService.sendPage('testUrl#two');
|
||||
expect(gaSpySendCalls()).toEqual([
|
||||
'/testUrl#one',
|
||||
'/testUrl#two'
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
it('should send same URL twice when other intervening URL', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
gaService.sendPage('testUrl2');
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpySendCalls()).toEqual([
|
||||
'/testUrl',
|
||||
'/testUrl2',
|
||||
'/testUrl'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should initialize ga with "create" when constructed', () => {
|
||||
const first = gaSpy.calls.first().args;
|
||||
expect(first[0]).toBe('create');
|
||||
});
|
||||
|
||||
describe('when no ambient GA', () => {
|
||||
let gaService: GaService;
|
||||
let logger: TestLogger;
|
||||
|
||||
it('should log with "create" when constructed', () => {
|
||||
gaService = injector.get(GaService);
|
||||
logger = injector.get(Logger);
|
||||
expect(logger.log.calls.count()).toBe(1, 'logger.log should be called');
|
||||
const first = logger.log.calls.first().args;
|
||||
expect(first[0]).toBe('ga:');
|
||||
expect(first[1][0]).toBe('create'); // first[1] is the array of args to ga()
|
||||
describe('#locationChanged(url)', () => {
|
||||
it('should send page to url w/ leading slash', () => {
|
||||
gaService.locationChanged('testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#sendPage(url)', () => {
|
||||
it('should set page to url w/ leading slash', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl');
|
||||
});
|
||||
|
||||
it('should send "pageview" ', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
});
|
||||
|
||||
it('should not send twice with same URL, back-to-back', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
gaSpy.calls.reset();
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should send again even if only the hash changes', () => {
|
||||
// Therefore it is up to caller NOT to call it when hash changes if this is unwanted.
|
||||
// See LocationService and its specs
|
||||
gaService.sendPage('testUrl#one');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl#one');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
gaSpy.calls.reset();
|
||||
gaService.sendPage('testUrl#two');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl#two');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
});
|
||||
|
||||
it('should send same URL twice when other intervening URL', () => {
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
gaSpy.calls.reset();
|
||||
gaService.sendPage('testUrl2');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl2');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
gaSpy.calls.reset();
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('set', 'page', '/testUrl');
|
||||
expect(gaSpy).toHaveBeenCalledWith('send', 'pageview');
|
||||
});
|
||||
});
|
||||
|
||||
it('should support replacing the `window.ga` function', () => {
|
||||
const gaSpy2 = jasmine.createSpy('new ga');
|
||||
mockWindow.ga = gaSpy2;
|
||||
gaSpy.calls.reset();
|
||||
|
||||
gaService.sendPage('testUrl');
|
||||
expect(gaSpy).not.toHaveBeenCalled();
|
||||
expect(gaSpy2).toHaveBeenCalledWith('set', 'page', '/testUrl');
|
||||
expect(gaSpy2).toHaveBeenCalledWith('send', 'pageview');
|
||||
});
|
||||
});
|
||||
|
||||
class TestLogger {
|
||||
log = jasmine.createSpy('log');
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
|
||||
import { environment } from '../../environments/environment';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { WindowToken } from 'app/shared/window';
|
||||
|
||||
@Injectable()
|
||||
/**
|
||||
@ -10,15 +10,10 @@ import { Logger } from 'app/shared/logger.service';
|
||||
* Associates data with a GA "property" from the environment (`gaId`).
|
||||
*/
|
||||
export class GaService {
|
||||
// ms to wait before acquiring window.ga after analytics library loads
|
||||
// empirically determined to allow time for e2e test setup
|
||||
static initializeDelay = 1000;
|
||||
|
||||
private previousUrl: string;
|
||||
private ga: (...rest: any[]) => void;
|
||||
|
||||
constructor(private logger: Logger) {
|
||||
this.initializeGa();
|
||||
constructor(@Inject(WindowToken) private window: Window) {
|
||||
this.ga('create', environment['gaId'] , 'auto');
|
||||
}
|
||||
|
||||
@ -34,27 +29,7 @@ export class GaService {
|
||||
this.ga('send', 'pageview');
|
||||
}
|
||||
|
||||
// These gyrations are necessary to make the service e2e testable
|
||||
// and to disable ga tracking during e2e tests.
|
||||
private initializeGa() {
|
||||
const ga = window['ga'];
|
||||
if (ga) {
|
||||
// Queue commands until GA analytics script has loaded.
|
||||
const gaQueue: any[][] = [];
|
||||
this.ga = (...rest: any[]) => { gaQueue.push(rest); };
|
||||
|
||||
// Then send queued commands to either real or e2e test ga();
|
||||
// after waiting to allow possible e2e test to replace global ga function
|
||||
ga(() => setTimeout(() => {
|
||||
// this.logger.log('GA fn:', window['ga'].toString());
|
||||
this.ga = window['ga'];
|
||||
gaQueue.forEach((command) => this.ga.apply(null, command));
|
||||
}, GaService.initializeDelay));
|
||||
|
||||
} else {
|
||||
// delegate `ga` calls to the logger if no ga installed
|
||||
this.ga = (...rest: any[]) => { this.logger.log('ga:', rest); };
|
||||
}
|
||||
ga(...args) {
|
||||
this.window['ga'](...args);
|
||||
}
|
||||
|
||||
}
|
||||
|
4
aio/src/app/shared/window.ts
Normal file
4
aio/src/app/shared/window.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const WindowToken = new InjectionToken('Window');
|
||||
export function windowProvider() { return window; }
|
@ -34,9 +34,13 @@
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
// Note this is a customised version of the GA tracking snippet to aid e2e testing
|
||||
// See the bit between /**/.../**/
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;/**/i.sessionStorage.__e2e__||/**/m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
<!-- End Google Analytics -->
|
||||
|
||||
<script>
|
||||
|
@ -28,7 +28,7 @@
|
||||
"angular-in-memory-web-api": "~0.4.6",
|
||||
"core-js": "^2.4.1",
|
||||
"express": "^4.14.1",
|
||||
"rxjs": "^5.1.0",
|
||||
"rxjs": "^5.5.0",
|
||||
"systemjs": "0.19.39",
|
||||
"web-animations-js": "^2.3.1",
|
||||
"zone.js": "^0.8.4"
|
||||
|
@ -20,7 +20,11 @@ exports.config = {
|
||||
|
||||
// Capabilities to be passed to the webdriver instance.
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
'browserName': 'chrome',
|
||||
// For Travis
|
||||
chromeOptions: {
|
||||
binary: process.env.CHROME_BIN
|
||||
}
|
||||
},
|
||||
|
||||
// Framework to use. Jasmine is recommended.
|
||||
|
@ -2,26 +2,26 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular-devkit/build-optimizer@~0.0.23":
|
||||
version "0.0.24"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.24.tgz#f361f34dd9f2a6b5ba14ab32327697419cc0ef7e"
|
||||
"@angular-devkit/build-optimizer@~0.0.28":
|
||||
version "0.0.31"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.0.31.tgz#511bc00e18c787a6cd4efe37b8cdb9f1bc6d4472"
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
source-map "^0.5.6"
|
||||
typescript "^2.3.3"
|
||||
webpack-sources "^1.0.1"
|
||||
|
||||
"@angular-devkit/core@0.0.16":
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.16.tgz#498efc22948e7e17fe7e826a67b5c23a60c5e873"
|
||||
"@angular-devkit/core@0.0.20":
|
||||
version "0.0.20"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.20.tgz#2ad36dd210fccd0e156d01c6499082ad4cd8c2af"
|
||||
dependencies:
|
||||
source-map "^0.5.6"
|
||||
|
||||
"@angular-devkit/schematics@~0.0.25":
|
||||
version "0.0.26"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.26.tgz#58b9b004dbe6bfd125674b883a63180a521f2d2d"
|
||||
"@angular-devkit/schematics@~0.0.34":
|
||||
version "0.0.34"
|
||||
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.34.tgz#c3ef61b0e49e585d9982f2828e9a67b3879a6b1b"
|
||||
dependencies:
|
||||
"@angular-devkit/core" "0.0.16"
|
||||
"@angular-devkit/core" "0.0.20"
|
||||
"@ngtools/json-schema" "^1.1.0"
|
||||
minimist "^1.2.0"
|
||||
rxjs "^5.4.2"
|
||||
@ -33,19 +33,19 @@
|
||||
tslib "^1.7.1"
|
||||
|
||||
"@angular/cli@^1.3.1":
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.4.5.tgz#b982e13799b19e13eb820c2e17ef2c62a3b10881"
|
||||
version "1.4.9"
|
||||
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-1.4.9.tgz#cd5cd7a8f3bf3a1c28dfcdc523a011f0fcebfb96"
|
||||
dependencies:
|
||||
"@angular-devkit/build-optimizer" "~0.0.23"
|
||||
"@angular-devkit/schematics" "~0.0.25"
|
||||
"@angular-devkit/build-optimizer" "~0.0.28"
|
||||
"@angular-devkit/schematics" "~0.0.34"
|
||||
"@ngtools/json-schema" "1.1.0"
|
||||
"@ngtools/webpack" "1.7.2"
|
||||
"@schematics/angular" "~0.0.38"
|
||||
"@ngtools/webpack" "1.7.4"
|
||||
"@schematics/angular" "~0.0.46"
|
||||
autoprefixer "^6.5.3"
|
||||
chalk "^2.0.1"
|
||||
circular-dependency-plugin "^3.0.0"
|
||||
common-tags "^1.3.1"
|
||||
copy-webpack-plugin "^4.0.1"
|
||||
copy-webpack-plugin "^4.1.1"
|
||||
core-object "^3.1.0"
|
||||
css-loader "^0.28.1"
|
||||
cssnano "^3.10.0"
|
||||
@ -53,7 +53,7 @@
|
||||
ember-cli-string-utils "^1.0.0"
|
||||
exports-loader "^0.6.3"
|
||||
extract-text-webpack-plugin "3.0.0"
|
||||
file-loader "^0.10.0"
|
||||
file-loader "^1.1.5"
|
||||
fs-extra "^4.0.0"
|
||||
glob "^7.0.3"
|
||||
html-webpack-plugin "^2.29.0"
|
||||
@ -82,12 +82,13 @@
|
||||
stylus "^0.54.5"
|
||||
stylus-loader "^3.0.1"
|
||||
typescript ">=2.0.0 <2.6.0"
|
||||
url-loader "^0.6.0"
|
||||
webpack "~3.6.0"
|
||||
url-loader "^0.6.2"
|
||||
webpack "~3.7.1"
|
||||
webpack-concat-plugin "1.4.0"
|
||||
webpack-dev-middleware "~1.12.0"
|
||||
webpack-dev-server "~2.7.1"
|
||||
webpack-merge "^4.1.0"
|
||||
webpack-sources "^1.0.0"
|
||||
zone.js "^0.8.14"
|
||||
optionalDependencies:
|
||||
node-sass "^4.3.0"
|
||||
@ -172,18 +173,20 @@
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/json-schema/-/json-schema-1.1.0.tgz#c3a0c544d62392acc2813a42c8a0dc6f58f86922"
|
||||
|
||||
"@ngtools/webpack@1.7.2", "@ngtools/webpack@^1.2.11":
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.7.2.tgz#3fc4de01786dcc2f50d8cbaaa117311e56799977"
|
||||
"@ngtools/webpack@1.7.4", "@ngtools/webpack@^1.2.11":
|
||||
version "1.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.7.4.tgz#5015c47ebd339045dd89a1bef0497f4524d2c8ed"
|
||||
dependencies:
|
||||
enhanced-resolve "^3.1.0"
|
||||
loader-utils "^1.0.2"
|
||||
magic-string "^0.22.3"
|
||||
source-map "^0.5.6"
|
||||
|
||||
"@schematics/angular@~0.0.38":
|
||||
version "0.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.0.39.tgz#6dc2b461e95e4f4756a48693f2f13a771f6d6cb5"
|
||||
"@schematics/angular@~0.0.46":
|
||||
version "0.0.48"
|
||||
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.0.48.tgz#6fa1bfb2676ba34c243811d2083173821d3cf07c"
|
||||
dependencies:
|
||||
"@angular-devkit/core" "0.0.20"
|
||||
|
||||
"@types/angular-animate@^1.5.5":
|
||||
version "1.5.8"
|
||||
@ -222,25 +225,33 @@
|
||||
"@types/angular" "*"
|
||||
|
||||
"@types/angular@*", "@types/angular@^1.5.16":
|
||||
version "1.6.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.32.tgz#fc791aad038227d9413eb5e552993e1076f8a509"
|
||||
version "1.6.36"
|
||||
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.36.tgz#15e73d632274b5655a391470844863548c7755f4"
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.16.7.tgz#455fc23fd0ddaaeda6cd6cbb653558276e5920fa"
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/express-serve-static-core@*":
|
||||
version "4.0.53"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.53.tgz#1723a35d1447f2c55e13c8721eab3448e42f4d82"
|
||||
version "4.0.55"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.55.tgz#f53868838a955f98b380819ec9134f5df7d9482f"
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/express@^4.0.35":
|
||||
version "4.0.37"
|
||||
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.37.tgz#625ac3765169676e01897ca47011c26375784971"
|
||||
"@types/express@*", "@types/express@^4.0.35":
|
||||
version "4.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.39.tgz#1441f21d52b33be8d4fa8a865c15a6a91cd0fa09"
|
||||
dependencies:
|
||||
"@types/body-parser" "*"
|
||||
"@types/express-serve-static-core" "*"
|
||||
"@types/serve-static" "*"
|
||||
|
||||
"@types/jasmine@*":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.6.0.tgz#997b41a27752b4850af2683bc4a8d8222c25bd02"
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.6.2.tgz#6e6d4cb183cd55c7a1ad6270bced10fdd5367a3c"
|
||||
|
||||
"@types/jasmine@~2.5.53":
|
||||
version "2.5.54"
|
||||
@ -257,12 +268,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
|
||||
|
||||
"@types/node@*":
|
||||
version "8.0.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.32.tgz#869a716538b6eec65ab3893f183d557be3cda206"
|
||||
version "8.0.47"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.47.tgz#968e596f91acd59069054558a00708c445ca30c2"
|
||||
|
||||
"@types/node@^6.0.45", "@types/node@^6.0.46":
|
||||
version "6.0.88"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.88.tgz#f618f11a944f6a18d92b5c472028728a3e3d4b66"
|
||||
version "6.0.90"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.90.tgz#0ed74833fa1b73dcdb9409dcb1c97ec0a8b13b02"
|
||||
|
||||
"@types/q@^0.0.32":
|
||||
version "0.0.32"
|
||||
@ -273,8 +284,8 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz#74cb77fb6052edaff2a8984ddafd88d419f25cac"
|
||||
|
||||
"@types/serve-static@*":
|
||||
version "1.7.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.32.tgz#0f6732e4dab0813771dd8fc8fe14940f34728b4c"
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.0.tgz#2ef359d8d5982bc011a7a8fa36016e629e8b7635"
|
||||
dependencies:
|
||||
"@types/express-serve-static-core" "*"
|
||||
"@types/mime" "*"
|
||||
@ -307,7 +318,7 @@ acorn@^4.0.3:
|
||||
version "4.0.13"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
|
||||
|
||||
acorn@^5.0.0, acorn@^5.1.1:
|
||||
acorn@^5.0.0, acorn@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7"
|
||||
|
||||
@ -346,13 +357,13 @@ ajv@^4.9.1:
|
||||
json-stable-stringify "^1.0.1"
|
||||
|
||||
ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.3.tgz#c06f598778c44c6b161abafe3466b81ad1814ed2"
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.3.0.tgz#4414ff74a50879c208ee5fdc826e32c303549eda"
|
||||
dependencies:
|
||||
co "^4.6.0"
|
||||
fast-deep-equal "^1.0.0"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.3.0"
|
||||
json-stable-stringify "^1.0.1"
|
||||
|
||||
align-text@^0.1.1, align-text@^0.1.3:
|
||||
version "0.1.4"
|
||||
@ -530,11 +541,7 @@ async@1.5.2, async@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
||||
|
||||
async@^0.9.0:
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
|
||||
|
||||
async@^2.1.2, async@^2.1.5, async@^2.4.1:
|
||||
async@^2.1.2, async@^2.1.5, async@^2.4.1, async@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
|
||||
dependencies:
|
||||
@ -851,8 +858,8 @@ browser-sync@^2.18.5:
|
||||
yargs "6.4.0"
|
||||
|
||||
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.8.tgz#c8fa3b1b7585bb7ba77c5560b60996ddec6d5309"
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f"
|
||||
dependencies:
|
||||
buffer-xor "^1.0.3"
|
||||
cipher-base "^1.0.0"
|
||||
@ -989,8 +996,8 @@ caniuse-api@^1.5.2:
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
|
||||
version "1.0.30000744"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000744.tgz#00758ff7dd5f7138d34a15608dccf71a59656ffe"
|
||||
version "1.0.30000752"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000752.tgz#daa129d54ce700e94ad14388bdef514ec1e2dd54"
|
||||
|
||||
canonical-path@0.0.2:
|
||||
version "0.0.2"
|
||||
@ -1027,17 +1034,9 @@ chalk@^1.1.1, chalk@^1.1.3:
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
|
||||
dependencies:
|
||||
ansi-styles "^3.1.0"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^4.0.0"
|
||||
|
||||
chalk@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d"
|
||||
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
|
||||
dependencies:
|
||||
ansi-styles "^3.1.0"
|
||||
escape-string-regexp "^1.0.5"
|
||||
@ -1215,10 +1214,10 @@ component-inherit@0.0.3:
|
||||
resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
|
||||
|
||||
compressible@~2.0.11:
|
||||
version "2.0.11"
|
||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a"
|
||||
version "2.0.12"
|
||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
|
||||
dependencies:
|
||||
mime-db ">= 1.29.0 < 2"
|
||||
mime-db ">= 1.30.0 < 2"
|
||||
|
||||
compression@^1.5.2:
|
||||
version "1.7.1"
|
||||
@ -1258,8 +1257,8 @@ concurrently@^3.0.0:
|
||||
tree-kill "^1.1.0"
|
||||
|
||||
connect-history-api-fallback@^1.1.0, connect-history-api-fallback@^1.2.0, connect-history-api-fallback@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.4.0.tgz#3db24f973f4b923b0e82f619ce0df02411ca623d"
|
||||
|
||||
connect-logger@0.0.1:
|
||||
version "0.0.1"
|
||||
@ -1327,9 +1326,9 @@ cookie@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
|
||||
|
||||
copy-webpack-plugin@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.1.1.tgz#53ae69e04955ebfa9fda411f54cbb968531d71fd"
|
||||
copy-webpack-plugin@^4.0.1, copy-webpack-plugin@^4.1.1:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.2.0.tgz#252bb94597f96399d23d7fad355f8d3a661ac096"
|
||||
dependencies:
|
||||
bluebird "^3.5.1"
|
||||
fs-extra "^4.0.2"
|
||||
@ -1579,8 +1578,8 @@ dashdash@^1.12.0:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
date-fns@^1.23.0:
|
||||
version "1.28.5"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.5.tgz#257cfc45d322df45ef5658665967ee841cd73faf"
|
||||
version "1.29.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6"
|
||||
|
||||
date-now@^0.1.4:
|
||||
version "0.1.4"
|
||||
@ -1711,8 +1710,8 @@ diff@^2.2.1:
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99"
|
||||
|
||||
diff@^3.1.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75"
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c"
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.2"
|
||||
@ -1840,8 +1839,8 @@ ejs@^2.5.7:
|
||||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
|
||||
|
||||
electron-to-chromium@^1.2.7:
|
||||
version "1.3.24"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.24.tgz#9b7b88bb05ceb9fa016a177833cc2dde388f21b6"
|
||||
version "1.3.27"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d"
|
||||
|
||||
elliptic@^6.0.0:
|
||||
version "6.4.0"
|
||||
@ -1978,20 +1977,20 @@ error-ex@^1.2.0:
|
||||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14:
|
||||
version "0.10.30"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.30.tgz#7141a16836697dbabfaaaeee41495ce29f52c939"
|
||||
es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
|
||||
version "0.10.35"
|
||||
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.35.tgz#18ee858ce6a3c45c7d79e91c15fcca9ec568494f"
|
||||
dependencies:
|
||||
es6-iterator "2"
|
||||
es6-symbol "~3.1"
|
||||
es6-iterator "~2.0.1"
|
||||
es6-symbol "~3.1.1"
|
||||
|
||||
es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512"
|
||||
es6-iterator@^2.0.1, es6-iterator@~2.0.1:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.14"
|
||||
es6-symbol "^3.1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-map@^0.1.3:
|
||||
version "0.1.5"
|
||||
@ -2018,7 +2017,7 @@ es6-set@~0.1.5:
|
||||
es6-symbol "3.1.1"
|
||||
event-emitter "~0.3.5"
|
||||
|
||||
es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
|
||||
es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
|
||||
dependencies:
|
||||
@ -2185,8 +2184,8 @@ express@2.5.x:
|
||||
qs "0.4.x"
|
||||
|
||||
express@^4.13.3, express@^4.14.1:
|
||||
version "4.16.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.1.tgz#6b33b560183c9b253b7b62144df33a4654ac9ed0"
|
||||
version "4.16.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
|
||||
dependencies:
|
||||
accepts "~1.3.4"
|
||||
array-flatten "1.1.1"
|
||||
@ -2263,6 +2262,10 @@ fast-deep-equal@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
|
||||
|
||||
fastparse@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
|
||||
@ -2285,18 +2288,19 @@ fd-slicer@~1.0.1:
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
file-loader@^0.10.0:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.10.1.tgz#815034119891fc6441fb5a64c11bc93c22ddd842"
|
||||
dependencies:
|
||||
loader-utils "^1.0.2"
|
||||
|
||||
file-loader@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.9.0.tgz#1d2daddd424ce6d1b07cfe3f79731bed3617ab42"
|
||||
dependencies:
|
||||
loader-utils "~0.2.5"
|
||||
|
||||
file-loader@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.5.tgz#91c25b6b6fbe56dae99f10a425fd64933b5c9daa"
|
||||
dependencies:
|
||||
loader-utils "^1.0.2"
|
||||
schema-utils "^0.3.0"
|
||||
|
||||
filename-regex@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
|
||||
@ -2812,8 +2816,8 @@ html-loader@^0.4.3:
|
||||
object-assign "^4.1.0"
|
||||
|
||||
html-minifier@^3.0.1, html-minifier@^3.2.3:
|
||||
version "3.5.5"
|
||||
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.5.tgz#3bdc9427e638bbe3dbde96c0eb988b044f02739e"
|
||||
version "3.5.6"
|
||||
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.6.tgz#7e4e661a09999599c7d8e8a2b8d7fb7430bb5c3e"
|
||||
dependencies:
|
||||
camel-case "3.0.x"
|
||||
clean-css "4.1.x"
|
||||
@ -3049,8 +3053,8 @@ is-binary-path@^1.0.0:
|
||||
binary-extensions "^1.0.0"
|
||||
|
||||
is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
|
||||
is-builtin-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
@ -3240,8 +3244,8 @@ istanbul-lib-coverage@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
|
||||
|
||||
istanbul-lib-instrument@^1.1.3:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532"
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e"
|
||||
dependencies:
|
||||
babel-generator "^6.18.0"
|
||||
babel-template "^6.16.0"
|
||||
@ -3499,8 +3503,8 @@ less-loader@^4.0.5:
|
||||
pify "^2.3.0"
|
||||
|
||||
less@^2.7.2:
|
||||
version "2.7.2"
|
||||
resolved "https://registry.yarnpkg.com/less/-/less-2.7.2.tgz#368d6cc73e1fb03981183280918743c5dcf9b3df"
|
||||
version "2.7.3"
|
||||
resolved "https://registry.yarnpkg.com/less/-/less-2.7.3.tgz#cc1260f51c900a9ec0d91fb6998139e02507b63b"
|
||||
optionalDependencies:
|
||||
errno "^0.1.1"
|
||||
graceful-fs "^4.1.2"
|
||||
@ -3508,12 +3512,12 @@ less@^2.7.2:
|
||||
mime "^1.2.11"
|
||||
mkdirp "^0.5.0"
|
||||
promise "^7.1.1"
|
||||
request "^2.72.0"
|
||||
request "2.81.0"
|
||||
source-map "^0.5.3"
|
||||
|
||||
license-webpack-plugin@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.1.0.tgz#99117ae985fb6c2a70df301b8a1ae0268f17f433"
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.1.1.tgz#76b2cedccc78f139fd7877e576f756cfc141b8c2"
|
||||
dependencies:
|
||||
ejs "^2.5.7"
|
||||
|
||||
@ -3635,8 +3639,8 @@ log4js@^0.6.31:
|
||||
semver "~4.3.3"
|
||||
|
||||
loglevel@^1.4.1:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.5.0.tgz#3863984a2c326b986fbb965f378758a6dc8a4324"
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.5.1.tgz#189078c94ab9053ee215a0acdbf24244ea0f6502"
|
||||
|
||||
longest@^1.0.1:
|
||||
version "1.0.1"
|
||||
@ -3776,7 +3780,11 @@ miller-rabin@^4.0.0:
|
||||
bn.js "^4.0.0"
|
||||
brorand "^1.0.1"
|
||||
|
||||
"mime-db@>= 1.29.0 < 2", mime-db@~1.30.0:
|
||||
"mime-db@>= 1.30.0 < 2":
|
||||
version "1.31.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.31.0.tgz#a49cd8f3ebf3ed1a482b60561d9105ad40ca74cb"
|
||||
|
||||
mime-db@~1.30.0:
|
||||
version "1.30.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
|
||||
|
||||
@ -3863,8 +3871,8 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd
|
||||
minimist "0.0.8"
|
||||
|
||||
moment@*:
|
||||
version "2.18.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
|
||||
version "2.19.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167"
|
||||
|
||||
ms@0.7.1:
|
||||
version "0.7.1"
|
||||
@ -4732,8 +4740,8 @@ pretty-error@^2.0.2:
|
||||
utila "~0.4"
|
||||
|
||||
private@~0.1.5:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||
|
||||
process-nextick-args@~1.0.6:
|
||||
version "1.0.7"
|
||||
@ -4811,8 +4819,8 @@ q@1.4.1:
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
|
||||
|
||||
q@^1.1.2, q@^1.4.1:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
|
||||
qjobs@^1.1.4:
|
||||
version "1.1.5"
|
||||
@ -4892,8 +4900,8 @@ raw-loader@^0.5.1:
|
||||
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
|
||||
|
||||
rc@^1.1.7:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
|
||||
dependencies:
|
||||
deep-extend "~0.4.0"
|
||||
ini "~1.3.0"
|
||||
@ -5068,7 +5076,7 @@ request-progress@~2.0.1:
|
||||
dependencies:
|
||||
throttleit "^1.0.0"
|
||||
|
||||
request@2, request@^2.72.0, request@^2.78.0, request@^2.79.0:
|
||||
request@2, request@^2.78.0, request@^2.79.0:
|
||||
version "2.83.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
|
||||
dependencies:
|
||||
@ -5143,8 +5151,8 @@ resolve@1.1.7:
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
|
||||
|
||||
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86"
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
|
||||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
@ -5179,10 +5187,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
inherits "^2.0.1"
|
||||
|
||||
rollup-plugin-commonjs@^8.0.2:
|
||||
version "8.2.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.1.tgz#5e40c78375eb163c14c76bce69da1750e5905a2e"
|
||||
version "8.2.5"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.5.tgz#924421278a6f879fd976b2ef1a28391b1e4f2a6e"
|
||||
dependencies:
|
||||
acorn "^5.1.1"
|
||||
acorn "^5.1.2"
|
||||
estree-walker "^0.5.0"
|
||||
magic-string "^0.22.4"
|
||||
resolve "^1.4.0"
|
||||
@ -5223,9 +5231,9 @@ rx@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
|
||||
|
||||
rxjs@^5.1.0, rxjs@^5.4.2:
|
||||
version "5.4.3"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.3.tgz#0758cddee6033d68e0fd53676f0f3596ce3d483f"
|
||||
rxjs@^5.4.2, rxjs@^5.5.0:
|
||||
version "5.5.2"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.2.tgz#28d403f0071121967f18ad665563255d54236ac3"
|
||||
dependencies:
|
||||
symbol-observable "^1.0.1"
|
||||
|
||||
@ -5481,8 +5489,8 @@ sntp@1.x.x:
|
||||
hoek "2.x.x"
|
||||
|
||||
sntp@2.x.x:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.0.2.tgz#5064110f0af85f7cfdb7d6b67a40028ce52b4b2b"
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
|
||||
dependencies:
|
||||
hoek "4.x.x"
|
||||
|
||||
@ -5604,12 +5612,12 @@ source-map-explorer@^1.3.2:
|
||||
underscore "^1.8.3"
|
||||
|
||||
source-map-loader@^0.2.0:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.2.tgz#1249348ff6a66ea64a2957fc98f74cb6bba67505"
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.3.tgz#d4b0c8cd47d54edce3e6bfa0f523f452b5b0e521"
|
||||
dependencies:
|
||||
async "^0.9.0"
|
||||
async "^2.5.0"
|
||||
loader-utils "~0.2.2"
|
||||
source-map "~0.1.33"
|
||||
source-map "~0.6.1"
|
||||
|
||||
source-map-support@^0.4.0, source-map-support@^0.4.1, source-map-support@^0.4.2, source-map-support@~0.4.0:
|
||||
version "0.4.18"
|
||||
@ -5617,7 +5625,7 @@ source-map-support@^0.4.0, source-map-support@^0.4.1, source-map-support@^0.4.2,
|
||||
dependencies:
|
||||
source-map "^0.5.6"
|
||||
|
||||
source-map@0.1.x, source-map@~0.1.33, source-map@~0.1.7:
|
||||
source-map@0.1.x, source-map@~0.1.7:
|
||||
version "0.1.43"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
|
||||
dependencies:
|
||||
@ -5633,7 +5641,7 @@ source-map@^0.4.2:
|
||||
dependencies:
|
||||
amdefine ">=0.0.4"
|
||||
|
||||
source-map@^0.6.1:
|
||||
source-map@^0.6.1, source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
|
||||
@ -5700,7 +5708,11 @@ sshpk@^1.7.0:
|
||||
jsbn "~0.1.0"
|
||||
tweetnacl "~0.14.0"
|
||||
|
||||
"statuses@>= 1.3.1 < 2", statuses@~1.3.0, statuses@~1.3.1:
|
||||
"statuses@>= 1.3.1 < 2":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
|
||||
|
||||
statuses@~1.3.0, statuses@~1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
|
||||
|
||||
@ -5849,8 +5861,8 @@ supports-color@^3.1.1, supports-color@^3.2.3:
|
||||
has-flag "^1.0.0"
|
||||
|
||||
supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e"
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
|
||||
dependencies:
|
||||
has-flag "^2.0.0"
|
||||
|
||||
@ -5881,8 +5893,8 @@ tapable@^0.2.7:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
|
||||
|
||||
tar-pack@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
fstream "^1.0.10"
|
||||
@ -6025,8 +6037,8 @@ tsickle@^0.21.0:
|
||||
source-map-support "^0.4.2"
|
||||
|
||||
tslib@^1.7.1:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec"
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.0.tgz#dc604ebad64bcbf696d613da6c954aa0e7ea1eb6"
|
||||
|
||||
tslint@^3.15.1:
|
||||
version "3.15.1"
|
||||
@ -6078,11 +6090,11 @@ ua-parser-js@0.7.12:
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
|
||||
|
||||
uglify-js@3.1.x:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.3.tgz#d61f0453b4718cab01581f3162aa90bab7520b42"
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.5.tgz#4c1a6d53b2fe77e4710dd94631853effd3ff5143"
|
||||
dependencies:
|
||||
commander "~2.11.0"
|
||||
source-map "~0.5.1"
|
||||
source-map "~0.6.1"
|
||||
|
||||
uglify-js@^2.6.1, uglify-js@^2.8.29:
|
||||
version "2.8.29"
|
||||
@ -6172,7 +6184,7 @@ url-join@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78"
|
||||
|
||||
url-loader@^0.6.0:
|
||||
url-loader@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7"
|
||||
dependencies:
|
||||
@ -6384,16 +6396,16 @@ webpack-sources@^0.1.0:
|
||||
source-list-map "~0.1.7"
|
||||
source-map "~0.5.3"
|
||||
|
||||
webpack-sources@^1.0.1:
|
||||
webpack-sources@^1.0.0, webpack-sources@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf"
|
||||
dependencies:
|
||||
source-list-map "^2.0.0"
|
||||
source-map "~0.5.3"
|
||||
|
||||
webpack@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.6.0.tgz#a89a929fbee205d35a4fa2cc487be9cbec8898bc"
|
||||
webpack@~3.7.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.7.1.tgz#6046b5c415ff7df7a0dc54c5b6b86098e8b952da"
|
||||
dependencies:
|
||||
acorn "^5.0.0"
|
||||
acorn-dynamic-import "^2.0.0"
|
||||
|
@ -129,7 +129,7 @@ class NgPackagesInstaller {
|
||||
* Yarn will also delete the local marker file for us.
|
||||
*/
|
||||
restoreNpmDependencies() {
|
||||
this._installDeps('--freeze-lockfile', '--check-files');
|
||||
this._installDeps('--frozen-lockfile', '--check-files');
|
||||
}
|
||||
|
||||
// Protected helpers
|
||||
|
@ -203,7 +203,7 @@ describe('NgPackagesInstaller', () => {
|
||||
it('should run `yarn install` in the specified directory, with the correct options', () => {
|
||||
spyOn(installer, '_installDeps');
|
||||
installer.restoreNpmDependencies();
|
||||
expect(installer._installDeps).toHaveBeenCalledWith('--freeze-lockfile', '--check-files');
|
||||
expect(installer._installDeps).toHaveBeenCalledWith('--frozen-lockfile', '--check-files');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -63,6 +63,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
|
||||
'router/index.ts',
|
||||
'router/testing/index.ts',
|
||||
'router/upgrade/index.ts',
|
||||
'service-worker/index.ts',
|
||||
'upgrade/index.ts',
|
||||
'upgrade/static/index.ts',
|
||||
];
|
||||
|
@ -21,6 +21,7 @@ const packageMap = {
|
||||
'platform-webworker': ['platform-webworker/index.ts'],
|
||||
'platform-webworker-dynamic': 'platform-webworker-dynamic/index.ts',
|
||||
router: ['router/index.ts', 'router/testing/index.ts', 'router/upgrade/index.ts'],
|
||||
'service-worker': ['service-worker/index.ts'],
|
||||
upgrade: ['upgrade/index.ts', 'upgrade/static/index.ts']
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "5.0.0-rc.3",
|
||||
"version": "5.0.0-rc.9",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -24,7 +24,7 @@
|
||||
"dependencies": {
|
||||
"core-js": "^2.4.1",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
"rxjs": "^5.5.0",
|
||||
"rxjs": "^5.5.2",
|
||||
"tslib": "^1.7.1",
|
||||
"zone.js": "^0.8.12"
|
||||
},
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -15,7 +15,7 @@ const globals = {
|
||||
'@angular/animations': 'ng.animations'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/animations/esm5/browser.js',
|
||||
dest: '../../../dist/packages-dist/animations/bundles/animations-browser.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -16,7 +16,7 @@ const globals = {
|
||||
'rxjs/Subject': 'Rx',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../../dist/packages-dist/animations/esm5/browser/testing.js',
|
||||
dest: '../../../../dist/packages-dist/animations/bundles/animations-browser-testing.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -16,7 +16,7 @@ const globals = {
|
||||
'rxjs/Subject': 'Rx',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/animations/esm5/animations.js',
|
||||
dest: '../../dist/packages-dist/animations/bundles/animations.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -60,6 +60,9 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}):
|
||||
inputs,
|
||||
expectedOuts
|
||||
});
|
||||
if (diagnostics.length) {
|
||||
console.error(ng.formatDiagnostics(diagnostics));
|
||||
}
|
||||
return diagnostics.every(d => d.category !== ts.DiagnosticCategory.Error);
|
||||
}
|
||||
|
||||
@ -204,9 +207,7 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
|
||||
{rootNames: files, options: compilerOpts, host: ngHost, emitCallback, gatherDiagnostics});
|
||||
const tsickleEmitResult = emitResult as tsickle.EmitResult;
|
||||
let externs = '/** @externs */\n';
|
||||
if (diagnostics.length) {
|
||||
console.error(ng.formatDiagnostics(diagnostics));
|
||||
} else {
|
||||
if (!diagnostics.length) {
|
||||
if (bazelOpts.tsickleGenerateExterns) {
|
||||
externs += tsickle.getGeneratedExterns(tsickleEmitResult.externs);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const globals = {
|
||||
'@angular/platform-browser': 'ng.platformBrowser',
|
||||
'@angular/common': 'ng.common',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Observer': 'Rx',
|
||||
'rxjs/Subject': 'Rx',
|
||||
|
||||
'rxjs/observable/of': 'Rx.Observable.prototype',
|
||||
@ -20,7 +21,7 @@ const globals = {
|
||||
'rxjs/operator/map': 'Rx.Observable.prototype',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/common/esm5/http.js',
|
||||
dest: '../../../dist/packages-dist/common/bundles/common-http.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -15,11 +15,14 @@ const globals = {
|
||||
'@angular/common': 'ng.common',
|
||||
'@angular/common/http': 'ng.common.http',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Observer': 'Rx',
|
||||
'rxjs/ReplaySubject': 'Rx',
|
||||
'rxjs/Subject': 'Rx',
|
||||
|
||||
'rxjs/operator/startWith': 'Rx.Observable.prototype',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../../dist/packages-dist/common/esm5/http/testing.js',
|
||||
dest: '../../../../dist/packages-dist/common/bundles/common-http-testing.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,16 +6,18 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Observer': 'Rx',
|
||||
'rxjs/Subject': 'Rx',
|
||||
'rxjs/Subscription': 'Rx',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/common/esm5/common.js',
|
||||
dest: '../../dist/packages-dist/common/bundles/common.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -16,7 +16,7 @@ const globals = {
|
||||
'rxjs/Subject': 'Rx'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/common/esm5/testing.js',
|
||||
dest: '../../../dist/packages-dist/common/bundles/common-testing.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,10 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import * as path from 'path';
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const path = require('path');
|
||||
|
||||
import 'reflect-metadata';
|
||||
require('reflect-metadata');
|
||||
|
||||
var m = /^\@angular\/((\w|\-)+)(\/(\w|\d|\/|\-)+)?$/;
|
||||
var location = normalize('../../dist/packages-dist') + '/';
|
||||
@ -49,7 +49,7 @@ function resolve(id, from) {
|
||||
// hack to get around issues with default exports
|
||||
var banner = `ts['default'] = ts['default'] || ts; fs['default'] = fs['default'] || fs;`;
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/compiler-cli/src/ngc.js',
|
||||
dest: './browser-bundle.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,13 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
|
||||
export {CodeGenerator} from './src/codegen';
|
||||
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './src/compiler_host';
|
||||
export {DiagnosticTemplateInfo, getExpressionScope, getTemplateExpressionDiagnostics} from './src/diagnostics/expression_diagnostics';
|
||||
export {AstType, ExpressionDiagnosticsContext} from './src/diagnostics/expression_type';
|
||||
export {BuiltinType, DeclarationKind, Definition, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './src/diagnostics/symbols';
|
||||
export {getClassMembersFromDeclaration, getPipesTable, getSymbolQuery} from './src/diagnostics/typescript_symbols';
|
||||
export {Extractor} from './src/extractor';
|
||||
export {VERSION} from './src/version';
|
||||
|
||||
export * from './src/metadata';
|
||||
@ -21,8 +18,7 @@ export * from './src/transformers/entry_points';
|
||||
|
||||
export * from './src/perform_compile';
|
||||
|
||||
// TODO(tbosch): remove this once everyone is on transformers
|
||||
// TODO(tbosch): remove this once cli 1.5 is fully released,
|
||||
// and usages in G3 are changed to `CompilerOptions`.
|
||||
export {CompilerOptions as AngularCompilerOptions} from './src/transformers/api';
|
||||
|
||||
// TODO(hansl): moving to Angular 4 need to update this API.
|
||||
export {NgTools_InternalApi_NG_2 as __NGTOOLS_PRIVATE_API_2} from './src/ngtools_api';
|
||||
|
@ -15,8 +15,6 @@ import * as ts from 'typescript';
|
||||
import * as assert from 'assert';
|
||||
import {__NGTOOLS_PRIVATE_API_2, readConfiguration} from '@angular/compiler-cli';
|
||||
|
||||
const glob = require('glob');
|
||||
|
||||
/* tslint:disable:no-console */
|
||||
/**
|
||||
* Main method.
|
||||
@ -27,9 +25,6 @@ function main() {
|
||||
console.log(`testing ngtools API...`);
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => codeGenTest())
|
||||
.then(() => codeGenTest(true))
|
||||
.then(() => i18nTest())
|
||||
.then(() => lazyRoutesTest())
|
||||
.then(() => {
|
||||
console.log('All done!');
|
||||
@ -42,152 +37,6 @@ function main() {
|
||||
});
|
||||
}
|
||||
|
||||
function codeGenTest(forceError = false) {
|
||||
const basePath = path.join(__dirname, '../ngtools_src');
|
||||
const srcPath = path.join(__dirname, '../src');
|
||||
const project = path.join(basePath, 'tsconfig-build.json');
|
||||
const readResources: string[] = [];
|
||||
const wroteFiles: string[] = [];
|
||||
|
||||
const config = readConfiguration(project);
|
||||
const delegateHost = ts.createCompilerHost(config.options, true);
|
||||
const host: ts.CompilerHost = Object.assign(
|
||||
{}, delegateHost,
|
||||
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
||||
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||
|
||||
config.options.basePath = basePath;
|
||||
|
||||
console.log(`>>> running codegen for ${project}`);
|
||||
if (forceError) {
|
||||
console.log(`>>> asserting that missingTranslation param with error value throws`);
|
||||
}
|
||||
return __NGTOOLS_PRIVATE_API_2
|
||||
.codeGen({
|
||||
basePath,
|
||||
compilerOptions: config.options, program, host,
|
||||
|
||||
angularCompilerOptions: config.options,
|
||||
|
||||
// i18n options.
|
||||
i18nFormat: 'xlf',
|
||||
i18nFile: path.join(srcPath, 'messages.fi.xlf'),
|
||||
locale: 'fi',
|
||||
missingTranslation: forceError ? 'error' : 'ignore',
|
||||
|
||||
readResource: (fileName: string) => {
|
||||
readResources.push(fileName);
|
||||
if (!host.fileExists(fileName)) {
|
||||
throw new Error(`Compilation failed. Resource file not found: ${fileName}`);
|
||||
}
|
||||
return Promise.resolve(host.readFile(fileName));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
console.log(`>>> codegen done, asserting read and wrote files`);
|
||||
|
||||
// Assert for each file that it has been read and each `ts` has a written file associated.
|
||||
const allFiles = glob.sync(path.join(basePath, '**/*'), {nodir: true});
|
||||
|
||||
allFiles.forEach((fileName: string) => {
|
||||
// Skip tsconfig.
|
||||
if (fileName.match(/tsconfig-build.json$/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Assert that file was read.
|
||||
if (fileName.match(/\.module\.ts$/)) {
|
||||
const factory = fileName.replace(/\.module\.ts$/, '.module.ngfactory.ts');
|
||||
assert(wroteFiles.indexOf(factory) != -1, `Expected file "${factory}" to be written.`);
|
||||
} else if (fileName.match(/\.css$/) || fileName.match(/\.html$/)) {
|
||||
assert(
|
||||
readResources.indexOf(fileName) != -1,
|
||||
`Expected resource "${fileName}" to be read.`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`done, no errors.`);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
if (forceError) {
|
||||
assert(
|
||||
e.message.match(`Missing translation for message`),
|
||||
`Expected error message for missing translations`);
|
||||
console.log(`done, error catched`);
|
||||
} else {
|
||||
console.error(e.stack);
|
||||
console.error('Compilation failed');
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function i18nTest() {
|
||||
const basePath = path.join(__dirname, '../ngtools_src');
|
||||
const project = path.join(basePath, 'tsconfig-build.json');
|
||||
const readResources: string[] = [];
|
||||
const wroteFiles: string[] = [];
|
||||
|
||||
const config = readConfiguration(project);
|
||||
const delegateHost = ts.createCompilerHost(config.options, true);
|
||||
const host: ts.CompilerHost = Object.assign(
|
||||
{}, delegateHost,
|
||||
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
||||
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||
|
||||
config.options.basePath = basePath;
|
||||
|
||||
console.log(`>>> running i18n extraction for ${project}`);
|
||||
return __NGTOOLS_PRIVATE_API_2
|
||||
.extractI18n({
|
||||
basePath,
|
||||
compilerOptions: config.options, program, host,
|
||||
angularCompilerOptions: config.options,
|
||||
i18nFormat: 'xlf',
|
||||
locale: undefined,
|
||||
outFile: undefined,
|
||||
readResource: (fileName: string) => {
|
||||
readResources.push(fileName);
|
||||
if (!host.fileExists(fileName)) {
|
||||
throw new Error(`Compilation failed. Resource file not found: ${fileName}`);
|
||||
}
|
||||
return Promise.resolve(host.readFile(fileName));
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
console.log(`>>> i18n extraction done, asserting read and wrote files`);
|
||||
|
||||
const allFiles = glob.sync(path.join(basePath, '**/*'), {nodir: true});
|
||||
|
||||
assert(wroteFiles.length == 1, `Expected a single message bundle file.`);
|
||||
|
||||
assert(
|
||||
wroteFiles[0].endsWith('/ngtools_src/messages.xlf'),
|
||||
`Expected the bundle file to be "message.xlf".`);
|
||||
|
||||
allFiles.forEach((fileName: string) => {
|
||||
// Skip tsconfig.
|
||||
if (fileName.match(/tsconfig-build.json$/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Assert that file was read.
|
||||
if (fileName.match(/\.css$/) || fileName.match(/\.html$/)) {
|
||||
assert(
|
||||
readResources.indexOf(fileName) != -1,
|
||||
`Expected resource "${fileName}" to be read.`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`done, no errors.`);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
console.error(e.stack);
|
||||
console.error('Extraction failed');
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
|
||||
function lazyRoutesTest() {
|
||||
const basePath = path.join(__dirname, '../ngtools_src');
|
||||
const project = path.join(basePath, 'tsconfig-build.json');
|
||||
|
@ -1,126 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import * as assert from 'assert';
|
||||
import {CompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext, readConfiguration} from '@angular/compiler-cli';
|
||||
|
||||
/* tslint:disable:no-console */
|
||||
/**
|
||||
* Main method.
|
||||
* Standalone program that executes the real codegen and tests that
|
||||
* ngsummary.json files are used for libraries.
|
||||
*/
|
||||
function main() {
|
||||
console.log(`testing usage of ngsummary.json files in libraries...`);
|
||||
const basePath = path.resolve(__dirname, '..');
|
||||
const project = path.resolve(basePath, 'tsconfig-build.json');
|
||||
const readFiles: string[] = [];
|
||||
const writtenFiles: {fileName: string, content: string}[] = [];
|
||||
|
||||
class AssertingHostContext extends NodeCompilerHostContext {
|
||||
readFile(fileName: string): string {
|
||||
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName) &&
|
||||
!/package\.json$/.test(fileName)) {
|
||||
// Only allow to read summaries and package.json files from node_modules
|
||||
// TODO (mhevery): Fix this. TypeScript.d.ts does not allow returning null.
|
||||
return null !;
|
||||
}
|
||||
readFiles.push(path.relative(basePath, fileName));
|
||||
return super.readFile(fileName);
|
||||
}
|
||||
readResource(s: string): Promise<string> {
|
||||
readFiles.push(path.relative(basePath, s));
|
||||
return super.readResource(s);
|
||||
}
|
||||
}
|
||||
|
||||
const config = readConfiguration(project);
|
||||
config.options.basePath = basePath;
|
||||
// This flag tells ngc do not recompile libraries.
|
||||
config.options.generateCodeForLibraries = false;
|
||||
|
||||
console.log(`>>> running codegen for ${project}`);
|
||||
codegen(
|
||||
config,
|
||||
(host) => {
|
||||
host.writeFile = (fileName: string, content: string) => {
|
||||
fileName = path.relative(basePath, fileName);
|
||||
writtenFiles.push({fileName, content});
|
||||
};
|
||||
return new AssertingHostContext();
|
||||
})
|
||||
.then((exitCode: any) => {
|
||||
console.log(`>>> codegen done, asserting read files`);
|
||||
assertSomeFileMatch(readFiles, /^node_modules\/.*\.ngsummary\.json$/);
|
||||
assertNoFileMatch(readFiles, /^node_modules\/.*\.metadata.json$/);
|
||||
assertNoFileMatch(readFiles, /^node_modules\/.*\.html$/);
|
||||
assertNoFileMatch(readFiles, /^node_modules\/.*\.css$/);
|
||||
|
||||
assertNoFileMatch(readFiles, /^src\/.*\.ngsummary\.json$/);
|
||||
assertSomeFileMatch(readFiles, /^src\/.*\.html$/);
|
||||
assertSomeFileMatch(readFiles, /^src\/.*\.css$/);
|
||||
|
||||
console.log(`>>> asserting written files`);
|
||||
assertWrittenFile(writtenFiles, /^src\/module\.ngfactory\.ts$/, /class MainModuleInjector/);
|
||||
|
||||
console.log(`done, no errors.`);
|
||||
process.exit(exitCode);
|
||||
})
|
||||
.catch((e: any) => {
|
||||
console.error(e.stack);
|
||||
console.error('Compilation failed');
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple adaption of main to just run codegen with a CompilerHostContext
|
||||
*/
|
||||
function codegen(
|
||||
config: {options: CompilerOptions, rootNames: string[]},
|
||||
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
|
||||
const host = ts.createCompilerHost(config.options, true);
|
||||
|
||||
// HACK: patch the realpath to solve symlink issue here:
|
||||
// https://github.com/Microsoft/TypeScript/issues/9552
|
||||
// todo(misko): remove once facade symlinks are removed
|
||||
host.realpath = (path) => path;
|
||||
|
||||
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||
|
||||
return CodeGenerator.create(config.options, {
|
||||
} as any, program, host, hostContextFactory(host)).codegen();
|
||||
}
|
||||
|
||||
function assertSomeFileMatch(fileNames: string[], pattern: RegExp) {
|
||||
assert(
|
||||
fileNames.some(fileName => pattern.test(fileName)),
|
||||
`Expected some read files match ${pattern}`);
|
||||
}
|
||||
|
||||
function assertNoFileMatch(fileNames: string[], pattern: RegExp) {
|
||||
const matches = fileNames.filter(fileName => pattern.test(fileName));
|
||||
assert(
|
||||
matches.length === 0,
|
||||
`Expected no read files match ${pattern}, but found: \n${matches.join('\n')}`);
|
||||
}
|
||||
|
||||
function assertWrittenFile(
|
||||
files: {fileName: string, content: string}[], filePattern: RegExp, contentPattern: RegExp) {
|
||||
assert(
|
||||
files.some(file => filePattern.test(file.fileName) && contentPattern.test(file.content)),
|
||||
`Expected some written files for ${filePattern} and content ${contentPattern}`);
|
||||
}
|
||||
|
||||
main();
|
@ -31,7 +31,6 @@
|
||||
"src/bootstrap",
|
||||
"test/all_spec",
|
||||
"test/test_ngtools_api",
|
||||
"test/test_summaries",
|
||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||
|
@ -26,7 +26,6 @@
|
||||
"src/bootstrap",
|
||||
"test/all_spec",
|
||||
"test/test_ngtools_api",
|
||||
"test/test_summaries",
|
||||
"benchmarks/src/tree/ng2/index_aot.ts",
|
||||
"benchmarks/src/tree/ng2_switch/index_aot.ts",
|
||||
"benchmarks/src/largetable/ng2/index_aot.ts",
|
||||
|
@ -1,117 +0,0 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transform template html and css into executable code.
|
||||
* Intended to be used in a build step.
|
||||
*/
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {readFileSync} from 'fs';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
|
||||
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||
import {CompilerOptions} from './transformers/api';
|
||||
|
||||
const GENERATED_META_FILES = /\.json$/;
|
||||
|
||||
const PREAMBLE = `/**
|
||||
* @fileoverview This file is generated by the Angular template compiler.
|
||||
* Do not edit.
|
||||
* @suppress {suspiciousCode,uselessCode,missingProperties,missingOverride}
|
||||
*/
|
||||
/* tslint:disable */
|
||||
|
||||
`;
|
||||
|
||||
export interface CodeGeneratorI18nOptions {
|
||||
i18nFormat: string|null;
|
||||
i18nFile: string|null;
|
||||
locale: string|null;
|
||||
missingTranslation: string|null;
|
||||
}
|
||||
|
||||
// TODO(tbosch): remove this once G3 uses the transformer compiler!
|
||||
export class CodeGenerator {
|
||||
constructor(
|
||||
private options: CompilerOptions, private program: ts.Program, public host: ts.CompilerHost,
|
||||
private compiler: compiler.AotCompiler, private ngCompilerHost: CompilerHost) {}
|
||||
|
||||
codegen(): Promise<string[]> {
|
||||
return this.compiler
|
||||
.analyzeModulesAsync(this.program.getSourceFiles().map(
|
||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)))
|
||||
.then(analyzedModules => this.emit(analyzedModules));
|
||||
}
|
||||
|
||||
codegenSync(): string[] {
|
||||
const analyzed = this.compiler.analyzeModulesSync(this.program.getSourceFiles().map(
|
||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName)));
|
||||
return this.emit(analyzed);
|
||||
}
|
||||
|
||||
private emit(analyzedModules: compiler.NgAnalyzedModules) {
|
||||
const generatedModules = this.compiler.emitAllImpls(analyzedModules);
|
||||
return generatedModules.map(generatedModule => {
|
||||
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
||||
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
|
||||
const source = generatedModule.source || compiler.toTypeScript(generatedModule, PREAMBLE);
|
||||
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
||||
return emitPath;
|
||||
});
|
||||
}
|
||||
|
||||
static create(
|
||||
options: CompilerOptions, i18nOptions: CodeGeneratorI18nOptions, program: ts.Program,
|
||||
tsCompilerHost: ts.CompilerHost, compilerHostContext?: CompilerHostContext,
|
||||
ngCompilerHost?: CompilerHost): CodeGenerator {
|
||||
if (!ngCompilerHost) {
|
||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
|
||||
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
|
||||
new CompilerHost(program, options, context);
|
||||
}
|
||||
let transContent: string = '';
|
||||
if (i18nOptions.i18nFile) {
|
||||
if (!i18nOptions.locale) {
|
||||
throw new Error(
|
||||
`The translation file (${i18nOptions.i18nFile}) locale must be provided. Use the --locale option.`);
|
||||
}
|
||||
transContent = readFileSync(i18nOptions.i18nFile, 'utf8');
|
||||
}
|
||||
let missingTranslation = compiler.core.MissingTranslationStrategy.Warning;
|
||||
if (i18nOptions.missingTranslation) {
|
||||
switch (i18nOptions.missingTranslation) {
|
||||
case 'error':
|
||||
missingTranslation = compiler.core.MissingTranslationStrategy.Error;
|
||||
break;
|
||||
case 'warning':
|
||||
missingTranslation = compiler.core.MissingTranslationStrategy.Warning;
|
||||
break;
|
||||
case 'ignore':
|
||||
missingTranslation = compiler.core.MissingTranslationStrategy.Ignore;
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown option for missingTranslation (${i18nOptions.missingTranslation}). Use either error, warning or ignore.`);
|
||||
}
|
||||
}
|
||||
if (!transContent) {
|
||||
missingTranslation = compiler.core.MissingTranslationStrategy.Ignore;
|
||||
}
|
||||
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
|
||||
translations: transContent,
|
||||
i18nFormat: i18nOptions.i18nFormat || undefined,
|
||||
locale: i18nOptions.locale || undefined, missingTranslation,
|
||||
enableLegacyTemplate: options.enableLegacyTemplate === true,
|
||||
enableSummariesForJit: options.enableSummariesForJit !== false,
|
||||
preserveWhitespaces: options.preserveWhitespaces,
|
||||
});
|
||||
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
|
||||
}
|
||||
}
|
@ -1,494 +0,0 @@
|
||||
/**
|
||||
* @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 {AotCompilerHost, StaticSymbol, UrlResolver, createOfflineCompileUrlResolver, syntaxError} from '@angular/compiler';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CollectorOptions, METADATA_VERSION, MetadataCollector, ModuleMetadata} from './metadata/index';
|
||||
import {CompilerOptions} from './transformers/api';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
const NODE_MODULES = '/node_modules/';
|
||||
const IS_GENERATED = /\.(ngfactory|ngstyle|ngsummary)$/;
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
|
||||
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
|
||||
const SHALLOW_IMPORT = /^((\w|-)+|(@(\w|-)+(\/(\w|-)+)+))$/;
|
||||
|
||||
export interface BaseAotCompilerHostContext extends ts.ModuleResolutionHost {
|
||||
readResource?(fileName: string): Promise<string>|string;
|
||||
}
|
||||
|
||||
export abstract class BaseAotCompilerHost<C extends BaseAotCompilerHostContext> implements
|
||||
AotCompilerHost {
|
||||
private resolverCache = new Map<string, ModuleMetadata[]>();
|
||||
private flatModuleIndexCache = new Map<string, boolean>();
|
||||
private flatModuleIndexNames = new Set<string>();
|
||||
private flatModuleIndexRedirectNames = new Set<string>();
|
||||
|
||||
constructor(protected options: CompilerOptions, protected context: C) {}
|
||||
|
||||
abstract moduleNameToFileName(m: string, containingFile: string): string|null;
|
||||
|
||||
abstract resourceNameToFileName(m: string, containingFile: string): string|null;
|
||||
|
||||
abstract fileNameToModuleName(importedFile: string, containingFile: string): string;
|
||||
|
||||
abstract toSummaryFileName(fileName: string, referringSrcFileName: string): string;
|
||||
|
||||
abstract fromSummaryFileName(fileName: string, referringLibFileName: string): string;
|
||||
|
||||
abstract getMetadataForSourceFile(filePath: string): ModuleMetadata|undefined;
|
||||
|
||||
getMetadataFor(filePath: string): ModuleMetadata[]|undefined {
|
||||
if (!this.context.fileExists(filePath)) {
|
||||
// If the file doesn't exists then we cannot return metadata for the file.
|
||||
// This will occur if the user referenced a declared module for which no file
|
||||
// exists for the module (i.e. jQuery or angularjs).
|
||||
return;
|
||||
}
|
||||
|
||||
if (DTS.test(filePath)) {
|
||||
let metadatas = this.readMetadata(filePath);
|
||||
if (!metadatas) {
|
||||
// If there is a .d.ts file but no metadata file we need to produce a
|
||||
// metadata from the .d.ts file as metadata files capture reexports
|
||||
// (starting with v3).
|
||||
metadatas = [this.upgradeMetadataWithDtsData(
|
||||
{'__symbolic': 'module', 'version': 1, 'metadata': {}}, filePath)];
|
||||
}
|
||||
return metadatas;
|
||||
}
|
||||
|
||||
// Attention: don't cache this, so that e.g. the LanguageService
|
||||
// can read in changes from source files in the metadata!
|
||||
const metadata = this.getMetadataForSourceFile(filePath);
|
||||
return metadata ? [metadata] : [];
|
||||
}
|
||||
|
||||
protected readMetadata(dtsFilePath: string): ModuleMetadata[]|undefined {
|
||||
let metadatas = this.resolverCache.get(dtsFilePath);
|
||||
if (metadatas) {
|
||||
return metadatas;
|
||||
}
|
||||
const metadataPath = dtsFilePath.replace(DTS, '.metadata.json');
|
||||
if (!this.context.fileExists(metadataPath)) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const metadataOrMetadatas = JSON.parse(this.context.readFile(metadataPath));
|
||||
const metadatas: ModuleMetadata[] = metadataOrMetadatas ?
|
||||
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
||||
[];
|
||||
if (metadatas.length) {
|
||||
let maxMetadata = metadatas.reduce((p, c) => p.version > c.version ? p : c);
|
||||
if (maxMetadata.version < METADATA_VERSION) {
|
||||
metadatas.push(this.upgradeMetadataWithDtsData(maxMetadata, dtsFilePath));
|
||||
}
|
||||
}
|
||||
this.resolverCache.set(dtsFilePath, metadatas);
|
||||
return metadatas;
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${metadataPath}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private upgradeMetadataWithDtsData(oldMetadata: ModuleMetadata, dtsFilePath: string):
|
||||
ModuleMetadata {
|
||||
// patch v1 to v3 by adding exports and the `extends` clause.
|
||||
// patch v3 to v4 by adding `interface` symbols for TypeAlias
|
||||
let newMetadata: ModuleMetadata = {
|
||||
'__symbolic': 'module',
|
||||
'version': METADATA_VERSION,
|
||||
'metadata': {...oldMetadata.metadata},
|
||||
};
|
||||
if (oldMetadata.exports) {
|
||||
newMetadata.exports = oldMetadata.exports;
|
||||
}
|
||||
if (oldMetadata.importAs) {
|
||||
newMetadata.importAs = oldMetadata.importAs;
|
||||
}
|
||||
if (oldMetadata.origins) {
|
||||
newMetadata.origins = oldMetadata.origins;
|
||||
}
|
||||
const dtsMetadata = this.getMetadataForSourceFile(dtsFilePath);
|
||||
if (dtsMetadata) {
|
||||
for (let prop in dtsMetadata.metadata) {
|
||||
if (!newMetadata.metadata[prop]) {
|
||||
newMetadata.metadata[prop] = dtsMetadata.metadata[prop];
|
||||
}
|
||||
}
|
||||
|
||||
// Only copy exports from exports from metadata prior to version 3.
|
||||
// Starting with version 3 the collector began collecting exports and
|
||||
// this should be redundant. Also, with bundler will rewrite the exports
|
||||
// which will hoist the exports from modules referenced indirectly causing
|
||||
// the imports to be different than the .d.ts files and using the .d.ts file
|
||||
// exports would cause the StaticSymbolResolver to redirect symbols to the
|
||||
// incorrect location.
|
||||
if ((!oldMetadata.version || oldMetadata.version < 3) && dtsMetadata.exports) {
|
||||
newMetadata.exports = dtsMetadata.exports;
|
||||
}
|
||||
}
|
||||
return newMetadata;
|
||||
}
|
||||
|
||||
loadResource(filePath: string): Promise<string>|string {
|
||||
if (this.context.readResource) return this.context.readResource(filePath);
|
||||
if (!this.context.fileExists(filePath)) {
|
||||
throw syntaxError(`Error: Resource file not found: ${filePath}`);
|
||||
}
|
||||
return this.context.readFile(filePath);
|
||||
}
|
||||
|
||||
loadSummary(filePath: string): string|null {
|
||||
if (this.context.fileExists(filePath)) {
|
||||
return this.context.readFile(filePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
isSourceFile(filePath: string): boolean {
|
||||
const excludeRegex =
|
||||
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
||||
if (excludeRegex.test(filePath)) {
|
||||
return false;
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
// Check for a bundle index.
|
||||
if (this.hasBundleIndex(filePath)) {
|
||||
const normalFilePath = path.normalize(filePath);
|
||||
return this.flatModuleIndexNames.has(normalFilePath) ||
|
||||
this.flatModuleIndexRedirectNames.has(normalFilePath);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private hasBundleIndex(filePath: string): boolean {
|
||||
const checkBundleIndex = (directory: string): boolean => {
|
||||
let result = this.flatModuleIndexCache.get(directory);
|
||||
if (result == null) {
|
||||
if (path.basename(directory) == 'node_module') {
|
||||
// Don't look outside the node_modules this package is installed in.
|
||||
result = false;
|
||||
} else {
|
||||
// A bundle index exists if the typings .d.ts file has a metadata.json that has an
|
||||
// importAs.
|
||||
try {
|
||||
const packageFile = path.join(directory, 'package.json');
|
||||
if (this.context.fileExists(packageFile)) {
|
||||
// Once we see a package.json file, assume false until it we find the bundle index.
|
||||
result = false;
|
||||
const packageContent: any = JSON.parse(this.context.readFile(packageFile));
|
||||
if (packageContent.typings) {
|
||||
const typings = path.normalize(path.join(directory, packageContent.typings));
|
||||
if (DTS.test(typings)) {
|
||||
const metadataFile = typings.replace(DTS, '.metadata.json');
|
||||
if (this.context.fileExists(metadataFile)) {
|
||||
const metadata = JSON.parse(this.context.readFile(metadataFile));
|
||||
if (metadata.flatModuleIndexRedirect) {
|
||||
this.flatModuleIndexRedirectNames.add(typings);
|
||||
// Note: don't set result = true,
|
||||
// as this would mark this folder
|
||||
// as having a bundleIndex too early without
|
||||
// filling the bundleIndexNames.
|
||||
} else if (metadata.importAs) {
|
||||
this.flatModuleIndexNames.add(typings);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const parent = path.dirname(directory);
|
||||
if (parent != directory) {
|
||||
// Try the parent directory.
|
||||
result = checkBundleIndex(parent);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If we encounter any errors assume we this isn't a bundle index.
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
this.flatModuleIndexCache.set(directory, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return checkBundleIndex(path.dirname(filePath));
|
||||
}
|
||||
}
|
||||
|
||||
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
||||
readResource?(fileName: string): Promise<string>|string;
|
||||
assumeFileExists(fileName: string): void;
|
||||
}
|
||||
|
||||
// TODO(tbosch): remove this once G3 uses the transformer compiler!
|
||||
export class CompilerHost extends BaseAotCompilerHost<CompilerHostContext> {
|
||||
protected metadataProvider: MetadataCollector;
|
||||
protected basePath: string;
|
||||
private moduleFileNames = new Map<string, string|null>();
|
||||
private isGenDirChildOfRootDir: boolean;
|
||||
private genDir: string;
|
||||
protected resolveModuleNameHost: CompilerHostContext;
|
||||
private urlResolver: UrlResolver;
|
||||
|
||||
constructor(
|
||||
protected program: ts.Program, options: CompilerOptions, context: CompilerHostContext,
|
||||
collectorOptions?: CollectorOptions) {
|
||||
super(options, context);
|
||||
this.metadataProvider = new MetadataCollector(collectorOptions);
|
||||
// normalize the path so that it never ends with '/'.
|
||||
this.basePath = path.normalize(path.join(this.options.basePath !, '.')).replace(/\\/g, '/');
|
||||
this.genDir = path.normalize(path.join(this.options.genDir !, '.')).replace(/\\/g, '/');
|
||||
|
||||
const genPath: string = path.relative(this.basePath, this.genDir);
|
||||
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
||||
|
||||
this.resolveModuleNameHost = Object.create(this.context);
|
||||
|
||||
// When calling ts.resolveModuleName,
|
||||
// additional allow checks for .d.ts files to be done based on
|
||||
// checks for .ngsummary.json files,
|
||||
// so that our codegen depends on fewer inputs and requires to be called
|
||||
// less often.
|
||||
// This is needed as we use ts.resolveModuleName in reflector_host
|
||||
// and it should be able to resolve summary file names.
|
||||
this.resolveModuleNameHost.fileExists = (fileName: string): boolean => {
|
||||
if (this.context.fileExists(fileName)) {
|
||||
return true;
|
||||
}
|
||||
if (DTS.test(fileName)) {
|
||||
const base = fileName.substring(0, fileName.length - 5);
|
||||
return this.context.fileExists(base + '.ngsummary.json');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
this.urlResolver = createOfflineCompileUrlResolver();
|
||||
}
|
||||
|
||||
protected getSourceFile(filePath: string): ts.SourceFile {
|
||||
let sf = this.program.getSourceFile(filePath);
|
||||
if (!sf) {
|
||||
if (this.context.fileExists(filePath)) {
|
||||
const sourceText = this.context.readFile(filePath);
|
||||
sf = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
|
||||
} else {
|
||||
throw new Error(`Source file ${filePath} not present in program.`);
|
||||
}
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
getMetadataForSourceFile(filePath: string): ModuleMetadata|undefined {
|
||||
return this.metadataProvider.getMetadata(this.getSourceFile(filePath));
|
||||
}
|
||||
|
||||
toSummaryFileName(fileName: string, referringSrcFileName: string): string {
|
||||
return fileName.replace(EXT, '') + '.d.ts';
|
||||
}
|
||||
|
||||
fromSummaryFileName(fileName: string, referringLibFileName: string): string { return fileName; }
|
||||
|
||||
calculateEmitPath(filePath: string): string {
|
||||
// Write codegen in a directory structure matching the sources.
|
||||
let root = this.options.basePath !;
|
||||
for (const eachRootDir of this.options.rootDirs || []) {
|
||||
if (this.options.trace) {
|
||||
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||
}
|
||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||
root = eachRootDir;
|
||||
}
|
||||
}
|
||||
|
||||
// transplant the codegen path to be inside the `genDir`
|
||||
let relativePath: string = path.relative(root, filePath);
|
||||
while (relativePath.startsWith('..' + path.sep)) {
|
||||
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
||||
// into `genDir`.
|
||||
relativePath = relativePath.substr(3);
|
||||
}
|
||||
|
||||
return path.join(this.options.genDir !, relativePath);
|
||||
}
|
||||
|
||||
// We use absolute paths on disk as canonical.
|
||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||
|
||||
moduleNameToFileName(m: string, containingFile: string): string|null {
|
||||
const key = m + ':' + (containingFile || '');
|
||||
let result: string|null = this.moduleFileNames.get(key) || null;
|
||||
if (!result) {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (m.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
||||
}
|
||||
m = m.replace(EXT, '');
|
||||
const resolved =
|
||||
ts.resolveModuleName(
|
||||
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
|
||||
.resolvedModule;
|
||||
result = resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
||||
this.moduleFileNames.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want a moduleId that will appear in import statements in the generated code.
|
||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
||||
*
|
||||
* The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
|
||||
* `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
|
||||
* existing file.
|
||||
*
|
||||
* | genDir | node_module | rootDir
|
||||
* --------------+----------+-------------+----------
|
||||
* generated | relative | relative | n/a
|
||||
* existing file | n/a | absolute | relative(*)
|
||||
*
|
||||
* NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
|
||||
*/
|
||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||
// If a file does not yet exist (because we compile it later), we still need to
|
||||
// assume it exists it so that the `resolve` method works!
|
||||
if (importedFile !== containingFile && !this.context.fileExists(importedFile)) {
|
||||
this.context.assumeFileExists(importedFile);
|
||||
}
|
||||
|
||||
containingFile = this.rewriteGenDirPath(containingFile);
|
||||
const containingDir = path.dirname(containingFile);
|
||||
// drop extension
|
||||
importedFile = importedFile.replace(EXT, '');
|
||||
|
||||
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
||||
const importModule = nodeModulesIndex === -1 ?
|
||||
null :
|
||||
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
||||
const isGeneratedFile = IS_GENERATED.test(importedFile);
|
||||
|
||||
if (isGeneratedFile) {
|
||||
// rewrite to genDir path
|
||||
if (importModule) {
|
||||
// it is generated, therefore we do a relative path to the factory
|
||||
return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
|
||||
} else {
|
||||
// assume that import is also in `genDir`
|
||||
importedFile = this.rewriteGenDirPath(importedFile);
|
||||
return this.dotRelative(containingDir, importedFile);
|
||||
}
|
||||
} else {
|
||||
// user code import
|
||||
if (importModule) {
|
||||
return importModule;
|
||||
} else {
|
||||
if (!this.isGenDirChildOfRootDir) {
|
||||
// assume that they are on top of each other.
|
||||
importedFile = importedFile.replace(this.basePath, this.genDir);
|
||||
}
|
||||
if (SHALLOW_IMPORT.test(importedFile)) {
|
||||
return importedFile;
|
||||
}
|
||||
return this.dotRelative(containingDir, importedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resourceNameToFileName(m: string, containingFile: string): string {
|
||||
return this.urlResolver.resolve(containingFile, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
||||
*/
|
||||
private rewriteGenDirPath(filepath: string) {
|
||||
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
||||
if (nodeModulesIndex !== -1) {
|
||||
// If we are in node_modules, transplant them into `genDir`.
|
||||
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
||||
} else {
|
||||
// pretend that containing file is on top of the `genDir` to normalize the paths.
|
||||
// we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
|
||||
return filepath.replace(this.basePath, this.genDir);
|
||||
}
|
||||
}
|
||||
|
||||
private dotRelative(from: string, to: string): string {
|
||||
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
||||
return rPath.startsWith('.') ? rPath : './' + rPath;
|
||||
}
|
||||
}
|
||||
|
||||
export class CompilerHostContextAdapter {
|
||||
protected assumedExists: {[fileName: string]: boolean} = {};
|
||||
|
||||
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
|
||||
}
|
||||
|
||||
export class ModuleResolutionHostAdapter extends CompilerHostContextAdapter implements
|
||||
CompilerHostContext {
|
||||
public directoryExists: ((directoryName: string) => boolean)|undefined;
|
||||
|
||||
constructor(private host: ts.ModuleResolutionHost) {
|
||||
super();
|
||||
if (host.directoryExists) {
|
||||
this.directoryExists = (directoryName: string) => host.directoryExists !(directoryName);
|
||||
}
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.assumedExists[fileName] || this.host.fileExists(fileName);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string { return this.host.readFile(fileName); }
|
||||
|
||||
readResource(s: string) {
|
||||
if (!this.host.fileExists(s)) {
|
||||
// TODO: We should really have a test for error cases like this!
|
||||
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
||||
}
|
||||
return Promise.resolve(this.host.readFile(s));
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeCompilerHostContext extends CompilerHostContextAdapter implements
|
||||
CompilerHostContext {
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.assumedExists[fileName] || fs.existsSync(fileName);
|
||||
}
|
||||
|
||||
directoryExists(directoryName: string): boolean {
|
||||
try {
|
||||
return fs.statSync(directoryName).isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
||||
|
||||
readResource(s: string) {
|
||||
if (!this.fileExists(s)) {
|
||||
// TODO: We should really have a test for error cases like this!
|
||||
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
||||
}
|
||||
return Promise.resolve(this.readFile(s));
|
||||
}
|
||||
}
|
@ -38,10 +38,10 @@ export function translateDiagnostics(host: TypeCheckHost, untranslatedDiagnostic
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ts.push(diagnostic);
|
||||
}
|
||||
ts.push(diagnostic);
|
||||
});
|
||||
return {ts, ng};
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Extract i18n messages from source code
|
||||
*/
|
||||
// Must be imported first, because Angular decorators throw on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
|
||||
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||
import {CompilerOptions} from './transformers/api';
|
||||
import {i18nExtract, i18nGetExtension, i18nSerialize} from './transformers/program';
|
||||
|
||||
export class Extractor {
|
||||
constructor(
|
||||
private options: CompilerOptions, private ngExtractor: compiler.Extractor,
|
||||
public host: ts.CompilerHost, private ngCompilerHost: CompilerHost,
|
||||
private program: ts.Program) {}
|
||||
|
||||
extract(formatName: string, outFile: string|null): Promise<string[]> {
|
||||
return this.extractBundle().then(
|
||||
bundle => i18nExtract(formatName, outFile, this.host, this.options, bundle));
|
||||
}
|
||||
|
||||
extractBundle(): Promise<compiler.MessageBundle> {
|
||||
const files = this.program.getSourceFiles().map(
|
||||
sf => this.ngCompilerHost.getCanonicalFileName(sf.fileName));
|
||||
|
||||
return this.ngExtractor.extract(files);
|
||||
}
|
||||
|
||||
serialize(bundle: compiler.MessageBundle, formatName: string): string {
|
||||
return i18nSerialize(bundle, formatName, this.options);
|
||||
}
|
||||
|
||||
getExtension(formatName: string): string { return i18nGetExtension(formatName); }
|
||||
|
||||
static create(
|
||||
options: CompilerOptions, program: ts.Program, tsCompilerHost: ts.CompilerHost,
|
||||
locale?: string|null, compilerHostContext?: CompilerHostContext,
|
||||
ngCompilerHost?: CompilerHost): Extractor {
|
||||
if (!ngCompilerHost) {
|
||||
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
|
||||
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
|
||||
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
|
||||
new CompilerHost(program, options, context);
|
||||
}
|
||||
|
||||
const {extractor: ngExtractor} = compiler.Extractor.create(ngCompilerHost, locale || null);
|
||||
|
||||
return new Extractor(options, ngExtractor, tsCompilerHost, ngCompilerHost, program);
|
||||
}
|
||||
}
|
@ -14,9 +14,10 @@ Angular modules and Typescript as this will indirectly add a dependency
|
||||
to the language service.
|
||||
|
||||
*/
|
||||
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './compiler_host';
|
||||
export {DiagnosticTemplateInfo, ExpressionDiagnostic, getExpressionDiagnostics, getExpressionScope, getTemplateExpressionDiagnostics} from './diagnostics/expression_diagnostics';
|
||||
export {AstType, DiagnosticKind, ExpressionDiagnosticsContext, TypeDiagnostic} from './diagnostics/expression_type';
|
||||
export {BuiltinType, DeclarationKind, Definition, Location, PipeInfo, Pipes, Signature, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './diagnostics/symbols';
|
||||
export {getClassFromStaticSymbol, getClassMembers, getClassMembersFromDeclaration, getPipesTable, getSymbolQuery} from './diagnostics/typescript_symbols';
|
||||
export {MetadataCollector, ModuleMetadata} from './metadata';
|
||||
export {CompilerOptions} from './transformers/api';
|
||||
export {MetadataReaderCache, MetadataReaderHost, createMetadataReaderCache, readMetadata} from './transformers/metadata_reader';
|
||||
|
@ -28,15 +28,15 @@ export function main(
|
||||
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
|
||||
config || readNgcCommandLineAndConfiguration(args);
|
||||
if (configErrors.length) {
|
||||
return reportErrorsAndExit(configErrors, consoleError);
|
||||
return reportErrorsAndExit(configErrors, /*options*/ undefined, consoleError);
|
||||
}
|
||||
if (watch) {
|
||||
const result = watchMode(project, options, consoleError);
|
||||
return reportErrorsAndExit(result.firstCompileResult, consoleError);
|
||||
return reportErrorsAndExit(result.firstCompileResult, options, consoleError);
|
||||
}
|
||||
const {diagnostics: compileDiags} = performCompilation(
|
||||
{rootNames, options, emitFlags, emitCallback: createEmitCallback(options)});
|
||||
return reportErrorsAndExit(compileDiags, consoleError);
|
||||
return reportErrorsAndExit(compileDiags, options, consoleError);
|
||||
}
|
||||
|
||||
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
|
||||
@ -45,6 +45,12 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
||||
if (!transformDecorators && !transformTypesToClosure) {
|
||||
return undefined;
|
||||
}
|
||||
if (transformDecorators) {
|
||||
// This is needed as a workaround for https://github.com/angular/tsickle/issues/635
|
||||
// Otherwise tsickle might emit references to non imported values
|
||||
// as TypeScript elided the import.
|
||||
options.emitDecoratorMetadata = true;
|
||||
}
|
||||
const tsickleHost: tsickle.TsickleHost = {
|
||||
shouldSkipTsickleProcessing: (fileName) =>
|
||||
/\.d\.ts$/.test(fileName) || GENERATED_FILES.test(fileName),
|
||||
@ -128,10 +134,17 @@ export function readCommandLineAndConfiguration(
|
||||
}
|
||||
|
||||
function reportErrorsAndExit(
|
||||
allDiagnostics: Diagnostics, consoleError: (s: string) => void = console.error): number {
|
||||
allDiagnostics: Diagnostics, options?: api.CompilerOptions,
|
||||
consoleError: (s: string) => void = console.error): number {
|
||||
const errorsAndWarnings = filterErrorsAndWarnings(allDiagnostics);
|
||||
if (errorsAndWarnings.length) {
|
||||
consoleError(formatDiagnostics(errorsAndWarnings));
|
||||
let currentDir = options ? options.basePath : undefined;
|
||||
const formatHost: ts.FormatDiagnosticsHost = {
|
||||
getCurrentDirectory: () => currentDir || ts.sys.getCurrentDirectory(),
|
||||
getCanonicalFileName: fileName => fileName,
|
||||
getNewLine: () => ts.sys.newLine
|
||||
};
|
||||
consoleError(formatDiagnostics(errorsAndWarnings, formatHost));
|
||||
}
|
||||
return exitCodeFromResult(allDiagnostics);
|
||||
}
|
||||
|
@ -13,15 +13,17 @@
|
||||
* something else.
|
||||
*/
|
||||
|
||||
import {AotCompilerHost, AotSummaryResolver, StaticReflector, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
|
||||
/**
|
||||
*********************************************************************
|
||||
* Changes to this file need to be approved by the Angular CLI team. *
|
||||
*********************************************************************
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CodeGenerator} from './codegen';
|
||||
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
|
||||
import {Extractor} from './extractor';
|
||||
import {listLazyRoutesOfModule} from './ngtools_impl';
|
||||
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
|
||||
import {CompilerOptions} from './transformers/api';
|
||||
import {CompilerHost, CompilerOptions, LazyRoute} from './transformers/api';
|
||||
import {getOriginalReferences} from './transformers/compiler_host';
|
||||
import {createProgram} from './transformers/entry_points';
|
||||
|
||||
export interface NgTools_InternalApi_NG2_CodeGen_Options {
|
||||
basePath: string;
|
||||
@ -66,47 +68,16 @@ export interface NgTools_InternalApi_NG2_ExtractI18n_Options {
|
||||
outFile?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ModuleResolutionHostAdapter that overrides the readResource() method with the one
|
||||
* passed in the interface.
|
||||
*/
|
||||
class CustomLoaderModuleResolutionHostAdapter extends ModuleResolutionHostAdapter {
|
||||
constructor(
|
||||
private _readResource: (path: string) => Promise<string>, host: ts.ModuleResolutionHost) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
readResource(path: string) { return this._readResource(path); }
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecatd Use ngtools_api2 instead!
|
||||
*/
|
||||
export class NgTools_InternalApi_NG_2 {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
static codeGen(options: NgTools_InternalApi_NG2_CodeGen_Options): Promise<any> {
|
||||
const hostContext: CompilerHostContext =
|
||||
new CustomLoaderModuleResolutionHostAdapter(options.readResource, options.host);
|
||||
|
||||
const i18nOptions = {
|
||||
i18nFormat: options.i18nFormat !,
|
||||
i18nFile: options.i18nFile !,
|
||||
locale: options.locale !,
|
||||
missingTranslation: options.missingTranslation !,
|
||||
};
|
||||
const ngOptions = options.angularCompilerOptions;
|
||||
if (ngOptions.enableSummariesForJit === undefined) {
|
||||
// default to false
|
||||
ngOptions.enableSummariesForJit = false;
|
||||
}
|
||||
|
||||
// Create the Code Generator.
|
||||
const codeGenerator =
|
||||
CodeGenerator.create(ngOptions, i18nOptions, options.program, options.host, hostContext);
|
||||
|
||||
return codeGenerator.codegen();
|
||||
throw throwNotSupportedError();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,42 +85,49 @@ export class NgTools_InternalApi_NG_2 {
|
||||
*/
|
||||
static listLazyRoutes(options: NgTools_InternalApi_NG2_ListLazyRoutes_Options):
|
||||
NgTools_InternalApi_NG_2_LazyRouteMap {
|
||||
const angularCompilerOptions = options.angularCompilerOptions;
|
||||
const program = options.program;
|
||||
// TODO(tbosch): Also throwNotSupportedError once Angular CLI 1.5.1 ships,
|
||||
// as we only needed this to support Angular CLI 1.5.0 rc.*
|
||||
const ngProgram = createProgram({
|
||||
rootNames: options.program.getRootFileNames(),
|
||||
options: {...options.angularCompilerOptions, collectAllErrors: true},
|
||||
host: options.host
|
||||
});
|
||||
const lazyRoutes = ngProgram.listLazyRoutes(options.entryModule);
|
||||
|
||||
const moduleResolutionHost = new ModuleResolutionHostAdapter(options.host);
|
||||
const usePathMapping =
|
||||
!!angularCompilerOptions.rootDirs && angularCompilerOptions.rootDirs.length > 0;
|
||||
const ngCompilerHost: AotCompilerHost = usePathMapping ?
|
||||
new PathMappedCompilerHost(program, angularCompilerOptions, moduleResolutionHost) :
|
||||
new CompilerHost(program, angularCompilerOptions, moduleResolutionHost);
|
||||
// reset the referencedFiles that the ng.Program added to the SourceFiles
|
||||
// as the host might be caching the source files!
|
||||
for (const sourceFile of options.program.getSourceFiles()) {
|
||||
const originalReferences = getOriginalReferences(sourceFile);
|
||||
if (originalReferences) {
|
||||
sourceFile.referencedFiles = originalReferences;
|
||||
}
|
||||
}
|
||||
|
||||
const symbolCache = new StaticSymbolCache();
|
||||
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
|
||||
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
|
||||
const staticReflector = new StaticReflector(summaryResolver, symbolResolver);
|
||||
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
|
||||
const result: NgTools_InternalApi_NG_2_LazyRouteMap = {};
|
||||
lazyRoutes.forEach(lazyRoute => {
|
||||
const route = lazyRoute.route;
|
||||
const referencedFilePath = lazyRoute.referencedModule.filePath;
|
||||
if (result[route] && result[route] != referencedFilePath) {
|
||||
throw new Error(
|
||||
`Duplicated path in loadChildren detected: "${route}" is used in 2 loadChildren, ` +
|
||||
`but they point to different modules "(${result[route]} and ` +
|
||||
`"${referencedFilePath}"). Webpack cannot distinguish on context and would fail to ` +
|
||||
'load the proper one.');
|
||||
}
|
||||
result[route] = referencedFilePath;
|
||||
});
|
||||
|
||||
return Object.keys(routeMap).reduce(
|
||||
(acc: NgTools_InternalApi_NG_2_LazyRouteMap, route: string) => {
|
||||
acc[route] = routeMap[route].absoluteFilePath;
|
||||
return acc;
|
||||
},
|
||||
{});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
static extractI18n(options: NgTools_InternalApi_NG2_ExtractI18n_Options): Promise<any> {
|
||||
const hostContext: CompilerHostContext =
|
||||
new CustomLoaderModuleResolutionHostAdapter(options.readResource, options.host);
|
||||
|
||||
// Create the i18n extractor.
|
||||
const locale = options.locale || null;
|
||||
const extractor = Extractor.create(
|
||||
options.angularCompilerOptions, options.program, options.host, locale, hostContext);
|
||||
|
||||
return extractor.extract(options.i18nFormat !, options.outFile || null);
|
||||
throw throwNotSupportedError();
|
||||
}
|
||||
}
|
||||
|
||||
function throwNotSupportedError() {
|
||||
throw new Error(`Please update @angular/cli. Angular 5+ requires at least Angular CLI 1.5+`);
|
||||
}
|
||||
|
@ -15,6 +15,12 @@
|
||||
* Once the ngc api is public and stable, this can be removed.
|
||||
*/
|
||||
|
||||
/**
|
||||
*********************************************************************
|
||||
* Changes to this file need to be approved by the Angular CLI team. *
|
||||
*********************************************************************
|
||||
*/
|
||||
|
||||
import {ParseSourceSpan} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
@ -94,6 +100,12 @@ export interface TsEmitArguments {
|
||||
|
||||
export interface TsEmitCallback { (args: TsEmitArguments): ts.EmitResult; }
|
||||
|
||||
export interface LazyRoute {
|
||||
module: {name: string, filePath: string};
|
||||
route: string;
|
||||
referencedModule: {name: string, filePath: string};
|
||||
}
|
||||
|
||||
export interface Program {
|
||||
getTsProgram(): ts.Program;
|
||||
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken): ts.Diagnostic[];
|
||||
@ -106,6 +118,7 @@ export interface Program {
|
||||
getNgSemanticDiagnostics(fileName?: string, cancellationToken?: ts.CancellationToken):
|
||||
Diagnostic[];
|
||||
loadNgStructureAsync(): Promise<void>;
|
||||
listLazyRoutes(entryRoute?: string): LazyRoute[];
|
||||
emit({emitFlags, cancellationToken, customTransformers, emitCallback}: {
|
||||
emitFlags?: EmitFlags,
|
||||
cancellationToken?: ts.CancellationToken,
|
||||
|
@ -1,215 +0,0 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a private API for the ngtools toolkit.
|
||||
*
|
||||
* This API should be stable for NG 2. It can be removed in NG 4..., but should be replaced by
|
||||
* something else.
|
||||
*/
|
||||
import {AotCompilerHost, StaticReflector, StaticSymbol, core} from '@angular/compiler';
|
||||
|
||||
|
||||
// We cannot depend directly to @angular/router.
|
||||
type Route = any;
|
||||
const ROUTER_MODULE_PATH = '@angular/router';
|
||||
const ROUTER_ROUTES_SYMBOL_NAME = 'ROUTES';
|
||||
|
||||
|
||||
// LazyRoute information between the extractors.
|
||||
export interface LazyRoute {
|
||||
routeDef: RouteDef;
|
||||
absoluteFilePath: string;
|
||||
}
|
||||
export type LazyRouteMap = {
|
||||
[route: string]: LazyRoute
|
||||
};
|
||||
|
||||
// A route definition. Normally the short form 'path/to/module#ModuleClassName' is used by
|
||||
// the user, and this is a helper class to extract information from it.
|
||||
export class RouteDef {
|
||||
private constructor(public readonly path: string, public readonly className: string|null = null) {
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (this.className === null || this.className == 'default') ?
|
||||
this.path :
|
||||
`${this.path}#${this.className}`;
|
||||
}
|
||||
|
||||
static fromString(entry: string): RouteDef {
|
||||
const split = entry.split('#');
|
||||
return new RouteDef(split[0], split[1] || null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function listLazyRoutesOfModule(
|
||||
entryModule: string, host: AotCompilerHost, reflector: StaticReflector): LazyRouteMap {
|
||||
const entryRouteDef = RouteDef.fromString(entryModule);
|
||||
const containingFile = _resolveModule(entryRouteDef.path, entryRouteDef.path, host);
|
||||
const modulePath = `./${containingFile.replace(/^(.*)\//, '')}`;
|
||||
const className = entryRouteDef.className !;
|
||||
|
||||
// List loadChildren of this single module.
|
||||
const appStaticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
|
||||
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
|
||||
const lazyRoutes: LazyRoute[] =
|
||||
_extractLazyRoutesFromStaticModule(appStaticSymbol, reflector, host, ROUTES);
|
||||
|
||||
const allLazyRoutes = lazyRoutes.reduce(
|
||||
function includeLazyRouteAndSubRoutes(allRoutes: LazyRouteMap, lazyRoute: LazyRoute):
|
||||
LazyRouteMap {
|
||||
const route: string = lazyRoute.routeDef.toString();
|
||||
_assertRoute(allRoutes, lazyRoute);
|
||||
allRoutes[route] = lazyRoute;
|
||||
|
||||
// StaticReflector does not support discovering annotations like `NgModule` on default
|
||||
// exports
|
||||
// Which means: if a default export NgModule was lazy-loaded, we can discover it, but,
|
||||
// we cannot parse its routes to see if they have loadChildren or not.
|
||||
if (!lazyRoute.routeDef.className) {
|
||||
return allRoutes;
|
||||
}
|
||||
|
||||
const lazyModuleSymbol = reflector.findDeclaration(
|
||||
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
|
||||
|
||||
const subRoutes =
|
||||
_extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
|
||||
|
||||
return subRoutes.reduce(includeLazyRouteAndSubRoutes, allRoutes);
|
||||
},
|
||||
{});
|
||||
|
||||
return allLazyRoutes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to resolve a module, and returns its absolute path.
|
||||
* @private
|
||||
*/
|
||||
function _resolveModule(modulePath: string, containingFile: string, host: AotCompilerHost) {
|
||||
const result = host.moduleNameToFileName(modulePath, containingFile);
|
||||
if (!result) {
|
||||
throw new Error(`Could not resolve "${modulePath}" from "${containingFile}".`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throw an exception if a route is in a route map, but does not point to the same module.
|
||||
*/
|
||||
function _assertRoute(map: LazyRouteMap, route: LazyRoute) {
|
||||
const r = route.routeDef.toString();
|
||||
if (map[r] && map[r].absoluteFilePath != route.absoluteFilePath) {
|
||||
throw new Error(
|
||||
`Duplicated path in loadChildren detected: "${r}" is used in 2 loadChildren, ` +
|
||||
`but they point to different modules "(${map[r].absoluteFilePath} and ` +
|
||||
`"${route.absoluteFilePath}"). Webpack cannot distinguish on context and would fail to ` +
|
||||
'load the proper one.');
|
||||
}
|
||||
}
|
||||
|
||||
export function flatten<T>(list: Array<T|T[]>): T[] {
|
||||
return list.reduce((flat: any[], item: T | T[]): T[] => {
|
||||
const flatItem = Array.isArray(item) ? flatten(item) : item;
|
||||
return (<T[]>flat).concat(flatItem);
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all the LazyRoutes from a module. This extracts all `loadChildren` keys from this
|
||||
* module and all statically referred modules.
|
||||
* @private
|
||||
*/
|
||||
function _extractLazyRoutesFromStaticModule(
|
||||
staticSymbol: StaticSymbol, reflector: StaticReflector, host: AotCompilerHost,
|
||||
ROUTES: StaticSymbol): LazyRoute[] {
|
||||
const moduleMetadata = _getNgModuleMetadata(staticSymbol, reflector);
|
||||
const imports = flatten(moduleMetadata.imports || []);
|
||||
const allRoutes: any = imports.filter(i => 'providers' in i).reduce((mem: Route[], m: any) => {
|
||||
return mem.concat(_collectRoutes(m.providers || [], reflector, ROUTES));
|
||||
}, _collectRoutes(moduleMetadata.providers || [], reflector, ROUTES));
|
||||
|
||||
const lazyRoutes: LazyRoute[] =
|
||||
_collectLoadChildren(allRoutes).reduce((acc: LazyRoute[], route: string) => {
|
||||
const routeDef = RouteDef.fromString(route);
|
||||
const absoluteFilePath = _resolveModule(routeDef.path, staticSymbol.filePath, host);
|
||||
acc.push({routeDef, absoluteFilePath});
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const importedSymbols =
|
||||
(imports as any[])
|
||||
.filter(i => i instanceof StaticSymbol || i.ngModule instanceof StaticSymbol)
|
||||
.map(i => {
|
||||
if (i instanceof StaticSymbol) return i;
|
||||
return i.ngModule;
|
||||
}) as StaticSymbol[];
|
||||
|
||||
return importedSymbols
|
||||
.reduce(
|
||||
(acc: LazyRoute[], i: StaticSymbol) => {
|
||||
return acc.concat(_extractLazyRoutesFromStaticModule(i, reflector, host, ROUTES));
|
||||
},
|
||||
[])
|
||||
.concat(lazyRoutes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the NgModule Metadata of a symbol.
|
||||
*/
|
||||
function _getNgModuleMetadata(
|
||||
staticSymbol: StaticSymbol, reflector: StaticReflector): core.NgModule {
|
||||
const ngModules =
|
||||
reflector.annotations(staticSymbol).filter((s: any) => core.createNgModule.isTypeOf(s));
|
||||
if (ngModules.length === 0) {
|
||||
throw new Error(`${staticSymbol.name} is not an NgModule`);
|
||||
}
|
||||
return ngModules[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the routes from the provider list.
|
||||
* @private
|
||||
*/
|
||||
function _collectRoutes(
|
||||
providers: any[], reflector: StaticReflector, ROUTES: StaticSymbol): Route[] {
|
||||
return providers.reduce((routeList: Route[], p: any) => {
|
||||
if (p.provide === ROUTES) {
|
||||
return routeList.concat(p.useValue);
|
||||
} else if (Array.isArray(p)) {
|
||||
return routeList.concat(_collectRoutes(p, reflector, ROUTES));
|
||||
} else {
|
||||
return routeList;
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the loadChildren values of a list of Route.
|
||||
*/
|
||||
function _collectLoadChildren(routes: Route[]): string[] {
|
||||
return routes.reduce((m, r) => {
|
||||
if (r.loadChildren && typeof r.loadChildren === 'string') {
|
||||
return m.concat(r.loadChildren);
|
||||
} else if (Array.isArray(r)) {
|
||||
return m.concat(_collectLoadChildren(r));
|
||||
} else if (r.children) {
|
||||
return m.concat(_collectLoadChildren(r.children));
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}, []);
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
/**
|
||||
* @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 {StaticSymbol} from '@angular/compiler';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost, CompilerHostContext} from './compiler_host';
|
||||
import {ModuleMetadata} from './metadata/index';
|
||||
import {CompilerOptions} from './transformers/api';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
/**
|
||||
* This version of the AotCompilerHost expects that the program will be compiled
|
||||
* and executed with a "path mapped" directory structure, where generated files
|
||||
* are in a parallel tree with the sources, and imported using a `./` relative
|
||||
* import. This requires using TS `rootDirs` option and also teaching the module
|
||||
* loader what to do.
|
||||
*/
|
||||
export class PathMappedCompilerHost extends CompilerHost {
|
||||
constructor(program: ts.Program, options: CompilerOptions, context: CompilerHostContext) {
|
||||
super(program, options, context);
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
if (!fileName) return fileName;
|
||||
// NB: the rootDirs should have been sorted longest-first
|
||||
for (const dir of this.options.rootDirs || []) {
|
||||
if (fileName.indexOf(dir) === 0) {
|
||||
fileName = fileName.substring(dir.length);
|
||||
}
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
moduleNameToFileName(m: string, containingFile: string): string|null {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (m.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
||||
}
|
||||
for (const root of this.options.rootDirs || ['']) {
|
||||
const rootedContainingFile = path.join(root, containingFile);
|
||||
const resolved =
|
||||
ts.resolveModuleName(m, rootedContainingFile, this.options, this.context).resolvedModule;
|
||||
if (resolved) {
|
||||
if (this.options.traceResolution) {
|
||||
console.error('resolve', m, containingFile, '=>', resolved.resolvedFileName);
|
||||
}
|
||||
return this.getCanonicalFileName(resolved.resolvedFileName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want a moduleId that will appear in import statements in the generated code.
|
||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
||||
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
|
||||
* they are resolvable by the moduleResolution strategy from the CompilerHost.
|
||||
*/
|
||||
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
||||
if (this.options.traceResolution) {
|
||||
console.error(
|
||||
'getImportPath from containingFile', containingFile, 'to importedFile', importedFile);
|
||||
}
|
||||
|
||||
// If a file does not yet exist (because we compile it later), we still need to
|
||||
// assume it exists so that the `resolve` method works!
|
||||
if (!this.context.fileExists(importedFile)) {
|
||||
if (this.options.rootDirs && this.options.rootDirs.length > 0) {
|
||||
this.context.assumeFileExists(path.join(this.options.rootDirs[0], importedFile));
|
||||
} else {
|
||||
this.context.assumeFileExists(importedFile);
|
||||
}
|
||||
}
|
||||
|
||||
const resolvable = (candidate: string) => {
|
||||
const resolved = this.moduleNameToFileName(candidate, importedFile);
|
||||
return resolved && resolved.replace(EXT, '') === importedFile.replace(EXT, '');
|
||||
};
|
||||
|
||||
const importModuleName = importedFile.replace(EXT, '');
|
||||
const parts = importModuleName.split(path.sep).filter(p => !!p);
|
||||
let foundRelativeImport: string = undefined !;
|
||||
for (let index = parts.length - 1; index >= 0; index--) {
|
||||
let candidate = parts.slice(index, parts.length).join(path.sep);
|
||||
if (resolvable(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
candidate = '.' + path.sep + candidate;
|
||||
if (resolvable(candidate)) {
|
||||
foundRelativeImport = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundRelativeImport) return foundRelativeImport;
|
||||
|
||||
// Try a relative import
|
||||
const candidate = path.relative(path.dirname(containingFile), importModuleName);
|
||||
if (resolvable(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
||||
}
|
||||
|
||||
getMetadataFor(filePath: string): ModuleMetadata[] {
|
||||
for (const root of this.options.rootDirs || []) {
|
||||
const rootedPath = path.join(root, filePath);
|
||||
if (!this.context.fileExists(rootedPath)) {
|
||||
// If the file doesn't exists then we cannot return metadata for the file.
|
||||
// This will occur if the user refernced a declared module for which no file
|
||||
// exists for the module (i.e. jQuery or angularjs).
|
||||
continue;
|
||||
}
|
||||
if (DTS.test(rootedPath)) {
|
||||
const metadatas = this.readMetadata(rootedPath);
|
||||
if (metadatas) {
|
||||
return metadatas;
|
||||
}
|
||||
} else {
|
||||
// Attention: don't cache this, so that e.g. the LanguageService
|
||||
// can read in changes from source files in the metadata!
|
||||
const metadata = this.getMetadataForSourceFile(rootedPath);
|
||||
return metadata ? [metadata] : [];
|
||||
}
|
||||
}
|
||||
return null !;
|
||||
}
|
||||
}
|
@ -129,6 +129,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
return {close, ready: cb => readyPromise.then(cb), firstCompileResult};
|
||||
|
||||
function cacheEntry(fileName: string): CacheEntry {
|
||||
fileName = path.normalize(fileName);
|
||||
let entry = fileCache.get(fileName);
|
||||
if (!entry) {
|
||||
entry = {};
|
||||
@ -191,6 +192,10 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
};
|
||||
}
|
||||
ingoreFilesForWatch.clear();
|
||||
const oldProgram = cachedProgram;
|
||||
// We clear out the `cachedProgram` here as a
|
||||
// program can only be used as `oldProgram` 1x
|
||||
cachedProgram = undefined;
|
||||
const compileResult = performCompilation({
|
||||
rootNames: cachedOptions.rootNames,
|
||||
options: cachedOptions.options,
|
||||
@ -245,7 +250,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
if (event === FileChangeEvent.CreateDeleteDir) {
|
||||
fileCache.clear();
|
||||
} else {
|
||||
fileCache.delete(fileName);
|
||||
fileCache.delete(path.normalize(fileName));
|
||||
}
|
||||
|
||||
if (!ingoreFilesForWatch.has(path.normalize(fileName))) {
|
||||
|
@ -150,6 +150,9 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
||||
* in JIT mode. This is off by default.
|
||||
*/
|
||||
enableSummariesForJit?: boolean;
|
||||
|
||||
/** @internal */
|
||||
collectAllErrors?: boolean;
|
||||
}
|
||||
|
||||
export interface CompilerHost extends ts.CompilerHost {
|
||||
@ -229,6 +232,12 @@ export interface LibrarySummary {
|
||||
sourceFile?: ts.SourceFile;
|
||||
}
|
||||
|
||||
export interface LazyRoute {
|
||||
route: string;
|
||||
module: {name: string, filePath: string};
|
||||
referencedModule: {name: string, filePath: string};
|
||||
}
|
||||
|
||||
export interface Program {
|
||||
/**
|
||||
* Retrieve the TypeScript program used to produce semantic diagnostics and emit the sources.
|
||||
@ -293,6 +302,14 @@ export interface Program {
|
||||
*/
|
||||
loadNgStructureAsync(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Returns the lazy routes in the program.
|
||||
* @param entryRoute A reference to an NgModule like `someModule#name`. If given,
|
||||
* will recursively analyze routes starting from this symbol only.
|
||||
* Otherwise will list all routes for all NgModules in the program/
|
||||
*/
|
||||
listLazyRoutes(entryRoute?: string): LazyRoute[];
|
||||
|
||||
/**
|
||||
* Emit the files requested by emitFlags implied by the program.
|
||||
*
|
||||
|
@ -6,19 +6,18 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {EmitterVisitorContext, ExternalReference, GeneratedFile, ParseSourceSpan, TypeScriptEmitter, collectExternalReferences, syntaxError} from '@angular/compiler';
|
||||
import {AotCompilerHost, EmitterVisitorContext, ExternalReference, GeneratedFile, ParseSourceSpan, TypeScriptEmitter, collectExternalReferences, syntaxError} from '@angular/compiler';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {BaseAotCompilerHost} from '../compiler_host';
|
||||
import {TypeCheckHost} from '../diagnostics/translate_diagnostics';
|
||||
import {ModuleMetadata} from '../metadata/index';
|
||||
import {METADATA_VERSION, ModuleMetadata} from '../metadata/index';
|
||||
|
||||
import {CompilerHost, CompilerOptions, LibrarySummary} from './api';
|
||||
import {GENERATED_FILES} from './util';
|
||||
import {MetadataReaderHost, createMetadataReaderCache, readMetadata} from './metadata_reader';
|
||||
import {DTS, GENERATED_FILES, isInRootDir, relativeToRootDirs} from './util';
|
||||
|
||||
const NODE_MODULES_PACKAGE_NAME = /node_modules\/((\w|-)+|(@(\w|-)+\/(\w|-)+))/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
|
||||
export function createCompilerHost(
|
||||
@ -48,9 +47,12 @@ export interface CodeGenerator {
|
||||
* - AotCompilerHost for @angular/compiler
|
||||
* - TypeCheckHost for mapping ts errors to ng errors (via translateDiagnostics)
|
||||
*/
|
||||
export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
BaseAotCompilerHost<CompilerHost> implements ts.CompilerHost,
|
||||
export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHost, AotCompilerHost,
|
||||
TypeCheckHost {
|
||||
private metadataReaderCache = createMetadataReaderCache();
|
||||
private flatModuleIndexCache = new Map<string, boolean>();
|
||||
private flatModuleIndexNames = new Set<string>();
|
||||
private flatModuleIndexRedirectNames = new Set<string>();
|
||||
private rootDirs: string[];
|
||||
private moduleResolutionCache: ts.ModuleResolutionCache;
|
||||
private originalSourceFiles = new Map<string, ts.SourceFile|undefined>();
|
||||
@ -58,6 +60,8 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
private generatedSourceFiles = new Map<string, GenSourceFile>();
|
||||
private generatedCodeFor = new Map<string, string[]>();
|
||||
private emitter = new TypeScriptEmitter();
|
||||
private metadataReaderHost: MetadataReaderHost;
|
||||
|
||||
getCancellationToken: () => ts.CancellationToken;
|
||||
getDefaultLibLocation: () => string;
|
||||
trace: (s: string) => void;
|
||||
@ -65,10 +69,9 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
directoryExists?: (directoryName: string) => boolean;
|
||||
|
||||
constructor(
|
||||
private rootFiles: string[], options: CompilerOptions, context: CompilerHost,
|
||||
private rootFiles: string[], private options: CompilerOptions, private context: CompilerHost,
|
||||
private metadataProvider: MetadataProvider, private codeGenerator: CodeGenerator,
|
||||
private librarySummaries = new Map<string, LibrarySummary>()) {
|
||||
super(options, context);
|
||||
this.moduleResolutionCache = ts.createModuleResolutionCache(
|
||||
this.context.getCurrentDirectory !(), this.context.getCanonicalFileName.bind(this.context));
|
||||
const basePath = this.options.basePath !;
|
||||
@ -103,6 +106,15 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
if (context.fromSummaryFileName) {
|
||||
this.fromSummaryFileName = context.fromSummaryFileName.bind(context);
|
||||
}
|
||||
this.metadataReaderHost = {
|
||||
cacheMetadata: () => true,
|
||||
getSourceFileMetadata: (filePath) => {
|
||||
const sf = this.getOriginalSourceFile(filePath);
|
||||
return sf ? this.metadataProvider.getMetadata(sf) : undefined;
|
||||
},
|
||||
fileExists: (filePath) => this.originalFileExists(filePath),
|
||||
readFile: (filePath) => this.context.readFile(filePath),
|
||||
};
|
||||
}
|
||||
|
||||
private resolveModuleName(moduleName: string, containingFile: string): ts.ResolvedModule
|
||||
@ -180,7 +192,8 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
const containingFilePackageName = getPackageName(containingFile);
|
||||
|
||||
let moduleName: string;
|
||||
if (importedFilePackagName === containingFilePackageName) {
|
||||
if (importedFilePackagName === containingFilePackageName ||
|
||||
GENERATED_FILES.test(originalImportedFile)) {
|
||||
const rootedContainingFile = relativeToRootDirs(containingFile, this.rootDirs);
|
||||
const rootedImportedFile = relativeToRootDirs(importedFile, this.rootDirs);
|
||||
|
||||
@ -251,14 +264,6 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
return sf;
|
||||
}
|
||||
|
||||
getMetadataForSourceFile(filePath: string): ModuleMetadata|undefined {
|
||||
const sf = this.getOriginalSourceFile(filePath);
|
||||
if (!sf) {
|
||||
return undefined;
|
||||
}
|
||||
return this.metadataProvider.getMetadata(sf);
|
||||
}
|
||||
|
||||
updateGeneratedFile(genFile: GeneratedFile): ts.SourceFile {
|
||||
if (!genFile.stmts) {
|
||||
throw new Error(
|
||||
@ -301,7 +306,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
shouldGenerateFile(fileName: string): {generate: boolean, baseFileName?: string} {
|
||||
// TODO(tbosch): allow generating files that are not in the rootDir
|
||||
// See https://github.com/angular/angular/issues/19337
|
||||
if (this.options.rootDir && !pathStartsWithPrefix(this.options.rootDir, fileName)) {
|
||||
if (!isInRootDir(fileName, this.options)) {
|
||||
return {generate: false};
|
||||
}
|
||||
const genMatch = GENERATED_FILES.exec(fileName);
|
||||
@ -335,7 +340,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
// TODO(tbosch): allow generating files that are not in the rootDir
|
||||
// See https://github.com/angular/angular/issues/19337
|
||||
return !GENERATED_FILES.test(fileName) && this.isSourceFile(fileName) &&
|
||||
(!this.options.rootDir || pathStartsWithPrefix(this.options.rootDir, fileName));
|
||||
isInRootDir(fileName, this.options);
|
||||
}
|
||||
|
||||
getSourceFile(
|
||||
@ -415,16 +420,39 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
if (summary) {
|
||||
return summary.text;
|
||||
}
|
||||
return super.loadSummary(filePath);
|
||||
if (this.originalFileExists(filePath)) {
|
||||
return this.context.readFile(filePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
isSourceFile(filePath: string): boolean {
|
||||
// Don't generate any files nor typecheck them
|
||||
// if skipTemplateCodegen is set and fullTemplateTypeCheck is not yet set,
|
||||
// for backwards compatibility.
|
||||
if (this.options.skipTemplateCodegen && !this.options.fullTemplateTypeCheck) {
|
||||
return false;
|
||||
}
|
||||
// If we have a summary from a previous compilation,
|
||||
// treat the file never as a source file.
|
||||
if (this.librarySummaries.has(filePath)) {
|
||||
return false;
|
||||
}
|
||||
return super.isSourceFile(filePath);
|
||||
if (GENERATED_FILES.test(filePath)) {
|
||||
return false;
|
||||
}
|
||||
if (this.options.generateCodeForLibraries === false && DTS.test(filePath)) {
|
||||
return false;
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
// Check for a bundle index.
|
||||
if (this.hasBundleIndex(filePath)) {
|
||||
const normalFilePath = path.normalize(filePath);
|
||||
return this.flatModuleIndexNames.has(normalFilePath) ||
|
||||
this.flatModuleIndexRedirectNames.has(normalFilePath);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
readFile(fileName: string) {
|
||||
@ -434,6 +462,76 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||
}
|
||||
return this.context.readFile(fileName);
|
||||
}
|
||||
|
||||
getMetadataFor(filePath: string): ModuleMetadata[]|undefined {
|
||||
return readMetadata(filePath, this.metadataReaderHost, this.metadataReaderCache);
|
||||
}
|
||||
|
||||
loadResource(filePath: string): Promise<string>|string {
|
||||
if (this.context.readResource) return this.context.readResource(filePath);
|
||||
if (!this.originalFileExists(filePath)) {
|
||||
throw syntaxError(`Error: Resource file not found: ${filePath}`);
|
||||
}
|
||||
return this.context.readFile(filePath);
|
||||
}
|
||||
|
||||
private hasBundleIndex(filePath: string): boolean {
|
||||
const checkBundleIndex = (directory: string): boolean => {
|
||||
let result = this.flatModuleIndexCache.get(directory);
|
||||
if (result == null) {
|
||||
if (path.basename(directory) == 'node_module') {
|
||||
// Don't look outside the node_modules this package is installed in.
|
||||
result = false;
|
||||
} else {
|
||||
// A bundle index exists if the typings .d.ts file has a metadata.json that has an
|
||||
// importAs.
|
||||
try {
|
||||
const packageFile = path.join(directory, 'package.json');
|
||||
if (this.originalFileExists(packageFile)) {
|
||||
// Once we see a package.json file, assume false until it we find the bundle index.
|
||||
result = false;
|
||||
const packageContent: any = JSON.parse(this.context.readFile(packageFile));
|
||||
if (packageContent.typings) {
|
||||
const typings = path.normalize(path.join(directory, packageContent.typings));
|
||||
if (DTS.test(typings)) {
|
||||
const metadataFile = typings.replace(DTS, '.metadata.json');
|
||||
if (this.originalFileExists(metadataFile)) {
|
||||
const metadata = JSON.parse(this.context.readFile(metadataFile));
|
||||
if (metadata.flatModuleIndexRedirect) {
|
||||
this.flatModuleIndexRedirectNames.add(typings);
|
||||
// Note: don't set result = true,
|
||||
// as this would mark this folder
|
||||
// as having a bundleIndex too early without
|
||||
// filling the bundleIndexNames.
|
||||
} else if (metadata.importAs) {
|
||||
this.flatModuleIndexNames.add(typings);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const parent = path.dirname(directory);
|
||||
if (parent != directory) {
|
||||
// Try the parent directory.
|
||||
result = checkBundleIndex(parent);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// If we encounter any errors assume we this isn't a bundle index.
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
this.flatModuleIndexCache.set(directory, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return checkBundleIndex(path.dirname(filePath));
|
||||
}
|
||||
|
||||
getDefaultLibFileName = (options: ts.CompilerOptions) =>
|
||||
this.context.getDefaultLibFileName(options)
|
||||
getCurrentDirectory = () => this.context.getCurrentDirectory();
|
||||
@ -482,22 +580,6 @@ function getPackageName(filePath: string): string|null {
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
export function relativeToRootDirs(filePath: string, rootDirs: string[]): string {
|
||||
if (!filePath) return filePath;
|
||||
for (const dir of rootDirs || []) {
|
||||
const rel = pathStartsWithPrefix(dir, filePath);
|
||||
if (rel) {
|
||||
return rel;
|
||||
}
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function pathStartsWithPrefix(prefix: string, fullPath: string): string|null {
|
||||
const rel = path.relative(prefix, fullPath);
|
||||
return rel.startsWith('..') ? null : rel;
|
||||
}
|
||||
|
||||
function stripNodeModulesPrefix(filePath: string): string {
|
||||
return filePath.replace(/.*node_modules\//, '');
|
||||
}
|
||||
|
@ -6,7 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {createLoweredSymbol, isLoweredSymbol} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CollectorOptions, MetadataCollector, MetadataValue, ModuleMetadata, isMetadataGlobalReferenceExpression} from '../metadata/index';
|
||||
|
||||
export interface LoweringRequest {
|
||||
@ -181,11 +183,13 @@ function createVariableStatementForDeclarations(declarations: Declaration[]): ts
|
||||
/* modifiers */ undefined, ts.createVariableDeclarationList(varDecls, ts.NodeFlags.Const));
|
||||
}
|
||||
|
||||
export function getExpressionLoweringTransformFactory(requestsMap: RequestsMap):
|
||||
(context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||
export function getExpressionLoweringTransformFactory(
|
||||
requestsMap: RequestsMap, program: ts.Program): (context: ts.TransformationContext) =>
|
||||
(sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||
// Return the factory
|
||||
return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile): ts.SourceFile => {
|
||||
const requests = requestsMap.getRequests(sourceFile);
|
||||
// We need to use the original SourceFile for reading metadata, and not the transformed one.
|
||||
const requests = requestsMap.getRequests(program.getSourceFile(sourceFile.fileName));
|
||||
if (requests && requests.size) {
|
||||
return transformSourceFile(sourceFile, requests, context);
|
||||
}
|
||||
@ -223,14 +227,12 @@ function shouldLower(node: ts.Node | undefined): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
const REWRITE_PREFIX = '\u0275';
|
||||
|
||||
function isPrimitive(value: any): boolean {
|
||||
return Object(value) !== value;
|
||||
}
|
||||
|
||||
function isRewritten(value: any): boolean {
|
||||
return isMetadataGlobalReferenceExpression(value) && value.name.startsWith(REWRITE_PREFIX);
|
||||
return isMetadataGlobalReferenceExpression(value) && isLoweredSymbol(value.name);
|
||||
}
|
||||
|
||||
function isLiteralFieldNamed(node: ts.Node, names: Set<string>): boolean {
|
||||
@ -274,7 +276,7 @@ export class LowerMetadataCache implements RequestsMap {
|
||||
|
||||
private getMetadataAndRequests(sourceFile: ts.SourceFile): MetadataAndLoweringRequests {
|
||||
let identNumber = 0;
|
||||
const freshIdent = () => REWRITE_PREFIX + identNumber++;
|
||||
const freshIdent = () => createLoweredSymbol(identNumber++);
|
||||
const requests = new Map<number, LoweringRequest>();
|
||||
|
||||
const isExportedSymbol = (() => {
|
||||
|
128
packages/compiler-cli/src/transformers/metadata_reader.ts
Normal file
128
packages/compiler-cli/src/transformers/metadata_reader.ts
Normal file
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @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 * as ts from 'typescript';
|
||||
|
||||
import {METADATA_VERSION, ModuleMetadata} from '../metadata';
|
||||
|
||||
import {DTS} from './util';
|
||||
|
||||
export interface MetadataReaderHost {
|
||||
getSourceFileMetadata(filePath: string): ModuleMetadata|undefined;
|
||||
cacheMetadata?(fileName: string): boolean;
|
||||
fileExists(filePath: string): boolean;
|
||||
readFile(filePath: string): string;
|
||||
}
|
||||
|
||||
export interface MetadataReaderCache {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
data: Map<string, ModuleMetadata[]|undefined>;
|
||||
}
|
||||
|
||||
export function createMetadataReaderCache(): MetadataReaderCache {
|
||||
const data = new Map<string, ModuleMetadata[]|undefined>();
|
||||
return {data};
|
||||
}
|
||||
|
||||
export function readMetadata(
|
||||
filePath: string, host: MetadataReaderHost, cache?: MetadataReaderCache): ModuleMetadata[]|
|
||||
undefined {
|
||||
let metadatas = cache && cache.data.get(filePath);
|
||||
if (metadatas) {
|
||||
return metadatas;
|
||||
}
|
||||
if (host.fileExists(filePath)) {
|
||||
// If the file doesn't exists then we cannot return metadata for the file.
|
||||
// This will occur if the user referenced a declared module for which no file
|
||||
// exists for the module (i.e. jQuery or angularjs).
|
||||
if (DTS.test(filePath)) {
|
||||
metadatas = readMetadataFile(host, filePath);
|
||||
if (!metadatas) {
|
||||
// If there is a .d.ts file but no metadata file we need to produce a
|
||||
// metadata from the .d.ts file as metadata files capture reexports
|
||||
// (starting with v3).
|
||||
metadatas = [upgradeMetadataWithDtsData(
|
||||
host, {'__symbolic': 'module', 'version': 1, 'metadata': {}}, filePath)];
|
||||
}
|
||||
} else {
|
||||
const metadata = host.getSourceFileMetadata(filePath);
|
||||
metadatas = metadata ? [metadata] : [];
|
||||
}
|
||||
}
|
||||
if (cache && (!host.cacheMetadata || host.cacheMetadata(filePath))) {
|
||||
cache.data.set(filePath, metadatas);
|
||||
}
|
||||
return metadatas;
|
||||
}
|
||||
|
||||
|
||||
function readMetadataFile(host: MetadataReaderHost, dtsFilePath: string): ModuleMetadata[]|
|
||||
undefined {
|
||||
const metadataPath = dtsFilePath.replace(DTS, '.metadata.json');
|
||||
if (!host.fileExists(metadataPath)) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const metadataOrMetadatas = JSON.parse(host.readFile(metadataPath));
|
||||
const metadatas: ModuleMetadata[] = metadataOrMetadatas ?
|
||||
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
||||
[];
|
||||
if (metadatas.length) {
|
||||
let maxMetadata = metadatas.reduce((p, c) => p.version > c.version ? p : c);
|
||||
if (maxMetadata.version < METADATA_VERSION) {
|
||||
metadatas.push(upgradeMetadataWithDtsData(host, maxMetadata, dtsFilePath));
|
||||
}
|
||||
}
|
||||
return metadatas;
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${metadataPath}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function upgradeMetadataWithDtsData(
|
||||
host: MetadataReaderHost, oldMetadata: ModuleMetadata, dtsFilePath: string): ModuleMetadata {
|
||||
// patch v1 to v3 by adding exports and the `extends` clause.
|
||||
// patch v3 to v4 by adding `interface` symbols for TypeAlias
|
||||
let newMetadata: ModuleMetadata = {
|
||||
'__symbolic': 'module',
|
||||
'version': METADATA_VERSION,
|
||||
'metadata': {...oldMetadata.metadata},
|
||||
};
|
||||
if (oldMetadata.exports) {
|
||||
newMetadata.exports = oldMetadata.exports;
|
||||
}
|
||||
if (oldMetadata.importAs) {
|
||||
newMetadata.importAs = oldMetadata.importAs;
|
||||
}
|
||||
if (oldMetadata.origins) {
|
||||
newMetadata.origins = oldMetadata.origins;
|
||||
}
|
||||
const dtsMetadata = host.getSourceFileMetadata(dtsFilePath);
|
||||
if (dtsMetadata) {
|
||||
for (let prop in dtsMetadata.metadata) {
|
||||
if (!newMetadata.metadata[prop]) {
|
||||
newMetadata.metadata[prop] = dtsMetadata.metadata[prop];
|
||||
}
|
||||
}
|
||||
|
||||
// Only copy exports from exports from metadata prior to version 3.
|
||||
// Starting with version 3 the collector began collecting exports and
|
||||
// this should be redundant. Also, with bundler will rewrite the exports
|
||||
// which will hoist the exports from modules referenced indirectly causing
|
||||
// the imports to be different than the .d.ts files and using the .d.ts file
|
||||
// exports would cause the StaticSymbolResolver to redirect symbols to the
|
||||
// incorrect location.
|
||||
if ((!oldMetadata.version || oldMetadata.version < 3) && dtsMetadata.exports) {
|
||||
newMetadata.exports = dtsMetadata.exports;
|
||||
}
|
||||
}
|
||||
return newMetadata;
|
||||
}
|
@ -144,12 +144,14 @@ class _NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
||||
const span = node.sourceSpan;
|
||||
if (span.start.file == span.end.file) {
|
||||
const file = span.start.file;
|
||||
let source = this._templateSources.get(file);
|
||||
if (!source) {
|
||||
source = ts.createSourceMapSource(file.url, file.content, pos => pos);
|
||||
this._templateSources.set(file, source);
|
||||
if (file.url) {
|
||||
let source = this._templateSources.get(file);
|
||||
if (!source) {
|
||||
source = ts.createSourceMapSource(file.url, file.content, pos => pos);
|
||||
this._templateSources.set(file, source);
|
||||
}
|
||||
return {pos: span.start.offset, end: span.end.offset, source};
|
||||
}
|
||||
return {pos: span.start.offset, end: span.end.offset, source};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -14,11 +14,13 @@ import * as ts from 'typescript';
|
||||
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
|
||||
import {ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
||||
|
||||
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, EmitFlags, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback} from './api';
|
||||
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, EmitFlags, LazyRoute, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback} from './api';
|
||||
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||
import {GENERATED_FILES, StructureIsReused, createMessageDiagnostic, tsStructureIsReused} from './util';
|
||||
import {GENERATED_FILES, StructureIsReused, createMessageDiagnostic, isInRootDir, ngToTsDiagnostic, tsStructureIsReused} from './util';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of files that are emitable via calling ts.Program.emit
|
||||
@ -50,8 +52,8 @@ class AngularCompilerProgram implements Program {
|
||||
private emittedSourceFiles: ts.SourceFile[]|undefined;
|
||||
|
||||
// Lazily initialized fields
|
||||
private _typeCheckHost: TypeCheckHost;
|
||||
private _compiler: AotCompiler;
|
||||
private _hostAdapter: TsCompilerAotCompilerTypeCheckHostAdapter;
|
||||
private _tsProgram: ts.Program;
|
||||
private _analyzedModules: NgAnalyzedModules|undefined;
|
||||
private _structuralDiagnostics: Diagnostic[]|undefined;
|
||||
@ -147,11 +149,9 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
getTsSemanticDiagnostics(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken):
|
||||
ts.Diagnostic[] {
|
||||
if (sourceFile) {
|
||||
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||
}
|
||||
const sourceFiles = sourceFile ? [sourceFile] : this.tsProgram.getSourceFiles();
|
||||
let diags: ts.Diagnostic[] = [];
|
||||
this.tsProgram.getSourceFiles().forEach(sf => {
|
||||
sourceFiles.forEach(sf => {
|
||||
if (!GENERATED_FILES.test(sf.fileName)) {
|
||||
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
||||
}
|
||||
@ -167,7 +167,7 @@ class AngularCompilerProgram implements Program {
|
||||
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
||||
}
|
||||
});
|
||||
const {ng} = translateDiagnostics(this.typeCheckHost, diags);
|
||||
const {ng} = translateDiagnostics(this.hostAdapter, diags);
|
||||
return ng;
|
||||
}
|
||||
|
||||
@ -175,16 +175,23 @@ class AngularCompilerProgram implements Program {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure already loaded');
|
||||
}
|
||||
const {tmpProgram, sourceFiles, hostAdapter, rootNames} = this._createProgramWithBasicStubs();
|
||||
return this._compiler.loadFilesAsync(sourceFiles)
|
||||
.catch(this.catchAnalysisError.bind(this))
|
||||
.then(analyzedModules => {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(
|
||||
tmpProgram, analyzedModules, hostAdapter, rootNames);
|
||||
});
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
return this.compiler.loadFilesAsync(sourceFiles).then(analyzedModules => {
|
||||
if (this._analyzedModules) {
|
||||
throw new Error('Angular structure loaded both synchronously and asynchronsly');
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
});
|
||||
})
|
||||
.catch(e => this._createProgramOnError(e));
|
||||
}
|
||||
|
||||
listLazyRoutes(route?: string): LazyRoute[] {
|
||||
// Note: Don't analyzedModules if a route is given
|
||||
// to be fast enough.
|
||||
return this.compiler.listLazyRoutes(route, route ? undefined : this.analyzedModules);
|
||||
}
|
||||
|
||||
emit(
|
||||
@ -293,6 +300,10 @@ class AngularCompilerProgram implements Program {
|
||||
}
|
||||
}
|
||||
this.emittedSourceFiles = emittedSourceFiles;
|
||||
// translate the diagnostics in the emitResult as well.
|
||||
const translatedEmitDiags = translateDiagnostics(this.hostAdapter, emitResult.diagnostics);
|
||||
emitResult.diagnostics = translatedEmitDiags.ts.concat(
|
||||
this.structuralDiagnostics.concat(translatedEmitDiags.ng).map(ngToTsDiagnostic));
|
||||
|
||||
if (!outSrcMapping.length) {
|
||||
// if no files were emitted by TypeScript, also don't emit .json files
|
||||
@ -341,11 +352,18 @@ class AngularCompilerProgram implements Program {
|
||||
// Private members
|
||||
private get compiler(): AotCompiler {
|
||||
if (!this._compiler) {
|
||||
this.initSync();
|
||||
this._createCompiler();
|
||||
}
|
||||
return this._compiler !;
|
||||
}
|
||||
|
||||
private get hostAdapter(): TsCompilerAotCompilerTypeCheckHostAdapter {
|
||||
if (!this._hostAdapter) {
|
||||
this._createCompiler();
|
||||
}
|
||||
return this._hostAdapter !;
|
||||
}
|
||||
|
||||
private get analyzedModules(): NgAnalyzedModules {
|
||||
if (!this._analyzedModules) {
|
||||
this.initSync();
|
||||
@ -367,19 +385,12 @@ class AngularCompilerProgram implements Program {
|
||||
return this._tsProgram !;
|
||||
}
|
||||
|
||||
private get typeCheckHost(): TypeCheckHost {
|
||||
if (!this._typeCheckHost) {
|
||||
this.initSync();
|
||||
}
|
||||
return this._typeCheckHost !;
|
||||
}
|
||||
|
||||
private calculateTransforms(
|
||||
genFiles: Map<string, GeneratedFile>,
|
||||
customTransformers?: CustomTransformers): ts.CustomTransformers {
|
||||
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||
if (!this.options.disableExpressionLowering) {
|
||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache, this.tsProgram));
|
||||
}
|
||||
beforeTs.push(getAngularEmitterTransformFactory(genFiles));
|
||||
if (customTransformers && customTransformers.beforeTs) {
|
||||
@ -393,19 +404,41 @@ class AngularCompilerProgram implements Program {
|
||||
if (this._analyzedModules) {
|
||||
return;
|
||||
}
|
||||
const {tmpProgram, sourceFiles, hostAdapter, rootNames} = this._createProgramWithBasicStubs();
|
||||
let analyzedModules: NgAnalyzedModules|null;
|
||||
try {
|
||||
analyzedModules = this._compiler.loadFilesSync(sourceFiles);
|
||||
const {tmpProgram, sourceFiles, rootNames} = this._createProgramWithBasicStubs();
|
||||
const analyzedModules = this.compiler.loadFilesSync(sourceFiles);
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, rootNames);
|
||||
} catch (e) {
|
||||
analyzedModules = this.catchAnalysisError(e);
|
||||
this._createProgramOnError(e);
|
||||
}
|
||||
this._updateProgramWithTypeCheckStubs(tmpProgram, analyzedModules, hostAdapter, rootNames);
|
||||
}
|
||||
|
||||
private _createCompiler() {
|
||||
const codegen: CodeGenerator = {
|
||||
generateFile: (genFileName, baseFileName) =>
|
||||
this._compiler.emitBasicStub(genFileName, baseFileName),
|
||||
findGeneratedFileNames: (fileName) => this._compiler.findGeneratedFileNames(fileName),
|
||||
};
|
||||
|
||||
this._hostAdapter = new TsCompilerAotCompilerTypeCheckHostAdapter(
|
||||
this.rootNames, this.options, this.host, this.metadataCache, codegen,
|
||||
this.oldProgramLibrarySummaries);
|
||||
const aotOptions = getAotCompilerOptions(this.options);
|
||||
this._structuralDiagnostics = [];
|
||||
const errorCollector =
|
||||
(this.options.collectAllErrors || this.options.fullTemplateTypeCheck) ? (err: any) => {
|
||||
this._structuralDiagnostics !.push({
|
||||
messageText: err.toString(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
});
|
||||
} : undefined;
|
||||
this._compiler = createAotCompiler(this._hostAdapter, aotOptions, errorCollector).compiler;
|
||||
}
|
||||
|
||||
private _createProgramWithBasicStubs(): {
|
||||
tmpProgram: ts.Program,
|
||||
hostAdapter: TsCompilerAotCompilerTypeCheckHostAdapter,
|
||||
rootNames: string[],
|
||||
sourceFiles: string[],
|
||||
} {
|
||||
@ -418,58 +451,54 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
const codegen: CodeGenerator = {
|
||||
generateFile: (genFileName, baseFileName) =>
|
||||
this._compiler.emitBasicStub(genFileName, baseFileName),
|
||||
findGeneratedFileNames: (fileName) => this._compiler.findGeneratedFileNames(fileName),
|
||||
this.compiler.emitBasicStub(genFileName, baseFileName),
|
||||
findGeneratedFileNames: (fileName) => this.compiler.findGeneratedFileNames(fileName),
|
||||
};
|
||||
|
||||
const hostAdapter = new TsCompilerAotCompilerTypeCheckHostAdapter(
|
||||
this.rootNames, this.options, this.host, this.metadataCache, codegen,
|
||||
this.oldProgramLibrarySummaries);
|
||||
const aotOptions = getAotCompilerOptions(this.options);
|
||||
this._compiler = createAotCompiler(hostAdapter, aotOptions).compiler;
|
||||
this._typeCheckHost = hostAdapter;
|
||||
this._structuralDiagnostics = [];
|
||||
|
||||
let rootNames =
|
||||
this.rootNames.filter(fn => !GENERATED_FILES.test(fn) || !hostAdapter.isSourceFile(fn));
|
||||
let rootNames = [...this.rootNames];
|
||||
if (this.options.generateCodeForLibraries !== false) {
|
||||
// if we should generateCodeForLibraries, never include
|
||||
// generated files in the program as otherwise we will
|
||||
// ovewrite them and typescript will report the error
|
||||
// TS5055: Cannot write file ... because it would overwrite input file.
|
||||
rootNames = rootNames.filter(fn => !GENERATED_FILES.test(fn));
|
||||
}
|
||||
if (this.options.noResolve) {
|
||||
this.rootNames.forEach(rootName => {
|
||||
if (hostAdapter.shouldGenerateFilesFor(rootName)) {
|
||||
rootNames.push(...this._compiler.findGeneratedFileNames(rootName));
|
||||
if (this.hostAdapter.shouldGenerateFilesFor(rootName)) {
|
||||
rootNames.push(...this.compiler.findGeneratedFileNames(rootName));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const tmpProgram = ts.createProgram(rootNames, this.options, hostAdapter, oldTsProgram);
|
||||
const tmpProgram = ts.createProgram(rootNames, this.options, this.hostAdapter, oldTsProgram);
|
||||
const sourceFiles: string[] = [];
|
||||
tmpProgram.getSourceFiles().forEach(sf => {
|
||||
if (hostAdapter.isSourceFile(sf.fileName)) {
|
||||
if (this.hostAdapter.isSourceFile(sf.fileName)) {
|
||||
sourceFiles.push(sf.fileName);
|
||||
}
|
||||
});
|
||||
return {tmpProgram, sourceFiles, hostAdapter, rootNames};
|
||||
return {tmpProgram, sourceFiles, rootNames};
|
||||
}
|
||||
|
||||
private _updateProgramWithTypeCheckStubs(
|
||||
tmpProgram: ts.Program, analyzedModules: NgAnalyzedModules|null,
|
||||
hostAdapter: TsCompilerAotCompilerTypeCheckHostAdapter, rootNames: string[]) {
|
||||
this._analyzedModules = analyzedModules || emptyModules;
|
||||
if (analyzedModules) {
|
||||
tmpProgram.getSourceFiles().forEach(sf => {
|
||||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||
const {generate, baseFileName} = hostAdapter.shouldGenerateFile(sf.fileName);
|
||||
if (generate) {
|
||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
||||
// for .ngfactory.ts files.
|
||||
const genFile = this._compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
||||
if (genFile) {
|
||||
hostAdapter.updateGeneratedFile(genFile);
|
||||
}
|
||||
tmpProgram: ts.Program, analyzedModules: NgAnalyzedModules, rootNames: string[]) {
|
||||
this._analyzedModules = analyzedModules;
|
||||
tmpProgram.getSourceFiles().forEach(sf => {
|
||||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||
const {generate, baseFileName} = this.hostAdapter.shouldGenerateFile(sf.fileName);
|
||||
if (generate) {
|
||||
// Note: ! is ok as hostAdapter.shouldGenerateFile will always return a basefileName
|
||||
// for .ngfactory.ts files.
|
||||
const genFile = this.compiler.emitTypeCheckStub(sf.fileName, baseFileName !);
|
||||
if (genFile) {
|
||||
this.hostAdapter.updateGeneratedFile(genFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this._tsProgram = ts.createProgram(rootNames, this.options, hostAdapter, tmpProgram);
|
||||
}
|
||||
});
|
||||
this._tsProgram = ts.createProgram(rootNames, this.options, this.hostAdapter, tmpProgram);
|
||||
// Note: the new ts program should be completely reusable by TypeScript as:
|
||||
// - we cache all the files in the hostAdapter
|
||||
// - new new stubs use the exactly same imports/exports as the old once (we assert that in
|
||||
@ -479,27 +508,37 @@ class AngularCompilerProgram implements Program {
|
||||
}
|
||||
}
|
||||
|
||||
private catchAnalysisError(e: any): NgAnalyzedModules|null {
|
||||
private _createProgramOnError(e: any) {
|
||||
// Still fill the analyzedModules and the tsProgram
|
||||
// so that we don't cause other errors for users who e.g. want to emit the ngProgram.
|
||||
this._analyzedModules = emptyModules;
|
||||
this.oldTsProgram = undefined;
|
||||
this._hostAdapter.isSourceFile = () => false;
|
||||
this._tsProgram = ts.createProgram(this.rootNames, this.options, this.hostAdapter);
|
||||
if (isSyntaxError(e)) {
|
||||
const parserErrors = getParseErrors(e);
|
||||
if (parserErrors && parserErrors.length) {
|
||||
this._structuralDiagnostics =
|
||||
parserErrors.map<Diagnostic>(e => ({
|
||||
messageText: e.contextualMessage(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
span: e.span,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}));
|
||||
this._structuralDiagnostics = [
|
||||
...(this._structuralDiagnostics || []),
|
||||
...parserErrors.map<Diagnostic>(e => ({
|
||||
messageText: e.contextualMessage(),
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
span: e.span,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}))
|
||||
];
|
||||
} else {
|
||||
this._structuralDiagnostics = [{
|
||||
messageText: e.message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}];
|
||||
this._structuralDiagnostics = [
|
||||
...(this._structuralDiagnostics || []), {
|
||||
messageText: e.message,
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
}
|
||||
];
|
||||
}
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@ -512,7 +551,10 @@ class AngularCompilerProgram implements Program {
|
||||
if (!(emitFlags & EmitFlags.Codegen)) {
|
||||
return {genFiles: [], genDiags: []};
|
||||
}
|
||||
let genFiles = this.compiler.emitAllImpls(this.analyzedModules);
|
||||
// TODO(tbosch): allow generating files that are not in the rootDir
|
||||
// See https://github.com/angular/angular/issues/19337
|
||||
let genFiles = this.compiler.emitAllImpls(this.analyzedModules)
|
||||
.filter(genFile => isInRootDir(genFile.genFileUrl, this.options));
|
||||
if (this.oldProgramEmittedGeneratedFiles) {
|
||||
const oldProgramEmittedGeneratedFiles = this.oldProgramEmittedGeneratedFiles;
|
||||
genFiles = genFiles.filter(genFile => {
|
||||
@ -672,6 +714,10 @@ function getNgOptionDiagnostics(options: CompilerOptions): Diagnostic[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function normalizeSeparators(path: string): string {
|
||||
return path.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that can adjust a path from source path to out path,
|
||||
* based on an existing mapping from source to out path.
|
||||
@ -693,17 +739,19 @@ export function createSrcToOutPathMapper(
|
||||
} = path): (srcFileName: string) => string {
|
||||
let srcToOutPath: (srcFileName: string) => string;
|
||||
if (outDir) {
|
||||
let path: {} = {}; // Ensure we error if we use `path` instead of `host`.
|
||||
if (sampleSrcFileName == null || sampleOutFileName == null) {
|
||||
throw new Error(`Can't calculate the rootDir without a sample srcFileName / outFileName. `);
|
||||
}
|
||||
const srcFileDir = host.dirname(sampleSrcFileName).replace(/\\/g, '/');
|
||||
const outFileDir = host.dirname(sampleOutFileName).replace(/\\/g, '/');
|
||||
const srcFileDir = normalizeSeparators(host.dirname(sampleSrcFileName));
|
||||
const outFileDir = normalizeSeparators(host.dirname(sampleOutFileName));
|
||||
if (srcFileDir === outFileDir) {
|
||||
return (srcFileName) => srcFileName;
|
||||
}
|
||||
// calculate the common suffix, stopping
|
||||
// at `outDir`.
|
||||
const srcDirParts = srcFileDir.split('/');
|
||||
const outDirParts = outFileDir.split('/');
|
||||
// calculate the common suffix
|
||||
const outDirParts = normalizeSeparators(host.relative(outDir, outFileDir)).split('/');
|
||||
let i = 0;
|
||||
while (i < Math.min(srcDirParts.length, outDirParts.length) &&
|
||||
srcDirParts[srcDirParts.length - 1 - i] === outDirParts[outDirParts.length - 1 - i])
|
||||
@ -719,7 +767,7 @@ export function createSrcToOutPathMapper(
|
||||
export function i18nExtract(
|
||||
formatName: string | null, outFile: string | null, host: ts.CompilerHost,
|
||||
options: CompilerOptions, bundle: MessageBundle): string[] {
|
||||
formatName = formatName || 'null';
|
||||
formatName = formatName || 'xlf';
|
||||
// Checks the format and returns the extension
|
||||
const ext = i18nGetExtension(formatName);
|
||||
const content = i18nSerialize(bundle, formatName, options);
|
||||
@ -753,7 +801,7 @@ export function i18nSerialize(
|
||||
}
|
||||
|
||||
export function i18nGetExtension(formatName: string): string {
|
||||
const format = (formatName || 'xlf').toLowerCase();
|
||||
const format = formatName.toLowerCase();
|
||||
|
||||
switch (format) {
|
||||
case 'xmb':
|
||||
|
@ -6,11 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {DEFAULT_ERROR_CODE, Diagnostic, SOURCE} from './api';
|
||||
import {CompilerOptions, DEFAULT_ERROR_CODE, Diagnostic, SOURCE} from './api';
|
||||
|
||||
export const GENERATED_FILES = /(.*?)\.(ngfactory|shim\.ngstyle|ngstyle|ngsummary)\.(js|d\.ts|ts)$/;
|
||||
export const DTS = /\.d\.ts$/;
|
||||
|
||||
export const enum StructureIsReused {Not = 0, SafeModules = 1, Completely = 2}
|
||||
|
||||
@ -29,3 +31,49 @@ export function createMessageDiagnostic(messageText: string): ts.Diagnostic&Diag
|
||||
source: SOURCE,
|
||||
};
|
||||
}
|
||||
|
||||
export function isInRootDir(fileName: string, options: CompilerOptions) {
|
||||
return !options.rootDir || pathStartsWithPrefix(options.rootDir, fileName);
|
||||
}
|
||||
|
||||
export function relativeToRootDirs(filePath: string, rootDirs: string[]): string {
|
||||
if (!filePath) return filePath;
|
||||
for (const dir of rootDirs || []) {
|
||||
const rel = pathStartsWithPrefix(dir, filePath);
|
||||
if (rel) {
|
||||
return rel;
|
||||
}
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function pathStartsWithPrefix(prefix: string, fullPath: string): string|null {
|
||||
const rel = path.relative(prefix, fullPath);
|
||||
return rel.startsWith('..') ? null : rel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a ng.Diagnostic into a ts.Diagnostic.
|
||||
* This looses some information, and also uses an incomplete object as `file`.
|
||||
*
|
||||
* I.e. only use this where the API allows only a ts.Diagnostic.
|
||||
*/
|
||||
export function ngToTsDiagnostic(ng: Diagnostic): ts.Diagnostic {
|
||||
let file: ts.SourceFile|undefined;
|
||||
let start: number|undefined;
|
||||
let length: number|undefined;
|
||||
if (ng.span) {
|
||||
// Note: We can't use a real ts.SourceFile,
|
||||
// but we can at least mirror the properties `fileName` and `text`, which
|
||||
// are mostly used for error reporting.
|
||||
file = { fileName: ng.span.start.file.url, text: ng.span.start.file.content } as ts.SourceFile;
|
||||
start = ng.span.start.offset;
|
||||
length = ng.span.end.offset - start;
|
||||
}
|
||||
return {
|
||||
file,
|
||||
messageText: ng.messageText,
|
||||
category: ng.category,
|
||||
code: ng.code, start, length,
|
||||
};
|
||||
}
|
||||
|
@ -1,310 +0,0 @@
|
||||
/**
|
||||
* @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 {METADATA_VERSION, ModuleMetadata} from '@angular/compiler-cli';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost} from '../src/compiler_host';
|
||||
|
||||
import {Directory, Entry, MockAotContext, MockCompilerHost} from './mocks';
|
||||
|
||||
describe('CompilerHost', () => {
|
||||
let context: MockAotContext;
|
||||
let program: ts.Program;
|
||||
let hostNestedGenDir: CompilerHost;
|
||||
let hostSiblingGenDir: CompilerHost;
|
||||
|
||||
beforeEach(() => {
|
||||
context = new MockAotContext('/tmp/src', clone(FILES));
|
||||
const host = new MockCompilerHost(context);
|
||||
program = ts.createProgram(
|
||||
['main.ts'], {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
},
|
||||
host);
|
||||
// Force a typecheck
|
||||
const errors = program.getSemanticDiagnostics();
|
||||
if (errors && errors.length) {
|
||||
throw new Error('Expected no errors');
|
||||
}
|
||||
hostNestedGenDir = new CompilerHost(
|
||||
program, {
|
||||
genDir: '/tmp/project/src/gen/',
|
||||
basePath: '/tmp/project/src',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
hostSiblingGenDir = new CompilerHost(
|
||||
program, {
|
||||
genDir: '/tmp/project/gen',
|
||||
basePath: '/tmp/project/src/',
|
||||
skipMetadataEmit: false,
|
||||
strictMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
});
|
||||
|
||||
describe('nestedGenDir', () => {
|
||||
it('should import node_module from factory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/node_modules/@angular/core.d.ts',
|
||||
'/tmp/project/src/gen/my.ngfactory.ts', ))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should import factory from factory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./my.other.ngfactory');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.css.ngstyle.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../my.other.css.ngstyle');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.shim.ngstyle.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./a/my.other.shim.ngstyle');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.sass.ngstyle.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../my.other.sass.ngstyle');
|
||||
});
|
||||
|
||||
it('should import application from factory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('../my.other');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../../my.other');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('../a/my.other');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.css.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('../a/my.other.css');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('../a/my.other.css.shim');
|
||||
});
|
||||
});
|
||||
|
||||
describe('siblingGenDir', () => {
|
||||
it('should import node_module from factory', () => {
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/node_modules/@angular/core.d.ts',
|
||||
'/tmp/project/src/gen/my.ngfactory.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should import factory from factory', () => {
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ngfactory.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./my.other.ngfactory');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.css.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../my.other.css');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.css.shim.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./a/my.other.css.shim');
|
||||
});
|
||||
|
||||
it('should import application from factory', () => {
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./my.other');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/my.other.ts', '/tmp/project/src/a/my.ngfactory.ts'))
|
||||
.toEqual('../my.other');
|
||||
expect(hostSiblingGenDir.fileNameToModuleName(
|
||||
'/tmp/project/src/a/my.other.ts', '/tmp/project/src/my.ngfactory.ts'))
|
||||
.toEqual('./a/my.other');
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main @angular/core', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'/tmp/project/node_modules/@angular/core.d.ts', '/tmp/project/src/main.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should be able to produce an import to a shallow import', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('@angular/core', '/tmp/project/src/main.ts'))
|
||||
.toEqual('@angular/core');
|
||||
expect(hostNestedGenDir.fileNameToModuleName(
|
||||
'@angular/upgrade/static', '/tmp/project/src/main.ts'))
|
||||
.toEqual('@angular/upgrade/static');
|
||||
expect(hostNestedGenDir.fileNameToModuleName('myLibrary', '/tmp/project/src/main.ts'))
|
||||
.toEqual('myLibrary');
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib23-43', '/tmp/project/src/main.ts'))
|
||||
.toEqual('lib23-43');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main to a sub-directory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib/utils.ts', 'main.ts')).toEqual('./lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a peer file', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib/collections.ts', 'lib/utils.ts'))
|
||||
.toEqual('./collections');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a sibling directory', () => {
|
||||
expect(hostNestedGenDir.fileNameToModuleName('lib/utils.ts', 'lib2/utils2.ts'))
|
||||
.toEqual('../lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to read a metadata file', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/core.d.ts')).toEqual([
|
||||
{__symbolic: 'module', version: METADATA_VERSION, metadata: {foo: {__symbolic: 'class'}}}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/unused.d.ts')).toEqual([
|
||||
dummyMetadata
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to read empty metadata ', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/empty.d.ts')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return undefined for missing modules', () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('node_modules/@angular/missing.d.ts')).toBeUndefined();
|
||||
});
|
||||
|
||||
it(`should add missing v${METADATA_VERSION} metadata from v1 metadata and .d.ts files`, () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1.d.ts')).toEqual([
|
||||
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata: {
|
||||
foo: {__symbolic: 'class'},
|
||||
aType: {__symbolic: 'interface'},
|
||||
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
|
||||
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
|
||||
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
|
||||
},
|
||||
exports: [{from: './lib/utils2', export: ['Export']}],
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it(`should upgrade a missing metadata file into v${METADATA_VERSION}`, () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v1_empty.d.ts')).toEqual([{
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata: {},
|
||||
exports: [{from: './lib/utils'}]
|
||||
}]);
|
||||
});
|
||||
|
||||
it(`should upgrade v3 metadata into v${METADATA_VERSION}`, () => {
|
||||
expect(hostNestedGenDir.getMetadataFor('metadata_versions/v3.d.ts')).toEqual([
|
||||
{__symbolic: 'module', version: 3, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata: {
|
||||
foo: {__symbolic: 'class'},
|
||||
aType: {__symbolic: 'interface'},
|
||||
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
|
||||
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
|
||||
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
|
||||
}
|
||||
// Note: exports is missing because it was elided in the original.
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
const dummyModule = 'export let foo: any[];';
|
||||
const dummyMetadata: ModuleMetadata = {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata:
|
||||
{foo: {__symbolic: 'error', message: 'Variable not initialized', line: 0, character: 11}}
|
||||
};
|
||||
const FILES: Entry = {
|
||||
'tmp': {
|
||||
'src': {
|
||||
'main.ts': `
|
||||
import * as c from '@angular/core';
|
||||
import * as r from '@angular/router';
|
||||
import * as u from './lib/utils';
|
||||
import * as cs from './lib/collections';
|
||||
import * as u2 from './lib2/utils2';
|
||||
`,
|
||||
'lib': {
|
||||
'utils.ts': dummyModule,
|
||||
'collections.ts': dummyModule,
|
||||
},
|
||||
'lib2': {'utils2.ts': dummyModule},
|
||||
'node_modules': {
|
||||
'@angular': {
|
||||
'core.d.ts': dummyModule,
|
||||
'core.metadata.json':
|
||||
`{"__symbolic":"module", "version": ${METADATA_VERSION}, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
|
||||
'unused.d.ts': dummyModule,
|
||||
'empty.d.ts': 'export declare var a: string;',
|
||||
'empty.metadata.json': '[]',
|
||||
}
|
||||
},
|
||||
'metadata_versions': {
|
||||
'v1.d.ts': `
|
||||
import {ReExport} from './lib/utils2';
|
||||
export {ReExport};
|
||||
|
||||
export {Export} from './lib/utils2';
|
||||
|
||||
export type aType = number;
|
||||
|
||||
export declare class Bar {
|
||||
ngOnInit() {}
|
||||
}
|
||||
export declare class BarChild extends Bar {}
|
||||
`,
|
||||
'v1.metadata.json':
|
||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'v1_empty.d.ts': `
|
||||
export * from './lib/utils';
|
||||
`,
|
||||
'v3.d.ts': `
|
||||
import {ReExport} from './lib/utils2';
|
||||
export {ReExport};
|
||||
|
||||
export {Export} from './lib/utils2';
|
||||
|
||||
export type aType = number;
|
||||
|
||||
export declare class Bar {
|
||||
ngOnInit() {}
|
||||
}
|
||||
export declare class BarChild extends Bar {}
|
||||
`,
|
||||
'v3.metadata.json':
|
||||
`{"__symbolic":"module", "version": 3, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function clone(entry: Entry): Entry {
|
||||
if (typeof entry === 'string') {
|
||||
return entry;
|
||||
} else {
|
||||
const result: Directory = {};
|
||||
for (const name in entry) {
|
||||
result[name] = clone(entry[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {StaticSymbol} from '@angular/compiler';
|
||||
import {CompilerHost} from '@angular/compiler-cli';
|
||||
import {ReflectorHost} from '@angular/language-service/src/reflector_host';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {getExpressionDiagnostics, getTemplateExpressionDiagnostics} from '../../src/diagnostics/expression_diagnostics';
|
||||
@ -21,7 +22,6 @@ describe('expression diagnostics', () => {
|
||||
let host: MockLanguageServiceHost;
|
||||
let service: ts.LanguageService;
|
||||
let context: DiagnosticContext;
|
||||
let aotHost: CompilerHost;
|
||||
let type: StaticSymbol;
|
||||
|
||||
beforeAll(() => {
|
||||
@ -33,8 +33,8 @@ describe('expression diagnostics', () => {
|
||||
const options: CompilerOptions = Object.create(host.getCompilationSettings());
|
||||
options.genDir = '/dist';
|
||||
options.basePath = '/src';
|
||||
aotHost = new CompilerHost(program, options, host, {verboseInvalidExpression: true});
|
||||
context = new DiagnosticContext(service, program, checker, aotHost);
|
||||
const symbolResolverHost = new ReflectorHost(() => program, host, options);
|
||||
context = new DiagnosticContext(service, program, checker, symbolResolverHost);
|
||||
type = context.getStaticSymbol('app/app.component.ts', 'AppComponent');
|
||||
});
|
||||
|
||||
|
@ -6,9 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AotCompilerHost, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, InterpolationConfig, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver} from '@angular/compiler';
|
||||
import {AotCompilerHost, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, InterpolationConfig, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, SummaryResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver} from '@angular/compiler';
|
||||
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||
import {CompilerHostContext} from 'compiler-cli';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
@ -25,7 +24,7 @@ function calcRootPath() {
|
||||
|
||||
const realFiles = new Map<string, string>();
|
||||
|
||||
export class MockLanguageServiceHost implements ts.LanguageServiceHost, CompilerHostContext {
|
||||
export class MockLanguageServiceHost implements ts.LanguageServiceHost {
|
||||
private options: ts.CompilerOptions;
|
||||
private context: MockAotContext;
|
||||
private assumedExist = new Set<string>();
|
||||
@ -122,7 +121,7 @@ export class DiagnosticContext {
|
||||
|
||||
constructor(
|
||||
public service: ts.LanguageService, public program: ts.Program,
|
||||
public checker: ts.TypeChecker, public host: AotCompilerHost) {}
|
||||
public checker: ts.TypeChecker, public host: StaticSymbolResolverHost) {}
|
||||
|
||||
private collectError(e: any, path?: string) { this._errors.push({e, path}); }
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
import {StaticSymbol} from '@angular/compiler';
|
||||
import {CompilerHost} from '@angular/compiler-cli';
|
||||
import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, MockMetadataBundlerHost, arrayToMockDir, arrayToMockMap, isSource, settings, setup, toMockFileArray} from '@angular/compiler/test/aot/test_util';
|
||||
import {ReflectorHost} from '@angular/language-service/src/reflector_host';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Symbol, SymbolQuery, SymbolTable} from '../../src/diagnostics/symbols';
|
||||
@ -44,8 +45,8 @@ describe('symbol query', () => {
|
||||
const options: CompilerOptions = Object.create(host.getCompilationSettings());
|
||||
options.genDir = '/dist';
|
||||
options.basePath = '/quickstart';
|
||||
const aotHost = new CompilerHost(program, options, host, {verboseInvalidExpression: true});
|
||||
context = new DiagnosticContext(service, program, checker, aotHost);
|
||||
const symbolResolverHost = new ReflectorHost(() => program, host, options);
|
||||
context = new DiagnosticContext(service, program, checker, symbolResolverHost);
|
||||
query = getSymbolQuery(program, checker, sourceFile, emptyPipes);
|
||||
});
|
||||
|
||||
|
@ -6,14 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompilerHostContext} from '@angular/compiler-cli/src/compiler_host';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export type Entry = string | Directory;
|
||||
|
||||
export interface Directory { [name: string]: Entry; }
|
||||
|
||||
export class MockAotContext implements CompilerHostContext {
|
||||
export class MockAotContext {
|
||||
constructor(public currentDirectory: string, private files: Entry) {}
|
||||
|
||||
fileExists(fileName: string): boolean { return typeof this.getEntry(fileName) === 'string'; }
|
||||
|
@ -502,29 +502,69 @@ describe('ngc transformer command-line', () => {
|
||||
it('should add metadata as decorators', () => {
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"annotationsAs": "decorators"
|
||||
},
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('aclass.ts', `export class AClass {}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {AClass} from './aclass';
|
||||
|
||||
@Component({template: ''})
|
||||
export class MyComp {
|
||||
fn(p: any) {}
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`);
|
||||
@NgModule({declarations: []})
|
||||
export class MyModule {
|
||||
constructor(importedClass: AClass) {}
|
||||
}
|
||||
`);
|
||||
|
||||
const exitCode = main(['-p', basePath], errorSpy);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).toContain('MyComp = __decorate([');
|
||||
expect(mymoduleSource).toContain('MyModule = __decorate([');
|
||||
expect(mymoduleSource).toContain(`import { AClass } from './aclass';`);
|
||||
expect(mymoduleSource).toContain(`__metadata("design:paramtypes", [AClass])`);
|
||||
});
|
||||
|
||||
it('should add metadata as static fields', () => {
|
||||
// Note: Don't specify emitDecoratorMetadata here on purpose,
|
||||
// as regression test for https://github.com/angular/angular/issues/19916.
|
||||
writeConfig(`{
|
||||
"extends": "./tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"annotationsAs": "static fields"
|
||||
},
|
||||
"files": ["mymodule.ts"]
|
||||
}`);
|
||||
write('aclass.ts', `export class AClass {}`);
|
||||
write('mymodule.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {AClass} from './aclass';
|
||||
|
||||
@NgModule({declarations: []})
|
||||
export class MyModule {
|
||||
constructor(importedClass: AClass) {}
|
||||
}
|
||||
`);
|
||||
|
||||
const exitCode = main(['-p', basePath], errorSpy);
|
||||
expect(exitCode).toEqual(0);
|
||||
|
||||
const mymodulejs = path.resolve(outDir, 'mymodule.js');
|
||||
const mymoduleSource = fs.readFileSync(mymodulejs, 'utf8');
|
||||
expect(mymoduleSource).not.toContain('__decorate');
|
||||
expect(mymoduleSource).toContain('args: [{ declarations: [] },] }');
|
||||
expect(mymoduleSource).not.toContain(`__metadata`);
|
||||
expect(mymoduleSource).toContain(`import { AClass } from './aclass';`);
|
||||
expect(mymoduleSource).toContain(`{ type: AClass, }`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1391,5 +1431,73 @@ describe('ngc transformer command-line', () => {
|
||||
main(['-p', path.join(basePath, 'src/tsconfig.json')], message => messages.push(message));
|
||||
expect(exitCode).toBe(0, 'Compile failed unexpectedly.\n ' + messages.join('\n '));
|
||||
});
|
||||
|
||||
it('should emit all structural errors', () => {
|
||||
write('src/tsconfig.json', `{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"files": ["test-module.ts"]
|
||||
}`);
|
||||
write('src/lib/indirect2.ts', `
|
||||
declare var f: any;
|
||||
export const t2 = f\`<p>hello</p>\`;
|
||||
`);
|
||||
write('src/lib/indirect1.ts', `
|
||||
import {t2} from './indirect2';
|
||||
export const t1 = t2 + ' ';
|
||||
`);
|
||||
write('src/lib/test.component.ts', `
|
||||
import {Component} from '@angular/core';
|
||||
import {t1} from './indirect1';
|
||||
|
||||
@Component({
|
||||
template: t1
|
||||
})
|
||||
export class TestComponent {}
|
||||
`);
|
||||
write('src/test-module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {TestComponent} from './lib/test.component';
|
||||
|
||||
@NgModule({declarations: [TestComponent]})
|
||||
export class TestModule {}
|
||||
`);
|
||||
const messages: string[] = [];
|
||||
const exitCode =
|
||||
main(['-p', path.join(basePath, 'src/tsconfig.json')], message => messages.push(message));
|
||||
expect(exitCode).toBe(1, 'Compile was expected to fail');
|
||||
expect(messages[0]).toContain(['Tagged template expressions are not supported in metadata']);
|
||||
});
|
||||
|
||||
it('should allow using 2 classes with the same name in declarations with noEmitOnError=true',
|
||||
() => {
|
||||
write('src/tsconfig.json', `{
|
||||
"extends": "../tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"noEmitOnError": true
|
||||
},
|
||||
"files": ["test-module.ts"]
|
||||
}`);
|
||||
function writeComp(fileName: string) {
|
||||
write(fileName, `
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp', template: ''})
|
||||
export class TestComponent {}
|
||||
`);
|
||||
}
|
||||
writeComp('src/comp1.ts');
|
||||
writeComp('src/comp2.ts');
|
||||
write('src/test-module.ts', `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {TestComponent as Comp1} from './comp1';
|
||||
import {TestComponent as Comp2} from './comp2';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Comp1, Comp2],
|
||||
})
|
||||
export class MyModule {}
|
||||
`);
|
||||
expect(main(['-p', path.join(basePath, 'src/tsconfig.json')])).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
90
packages/compiler-cli/test/ngtools_api_spec.ts
Normal file
90
packages/compiler-cli/test/ngtools_api_spec.ts
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @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 * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {NgTools_InternalApi_NG_2} from '../src/ngtools_api';
|
||||
|
||||
import {TestSupport, setup} from './test_support';
|
||||
|
||||
describe('ngtools_api (deprecated)', () => {
|
||||
let testSupport: TestSupport;
|
||||
|
||||
beforeEach(() => { testSupport = setup(); });
|
||||
|
||||
function createProgram(rootNames: string[]) {
|
||||
const options = testSupport.createCompilerOptions();
|
||||
const host = ts.createCompilerHost(options, true);
|
||||
const program =
|
||||
ts.createProgram(rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host);
|
||||
return {program, host, options};
|
||||
}
|
||||
|
||||
function writeSomeRoutes() {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
// Component with metadata errors.
|
||||
@Component(() => {if (1==1) return null as any;})
|
||||
export class ErrorComp2 {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ErrorComp2, NonExistentComp],
|
||||
imports: [RouterModule.forRoot([{loadChildren: './child#ChildModule'}])]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
'src/child.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([{loadChildren: './child2#ChildModule2'}])]
|
||||
})
|
||||
export class ChildModule {}
|
||||
`,
|
||||
'src/child2.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class ChildModule2 {}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
it('should list lazy routes recursively', () => {
|
||||
writeSomeRoutes();
|
||||
const {program, host, options} = createProgram(['src/main.ts']);
|
||||
const routes = NgTools_InternalApi_NG_2.listLazyRoutes({
|
||||
program,
|
||||
host,
|
||||
angularCompilerOptions: options,
|
||||
entryModule: 'src/main#MainModule',
|
||||
});
|
||||
expect(routes).toEqual({
|
||||
'./child#ChildModule': path.resolve(testSupport.basePath, 'src/child.ts'),
|
||||
'./child2#ChildModule2': path.resolve(testSupport.basePath, 'src/child2.ts'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow to emit the program after analyzing routes', () => {
|
||||
writeSomeRoutes();
|
||||
const {program, host, options} = createProgram(['src/main.ts']);
|
||||
NgTools_InternalApi_NG_2.listLazyRoutes({
|
||||
program,
|
||||
host,
|
||||
angularCompilerOptions: options,
|
||||
entryModule: 'src/main#MainModule',
|
||||
});
|
||||
program.emit();
|
||||
testSupport.shouldExist('built/src/main.js');
|
||||
});
|
||||
});
|
@ -105,6 +105,47 @@ describe('perform watch', () => {
|
||||
expect(getSourceFileSpy !).toHaveBeenCalledWith(mainTsPath, ts.ScriptTarget.ES5);
|
||||
expect(getSourceFileSpy !).toHaveBeenCalledWith(utilTsPath, ts.ScriptTarget.ES5);
|
||||
});
|
||||
|
||||
it('should recover from static analysis errors', () => {
|
||||
const config = createConfig();
|
||||
const host = new MockWatchHost(config);
|
||||
|
||||
const okFileContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class MyModule {}
|
||||
`;
|
||||
const errorFileContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule(() => (1===1 ? null as any : null as any))
|
||||
export class MyModule {}
|
||||
`;
|
||||
const indexTsPath = path.resolve(testSupport.basePath, 'src', 'index.ts');
|
||||
|
||||
testSupport.write(indexTsPath, okFileContent);
|
||||
|
||||
performWatchCompilation(host);
|
||||
expectNoDiagnostics(config.options, host.diagnostics);
|
||||
|
||||
// Do it multiple times as the watch mode switches internal modes.
|
||||
// E.g. from regular compile to using summaries, ...
|
||||
for (let i = 0; i < 3; i++) {
|
||||
host.diagnostics = [];
|
||||
testSupport.write(indexTsPath, okFileContent);
|
||||
host.triggerFileChange(FileChangeEvent.Change, indexTsPath);
|
||||
expectNoDiagnostics(config.options, host.diagnostics);
|
||||
|
||||
host.diagnostics = [];
|
||||
testSupport.write(indexTsPath, errorFileContent);
|
||||
host.triggerFileChange(FileChangeEvent.Change, indexTsPath);
|
||||
|
||||
const errDiags = host.diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
|
||||
expect(errDiags.length).toBe(1);
|
||||
expect(errDiags[0].messageText).toContain('Function calls are not supported.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function createModuleAndCompSource(prefix: string, template: string = prefix + 'template') {
|
||||
@ -122,7 +163,8 @@ function createModuleAndCompSource(prefix: string, template: string = prefix + '
|
||||
}
|
||||
|
||||
class MockWatchHost {
|
||||
timeoutListeners: Array<(() => void)|null> = [];
|
||||
nextTimeoutListenerId = 1;
|
||||
timeoutListeners: {[id: string]: (() => void)} = {};
|
||||
fileChangeListeners: Array<((event: FileChangeEvent, fileName: string) => void)|null> = [];
|
||||
diagnostics: ng.Diagnostics = [];
|
||||
constructor(public config: ng.ParsedConfiguration) {}
|
||||
@ -141,16 +183,16 @@ class MockWatchHost {
|
||||
close: () => this.fileChangeListeners[id] = null,
|
||||
};
|
||||
}
|
||||
setTimeout(callback: () => void, ms: number): any {
|
||||
const id = this.timeoutListeners.length;
|
||||
this.timeoutListeners.push(callback);
|
||||
setTimeout(callback: () => void): any {
|
||||
const id = this.nextTimeoutListenerId++;
|
||||
this.timeoutListeners[id] = callback;
|
||||
return id;
|
||||
}
|
||||
clearTimeout(timeoutId: any): void { this.timeoutListeners[timeoutId] = null; }
|
||||
clearTimeout(timeoutId: any): void { delete this.timeoutListeners[timeoutId]; }
|
||||
flushTimeouts() {
|
||||
this.timeoutListeners.forEach(cb => {
|
||||
if (cb) cb();
|
||||
});
|
||||
const listeners = this.timeoutListeners;
|
||||
this.timeoutListeners = {};
|
||||
Object.keys(listeners).forEach(id => listeners[id]());
|
||||
}
|
||||
triggerFileChange(event: FileChangeEvent, fileName: string) {
|
||||
this.fileChangeListeners.forEach(listener => {
|
||||
|
@ -64,10 +64,10 @@ export function setup(): TestSupport {
|
||||
function write(fileName: string, content: string) {
|
||||
const dir = path.dirname(fileName);
|
||||
if (dir != '.') {
|
||||
const newDir = path.join(basePath, dir);
|
||||
const newDir = path.resolve(basePath, dir);
|
||||
if (!fs.existsSync(newDir)) fs.mkdirSync(newDir);
|
||||
}
|
||||
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
|
||||
fs.writeFileSync(path.resolve(basePath, fileName), content, {encoding: 'utf-8'});
|
||||
}
|
||||
|
||||
function writeFiles(...mockDirs: {[fileName: string]: string}[]) {
|
||||
|
@ -90,6 +90,14 @@ describe('NgCompilerHost', () => {
|
||||
.toBe('./a/child');
|
||||
});
|
||||
|
||||
it('should use a relative import when accessing generated files, even if crossing packages',
|
||||
() => {
|
||||
expect(host.fileNameToModuleName(
|
||||
'/tmp/node_modules/mod2/b.ngfactory.d.ts',
|
||||
'/tmp/node_modules/mod1/a.ngfactory.d.ts'))
|
||||
.toBe('../mod2/b.ngfactory');
|
||||
});
|
||||
|
||||
it('should support multiple rootDirs when accessing a source file form a source file', () => {
|
||||
const hostWithMultipleRoots = createHost({
|
||||
options: {
|
||||
|
@ -181,13 +181,15 @@ function convert(annotatedSource: string) {
|
||||
[fileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
|
||||
const moduleSourceFile = program.getSourceFile(fileName);
|
||||
const transformers: ts.CustomTransformers = {
|
||||
before: [getExpressionLoweringTransformFactory({
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
||||
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
||||
return requests;
|
||||
} else {return new Map();}
|
||||
}
|
||||
})]
|
||||
before: [getExpressionLoweringTransformFactory(
|
||||
{
|
||||
getRequests(sourceFile: ts.SourceFile): RequestLocationMap{
|
||||
if (sourceFile.fileName == moduleSourceFile.fileName) {
|
||||
return requests;
|
||||
} else {return new Map();}
|
||||
}
|
||||
},
|
||||
program)]
|
||||
};
|
||||
let result: string = '';
|
||||
const emitResult = program.emit(
|
||||
|
177
packages/compiler-cli/test/transformers/metadata_reader_spec.ts
Normal file
177
packages/compiler-cli/test/transformers/metadata_reader_spec.ts
Normal file
@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @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 * as ts from 'typescript';
|
||||
|
||||
import {METADATA_VERSION, MetadataCollector, ModuleMetadata} from '../../src/metadata';
|
||||
import {MetadataReaderHost, readMetadata} from '../../src/transformers/metadata_reader';
|
||||
import {Directory, Entry, MockAotContext} from '../mocks';
|
||||
|
||||
describe('metadata reader', () => {
|
||||
let host: MetadataReaderHost;
|
||||
|
||||
beforeEach(() => {
|
||||
const context = new MockAotContext('/tmp/src', clone(FILES));
|
||||
const metadataCollector = new MetadataCollector();
|
||||
host = {
|
||||
fileExists: (fileName) => context.fileExists(fileName),
|
||||
readFile: (fileName) => context.readFile(fileName),
|
||||
getSourceFileMetadata: (fileName) => {
|
||||
const sourceText = context.readFile(fileName);
|
||||
return sourceText != null ?
|
||||
metadataCollector.getMetadata(
|
||||
ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest)) :
|
||||
undefined;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
it('should be able to read a metadata file', () => {
|
||||
expect(readMetadata('node_modules/@angular/core.d.ts', host)).toEqual([
|
||||
{__symbolic: 'module', version: METADATA_VERSION, metadata: {foo: {__symbolic: 'class'}}}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to read metadata from an otherwise unused .d.ts file ', () => {
|
||||
expect(readMetadata('node_modules/@angular/unused.d.ts', host)).toEqual([dummyMetadata]);
|
||||
});
|
||||
|
||||
it('should be able to read empty metadata ',
|
||||
() => { expect(readMetadata('node_modules/@angular/empty.d.ts', host)).toEqual([]); });
|
||||
|
||||
it('should return undefined for missing modules',
|
||||
() => { expect(readMetadata('node_modules/@angular/missing.d.ts', host)).toBeUndefined(); });
|
||||
|
||||
it(`should add missing v${METADATA_VERSION} metadata from v1 metadata and .d.ts files`, () => {
|
||||
expect(readMetadata('metadata_versions/v1.d.ts', host)).toEqual([
|
||||
{__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata: {
|
||||
foo: {__symbolic: 'class'},
|
||||
aType: {__symbolic: 'interface'},
|
||||
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
|
||||
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
|
||||
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
|
||||
},
|
||||
exports: [{from: './lib/utils2', export: ['Export']}],
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it(`should upgrade a missing metadata file into v${METADATA_VERSION}`, () => {
|
||||
expect(readMetadata('metadata_versions/v1_empty.d.ts', host)).toEqual([{
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata: {},
|
||||
exports: [{from: './lib/utils'}]
|
||||
}]);
|
||||
});
|
||||
|
||||
it(`should upgrade v3 metadata into v${METADATA_VERSION}`, () => {
|
||||
expect(readMetadata('metadata_versions/v3.d.ts', host)).toEqual([
|
||||
{__symbolic: 'module', version: 3, metadata: {foo: {__symbolic: 'class'}}}, {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata: {
|
||||
foo: {__symbolic: 'class'},
|
||||
aType: {__symbolic: 'interface'},
|
||||
Bar: {__symbolic: 'class', members: {ngOnInit: [{__symbolic: 'method'}]}},
|
||||
BarChild: {__symbolic: 'class', extends: {__symbolic: 'reference', name: 'Bar'}},
|
||||
ReExport: {__symbolic: 'reference', module: './lib/utils2', name: 'ReExport'},
|
||||
}
|
||||
// Note: exports is missing because it was elided in the original.
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
const dummyModule = 'export let foo: any[];';
|
||||
const dummyMetadata: ModuleMetadata = {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
metadata:
|
||||
{foo: {__symbolic: 'error', message: 'Variable not initialized', line: 0, character: 11}}
|
||||
};
|
||||
const FILES: Entry = {
|
||||
'tmp': {
|
||||
'src': {
|
||||
'main.ts': `
|
||||
import * as c from '@angular/core';
|
||||
import * as r from '@angular/router';
|
||||
import * as u from './lib/utils';
|
||||
import * as cs from './lib/collections';
|
||||
import * as u2 from './lib2/utils2';
|
||||
`,
|
||||
'lib': {
|
||||
'utils.ts': dummyModule,
|
||||
'collections.ts': dummyModule,
|
||||
},
|
||||
'lib2': {'utils2.ts': dummyModule},
|
||||
'node_modules': {
|
||||
'@angular': {
|
||||
'core.d.ts': dummyModule,
|
||||
'core.metadata.json':
|
||||
`{"__symbolic":"module", "version": ${METADATA_VERSION}, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'router': {'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}},
|
||||
'unused.d.ts': dummyModule,
|
||||
'empty.d.ts': 'export declare var a: string;',
|
||||
'empty.metadata.json': '[]',
|
||||
}
|
||||
},
|
||||
'metadata_versions': {
|
||||
'v1.d.ts': `
|
||||
import {ReExport} from './lib/utils2';
|
||||
export {ReExport};
|
||||
|
||||
export {Export} from './lib/utils2';
|
||||
|
||||
export type aType = number;
|
||||
|
||||
export declare class Bar {
|
||||
ngOnInit() {}
|
||||
}
|
||||
export declare class BarChild extends Bar {}
|
||||
`,
|
||||
'v1.metadata.json':
|
||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'v1_empty.d.ts': `
|
||||
export * from './lib/utils';
|
||||
`,
|
||||
'v3.d.ts': `
|
||||
import {ReExport} from './lib/utils2';
|
||||
export {ReExport};
|
||||
|
||||
export {Export} from './lib/utils2';
|
||||
|
||||
export type aType = number;
|
||||
|
||||
export declare class Bar {
|
||||
ngOnInit() {}
|
||||
}
|
||||
export declare class BarChild extends Bar {}
|
||||
`,
|
||||
'v3.metadata.json':
|
||||
`{"__symbolic":"module", "version": 3, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function clone(entry: Entry): Entry {
|
||||
if (typeof entry === 'string') {
|
||||
return entry;
|
||||
} else {
|
||||
const result: Directory = {};
|
||||
for (const name in entry) {
|
||||
result[name] = clone(entry[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -11,7 +11,8 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompilerHost} from '../../src/transformers/api';
|
||||
import {formatDiagnostics} from '../../src/perform_compile';
|
||||
import {CompilerHost, EmitFlags, LazyRoute} from '../../src/transformers/api';
|
||||
import {createSrcToOutPathMapper} from '../../src/transformers/program';
|
||||
import {GENERATED_FILES, StructureIsReused, tsStructureIsReused} from '../../src/transformers/util';
|
||||
import {TestSupport, expectNoDiagnosticsInProgram, setup} from '../test_support';
|
||||
@ -77,6 +78,15 @@ describe('ng program', () => {
|
||||
return {emitResult, program};
|
||||
}
|
||||
|
||||
function resolveFiles(rootNames: string[]) {
|
||||
const preOptions = testSupport.createCompilerOptions();
|
||||
const preHost = ts.createCompilerHost(preOptions);
|
||||
// don't resolve symlinks
|
||||
preHost.realpath = (f) => f;
|
||||
const preProgram = ts.createProgram(rootNames, preOptions, preHost);
|
||||
return preProgram.getSourceFiles().map(sf => sf.fileName);
|
||||
}
|
||||
|
||||
describe('reuse of old program', () => {
|
||||
it('should reuse generated code for libraries from old programs', () => {
|
||||
compileLib('lib');
|
||||
@ -309,11 +319,35 @@ describe('ng program', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should typecheck templates even if skipTemplateCodegen is set', () => {
|
||||
it('should not typecheck templates if skipTemplateCodegen is set but fullTemplateTypeCheck is not',
|
||||
() => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule(() => {if (1==1) return null as any;})
|
||||
export class SomeClassWithInvalidMetadata {}
|
||||
`,
|
||||
});
|
||||
const options = testSupport.createCompilerOptions({skipTemplateCodegen: true});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
const emitResult = program.emit({emitFlags: EmitFlags.All});
|
||||
expect(emitResult.diagnostics.length).toBe(0);
|
||||
|
||||
testSupport.shouldExist('built/src/main.metadata.json');
|
||||
});
|
||||
|
||||
it('should typecheck templates if skipTemplateCodegen and fullTemplateTypeCheck is set', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': createModuleAndCompSource('main', `{{nonExistent}}`),
|
||||
});
|
||||
const options = testSupport.createCompilerOptions({skipTemplateCodegen: true});
|
||||
const options = testSupport.createCompilerOptions({
|
||||
skipTemplateCodegen: true,
|
||||
fullTemplateTypeCheck: true,
|
||||
});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||
@ -348,13 +382,7 @@ describe('ng program', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': createModuleAndCompSource('main'),
|
||||
});
|
||||
const preOptions = testSupport.createCompilerOptions();
|
||||
const preHost = ts.createCompilerHost(preOptions);
|
||||
// don't resolve symlinks
|
||||
preHost.realpath = (f) => f;
|
||||
const preProgram =
|
||||
ts.createProgram([path.resolve(testSupport.basePath, 'src/main.ts')], preOptions, preHost);
|
||||
const allRootNames = preProgram.getSourceFiles().map(sf => sf.fileName);
|
||||
const allRootNames = resolveFiles([path.resolve(testSupport.basePath, 'src/main.ts')]);
|
||||
|
||||
// now do the actual test with noResolve
|
||||
const program = compile(undefined, {noResolve: true}, allRootNames);
|
||||
@ -454,25 +482,33 @@ describe('ng program', () => {
|
||||
});
|
||||
|
||||
it('should not emit generated files whose sources are outside of the rootDir', () => {
|
||||
compileLib('lib');
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': createModuleAndCompSource('main'),
|
||||
'src/index.ts': `
|
||||
export * from './main';
|
||||
export * from 'lib/index';
|
||||
`
|
||||
});
|
||||
compile(undefined, {rootDir: path.resolve(testSupport.basePath, 'src')});
|
||||
const options =
|
||||
testSupport.createCompilerOptions({rootDir: path.resolve(testSupport.basePath, 'src')});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const writtenFileNames: string[] = [];
|
||||
const oldWriteFile = host.writeFile;
|
||||
host.writeFile = (fileName, data, writeByteOrderMark) => {
|
||||
writtenFileNames.push(fileName);
|
||||
oldWriteFile(fileName, data, writeByteOrderMark);
|
||||
};
|
||||
|
||||
compile(/*oldProgram*/ undefined, options, /*rootNames*/ undefined, host);
|
||||
|
||||
// no emit for files from node_modules as they are outside of rootDir
|
||||
expect(writtenFileNames.some(f => /node_modules/.test(f))).toBe(false);
|
||||
|
||||
// emit all gen files for files under src/
|
||||
testSupport.shouldExist('built/main.js');
|
||||
testSupport.shouldExist('built/main.d.ts');
|
||||
testSupport.shouldExist('built/main.ngfactory.js');
|
||||
testSupport.shouldExist('built/main.ngfactory.d.ts');
|
||||
testSupport.shouldExist('built/main.ngsummary.json');
|
||||
testSupport.shouldNotExist('built/node_modules/lib/index.js');
|
||||
testSupport.shouldNotExist('built/node_modules/lib/index.d.ts');
|
||||
testSupport.shouldNotExist('built/node_modules/lib/index.ngfactory.js');
|
||||
testSupport.shouldNotExist('built/node_modules/lib/index.ngfactory.d.ts');
|
||||
testSupport.shouldNotExist('built/node_modules/lib/index.ngsummary.json');
|
||||
});
|
||||
|
||||
describe('createSrcToOutPathMapper', () => {
|
||||
@ -492,10 +528,17 @@ describe('ng program', () => {
|
||||
});
|
||||
|
||||
it('should adjust the filename if the outDir is outside of the rootDir', () => {
|
||||
const mapper = createSrcToOutPathMapper('/out', '/tmp/a/x.ts', '/a/x.js');
|
||||
const mapper = createSrcToOutPathMapper('/out', '/tmp/a/x.ts', '/out/a/x.js');
|
||||
expect(mapper('/tmp/b/y.js')).toBe('/out/b/y.js');
|
||||
});
|
||||
|
||||
it('should adjust the filename if the common prefix of sampleSrc and sampleOut is outside of outDir',
|
||||
() => {
|
||||
const mapper =
|
||||
createSrcToOutPathMapper('/dist/common', '/src/common/x.ts', '/dist/common/x.js');
|
||||
expect(mapper('/src/common/y.js')).toBe('/dist/common/y.js');
|
||||
});
|
||||
|
||||
it('should work on windows with normalized paths', () => {
|
||||
const mapper =
|
||||
createSrcToOutPathMapper('c:/tmp/out', 'c:/tmp/a/x.ts', 'c:/tmp/out/a/x.js', path.win32);
|
||||
@ -508,4 +551,425 @@ describe('ng program', () => {
|
||||
expect(mapper('c:\\tmp\\b\\y.js')).toBe('c:\\tmp\\out\\b\\y.js');
|
||||
});
|
||||
});
|
||||
|
||||
describe('listLazyRoutes', () => {
|
||||
function writeSomeRoutes() {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot([{loadChildren: './child#ChildModule'}])]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
'src/child.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([{loadChildren: './child2#ChildModule2'}])]
|
||||
})
|
||||
export class ChildModule {}
|
||||
`,
|
||||
'src/child2.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class ChildModule2 {}
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
function createProgram(rootNames: string[], overrideOptions: ng.CompilerOptions = {}) {
|
||||
const options = testSupport.createCompilerOptions(overrideOptions);
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host});
|
||||
return {program, options};
|
||||
}
|
||||
|
||||
function normalizeRoutes(lazyRoutes: LazyRoute[]) {
|
||||
return lazyRoutes.map(
|
||||
r => ({
|
||||
route: r.route,
|
||||
module: {name: r.module.name, filePath: r.module.filePath},
|
||||
referencedModule:
|
||||
{name: r.referencedModule.name, filePath: r.referencedModule.filePath},
|
||||
}));
|
||||
}
|
||||
|
||||
it('should list all lazyRoutes', () => {
|
||||
writeSomeRoutes();
|
||||
const {program, options} = createProgram(['src/main.ts', 'src/child.ts', 'src/child2.ts']);
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
expect(normalizeRoutes(program.listLazyRoutes())).toEqual([
|
||||
{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
route: './child#ChildModule'
|
||||
},
|
||||
{
|
||||
module:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule2', filePath: path.resolve(testSupport.basePath, 'src/child2.ts')},
|
||||
route: './child2#ChildModule2'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should emit correctly after listing lazyRoutes', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot([{loadChildren: './lazy/lazy#LazyModule'}])]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
'src/lazy/lazy.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class ChildModule {}
|
||||
`,
|
||||
});
|
||||
const {program, options} = createProgram(['src/main.ts', 'src/lazy/lazy.ts']);
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
program.listLazyRoutes();
|
||||
program.emit();
|
||||
|
||||
const lazyNgFactory =
|
||||
fs.readFileSync(path.resolve(testSupport.basePath, 'built/src/lazy/lazy.ngfactory.js'));
|
||||
expect(lazyNgFactory).toContain('import * as i1 from "./lazy";');
|
||||
});
|
||||
|
||||
it('should list lazyRoutes given an entryRoute recursively', () => {
|
||||
writeSomeRoutes();
|
||||
const {program, options} = createProgram(['src/main.ts']);
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([
|
||||
{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
route: './child#ChildModule'
|
||||
},
|
||||
{
|
||||
module:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule2', filePath: path.resolve(testSupport.basePath, 'src/child2.ts')},
|
||||
route: './child2#ChildModule2'
|
||||
},
|
||||
]);
|
||||
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/child#ChildModule'))).toEqual([
|
||||
{
|
||||
module:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule2', filePath: path.resolve(testSupport.basePath, 'src/child2.ts')},
|
||||
route: './child2#ChildModule2'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should list lazyRoutes pointing to a default export', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot([{loadChildren: './child'}])]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
'src/child.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export default class ChildModule {}
|
||||
`,
|
||||
});
|
||||
const {program, options} = createProgram(['src/main.ts']);
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([
|
||||
{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
{name: undefined, filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
route: './child'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should list lazyRoutes from imported modules', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {NestedMainModule} from './nested/main';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot([{loadChildren: './child#ChildModule'}]),
|
||||
NestedMainModule,
|
||||
]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
'src/child.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class ChildModule {}
|
||||
`,
|
||||
'src/nested/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([{loadChildren: './child#NestedChildModule'}])]
|
||||
})
|
||||
export class NestedMainModule {}
|
||||
`,
|
||||
'src/nested/child.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class NestedChildModule {}
|
||||
`,
|
||||
});
|
||||
const {program, options} = createProgram(['src/main.ts']);
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([
|
||||
{
|
||||
module: {
|
||||
name: 'NestedMainModule',
|
||||
filePath: path.resolve(testSupport.basePath, 'src/nested/main.ts')
|
||||
},
|
||||
referencedModule: {
|
||||
name: 'NestedChildModule',
|
||||
filePath: path.resolve(testSupport.basePath, 'src/nested/child.ts')
|
||||
},
|
||||
route: './child#NestedChildModule'
|
||||
},
|
||||
{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
route: './child#ChildModule'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should dedupe lazyRoutes given an entryRoute', () => {
|
||||
writeSomeRoutes();
|
||||
testSupport.writeFiles({
|
||||
'src/index.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot([{loadChildren: './main#MainModule'}]),
|
||||
RouterModule.forRoot([{loadChildren: './child#ChildModule'}]),
|
||||
]
|
||||
})
|
||||
export class MainModule {}
|
||||
`,
|
||||
});
|
||||
const {program, options} = createProgram(['src/index.ts']);
|
||||
expectNoDiagnosticsInProgram(options, program);
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([
|
||||
{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
route: './child#ChildModule'
|
||||
},
|
||||
{
|
||||
module:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule2', filePath: path.resolve(testSupport.basePath, 'src/child2.ts')},
|
||||
route: './child2#ChildModule2'
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should list lazyRoutes given an entryRoute even with static errors', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {NgModule, Component} from '@angular/core';
|
||||
import {RouterModule} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'url-comp',
|
||||
// Non existent external template
|
||||
templateUrl: 'non-existent.html',
|
||||
})
|
||||
export class ErrorComp {}
|
||||
|
||||
@Component({
|
||||
selector: 'err-comp',
|
||||
// Error in template
|
||||
template: '<input/>{{',
|
||||
})
|
||||
export class ErrorComp2 {}
|
||||
|
||||
// Component with metadata errors.
|
||||
@Component(() => {if (1==1) return null as any;})
|
||||
export class ErrorComp3 {}
|
||||
|
||||
// Unused component
|
||||
@Component({
|
||||
selector: 'unused-comp',
|
||||
template: ''
|
||||
})
|
||||
export class UnusedComp {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ErrorComp, ErrorComp2, ErrorComp3, NonExistentComp],
|
||||
imports: [RouterModule.forRoot([{loadChildren: './child#ChildModule'}])]
|
||||
})
|
||||
export class MainModule {}
|
||||
|
||||
@NgModule({
|
||||
// Component used in 2 NgModules
|
||||
declarations: [ErrorComp],
|
||||
})
|
||||
export class Mod2 {}
|
||||
`,
|
||||
'src/child.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class ChildModule {}
|
||||
`,
|
||||
});
|
||||
const program = createProgram(['src/main.ts'], {collectAllErrors: true}).program;
|
||||
expect(normalizeRoutes(program.listLazyRoutes('src/main#MainModule'))).toEqual([{
|
||||
module: {name: 'MainModule', filePath: path.resolve(testSupport.basePath, 'src/main.ts')},
|
||||
referencedModule:
|
||||
{name: 'ChildModule', filePath: path.resolve(testSupport.basePath, 'src/child.ts')},
|
||||
route: './child#ChildModule'
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should report errors for ts and ng errors on emit with noEmitOnError=true', () => {
|
||||
testSupport.writeFiles({
|
||||
'src/main.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
// Ts error
|
||||
let x: string = 1;
|
||||
|
||||
// Ng error
|
||||
@Component({selector: 'comp', templateUrl: './main.html'})
|
||||
export class MyComp {}
|
||||
|
||||
@NgModule({declarations: [MyComp]})
|
||||
export class MyModule {}
|
||||
`,
|
||||
'src/main.html': '{{nonExistent}}'
|
||||
});
|
||||
const options = testSupport.createCompilerOptions({noEmitOnError: true});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program1 = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host});
|
||||
const errorDiags =
|
||||
program1.emit().diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
|
||||
expect(formatDiagnostics(errorDiags))
|
||||
.toContain(`src/main.ts(5,13): error TS2322: Type '1' is not assignable to type 'string'.`);
|
||||
expect(formatDiagnostics(errorDiags))
|
||||
.toContain(
|
||||
`src/main.html(1,1): error TS100: Property 'nonExistent' does not exist on type 'MyComp'.`);
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
const fileWithStructuralError = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule(() => (1===1 ? null as any : null as any))
|
||||
export class MyModule {}
|
||||
`;
|
||||
const fileWithGoodContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule()
|
||||
export class MyModule {}
|
||||
`;
|
||||
|
||||
it('should not throw on structural errors but collect them', () => {
|
||||
testSupport.write('src/index.ts', fileWithStructuralError);
|
||||
|
||||
const options = testSupport.createCompilerOptions();
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host});
|
||||
|
||||
const structuralErrors = program.getNgStructuralDiagnostics();
|
||||
expect(structuralErrors.length).toBe(1);
|
||||
expect(structuralErrors[0].messageText).toContain('Function calls are not supported.');
|
||||
});
|
||||
|
||||
it('should not throw on structural errors but collect them (loadNgStructureAsync)', (done) => {
|
||||
testSupport.write('src/index.ts', fileWithStructuralError);
|
||||
|
||||
const options = testSupport.createCompilerOptions();
|
||||
const host = ng.createCompilerHost({options});
|
||||
const program = ng.createProgram(
|
||||
{rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host});
|
||||
program.loadNgStructureAsync().then(() => {
|
||||
const structuralErrors = program.getNgStructuralDiagnostics();
|
||||
expect(structuralErrors.length).toBe(1);
|
||||
expect(structuralErrors[0].messageText).toContain('Function calls are not supported.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able report structural errors with noResolve:true and generateCodeForLibraries:false ' +
|
||||
'even if getSourceFile throws for non existent files',
|
||||
() => {
|
||||
testSupport.write('src/index.ts', fileWithGoodContent);
|
||||
|
||||
// compile angular and produce .ngsummary.json / ngfactory.d.ts files
|
||||
compile();
|
||||
|
||||
testSupport.write('src/ok.ts', fileWithGoodContent);
|
||||
testSupport.write('src/error.ts', fileWithStructuralError);
|
||||
|
||||
// Make sure the ok.ts file is before the error.ts file,
|
||||
// so we added a .ngfactory.ts file for it.
|
||||
const allRootNames = resolveFiles(
|
||||
['src/ok.ts', 'src/error.ts'].map(fn => path.resolve(testSupport.basePath, fn)));
|
||||
|
||||
const options = testSupport.createCompilerOptions({
|
||||
noResolve: true,
|
||||
generateCodeForLibraries: false,
|
||||
});
|
||||
const host = ng.createCompilerHost({options});
|
||||
const originalGetSourceFile = host.getSourceFile;
|
||||
host.getSourceFile =
|
||||
(fileName: string, languageVersion: ts.ScriptTarget,
|
||||
onError?: ((message: string) => void) | undefined): ts.SourceFile => {
|
||||
// We should never try to load .ngfactory.ts files
|
||||
if (fileName.match(/\.ngfactory\.ts$/)) {
|
||||
throw new Error(`Non existent ngfactory file: ` + fileName);
|
||||
}
|
||||
return originalGetSourceFile.call(host, fileName, languageVersion, onError);
|
||||
};
|
||||
const program = ng.createProgram({rootNames: allRootNames, options, host});
|
||||
const structuralErrors = program.getNgStructuralDiagnostics();
|
||||
expect(structuralErrors.length).toBe(1);
|
||||
expect(structuralErrors[0].messageText).toContain('Function calls are not supported.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -15,7 +15,7 @@ const globals = {
|
||||
'rxjs/Subject': 'Rx',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/compiler/esm5/compiler.js',
|
||||
dest: '../../dist/packages-dist/compiler/bundles/compiler.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileStylesheetMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, flatten, identifierName, templateSourceUrl} from '../compile_metadata';
|
||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileNgModuleSummary, CompilePipeMetadata, CompilePipeSummary, CompileProviderMetadata, CompileStylesheetMetadata, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, componentFactoryName, flatten, identifierName, templateSourceUrl, tokenReference} from '../compile_metadata';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {ViewEncapsulation} from '../core';
|
||||
import {MessageBundle} from '../i18n/message_bundle';
|
||||
@ -29,6 +29,7 @@ import {ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||
import {AotCompilerHost} from './compiler_host';
|
||||
import {AotCompilerOptions} from './compiler_options';
|
||||
import {GeneratedFile} from './generated_file';
|
||||
import {LazyRoute, listLazyRoutes, parseLazyRoute} from './lazy_routes';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||
@ -192,6 +193,7 @@ export class AotCompiler {
|
||||
|
||||
private _createNgFactoryStub(
|
||||
outputCtx: OutputContext, file: NgAnalyzedFile, emitFlags: StubEmitFlags) {
|
||||
let componentId = 0;
|
||||
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
||||
// Note: the code below needs to executed for StubEmitFlags.Basic and StubEmitFlags.TypeCheck,
|
||||
// so we don't change the .ngfactory file too much when adding the typecheck block.
|
||||
@ -229,12 +231,14 @@ export class AotCompiler {
|
||||
if (!compMeta.isComponent) {
|
||||
return;
|
||||
}
|
||||
componentId++;
|
||||
this._createTypeCheckBlock(
|
||||
outputCtx, ngModuleMeta, this._metadataResolver.getHostComponentMetadata(compMeta),
|
||||
[compMeta.type], externalReferenceVars);
|
||||
this._createTypeCheckBlock(
|
||||
outputCtx, ngModuleMeta, compMeta, ngModuleMeta.transitiveModule.directives,
|
||||
outputCtx, `${compMeta.type.reference.name}_Host_${componentId}`, ngModuleMeta,
|
||||
this._metadataResolver.getHostComponentMetadata(compMeta), [compMeta.type],
|
||||
externalReferenceVars);
|
||||
this._createTypeCheckBlock(
|
||||
outputCtx, `${compMeta.type.reference.name}_${componentId}`, ngModuleMeta, compMeta,
|
||||
ngModuleMeta.transitiveModule.directives, externalReferenceVars);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -245,12 +249,13 @@ export class AotCompiler {
|
||||
}
|
||||
|
||||
private _createTypeCheckBlock(
|
||||
ctx: OutputContext, moduleMeta: CompileNgModuleMetadata, compMeta: CompileDirectiveMetadata,
|
||||
directives: CompileIdentifierMetadata[], externalReferenceVars: Map<any, string>) {
|
||||
ctx: OutputContext, componentId: string, moduleMeta: CompileNgModuleMetadata,
|
||||
compMeta: CompileDirectiveMetadata, directives: CompileIdentifierMetadata[],
|
||||
externalReferenceVars: Map<any, string>) {
|
||||
const {template: parsedTemplate, pipes: usedPipes} =
|
||||
this._parseTemplate(compMeta, moduleMeta, directives);
|
||||
ctx.statements.push(...this._typeCheckCompiler.compileComponent(
|
||||
compMeta, parsedTemplate, usedPipes, externalReferenceVars));
|
||||
componentId, compMeta, parsedTemplate, usedPipes, externalReferenceVars));
|
||||
}
|
||||
|
||||
emitMessageBundle(analyzeResult: NgAnalyzedModules, locale: string|null): MessageBundle {
|
||||
@ -500,13 +505,13 @@ export class AotCompiler {
|
||||
}
|
||||
const arity = this._symbolResolver.getTypeArity(symbol) || 0;
|
||||
const {filePath, name, members} = this._symbolResolver.getImportAs(symbol) || symbol;
|
||||
const importModule = this._symbolResolver.fileNameToModuleName(filePath, genFilePath);
|
||||
const importModule = this._fileNameToModuleName(filePath, genFilePath);
|
||||
|
||||
// It should be good enough to compare filePath to genFilePath and if they are equal
|
||||
// there is a self reference. However, ngfactory files generate to .ts but their
|
||||
// symbols have .d.ts so a simple compare is insufficient. They should be canonical
|
||||
// and is tracked by #17705.
|
||||
const selfReference = this._symbolResolver.fileNameToModuleName(genFilePath, genFilePath);
|
||||
const selfReference = this._fileNameToModuleName(genFilePath, genFilePath);
|
||||
const moduleName = importModule === selfReference ? null : importModule;
|
||||
|
||||
// If we are in a type expression that refers to a generic type then supply
|
||||
@ -527,6 +532,12 @@ export class AotCompiler {
|
||||
return {statements: [], genFilePath, importExpr};
|
||||
}
|
||||
|
||||
private _fileNameToModuleName(importedFilePath: string, containingFilePath: string): string {
|
||||
return this._summaryResolver.getKnownModuleName(importedFilePath) ||
|
||||
this._symbolResolver.getKnownModuleName(importedFilePath) ||
|
||||
this._host.fileNameToModuleName(importedFilePath, containingFilePath);
|
||||
}
|
||||
|
||||
private _codegenStyles(
|
||||
srcFileUrl: string, compMeta: CompileDirectiveMetadata,
|
||||
stylesheetMetadata: CompileStylesheetMetadata, isShimmed: boolean,
|
||||
@ -542,6 +553,43 @@ export class AotCompiler {
|
||||
private _codegenSourceModule(srcFileUrl: string, ctx: OutputContext): GeneratedFile {
|
||||
return new GeneratedFile(srcFileUrl, ctx.genFilePath, ctx.statements);
|
||||
}
|
||||
|
||||
listLazyRoutes(entryRoute?: string, analyzedModules?: NgAnalyzedModules): LazyRoute[] {
|
||||
const self = this;
|
||||
if (entryRoute) {
|
||||
const symbol = parseLazyRoute(entryRoute, this._reflector).referencedModule;
|
||||
return visitLazyRoute(symbol);
|
||||
} else if (analyzedModules) {
|
||||
const allLazyRoutes: LazyRoute[] = [];
|
||||
for (const ngModule of analyzedModules.ngModules) {
|
||||
const lazyRoutes = listLazyRoutes(ngModule, this._reflector);
|
||||
for (const lazyRoute of lazyRoutes) {
|
||||
allLazyRoutes.push(lazyRoute);
|
||||
}
|
||||
}
|
||||
return allLazyRoutes;
|
||||
} else {
|
||||
throw new Error(`Either route or analyzedModules has to be specified!`);
|
||||
}
|
||||
|
||||
function visitLazyRoute(
|
||||
symbol: StaticSymbol, seenRoutes = new Set<StaticSymbol>(),
|
||||
allLazyRoutes: LazyRoute[] = []): LazyRoute[] {
|
||||
// Support pointing to default exports, but stop recursing there,
|
||||
// as the StaticReflector does not yet support default exports.
|
||||
if (seenRoutes.has(symbol) || !symbol.name) {
|
||||
return allLazyRoutes;
|
||||
}
|
||||
seenRoutes.add(symbol);
|
||||
const lazyRoutes = listLazyRoutes(
|
||||
self._metadataResolver.getNgModuleMetadata(symbol, true) !, self._reflector);
|
||||
for (const lazyRoute of lazyRoutes) {
|
||||
allLazyRoutes.push(lazyRoute);
|
||||
visitLazyRoute(lazyRoute.referencedModule, seenRoutes, allLazyRoutes);
|
||||
}
|
||||
return allLazyRoutes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _createEmptyStub(outputCtx: OutputContext) {
|
||||
|
@ -52,15 +52,18 @@ export function createAotUrlResolver(host: {
|
||||
/**
|
||||
* Creates a new AotCompiler based on options and a host.
|
||||
*/
|
||||
export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCompilerOptions):
|
||||
{compiler: AotCompiler, reflector: StaticReflector} {
|
||||
export function createAotCompiler(
|
||||
compilerHost: AotCompilerHost, options: AotCompilerOptions,
|
||||
errorCollector?: (error: any, type?: any) =>
|
||||
void): {compiler: AotCompiler, reflector: StaticReflector} {
|
||||
let translations: string = options.translations || '';
|
||||
|
||||
const urlResolver = createAotUrlResolver(compilerHost);
|
||||
const symbolCache = new StaticSymbolCache();
|
||||
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
|
||||
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
|
||||
const staticReflector = new StaticReflector(summaryResolver, symbolResolver);
|
||||
const staticReflector =
|
||||
new StaticReflector(summaryResolver, symbolResolver, [], [], errorCollector);
|
||||
const htmlParser = new I18NHtmlParser(
|
||||
new HtmlParser(), translations, options.i18nFormat, options.missingTranslation, console);
|
||||
const config = new CompilerConfig({
|
||||
@ -80,7 +83,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||
const resolver = new CompileMetadataResolver(
|
||||
config, htmlParser, new NgModuleResolver(staticReflector),
|
||||
new DirectiveResolver(staticReflector), new PipeResolver(staticReflector), summaryResolver,
|
||||
elementSchemaRegistry, normalizer, console, symbolCache, staticReflector);
|
||||
elementSchemaRegistry, normalizer, console, symbolCache, staticReflector, errorCollector);
|
||||
// TODO(vicb): do not pass options.i18nFormat here
|
||||
const viewCompiler = new ViewCompiler(staticReflector);
|
||||
const typeCheckCompiler = new TypeCheckCompiler(options, staticReflector);
|
||||
|
@ -14,6 +14,13 @@ import {AotSummaryResolverHost} from './summary_resolver';
|
||||
* services and from underlying file systems.
|
||||
*/
|
||||
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
|
||||
/**
|
||||
* Converts a file path to a module name that can be used as an `import.
|
||||
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
||||
*
|
||||
* See ImportResolver.
|
||||
*/
|
||||
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string;
|
||||
/**
|
||||
* Converts a path that refers to a resource into an absolute filePath
|
||||
* that can be later on used for loading the resource via `loadResource.
|
||||
|
62
packages/compiler/src/aot/lazy_routes.ts
Normal file
62
packages/compiler/src/aot/lazy_routes.ts
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @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 {CompileNgModuleMetadata, tokenReference} from '../compile_metadata';
|
||||
import {Route} from '../core';
|
||||
import {CompileMetadataResolver} from '../metadata_resolver';
|
||||
|
||||
import {AotCompilerHost} from './compiler_host';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {StaticSymbol} from './static_symbol';
|
||||
|
||||
export interface LazyRoute {
|
||||
module: StaticSymbol;
|
||||
route: string;
|
||||
referencedModule: StaticSymbol;
|
||||
}
|
||||
|
||||
export function listLazyRoutes(
|
||||
moduleMeta: CompileNgModuleMetadata, reflector: StaticReflector): LazyRoute[] {
|
||||
const allLazyRoutes: LazyRoute[] = [];
|
||||
for (const {provider, module} of moduleMeta.transitiveModule.providers) {
|
||||
if (tokenReference(provider.token) === reflector.ROUTES) {
|
||||
const loadChildren = _collectLoadChildren(provider.useValue);
|
||||
for (const route of loadChildren) {
|
||||
allLazyRoutes.push(parseLazyRoute(route, reflector, module.reference));
|
||||
}
|
||||
}
|
||||
}
|
||||
return allLazyRoutes;
|
||||
}
|
||||
|
||||
function _collectLoadChildren(routes: string | Route | Route[], target: string[] = []): string[] {
|
||||
if (typeof routes === 'string') {
|
||||
target.push(routes);
|
||||
} else if (Array.isArray(routes)) {
|
||||
for (const route of routes) {
|
||||
_collectLoadChildren(route, target);
|
||||
}
|
||||
} else if (routes.loadChildren) {
|
||||
_collectLoadChildren(routes.loadChildren, target);
|
||||
} else if (routes.children) {
|
||||
_collectLoadChildren(routes.children, target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
export function parseLazyRoute(
|
||||
route: string, reflector: StaticReflector, module?: StaticSymbol): LazyRoute {
|
||||
const [routePath, routeName] = route.split('#');
|
||||
const referencedModule = reflector.resolveExternalReference(
|
||||
{
|
||||
moduleName: routePath,
|
||||
name: routeName,
|
||||
},
|
||||
module ? module.filePath : undefined);
|
||||
return {route: route, module: module || referencedModule, referencedModule};
|
||||
}
|
@ -45,7 +45,7 @@ export class StaticReflector implements CompileReflector {
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
private injectionToken: StaticSymbol;
|
||||
private opaqueToken: StaticSymbol;
|
||||
private ROUTES: StaticSymbol;
|
||||
ROUTES: StaticSymbol;
|
||||
private ANALYZE_FOR_ENTRY_COMPONENTS: StaticSymbol;
|
||||
private annotationForParentClassWithSummaryKind =
|
||||
new Map<CompileSummaryKind, MetadataFactory<any>[]>();
|
||||
@ -76,11 +76,14 @@ export class StaticReflector implements CompileReflector {
|
||||
return this.symbolResolver.getResourcePath(staticSymbol);
|
||||
}
|
||||
|
||||
resolveExternalReference(ref: o.ExternalReference): StaticSymbol {
|
||||
const refSymbol = this.symbolResolver.getSymbolByModule(ref.moduleName !, ref.name !);
|
||||
resolveExternalReference(ref: o.ExternalReference, containingFile?: string): StaticSymbol {
|
||||
const refSymbol =
|
||||
this.symbolResolver.getSymbolByModule(ref.moduleName !, ref.name !, containingFile);
|
||||
const declarationSymbol = this.findSymbolDeclaration(refSymbol);
|
||||
this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !);
|
||||
this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
||||
if (!containingFile) {
|
||||
this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName !);
|
||||
this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
||||
}
|
||||
return declarationSymbol;
|
||||
}
|
||||
|
||||
@ -749,7 +752,7 @@ class PopulatedScope extends BindingScope {
|
||||
}
|
||||
|
||||
function positionalError(message: string, fileName: string, line: number, column: number): Error {
|
||||
const result = new Error(message);
|
||||
const result = syntaxError(message);
|
||||
(result as any).fileName = fileName;
|
||||
(result as any).line = line;
|
||||
(result as any).column = column;
|
||||
|
@ -39,13 +39,6 @@ export interface StaticSymbolResolverHost {
|
||||
* `path/to/containingFile.ts` containing `import {...} from 'module-name'`.
|
||||
*/
|
||||
moduleNameToFileName(moduleName: string, containingFile?: string): string|null;
|
||||
/**
|
||||
* Converts a file path to a module name that can be used as an `import.
|
||||
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
|
||||
*
|
||||
* See ImportResolver.
|
||||
*/
|
||||
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string;
|
||||
}
|
||||
|
||||
const SUPPORTED_SCHEMA_VERSION = 4;
|
||||
@ -160,15 +153,6 @@ export class StaticSymbolResolver {
|
||||
return (resolvedSymbol && resolvedSymbol.metadata && resolvedSymbol.metadata.arity) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a file path to a module name that can be used as an `import`.
|
||||
*/
|
||||
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string {
|
||||
return this.summaryResolver.getKnownModuleName(importedFilePath) ||
|
||||
this.knownFileNameToModuleNames.get(importedFilePath) ||
|
||||
this.host.fileNameToModuleName(importedFilePath, containingFilePath);
|
||||
}
|
||||
|
||||
getKnownModuleName(filePath: string): string|null {
|
||||
return this.knownFileNameToModuleNames.get(filePath) || null;
|
||||
}
|
||||
@ -498,9 +482,8 @@ export class StaticSymbolResolver {
|
||||
const filePath = this.resolveModule(module, containingFile);
|
||||
if (!filePath) {
|
||||
this.reportError(
|
||||
new Error(`Could not resolve module ${module}${containingFile ? ` relative to $ {
|
||||
containingFile
|
||||
} `: ''}`));
|
||||
new Error(`Could not resolve module ${module}${containingFile ? ' relative to ' +
|
||||
containingFile : ''}`));
|
||||
return this.getStaticSymbol(`ERROR:${module}`, symbolName);
|
||||
}
|
||||
return this.getStaticSymbol(filePath, symbolName);
|
||||
|
@ -10,7 +10,7 @@ import {Summary, SummaryResolver} from '../summary_resolver';
|
||||
|
||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||
import {deserializeSummaries} from './summary_serializer';
|
||||
import {ngfactoryFilePath, stripGeneratedFileSuffix, summaryFileName} from './util';
|
||||
import {stripGeneratedFileSuffix, summaryFileName} from './util';
|
||||
|
||||
export interface AotSummaryResolverHost {
|
||||
/**
|
||||
@ -119,11 +119,7 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
|
||||
if (moduleName) {
|
||||
this.knownFileNameToModuleNames.set(filePath, moduleName);
|
||||
}
|
||||
importAs.forEach((importAs) => {
|
||||
this.importAs.set(
|
||||
importAs.symbol,
|
||||
this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs));
|
||||
});
|
||||
importAs.forEach((importAs) => { this.importAs.set(importAs.symbol, importAs.importAs); });
|
||||
}
|
||||
return hasSummary;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {OutputContext, ValueTransformer, ValueVisitor, visitValue} from '../util
|
||||
|
||||
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
|
||||
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
|
||||
import {summaryForJitFileName, summaryForJitName} from './util';
|
||||
import {isLoweredSymbol, ngfactoryFilePath, summaryForJitFileName, summaryForJitName} from './util';
|
||||
|
||||
export function serializeSummaries(
|
||||
srcFileName: string, forJitCtx: OutputContext | null,
|
||||
@ -38,7 +38,7 @@ export function serializeSummaries(
|
||||
});
|
||||
const {json, exportAs} = toJsonSerializer.serialize();
|
||||
if (forJitCtx) {
|
||||
const forJitSerializer = new ForJitSerializer(forJitCtx, symbolResolver);
|
||||
const forJitSerializer = new ForJitSerializer(forJitCtx, symbolResolver, summaryResolver);
|
||||
types.forEach(({summary, metadata}) => { forJitSerializer.addSourceType(summary, metadata); });
|
||||
toJsonSerializer.unprocessedSymbolSummariesBySymbol.forEach((summary) => {
|
||||
if (summaryResolver.isLibraryFile(summary.symbol.filePath) && summary.type) {
|
||||
@ -55,7 +55,7 @@ export function deserializeSummaries(
|
||||
libraryFileName: string, json: string): {
|
||||
moduleName: string | null,
|
||||
summaries: Summary<StaticSymbol>[],
|
||||
importAs: {symbol: StaticSymbol, importAs: string}[]
|
||||
importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[]
|
||||
} {
|
||||
const deserializer = new FromJsonDeserializer(symbolCache, summaryResolver);
|
||||
return deserializer.deserialize(libraryFileName, json);
|
||||
@ -83,6 +83,7 @@ class ToJsonSerializer extends ValueTransformer {
|
||||
// Note: This only contains symbols without members.
|
||||
private symbols: StaticSymbol[] = [];
|
||||
private indexBySymbol = new Map<StaticSymbol, number>();
|
||||
private reexportedBy = new Map<StaticSymbol, StaticSymbol>();
|
||||
// This now contains a `__symbol: number` in the place of
|
||||
// StaticSymbols, but otherwise has the same shape as the original objects.
|
||||
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
|
||||
@ -126,9 +127,32 @@ class ToJsonSerializer extends ValueTransformer {
|
||||
}
|
||||
});
|
||||
metadata = clone;
|
||||
} else if (isCall(metadata)) {
|
||||
if (!isFunctionCall(metadata) && !isMethodCallOnVariable(metadata)) {
|
||||
// Don't store complex calls as we won't be able to simplify them anyways later on.
|
||||
metadata = {
|
||||
__symbolic: 'error',
|
||||
message: 'Complex function calls are not supported.',
|
||||
};
|
||||
}
|
||||
}
|
||||
// Note: We need to keep storing ctor calls for e.g.
|
||||
// `export const x = new InjectionToken(...)`
|
||||
unprocessedSummary.metadata = metadata;
|
||||
processedSummary.metadata = this.processValue(metadata, SerializationFlags.ResolveValue);
|
||||
if (metadata instanceof StaticSymbol &&
|
||||
this.summaryResolver.isLibraryFile(metadata.filePath)) {
|
||||
const declarationSymbol = this.symbols[this.indexBySymbol.get(metadata) !];
|
||||
if (!isLoweredSymbol(declarationSymbol.name)) {
|
||||
// Note: symbols that were introduced during codegen in the user file can have a reexport
|
||||
// if a user used `export *`. However, we can't rely on this as tsickle will change
|
||||
// `export *` into named exports, using only the information from the typechecker.
|
||||
// As we introduce the new symbols after typecheck, Tsickle does not know about them,
|
||||
// and omits them when expanding `export *`.
|
||||
// So we have to keep reexporting these symbols manually via .ngfactory files.
|
||||
this.reexportedBy.set(declarationSymbol, summary.symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!unprocessedSummary.type && summary.type) {
|
||||
unprocessedSummary.type = summary.type;
|
||||
@ -161,12 +185,17 @@ class ToJsonSerializer extends ValueTransformer {
|
||||
summaries: this.processedSummaries,
|
||||
symbols: this.symbols.map((symbol, index) => {
|
||||
symbol.assertNoMembers();
|
||||
let importAs: string = undefined !;
|
||||
let importAs: string|number = undefined !;
|
||||
if (this.summaryResolver.isLibraryFile(symbol.filePath)) {
|
||||
const summary = this.unprocessedSymbolSummariesBySymbol.get(symbol);
|
||||
if (!summary || !summary.metadata || summary.metadata.__symbolic !== 'interface') {
|
||||
importAs = `${symbol.name}_${index}`;
|
||||
exportAs.push({symbol, exportAs: importAs});
|
||||
const reexportSymbol = this.reexportedBy.get(symbol);
|
||||
if (reexportSymbol) {
|
||||
importAs = this.indexBySymbol.get(reexportSymbol) !;
|
||||
} else {
|
||||
const summary = this.unprocessedSymbolSummariesBySymbol.get(symbol);
|
||||
if (!summary || !summary.metadata || summary.metadata.__symbolic !== 'interface') {
|
||||
importAs = `${symbol.name}_${index}`;
|
||||
exportAs.push({symbol, exportAs: importAs});
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
@ -246,29 +275,35 @@ class ToJsonSerializer extends ValueTransformer {
|
||||
}
|
||||
|
||||
class ForJitSerializer {
|
||||
private data = new Map<StaticSymbol, {
|
||||
private data: Array<{
|
||||
summary: CompileTypeSummary,
|
||||
metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|CompilePipeMetadata|
|
||||
CompileTypeMetadata|null,
|
||||
isLibrary: boolean
|
||||
}>();
|
||||
}> = [];
|
||||
|
||||
constructor(private outputCtx: OutputContext, private symbolResolver: StaticSymbolResolver) {}
|
||||
constructor(
|
||||
private outputCtx: OutputContext, private symbolResolver: StaticSymbolResolver,
|
||||
private summaryResolver: SummaryResolver<StaticSymbol>) {}
|
||||
|
||||
addSourceType(
|
||||
summary: CompileTypeSummary, metadata: CompileNgModuleMetadata|CompileDirectiveMetadata|
|
||||
CompilePipeMetadata|CompileTypeMetadata) {
|
||||
this.data.set(summary.type.reference, {summary, metadata, isLibrary: false});
|
||||
this.data.push({summary, metadata, isLibrary: false});
|
||||
}
|
||||
|
||||
addLibType(summary: CompileTypeSummary) {
|
||||
this.data.set(summary.type.reference, {summary, metadata: null, isLibrary: true});
|
||||
this.data.push({summary, metadata: null, isLibrary: true});
|
||||
}
|
||||
|
||||
serialize(exportAs: {symbol: StaticSymbol, exportAs: string}[]): void {
|
||||
serialize(exportAsArr: {symbol: StaticSymbol, exportAs: string}[]): void {
|
||||
const exportAsBySymbol = new Map<StaticSymbol, string>();
|
||||
for (const {symbol, exportAs} of exportAsArr) {
|
||||
exportAsBySymbol.set(symbol, exportAs);
|
||||
}
|
||||
const ngModuleSymbols = new Set<StaticSymbol>();
|
||||
|
||||
Array.from(this.data.values()).forEach(({summary, metadata, isLibrary}) => {
|
||||
for (const {summary, metadata, isLibrary} of this.data) {
|
||||
if (summary.summaryKind === CompileSummaryKind.NgModule) {
|
||||
// collect the symbols that refer to NgModule classes.
|
||||
// Note: we can't just rely on `summary.type.summaryKind` to determine this as
|
||||
@ -276,7 +311,9 @@ class ForJitSerializer {
|
||||
// See serializeSummaries for details.
|
||||
ngModuleSymbols.add(summary.type.reference);
|
||||
const modSummary = <CompileNgModuleSummary>summary;
|
||||
modSummary.modules.forEach((mod) => { ngModuleSymbols.add(mod.reference); });
|
||||
for (const mod of modSummary.modules) {
|
||||
ngModuleSymbols.add(mod.reference);
|
||||
}
|
||||
}
|
||||
if (!isLibrary) {
|
||||
const fnName = summaryForJitName(summary.type.reference.name);
|
||||
@ -284,16 +321,15 @@ class ForJitSerializer {
|
||||
this.outputCtx, summary.type.reference,
|
||||
this.serializeSummaryWithDeps(summary, metadata !));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exportAs.forEach((entry) => {
|
||||
const symbol = entry.symbol;
|
||||
if (ngModuleSymbols.has(symbol)) {
|
||||
const jitExportAsName = summaryForJitName(entry.exportAs);
|
||||
this.outputCtx.statements.push(
|
||||
o.variable(jitExportAsName).set(this.serializeSummaryRef(symbol)).toDeclStmt(null, [
|
||||
o.StmtModifier.Exported
|
||||
]));
|
||||
ngModuleSymbols.forEach((ngModuleSymbol) => {
|
||||
if (this.summaryResolver.isLibraryFile(ngModuleSymbol.filePath)) {
|
||||
let exportAs = exportAsBySymbol.get(ngModuleSymbol) || ngModuleSymbol.name;
|
||||
const jitExportAsName = summaryForJitName(exportAs);
|
||||
this.outputCtx.statements.push(o.variable(jitExportAsName)
|
||||
.set(this.serializeSummaryRef(ngModuleSymbol))
|
||||
.toDeclStmt(null, [o.StmtModifier.Exported]));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -378,22 +414,26 @@ class FromJsonDeserializer extends ValueTransformer {
|
||||
deserialize(libraryFileName: string, json: string): {
|
||||
moduleName: string | null,
|
||||
summaries: Summary<StaticSymbol>[],
|
||||
importAs: {symbol: StaticSymbol, importAs: string}[]
|
||||
importAs: {symbol: StaticSymbol, importAs: StaticSymbol}[]
|
||||
} {
|
||||
const data: {moduleName: string | null, summaries: any[], symbols: any[]} = JSON.parse(json);
|
||||
const importAs: {symbol: StaticSymbol, importAs: string}[] = [];
|
||||
this.symbols = [];
|
||||
data.symbols.forEach((serializedSymbol) => {
|
||||
const symbol = this.symbolCache.get(
|
||||
this.summaryResolver.fromSummaryFileName(serializedSymbol.filePath, libraryFileName),
|
||||
serializedSymbol.name);
|
||||
this.symbols.push(symbol);
|
||||
if (serializedSymbol.importAs) {
|
||||
importAs.push({symbol: symbol, importAs: serializedSymbol.importAs});
|
||||
const allImportAs: {symbol: StaticSymbol, importAs: StaticSymbol}[] = [];
|
||||
this.symbols = data.symbols.map(
|
||||
(serializedSymbol) => this.symbolCache.get(
|
||||
this.summaryResolver.fromSummaryFileName(serializedSymbol.filePath, libraryFileName),
|
||||
serializedSymbol.name));
|
||||
data.symbols.forEach((serializedSymbol, index) => {
|
||||
const symbol = this.symbols[index];
|
||||
const importAs = serializedSymbol.importAs;
|
||||
if (typeof importAs === 'number') {
|
||||
allImportAs.push({symbol, importAs: this.symbols[importAs]});
|
||||
} else if (typeof importAs === 'string') {
|
||||
allImportAs.push(
|
||||
{symbol, importAs: this.symbolCache.get(ngfactoryFilePath(libraryFileName), importAs)});
|
||||
}
|
||||
});
|
||||
const summaries = visitValue(data.summaries, this, null);
|
||||
return {moduleName: data.moduleName, summaries, importAs};
|
||||
const summaries = visitValue(data.summaries, this, null) as Summary<StaticSymbol>[];
|
||||
return {moduleName: data.moduleName, summaries, importAs: allImportAs};
|
||||
}
|
||||
|
||||
visitStringMap(map: {[key: string]: any}, context: any): any {
|
||||
@ -407,3 +447,16 @@ class FromJsonDeserializer extends ValueTransformer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isCall(metadata: any): boolean {
|
||||
return metadata && metadata.__symbolic === 'call';
|
||||
}
|
||||
|
||||
function isFunctionCall(metadata: any): boolean {
|
||||
return isCall(metadata) && metadata.expression instanceof StaticSymbol;
|
||||
}
|
||||
|
||||
function isMethodCallOnVariable(metadata: any): boolean {
|
||||
return isCall(metadata) && metadata.expression && metadata.expression.__symbolic === 'select' &&
|
||||
metadata.expression.expression instanceof StaticSymbol;
|
||||
}
|
||||
|
@ -58,4 +58,14 @@ export function summaryForJitName(symbolName: string): string {
|
||||
|
||||
export function stripSummaryForJitNameSuffix(symbolName: string): string {
|
||||
return symbolName.replace(JIT_SUMMARY_NAME, '');
|
||||
}
|
||||
}
|
||||
|
||||
const LOWERED_SYMBOL = /\u0275\d+/;
|
||||
|
||||
export function isLoweredSymbol(name: string) {
|
||||
return LOWERED_SYMBOL.test(name);
|
||||
}
|
||||
|
||||
export function createLoweredSymbol(id: number): string {
|
||||
return `\u0275${id}`;
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ export * from './aot/static_reflector';
|
||||
export * from './aot/static_symbol';
|
||||
export * from './aot/static_symbol_resolver';
|
||||
export * from './aot/summary_resolver';
|
||||
export {isLoweredSymbol, createLoweredSymbol} from './aot/util';
|
||||
export {LazyRoute} from './aot/lazy_routes';
|
||||
export * from './ast_path';
|
||||
export * from './summary_resolver';
|
||||
export {Identifiers} from './identifiers';
|
||||
|
@ -260,3 +260,8 @@ function makeMetadataFactory<T>(name: string, props?: (...args: any[]) => T): Me
|
||||
factory.ngMetadataName = name;
|
||||
return factory;
|
||||
}
|
||||
|
||||
export interface Route {
|
||||
children?: Route[];
|
||||
loadChildren?: string|Type|any;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {AotCompilerOptions} from '../aot/compiler_options';
|
||||
import {StaticReflector} from '../aot/static_reflector';
|
||||
import {StaticSymbol} from '../aot/static_symbol';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeSummary, viewClassName} from '../compile_metadata';
|
||||
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeSummary} from '../compile_metadata';
|
||||
import {BuiltinConverter, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
|
||||
import {Identifiers} from '../identifiers';
|
||||
@ -33,7 +33,8 @@ export class TypeCheckCompiler {
|
||||
* and also violate the point above.
|
||||
*/
|
||||
compileComponent(
|
||||
component: CompileDirectiveMetadata, template: TemplateAst[], usedPipes: CompilePipeSummary[],
|
||||
componentId: string, component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
usedPipes: CompilePipeSummary[],
|
||||
externalReferenceVars: Map<StaticSymbol, string>): o.Statement[] {
|
||||
const pipes = new Map<string, StaticSymbol>();
|
||||
usedPipes.forEach(p => pipes.set(p.name, p.type.reference));
|
||||
@ -48,7 +49,7 @@ export class TypeCheckCompiler {
|
||||
const visitor = viewBuilderFactory(null);
|
||||
visitor.visitAll([], template);
|
||||
|
||||
return visitor.build();
|
||||
return visitor.build(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,8 +104,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
templateVisitAll(this, astNodes);
|
||||
}
|
||||
|
||||
build(targetStatements: o.Statement[] = []): o.Statement[] {
|
||||
this.children.forEach((child) => child.build(targetStatements));
|
||||
build(componentId: string, targetStatements: o.Statement[] = []): o.Statement[] {
|
||||
this.children.forEach((child) => child.build(componentId, targetStatements));
|
||||
const viewStmts: o.Statement[] =
|
||||
[o.variable(DYNAMIC_VAR_NAME).set(o.NULL_EXPR).toDeclStmt(o.DYNAMIC_TYPE)];
|
||||
let bindingCount = 0;
|
||||
@ -128,7 +129,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
(stmt: o.Statement) => o.applySourceSpanToStatementIfNeeded(stmt, sourceSpan)));
|
||||
});
|
||||
|
||||
const viewName = `_View_${this.component.name}_${this.embeddedViewIndex}`;
|
||||
const viewName = `_View_${componentId}_${this.embeddedViewIndex}`;
|
||||
const viewFactory = new o.DeclareFunctionStmt(viewName, [], viewStmts);
|
||||
targetStatements.push(viewFactory);
|
||||
return targetStatements;
|
||||
|
@ -491,10 +491,11 @@ describe('compiler (unbundled Angular)', () => {
|
||||
const libInput: MockDirectory = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
export class AValue {}
|
||||
export type AType = {};
|
||||
|
||||
export class AClass {
|
||||
constructor(a: AType) {}
|
||||
constructor(a: AType, b: AValue) {}
|
||||
}
|
||||
`
|
||||
}
|
||||
@ -502,7 +503,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||
const appInput: MockDirectory = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
export * from '../lib/base';
|
||||
export {AClass} from '../lib/base';
|
||||
`
|
||||
}
|
||||
};
|
||||
@ -511,7 +512,105 @@ describe('compiler (unbundled Angular)', () => {
|
||||
const {genFiles: appGenFiles} =
|
||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
||||
expect(toTypeScript(appNgFactory)).not.toContain('AType');
|
||||
const appNgFactoryTs = toTypeScript(appNgFactory);
|
||||
expect(appNgFactoryTs).not.toContain('AType');
|
||||
expect(appNgFactoryTs).toContain('AValue');
|
||||
});
|
||||
|
||||
it('should not reexport complex function calls', () => {
|
||||
const libInput: MockDirectory = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
export class AClass {
|
||||
constructor(arg: any) {}
|
||||
|
||||
static create(arg: any = null): AClass { return new AClass(arg); }
|
||||
|
||||
call(arg: any) {}
|
||||
}
|
||||
|
||||
export function simple(arg: any) { return [arg]; }
|
||||
|
||||
export const ctor_arg = {};
|
||||
export const ctor_call = new AClass(ctor_arg);
|
||||
|
||||
export const static_arg = {};
|
||||
export const static_call = AClass.create(static_arg);
|
||||
|
||||
export const complex_arg = {};
|
||||
export const complex_call = AClass.create().call(complex_arg);
|
||||
|
||||
export const simple_arg = {};
|
||||
export const simple_call = simple(simple_arg);
|
||||
`
|
||||
}
|
||||
};
|
||||
const appInput: MockDirectory = {
|
||||
'app': {
|
||||
'main.ts': `
|
||||
import {ctor_call, static_call, complex_call, simple_call} from '../lib/base';
|
||||
|
||||
export const calls = [ctor_call, static_call, complex_call, simple_call];
|
||||
`,
|
||||
}
|
||||
};
|
||||
|
||||
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
||||
const {genFiles: appGenFiles} =
|
||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
||||
const appNgFactoryTs = toTypeScript(appNgFactory);
|
||||
|
||||
// metadata of ctor calls is preserved, so we reexport the argument
|
||||
expect(appNgFactoryTs).toContain('ctor_arg');
|
||||
expect(appNgFactoryTs).toContain('ctor_call');
|
||||
|
||||
// metadata of static calls is preserved, so we reexport the argument
|
||||
expect(appNgFactoryTs).toContain('static_arg');
|
||||
expect(appNgFactoryTs).toContain('AClass');
|
||||
expect(appNgFactoryTs).toContain('static_call');
|
||||
|
||||
// metadata of complex calls is elided, so we don't reexport the argument
|
||||
expect(appNgFactoryTs).not.toContain('complex_arg');
|
||||
expect(appNgFactoryTs).toContain('complex_call');
|
||||
|
||||
// metadata of simple calls is preserved, so we reexport the argument
|
||||
expect(appNgFactoryTs).toContain('simple_arg');
|
||||
expect(appNgFactoryTs).toContain('simple_call');
|
||||
});
|
||||
|
||||
it('should not reexport already exported symbols except for lowered symbols', () => {
|
||||
const libInput: MockDirectory = {
|
||||
'lib': {
|
||||
'base.ts': `
|
||||
export const exportedVar = 1;
|
||||
|
||||
// A symbol introduced by lowering expressions
|
||||
export const ɵ1 = 'lowered symbol';
|
||||
`
|
||||
}
|
||||
};
|
||||
const appInput: MockDirectory = {
|
||||
'app': {
|
||||
'main.ts': `export * from '../lib/base';`,
|
||||
}
|
||||
};
|
||||
|
||||
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
||||
const {genFiles: appGenFiles} =
|
||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
||||
const appNgFactoryTs = toTypeScript(appNgFactory);
|
||||
|
||||
// we don't need to reexport exported symbols via the .ngfactory
|
||||
// as we can refer to them via the reexport.
|
||||
expect(appNgFactoryTs).not.toContain('exportedVar');
|
||||
|
||||
// although ɵ1 is reexported via `export *`, we still need to reexport it
|
||||
// via the .ngfactory as tsickle expands `export *` into named exports,
|
||||
// and doesn't know about our lowered symbols as we introduce them
|
||||
// after the typecheck phase.
|
||||
expect(appNgFactoryTs).toContain('ɵ1');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1062,6 +1062,22 @@ describe('StaticReflector', () => {
|
||||
.useValue)
|
||||
.toEqual({path: 'foo', data: {e: 1}});
|
||||
});
|
||||
|
||||
describe('resolveExternalReference', () => {
|
||||
it('should register modules names in the StaticSymbolResolver if no containingFile is given',
|
||||
() => {
|
||||
init({
|
||||
'/tmp/root.ts': ``,
|
||||
'/tmp/a.ts': `export const x = 1;`,
|
||||
});
|
||||
let symbol =
|
||||
reflector.resolveExternalReference({moduleName: './a', name: 'x'}, '/tmp/root.ts');
|
||||
expect(symbolResolver.getKnownModuleName(symbol.filePath)).toBeFalsy();
|
||||
|
||||
symbol = reflector.resolveExternalReference({moduleName: 'a', name: 'x'});
|
||||
expect(symbolResolver.getKnownModuleName(symbol.filePath)).toBe('a');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const DEFAULT_TEST_DATA: {[key: string]: any} = {
|
||||
|
@ -465,10 +465,6 @@ export class MockStaticSymbolResolverHost implements StaticSymbolResolverHost {
|
||||
return '/tmp/' + modulePath + '.d.ts';
|
||||
}
|
||||
|
||||
fileNameToModuleName(filePath: string, containingFile: string) {
|
||||
return filePath.replace(/(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/, '');
|
||||
}
|
||||
|
||||
getMetadataFor(moduleId: string): any { return this._getMetadataFor(moduleId); }
|
||||
|
||||
private _getMetadataFor(filePath: string): any {
|
||||
|
@ -317,37 +317,96 @@ export function main() {
|
||||
expect(summaries[1].metadata).toBe('someString');
|
||||
});
|
||||
|
||||
it('should not create "importAs" names for reexported types in libraries', () => {
|
||||
it('should not create "importAs" names for ctor arguments which are types of reexported classes in libraries',
|
||||
() => {
|
||||
init();
|
||||
const externalSerialized = serializeSummaries(
|
||||
'someFile.ts', createMockOutputContext(), summaryResolver, symbolResolver,
|
||||
[
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.ts', 'type'),
|
||||
metadata: {__symbolic: 'interface'}
|
||||
},
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.ts', 'value'),
|
||||
metadata: {__symbolic: 'class'}
|
||||
},
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.ts', 'reexportClass'),
|
||||
metadata: {
|
||||
__symbolic: 'class',
|
||||
'members': {
|
||||
'__ctor__': [{
|
||||
'__symbolic': 'constructor',
|
||||
'parameters': [
|
||||
symbolCache.get('/tmp/external.ts', 'type'),
|
||||
symbolCache.get('/tmp/external.ts', 'value'),
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
],
|
||||
[]);
|
||||
expect(externalSerialized.exportAs).toEqual([]);
|
||||
init({
|
||||
'/tmp/external.ngsummary.json': externalSerialized.json,
|
||||
});
|
||||
const serialized = serializeSummaries(
|
||||
'someFile.ts', createMockOutputContext(), summaryResolver, symbolResolver, [{
|
||||
symbol: symbolCache.get('/tmp/test.ts', 'mainClass'),
|
||||
metadata: symbolCache.get('/tmp/external.d.ts', 'reexportClass'),
|
||||
}],
|
||||
[]);
|
||||
const importAs =
|
||||
deserializeSummaries(symbolCache, summaryResolver, 'someFile.d.ts', serialized.json)
|
||||
.importAs;
|
||||
expect(importAs).toEqual([
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.d.ts', 'reexportClass'),
|
||||
importAs: symbolCache.get('/tmp/test.d.ts', 'mainClass'),
|
||||
},
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.d.ts', 'value'),
|
||||
importAs: symbolCache.get('someFile.ngfactory.d.ts', 'value_3'),
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should use existing reexports for "importAs" for symbols of libraries', () => {
|
||||
init();
|
||||
const externalSerialized = serializeSummaries(
|
||||
'someFile.ts', createMockOutputContext(), summaryResolver, symbolResolver,
|
||||
[
|
||||
{symbol: symbolCache.get('/tmp/external.ts', 'value'), metadata: 'aValue'},
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.ts', 'type'),
|
||||
metadata: {__symbolic: 'interface'}
|
||||
},
|
||||
{
|
||||
symbol: symbolCache.get('/tmp/external.ts', 'reexportType'),
|
||||
metadata: symbolCache.get('/tmp/external.ts', 'type')
|
||||
symbol: symbolCache.get('/tmp/external.ts', 'reexportValue'),
|
||||
metadata: symbolCache.get('/tmp/external.ts', 'value')
|
||||
},
|
||||
],
|
||||
[]);
|
||||
expect(externalSerialized.exportAs).toEqual([]);
|
||||
init({
|
||||
'/tmp/external.ngsummary.json': externalSerialized.json,
|
||||
});
|
||||
const serialized = serializeSummaries(
|
||||
'someFile.ts', createMockOutputContext(), summaryResolver, symbolResolver, [{
|
||||
symbol: symbolCache.get('/tmp/test.ts', 'mainType'),
|
||||
metadata: symbolCache.get('/tmp/external.d.ts', 'reexportType'),
|
||||
symbol: symbolCache.get('/tmp/test.ts', 'mainValue'),
|
||||
metadata: symbolCache.get('/tmp/external.d.ts', 'reexportValue'),
|
||||
}],
|
||||
[]);
|
||||
expect(serialized.exportAs).toEqual([]);
|
||||
const importAs =
|
||||
deserializeSummaries(symbolCache, summaryResolver, 'someFile.d.ts', serialized.json)
|
||||
.importAs;
|
||||
expect(importAs).toEqual([]);
|
||||
expect(importAs).toEqual([{
|
||||
symbol: symbolCache.get('/tmp/external.d.ts', 'value'),
|
||||
importAs: symbolCache.get('/tmp/test.d.ts', 'mainValue'),
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should create "importAs" names for non source symbols', () => {
|
||||
it('should create reexports in the ngfactory for symbols of libraries', () => {
|
||||
init();
|
||||
const serialized = serializeSummaries(
|
||||
'someFile.ts', createMockOutputContext(), summaryResolver, symbolResolver, [{
|
||||
@ -366,9 +425,10 @@ export function main() {
|
||||
const deserialized =
|
||||
deserializeSummaries(symbolCache, summaryResolver, 'someFile.d.ts', serialized.json);
|
||||
// Note: no entry for the symbol with members!
|
||||
expect(deserialized.importAs).toEqual([
|
||||
{symbol: symbolCache.get('/tmp/external.d.ts', 'lib'), importAs: 'lib_1'}
|
||||
]);
|
||||
expect(deserialized.importAs).toEqual([{
|
||||
symbol: symbolCache.get('/tmp/external.d.ts', 'lib'),
|
||||
importAs: symbolCache.get('someFile.ngfactory.d.ts', 'lib_1')
|
||||
}]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -652,7 +652,7 @@ export function compile(
|
||||
const tsSettings = {...settings, ...tsOptions};
|
||||
const program = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
||||
preCompile(program);
|
||||
const {compiler, reflector} = createAotCompiler(aotHost, options);
|
||||
const {compiler, reflector} = createAotCompiler(aotHost, options, (err) => { throw err; });
|
||||
const analyzedModules =
|
||||
compiler.analyzeModulesSync(program.getSourceFiles().map(sf => sf.fileName));
|
||||
const genFiles = compiler.emitAllImpls(analyzedModules);
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -17,7 +17,7 @@ const globals = {
|
||||
'rxjs/Subject': 'Rx'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/compiler/esm5/testing.js',
|
||||
dest: '../../../dist/packages-dist/compiler/bundles/compiler-testing.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'rxjs/Observable': 'Rx',
|
||||
@ -18,7 +18,7 @@ const globals = {
|
||||
'rxjs/operator/share': 'Rx.Observable.prototype'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/core/esm5/core.js',
|
||||
dest: '../../dist/packages-dist/core/bundles/core.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -15,7 +15,7 @@ const globals = {
|
||||
'rxjs/Subject': 'Rx',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/core/esm5/testing.js',
|
||||
dest: '../../../dist/packages-dist/core/bundles/core-testing.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -21,7 +21,7 @@ const globals = {
|
||||
'rxjs/operator/map': 'Rx.Observable.prototype'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/forms/esm5/forms.js',
|
||||
dest: '../../dist/packages-dist/forms/bundles/forms.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,18 +6,19 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
'@angular/compiler': 'ng.compiler',
|
||||
'@angular/platform-browser': 'ng.platformBrowser',
|
||||
'rxjs/Observable': 'Rx',
|
||||
'rxjs/Observer': 'Rx',
|
||||
'rxjs/Subject': 'Rx'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/http/esm5/http.js',
|
||||
dest: '../../dist/packages-dist/http/bundles/http.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -20,7 +20,7 @@ const globals = {
|
||||
'rxjs/operator/take': 'Rx.Observable.prototype'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/http/esm5/testing.js',
|
||||
dest: '../../../dist/packages-dist/http/bundles/http-testing.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
import * as path from 'path';
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
const path = require('path');
|
||||
|
||||
var m = /^\@angular\/((\w|\-)+)(\/(\w|\d|\/|\-)+)?$/;
|
||||
var location = normalize('../../dist/packages-dist') + '/';
|
||||
@ -60,7 +60,7 @@ module.exports = function(provided) {
|
||||
}
|
||||
`;
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/language-service/esm5/language-service.js',
|
||||
dest: '../../dist/packages-dist/language-service/bundles/language-service.umd.js',
|
||||
format: 'amd',
|
||||
|
@ -6,12 +6,17 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AotCompilerHost} from '@angular/compiler';
|
||||
import {CompilerHost, CompilerOptions, ModuleResolutionHostAdapter} from '@angular/compiler-cli/src/language_services';
|
||||
import {StaticSymbolResolverHost} from '@angular/compiler';
|
||||
import {CompilerOptions, MetadataCollector, MetadataReaderCache, MetadataReaderHost, createMetadataReaderCache, readMetadata} from '@angular/compiler-cli/src/language_services';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
|
||||
constructor(private host: ts.LanguageServiceHost) {
|
||||
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, MetadataReaderHost {
|
||||
// Note: verboseInvalidExpressions is important so that
|
||||
// the collector will collect errors instead of throwing
|
||||
private metadataCollector = new MetadataCollector({verboseInvalidExpression: true});
|
||||
|
||||
constructor(private host: ts.LanguageServiceHost, private getProgram: () => ts.Program) {
|
||||
if (host.directoryExists)
|
||||
this.directoryExists = directoryName => this.host.directoryExists !(directoryName);
|
||||
}
|
||||
@ -29,24 +34,46 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
|
||||
}
|
||||
|
||||
directoryExists: (directoryName: string) => boolean;
|
||||
|
||||
getSourceFileMetadata(fileName: string) {
|
||||
const sf = this.getProgram().getSourceFile(fileName);
|
||||
return sf ? this.metadataCollector.getMetadata(sf) : undefined;
|
||||
}
|
||||
|
||||
cacheMetadata(fileName: string) {
|
||||
// Don't cache the metadata for .ts files as they might change in the editor!
|
||||
return fileName.endsWith('.d.ts');
|
||||
}
|
||||
}
|
||||
|
||||
// This reflector host's purpose is to first set verboseInvalidExpressions to true so the
|
||||
// reflector will collect errors instead of throwing, and second to all deferring the creation
|
||||
// of the program until it is actually needed.
|
||||
export class ReflectorHost extends CompilerHost {
|
||||
export class ReflectorHost implements StaticSymbolResolverHost {
|
||||
private moduleResolutionCache: ts.ModuleResolutionCache;
|
||||
private hostAdapter: ReflectorModuleModuleResolutionHost;
|
||||
private metadataReaderCache = createMetadataReaderCache();
|
||||
|
||||
constructor(
|
||||
private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
|
||||
options: CompilerOptions) {
|
||||
super(
|
||||
// The ancestor value for program is overridden below so passing null here is safe.
|
||||
/* program */ null !, options,
|
||||
new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)),
|
||||
{verboseInvalidExpression: true});
|
||||
getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
|
||||
private options: CompilerOptions) {
|
||||
this.hostAdapter = new ReflectorModuleModuleResolutionHost(serviceHost, getProgram);
|
||||
this.moduleResolutionCache =
|
||||
ts.createModuleResolutionCache(serviceHost.getCurrentDirectory(), (s) => s);
|
||||
}
|
||||
|
||||
protected get program() { return this.getProgram(); }
|
||||
protected set program(value: ts.Program) {
|
||||
// Discard the result set by ancestor constructor
|
||||
getMetadataFor(modulePath: string): {[key: string]: any}[]|undefined {
|
||||
return readMetadata(modulePath, this.hostAdapter, this.metadataReaderCache);
|
||||
}
|
||||
|
||||
moduleNameToFileName(moduleName: string, containingFile?: string): string|null {
|
||||
if (!containingFile) {
|
||||
if (moduleName.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = path.join(this.options.basePath !, 'index.ts');
|
||||
}
|
||||
const resolved =
|
||||
ts.resolveModuleName(moduleName, containingFile, this.options, this.hostAdapter)
|
||||
.resolvedModule;
|
||||
return resolved ? resolved.resolvedFileName : null;
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -16,7 +16,7 @@ const globals = {
|
||||
'@angular/platform-browser': 'ng.platformBrowser',
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../dist/packages-dist/platform-browser-dynamic/esm5/platform-browser-dynamic.js',
|
||||
dest: '../../dist/packages-dist/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
format: 'umd',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -20,7 +20,7 @@ const globals = {
|
||||
'@angular/platform-browser-dynamic': 'ng.platformBrowserDynamic'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/platform-browser-dynamic/esm5/testing.js',
|
||||
dest:
|
||||
'../../../dist/packages-dist/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
@ -17,7 +17,7 @@ const globals = {
|
||||
'@angular/animations/browser': 'ng.animations.browser'
|
||||
};
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
entry: '../../../dist/packages-dist/platform-browser/esm5/animations.js',
|
||||
dest: '../../../dist/packages-dist/platform-browser/bundles/platform-browser-animations.umd.js',
|
||||
format: 'umd',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user