Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
a8134dcfd4 | |||
a1bf0de711 | |||
3dbc076159 | |||
3409efbeb3 | |||
3812f57789 | |||
8292e1cc51 | |||
d8de689080 | |||
d53cfb510c | |||
fb51e10954 | |||
7813529f4e | |||
066fca07f4 | |||
31be06a6f6 | |||
fd795da9d9 | |||
69302adc02 | |||
9891cef6e4 | |||
65d4e7a8af | |||
0fa208f624 | |||
15fa4bbdaf | |||
ed35adbea6 | |||
4645f43c3c | |||
b613639e8a | |||
bc6ff7745e | |||
33aaa9e7d0 | |||
689651b52f | |||
875fcfe5a9 | |||
0d238aa9d9 | |||
398b44640f | |||
5b1c714068 | |||
6df71d52c1 | |||
be25ccb94c | |||
6e994272e8 | |||
79ac811550 | |||
bbe6cf38ff | |||
c95fabf96d | |||
3fad0ffb3a | |||
ef0be182bb | |||
139c5b4eab | |||
1328236810 | |||
5453772648 | |||
7c5f89d2c3 | |||
bc0d140a1d | |||
14ecc9ead2 | |||
62a2fc8981 | |||
8f59e3750b | |||
3d9ebb4a52 | |||
ea141f86d3 | |||
c100dbe860 | |||
9c82e27ab6 | |||
4035e472d0 | |||
d03e2e35cb | |||
b29c32b758 | |||
3e9986871c | |||
617858df61 | |||
68cb77d9ab | |||
5cdf2e4e30 | |||
39bfa349c7 | |||
b3752e6524 | |||
9833b0b31c | |||
2eb8447c95 | |||
78c9972195 | |||
5bf55132fb | |||
50f5827fb4 | |||
e98f11ac9e | |||
1f2c7f6728 | |||
3d2799b73c | |||
685281337b | |||
9e8fa0748f | |||
525af1e5f0 | |||
58f2abef01 | |||
9d918d8e6c | |||
8236904933 | |||
73550967e4 | |||
c6505001a9 |
1
.github/angular-robot.yml
vendored
1
.github/angular-robot.yml
vendored
@ -48,6 +48,7 @@ merge:
|
||||
- "packages/bazel/src/protractor/**"
|
||||
- "packages/bazel/src/schematics/**"
|
||||
- "packages/compiler-cli/ngcc/**"
|
||||
- "packages/compiler-cli/src/ngtsc/sourcemaps/**",
|
||||
- "packages/docs/**"
|
||||
- "packages/elements/schematics/**"
|
||||
- "packages/examples/**"
|
||||
|
@ -3,6 +3,7 @@ import {commitMessage} from './commit-message';
|
||||
import {format} from './format';
|
||||
import {github} from './github';
|
||||
import {merge} from './merge';
|
||||
import {release} from './release';
|
||||
|
||||
module.exports = {
|
||||
commitMessage,
|
||||
@ -10,4 +11,5 @@ module.exports = {
|
||||
github,
|
||||
merge,
|
||||
caretaker,
|
||||
release,
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
|
||||
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
|
||||
import {github} from './github';
|
||||
import {release} from './release';
|
||||
|
||||
/**
|
||||
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
|
||||
@ -13,7 +14,9 @@ export const merge: DevInfraMergeConfig['merge'] = async api => {
|
||||
mergeReadyLabel: /^action: merge(-assistance)?/,
|
||||
caretakerNoteLabel: 'action: merge-assistance',
|
||||
commitMessageFixupLabel: 'commit message fixup',
|
||||
labels: await getDefaultTargetLabelConfiguration(api, github, '@angular/core'),
|
||||
// We can pick any of the NPM packages as we are in a monorepo where all packages are
|
||||
// published together with the same version and branching.
|
||||
labels: await getDefaultTargetLabelConfiguration(api, github, release),
|
||||
requiredBaseCommits: {
|
||||
// PRs that target either `master` or the patch branch, need to be rebased
|
||||
// on top of the latest commit message validation fix.
|
||||
|
33
.ng-dev/release.ts
Normal file
33
.ng-dev/release.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {join} from 'path';
|
||||
import {exec} from 'shelljs';
|
||||
import {ReleaseConfig} from '../dev-infra/release/config';
|
||||
|
||||
/** Configuration for the `ng-dev release` command. */
|
||||
export const release: ReleaseConfig = {
|
||||
npmPackages: [
|
||||
'@angular/animations',
|
||||
'@angular/bazel',
|
||||
'@angular/common',
|
||||
'@angular/compiler',
|
||||
'@angular/compiler-cli',
|
||||
'@angular/core',
|
||||
'@angular/elements',
|
||||
'@angular/forms',
|
||||
'@angular/language-service',
|
||||
'@angular/localize',
|
||||
'@angular/platform-browser',
|
||||
'@angular/platform-browser-dynamic',
|
||||
'@angular/platform-server',
|
||||
'@angular/platform-webworker',
|
||||
'@angular/platform-webworker-dynamic',
|
||||
'@angular/router',
|
||||
'@angular/service-worker',
|
||||
'@angular/upgrade',
|
||||
],
|
||||
// TODO: Implement release package building here.
|
||||
buildPackages: async () => [],
|
||||
// TODO: This can be removed once there is a org-wide tool for changelog generation.
|
||||
generateReleaseNotesForHead: async () => {
|
||||
exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')});
|
||||
},
|
||||
};
|
@ -284,7 +284,7 @@ groups:
|
||||
users:
|
||||
- alxhub
|
||||
- crisbeto
|
||||
- devversion
|
||||
# OOO as of 2020-09-28 - devversion
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -419,7 +419,7 @@ groups:
|
||||
- atscott
|
||||
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
||||
- mhevery
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -662,7 +662,7 @@ groups:
|
||||
users:
|
||||
- AndrewKushnir
|
||||
- IgorMinar
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -679,7 +679,7 @@ groups:
|
||||
reviewers:
|
||||
users:
|
||||
- IgorMinar
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -697,7 +697,7 @@ groups:
|
||||
users:
|
||||
- IgorMinar
|
||||
- jelbourn
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
# =========================================================
|
||||
@ -723,7 +723,7 @@ groups:
|
||||
- IgorMinar
|
||||
- mhevery
|
||||
- jelbourn
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
reviews:
|
||||
request: -1 # request reviews from everyone
|
||||
required: 2 # require at least 2 approvals
|
||||
@ -1150,7 +1150,7 @@ groups:
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
- devversion
|
||||
# OOO as of 2020-09-28 - devversion
|
||||
- filipesilva
|
||||
- gkalpak
|
||||
- IgorMinar
|
||||
@ -1184,7 +1184,7 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- petebacondarwin
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
reviews:
|
||||
request: 4 # Request reviews from four people
|
||||
required: 3 # Require that three people approve
|
||||
@ -1212,7 +1212,7 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- petebacondarwin
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
reviews:
|
||||
request: 4 # Request reviews from four people
|
||||
required: 2 # Require that two people approve
|
||||
@ -1240,7 +1240,7 @@ groups:
|
||||
- atscott
|
||||
- jelbourn
|
||||
- petebacondarwin
|
||||
- pkozlowski-opensource
|
||||
# OOO as of 2020-09-28 - pkozlowski-opensource
|
||||
|
||||
|
||||
####################################################################################
|
||||
|
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,3 +1,13 @@
|
||||
<a name="10.1.4"></a>
|
||||
## 10.1.4 (2020-09-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** enable [@types](https://github.com/types) discovery in incremental rebuilds ([#39011](https://github.com/angular/angular/issues/39011)) ([6e99427](https://github.com/angular/angular/commit/6e99427)), closes [#38979](https://github.com/angular/angular/issues/38979)
|
||||
|
||||
|
||||
|
||||
<a name="10.1.3"></a>
|
||||
## 10.1.3 (2020-09-23)
|
||||
|
||||
|
@ -6,6 +6,7 @@ Everything in this folder is part of the documentation project. This includes
|
||||
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
||||
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
||||
|
||||
<a name="developer-tasks"></a>
|
||||
## Developer tasks
|
||||
|
||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
||||
|
@ -22,7 +22,7 @@ you don't need to specify values for those.
|
||||
The domain name of the server.
|
||||
|
||||
- `AIO_GITHUB_ORGANIZATION`:
|
||||
The GitHub organization whose teams are whitelisted for accepting build artifacts.
|
||||
The GitHub organization whose teams are trusted for accepting build artifacts.
|
||||
See also `AIO_GITHUB_TEAM_SLUGS`.
|
||||
|
||||
- `AIO_GITHUB_REPO`:
|
||||
|
@ -98,7 +98,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
||||
Such a label can only have been added by a maintainer (with the necessary rights) and
|
||||
designates that they have manually verified the PR contents.
|
||||
2. We can verify (again using the GitHub API) the author's membership in one of the
|
||||
whitelisted/trusted GitHub teams. For this operation, we need a Personal Access Token with the
|
||||
trusted GitHub teams. For this operation, we need a Personal Access Token with the
|
||||
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
||||
Here too, we use the token by @mary-poppins.
|
||||
|
||||
|
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -17,6 +17,7 @@
|
||||
**/e2e/tsconfig.e2e.json
|
||||
**/src/karma.conf.js
|
||||
**/.angular-cli.json
|
||||
**/.browserslistrc
|
||||
**/.editorconfig
|
||||
**/.gitignore
|
||||
**/angular.json
|
||||
@ -30,7 +31,6 @@
|
||||
**/tslint.json
|
||||
**/karma-test-shim.js
|
||||
**/browser-test-shim.js
|
||||
**/browserslist
|
||||
**/node_modules
|
||||
|
||||
# built files
|
||||
|
@ -1,12 +1,8 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
describe('Component Communication Cookbook Tests', () => {
|
||||
|
||||
// Note: '?e2e' which app can read to know it is running in protractor
|
||||
// e.g. `if (!/e2e/.test(location.search)) { ...`
|
||||
beforeAll(() => {
|
||||
browser.get('?e2e');
|
||||
});
|
||||
beforeEach(() => browser.get(browser.baseUrl));
|
||||
|
||||
describe('Parent-to-child communication', () => {
|
||||
// #docregion parent-to-child
|
||||
@ -15,7 +11,7 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
const masterName = 'Master';
|
||||
|
||||
it('should pass properties to children properly', () => {
|
||||
const parent = element.all(by.tagName('app-hero-parent')).get(0);
|
||||
const parent = element(by.tagName('app-hero-parent'));
|
||||
const heroes = parent.all(by.tagName('app-hero-child'));
|
||||
|
||||
for (let i = 0; i < heroNames.length; i++) {
|
||||
@ -35,7 +31,7 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
it('should display trimmed, non-empty names', () => {
|
||||
const nonEmptyNameIndex = 0;
|
||||
const nonEmptyName = '"Dr IQ"';
|
||||
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
||||
const parent = element(by.tagName('app-name-parent'));
|
||||
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex);
|
||||
|
||||
const displayName = hero.element(by.tagName('h3')).getText();
|
||||
@ -45,7 +41,7 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
it('should replace empty name with default name', () => {
|
||||
const emptyNameIndex = 1;
|
||||
const defaultName = '"<no name set>"';
|
||||
const parent = element.all(by.tagName('app-name-parent')).get(0);
|
||||
const parent = element(by.tagName('app-name-parent'));
|
||||
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex);
|
||||
|
||||
const displayName = hero.element(by.tagName('h3')).getText();
|
||||
@ -70,38 +66,36 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
expect(actual.logs.get(0).getText()).toBe(initialLog);
|
||||
});
|
||||
|
||||
it('should set expected values after clicking \'Minor\' twice', () => {
|
||||
it('should set expected values after clicking \'Minor\' twice', async () => {
|
||||
const repoTag = element(by.tagName('app-version-parent'));
|
||||
const newMinorButton = repoTag.all(by.tagName('button')).get(0);
|
||||
|
||||
newMinorButton.click().then(() => {
|
||||
newMinorButton.click().then(() => {
|
||||
const actual = getActual();
|
||||
await newMinorButton.click();
|
||||
await newMinorButton.click();
|
||||
|
||||
const labelAfter2Minor = 'Version 1.25';
|
||||
const logAfter2Minor = 'minor changed from 24 to 25';
|
||||
const actual = getActual();
|
||||
|
||||
expect(actual.label).toBe(labelAfter2Minor);
|
||||
expect(actual.count).toBe(3);
|
||||
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
||||
});
|
||||
});
|
||||
const labelAfter2Minor = 'Version 1.25';
|
||||
const logAfter2Minor = 'minor changed from 24 to 25';
|
||||
|
||||
expect(actual.label).toBe(labelAfter2Minor);
|
||||
expect(actual.count).toBe(3);
|
||||
expect(actual.logs.get(2).getText()).toBe(logAfter2Minor);
|
||||
});
|
||||
|
||||
it('should set expected values after clicking \'Major\' once', () => {
|
||||
it('should set expected values after clicking \'Major\' once', async () => {
|
||||
const repoTag = element(by.tagName('app-version-parent'));
|
||||
const newMajorButton = repoTag.all(by.tagName('button')).get(1);
|
||||
|
||||
newMajorButton.click().then(() => {
|
||||
const actual = getActual();
|
||||
await newMajorButton.click();
|
||||
const actual = getActual();
|
||||
|
||||
const labelAfterMajor = 'Version 2.0';
|
||||
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
|
||||
const labelAfterMajor = 'Version 2.0';
|
||||
const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0';
|
||||
|
||||
expect(actual.label).toBe(labelAfterMajor);
|
||||
expect(actual.count).toBe(4);
|
||||
expect(actual.logs.get(3).getText()).toBe(logAfterMajor);
|
||||
});
|
||||
expect(actual.label).toBe(labelAfterMajor);
|
||||
expect(actual.count).toBe(2);
|
||||
expect(actual.logs.get(1).getText()).toBe(logAfterMajor);
|
||||
});
|
||||
|
||||
function getActual() {
|
||||
@ -118,110 +112,125 @@ describe('Component Communication Cookbook Tests', () => {
|
||||
}
|
||||
// ...
|
||||
// #enddocregion parent-to-child-onchanges
|
||||
|
||||
});
|
||||
|
||||
describe('Child-to-parent communication', () => {
|
||||
// #docregion child-to-parent
|
||||
// ...
|
||||
it('should not emit the event initially', () => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||
.element(by.tagName('h3')).getText();
|
||||
expect(voteLabel).toBe('Agree: 0, Disagree: 0');
|
||||
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 0');
|
||||
});
|
||||
|
||||
it('should process Agree vote', () => {
|
||||
it('should process Agree vote', async () => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||
const agreeButton1 = element.all(by.tagName('app-voter')).get(0)
|
||||
.all(by.tagName('button')).get(0);
|
||||
agreeButton1.click().then(() => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||
.element(by.tagName('h3')).getText();
|
||||
expect(voteLabel).toBe('Agree: 1, Disagree: 0');
|
||||
});
|
||||
|
||||
await agreeButton1.click();
|
||||
|
||||
expect(voteLabel.getText()).toBe('Agree: 1, Disagree: 0');
|
||||
});
|
||||
|
||||
it('should process Disagree vote', () => {
|
||||
it('should process Disagree vote', async () => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3'));
|
||||
const agreeButton1 = element.all(by.tagName('app-voter')).get(1)
|
||||
.all(by.tagName('button')).get(1);
|
||||
agreeButton1.click().then(() => {
|
||||
const voteLabel = element(by.tagName('app-vote-taker'))
|
||||
.element(by.tagName('h3')).getText();
|
||||
expect(voteLabel).toBe('Agree: 1, Disagree: 1');
|
||||
});
|
||||
|
||||
await agreeButton1.click();
|
||||
|
||||
expect(voteLabel.getText()).toBe('Agree: 0, Disagree: 1');
|
||||
});
|
||||
// ...
|
||||
// #enddocregion child-to-parent
|
||||
});
|
||||
|
||||
// Can't run timer tests in protractor because
|
||||
// interaction w/ zones causes all tests to freeze & timeout.
|
||||
xdescribe('Parent calls child via local var', () => {
|
||||
countDownTimerTests('countdown-parent-lv');
|
||||
describe('Parent calls child via local var', () => {
|
||||
countDownTimerTests('app-countdown-parent-lv');
|
||||
});
|
||||
|
||||
xdescribe('Parent calls ViewChild', () => {
|
||||
countDownTimerTests('countdown-parent-vc');
|
||||
describe('Parent calls ViewChild', () => {
|
||||
countDownTimerTests('app-countdown-parent-vc');
|
||||
});
|
||||
|
||||
function countDownTimerTests(parentTag: string) {
|
||||
// #docregion countdown-timer-tests
|
||||
// ...
|
||||
it('timer and parent seconds should match', () => {
|
||||
// The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent
|
||||
// the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples
|
||||
// for more details.
|
||||
// To allow the tests to complete, we will disable automatically waiting for the Angular app to
|
||||
// stabilize.
|
||||
beforeEach(() => browser.waitForAngularEnabled(false));
|
||||
afterEach(() => browser.waitForAngularEnabled(true));
|
||||
|
||||
it('timer and parent seconds should match', async () => {
|
||||
const parent = element(by.tagName(parentTag));
|
||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
||||
browser.sleep(10); // give `seconds` a chance to catchup with `message`
|
||||
const seconds = parent.element(by.className('seconds')).getText();
|
||||
expect(message).toContain(seconds);
|
||||
const startButton = parent.element(by.buttonText('Start'));
|
||||
const seconds = parent.element(by.className('seconds'));
|
||||
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||
|
||||
await startButton.click();
|
||||
|
||||
// Wait for `<app-countdown-timer>` to be populated with any text.
|
||||
await browser.wait(() => timer.getText(), 2000);
|
||||
|
||||
expect(await timer.getText()).toContain(await seconds.getText());
|
||||
});
|
||||
|
||||
it('should stop the countdown', () => {
|
||||
it('should stop the countdown', async () => {
|
||||
const parent = element(by.tagName(parentTag));
|
||||
const stopButton = parent.all(by.tagName('button')).get(1);
|
||||
const startButton = parent.element(by.buttonText('Start'));
|
||||
const stopButton = parent.element(by.buttonText('Stop'));
|
||||
const timer = parent.element(by.tagName('app-countdown-timer'));
|
||||
|
||||
stopButton.click().then(() => {
|
||||
const message = parent.element(by.tagName('app-countdown-timer')).getText();
|
||||
expect(message).toContain('Holding');
|
||||
});
|
||||
await startButton.click();
|
||||
expect(await timer.getText()).not.toContain('Holding');
|
||||
|
||||
await stopButton.click();
|
||||
expect(await timer.getText()).toContain('Holding');
|
||||
});
|
||||
// ...
|
||||
// #enddocregion countdown-timer-tests
|
||||
}
|
||||
|
||||
|
||||
describe('Parent and children communicate via a service', () => {
|
||||
// #docregion bidirectional-service
|
||||
// ...
|
||||
it('should announce a mission', () => {
|
||||
it('should announce a mission', async () => {
|
||||
const missionControl = element(by.tagName('app-mission-control'));
|
||||
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||
announceButton.click().then(() => {
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
expect(history.count()).toBe(1);
|
||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||
});
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
|
||||
await announceButton.click();
|
||||
|
||||
expect(history.count()).toBe(1);
|
||||
expect(history.get(0).getText()).toMatch(/Mission.* announced/);
|
||||
});
|
||||
|
||||
it('should confirm the mission by Lovell', () => {
|
||||
testConfirmMission(1, 2, 'Lovell');
|
||||
it('should confirm the mission by Lovell', async () => {
|
||||
await testConfirmMission(1, 'Lovell');
|
||||
});
|
||||
|
||||
it('should confirm the mission by Haise', () => {
|
||||
testConfirmMission(3, 3, 'Haise');
|
||||
it('should confirm the mission by Haise', async () => {
|
||||
await testConfirmMission(3, 'Haise');
|
||||
});
|
||||
|
||||
it('should confirm the mission by Swigert', () => {
|
||||
testConfirmMission(2, 4, 'Swigert');
|
||||
it('should confirm the mission by Swigert', async () => {
|
||||
await testConfirmMission(2, 'Swigert');
|
||||
});
|
||||
|
||||
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
|
||||
const confirmedLog = ' confirmed the mission';
|
||||
async function testConfirmMission(buttonIndex: number, astronaut: string) {
|
||||
const missionControl = element(by.tagName('app-mission-control'));
|
||||
const announceButton = missionControl.all(by.tagName('button')).get(0);
|
||||
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
|
||||
confirmButton.click().then(() => {
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
expect(history.count()).toBe(expectedLogCount);
|
||||
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog);
|
||||
});
|
||||
const history = missionControl.all(by.tagName('li'));
|
||||
|
||||
await announceButton.click();
|
||||
await confirmButton.click();
|
||||
|
||||
expect(history.count()).toBe(2);
|
||||
expect(history.get(1).getText()).toBe(`${astronaut} confirmed the mission`);
|
||||
}
|
||||
// ...
|
||||
// #enddocregion bidirectional-service
|
||||
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"tests": [
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -30,22 +30,21 @@
|
||||
<app-vote-taker></app-vote-taker>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
||||
<hr>
|
||||
<div id="parent-to-child-local-var">
|
||||
<app-countdown-parent-lv></app-countdown-parent-lv>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
||||
<hr>
|
||||
<div id="parent-to-view-child">
|
||||
<app-countdown-parent-vc></app-countdown-parent-vc>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
||||
<hr>
|
||||
<div id="bidirectional-service">
|
||||
<app-mission-control></app-mission-control>
|
||||
</div>
|
||||
<a href="#top" class="to-top">Back to Top</a>
|
||||
<hr>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component';
|
||||
import { VoterComponent } from './voter.component';
|
||||
import { VoteTakerComponent } from './votetaker.component';
|
||||
|
||||
const directives: any[] = [
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
AstronautComponent,
|
||||
CountdownLocalVarParentComponent,
|
||||
CountdownTimerComponent,
|
||||
CountdownViewChildParentComponent,
|
||||
HeroChildComponent,
|
||||
HeroParentComponent,
|
||||
MissionControlComponent,
|
||||
@ -27,28 +34,8 @@ const directives: any[] = [
|
||||
VersionChildComponent,
|
||||
VersionParentComponent,
|
||||
VoterComponent,
|
||||
VoteTakerComponent
|
||||
];
|
||||
|
||||
const schemas: any[] = [];
|
||||
|
||||
// Include Countdown examples
|
||||
// unless in e2e tests which they break.
|
||||
if (!/e2e/.test(location.search)) {
|
||||
console.log('adding countdown timer examples');
|
||||
directives.push(CountdownLocalVarParentComponent);
|
||||
directives.push(CountdownViewChildParentComponent);
|
||||
} else {
|
||||
// In e2e test use CUSTOM_ELEMENTS_SCHEMA to suppress unknown element errors
|
||||
schemas.push(CUSTOM_ELEMENTS_SCHEMA);
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule
|
||||
VoteTakerComponent,
|
||||
],
|
||||
declarations: directives,
|
||||
bootstrap: [ AppComponent ],
|
||||
schemas
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -1,19 +1,16 @@
|
||||
// #docregion
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-countdown-timer',
|
||||
template: '<p>{{message}}</p>'
|
||||
})
|
||||
export class CountdownTimerComponent implements OnInit, OnDestroy {
|
||||
export class CountdownTimerComponent implements OnDestroy {
|
||||
|
||||
intervalId = 0;
|
||||
message = '';
|
||||
seconds = 11;
|
||||
|
||||
clearTimer() { clearInterval(this.intervalId); }
|
||||
|
||||
ngOnInit() { this.start(); }
|
||||
ngOnDestroy() { this.clearTimer(); }
|
||||
|
||||
start() { this.countDown(); }
|
||||
@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy {
|
||||
this.message = `Holding at T-${this.seconds} seconds`;
|
||||
}
|
||||
|
||||
private clearTimer() { clearInterval(this.intervalId); }
|
||||
|
||||
private countDown() {
|
||||
this.clearTimer();
|
||||
this.intervalId = window.setInterval(() => {
|
||||
|
@ -24,7 +24,7 @@ export class UploaderService {
|
||||
// }
|
||||
|
||||
upload(file: File) {
|
||||
if (!file) { return; }
|
||||
if (!file) { return of<string>(); }
|
||||
|
||||
// COULD HAVE WRITTEN:
|
||||
// return this.http.post('/upload/file', file, {
|
||||
|
@ -0,0 +1,70 @@
|
||||
import { interval } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { backoff } from './backoff';
|
||||
|
||||
describe('backoff()', () => {
|
||||
beforeEach(() => jasmine.clock().install());
|
||||
afterEach(() => jasmine.clock().uninstall());
|
||||
|
||||
it('should retry in case of error', () => {
|
||||
const mockConsole = {log: jasmine.createSpy('log')};
|
||||
const source = interval(10).pipe(
|
||||
tap(i => {
|
||||
if (i > 0) {
|
||||
throw new Error('Test error');
|
||||
}
|
||||
}),
|
||||
backoff(3, 100),
|
||||
);
|
||||
source.subscribe({
|
||||
next: v => mockConsole.log(`Emitted: ${v}`),
|
||||
error: e => mockConsole.log(`Errored: ${e.message || e}`),
|
||||
complete: () => mockConsole.log('Completed'),
|
||||
});
|
||||
|
||||
// Initial try:
|
||||
// Errors on second emission and schedules retrying (with delay).
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
mockConsole.log.calls.reset();
|
||||
|
||||
// First re-attempt after 100ms:
|
||||
// Errors again on second emission and schedules retrying (with larger delay).
|
||||
jasmine.clock().tick(100);
|
||||
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
mockConsole.log.calls.reset();
|
||||
|
||||
// Second re-attempt after 400ms:
|
||||
// Errors again on second emission and schedules retrying (with even larger delay).
|
||||
jasmine.clock().tick(400);
|
||||
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
mockConsole.log.calls.reset();
|
||||
|
||||
// Third re-attempt after 900ms:
|
||||
// Errors again on second emission and gives up (no retrying).
|
||||
jasmine.clock().tick(900);
|
||||
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]);
|
||||
mockConsole.log.calls.reset();
|
||||
|
||||
jasmine.clock().tick(10);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([['Errored: Test error']]);
|
||||
});
|
||||
});
|
@ -1,23 +1,32 @@
|
||||
// TODO: Add unit tests for this file.
|
||||
import { pipe, range, timer, zip } from 'rxjs';
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { of, pipe, range, throwError, timer, zip } from 'rxjs';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { retryWhen, map, mergeMap } from 'rxjs/operators';
|
||||
import { map, mergeMap, retryWhen } from 'rxjs/operators';
|
||||
|
||||
function backoff(maxTries, ms) {
|
||||
return pipe(
|
||||
retryWhen(attempts => zip(range(1, maxTries), attempts)
|
||||
.pipe(
|
||||
map(([i]) => i * i),
|
||||
mergeMap(i => timer(i * ms))
|
||||
)
|
||||
)
|
||||
);
|
||||
export function backoff(maxTries, delay) {
|
||||
return pipe(
|
||||
retryWhen(attempts =>
|
||||
zip(range(1, maxTries + 1), attempts).pipe(
|
||||
mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)),
|
||||
map(i => i * i),
|
||||
mergeMap(v => timer(v * delay)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion
|
||||
/*
|
||||
This function declaration is necessary to ensure that it does not get called
|
||||
when running the unit tests. It will not get rendered into the docs.
|
||||
The indentation needs to start in the leftmost level position as well because of how
|
||||
the docplaster combines the different regions together.
|
||||
*/
|
||||
function docRegionAjaxCall() {
|
||||
// #docregion
|
||||
ajax('/api/endpoint')
|
||||
.pipe(backoff(3, 250))
|
||||
.subscribe(data => handleData(data));
|
||||
|
||||
function handleData(data) {
|
||||
// ...
|
||||
.subscribe(function handleData(data) { /* ... */ });
|
||||
// #enddocregion
|
||||
}
|
||||
|
@ -2,7 +2,11 @@
|
||||
"tests": [
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
||||
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||
},
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
46
aio/content/examples/rx-library/src/error-handling.spec.ts
Normal file
46
aio/content/examples/rx-library/src/error-handling.spec.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Subject, throwError } from 'rxjs';
|
||||
import { docRegionDefault } from './error-handling';
|
||||
|
||||
describe('error-handling', () => {
|
||||
let mockConsole;
|
||||
let ajaxSubject;
|
||||
let ajax;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConsole = {log: jasmine.createSpy('log')};
|
||||
ajaxSubject = new Subject();
|
||||
ajax = jasmine
|
||||
.createSpy('ajax')
|
||||
.and.callFake((url: string) => ajaxSubject);
|
||||
});
|
||||
|
||||
afterEach(() => ajaxSubject.unsubscribe());
|
||||
|
||||
it('should return the response object', () => {
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
|
||||
ajaxSubject.next({response: {foo: 'bar'}});
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['data: ', {foo: 'bar'}]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array when using an object without a `response` property', () => {
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
|
||||
ajaxSubject.next({foo: 'bar'});
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['data: ', []]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array when the ajax observable errors', () => {
|
||||
ajax.and.returnValue(throwError('Test Error'));
|
||||
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['data: ', []]
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,25 +1,36 @@
|
||||
|
||||
import { of } from 'rxjs';
|
||||
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:no-shadowed-variable */
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { map, catchError } from 'rxjs/operators';
|
||||
// Return "response" from the API. If an error happens,
|
||||
// return an empty array.
|
||||
const apiData = ajax('/api/data').pipe(
|
||||
map(res => {
|
||||
if (!res.response) {
|
||||
throw new Error('Value expected!');
|
||||
}
|
||||
return res.response;
|
||||
}),
|
||||
catchError(err => of([]))
|
||||
);
|
||||
|
||||
apiData.subscribe({
|
||||
next(x) { console.log('data: ', x); },
|
||||
error(err) { console.log('errors already caught... will not run'); }
|
||||
});
|
||||
import { of } from 'rxjs';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { map, catchError } from 'rxjs/operators';
|
||||
|
||||
// #enddocregion
|
||||
|
||||
export function docRegionDefault(console, ajax) {
|
||||
// #docregion
|
||||
// Return "response" from the API. If an error happens,
|
||||
// return an empty array.
|
||||
const apiData = ajax('/api/data').pipe(
|
||||
map((res: any) => {
|
||||
if (!res.response) {
|
||||
throw new Error('Value expected!');
|
||||
}
|
||||
return res.response;
|
||||
}),
|
||||
catchError(err => of([]))
|
||||
);
|
||||
|
||||
apiData.subscribe({
|
||||
next(x) { console.log('data: ', x); },
|
||||
error(err) { console.log('errors already caught... will not run'); }
|
||||
});
|
||||
|
||||
// #enddocregion
|
||||
return apiData;
|
||||
}
|
||||
|
14
aio/content/examples/rx-library/src/operators.1.spec.ts
Normal file
14
aio/content/examples/rx-library/src/operators.1.spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { docRegionDefault } from './operators.1';
|
||||
|
||||
describe('squareOdd - operators.1.ts', () => {
|
||||
it('should return square odds', () => {
|
||||
const console = {log: jasmine.createSpy('log')};
|
||||
docRegionDefault(console);
|
||||
expect(console.log).toHaveBeenCalledTimes(3);
|
||||
expect(console.log.calls.allArgs()).toEqual([
|
||||
[1],
|
||||
[9],
|
||||
[25],
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,23 +1,30 @@
|
||||
import { of, pipe } from 'rxjs';
|
||||
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
const nums = of(1, 2, 3, 4, 5);
|
||||
|
||||
// Create a function that accepts an Observable.
|
||||
const squareOddVals = pipe(
|
||||
filter((n: number) => n % 2 !== 0),
|
||||
map(n => n * n)
|
||||
);
|
||||
|
||||
// Create an Observable that will run the filter and map functions
|
||||
const squareOdd = squareOddVals(nums);
|
||||
|
||||
// Subscribe to run the combined functions
|
||||
squareOdd.subscribe(x => console.log(x));
|
||||
import { of, pipe } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
// #enddocregion
|
||||
|
||||
export function docRegionDefault(console) {
|
||||
// #docregion
|
||||
const nums = of(1, 2, 3, 4, 5);
|
||||
|
||||
// Create a function that accepts an Observable.
|
||||
const squareOddVals = pipe(
|
||||
filter((n: number) => n % 2 !== 0),
|
||||
map(n => n * n)
|
||||
);
|
||||
|
||||
// Create an Observable that will run the filter and map functions
|
||||
const squareOdd = squareOddVals(nums);
|
||||
|
||||
// Subscribe to run the combined functions
|
||||
squareOdd.subscribe(x => console.log(x));
|
||||
|
||||
// #enddocregion
|
||||
}
|
||||
|
14
aio/content/examples/rx-library/src/operators.2.spec.ts
Normal file
14
aio/content/examples/rx-library/src/operators.2.spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { docRegionDefault } from './operators.2';
|
||||
|
||||
describe('squareOdd - operators.2.ts', () => {
|
||||
it('should return square odds', () => {
|
||||
const console = {log: jasmine.createSpy('log')};
|
||||
docRegionDefault(console);
|
||||
expect(console.log).toHaveBeenCalledTimes(3);
|
||||
expect(console.log.calls.allArgs()).toEqual([
|
||||
[1],
|
||||
[9],
|
||||
[25],
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,16 +1,25 @@
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
const squareOdd = of(1, 2, 3, 4, 5)
|
||||
.pipe(
|
||||
filter(n => n % 2 !== 0),
|
||||
map(n => n * n)
|
||||
);
|
||||
|
||||
// Subscribe to get values
|
||||
squareOdd.subscribe(x => console.log(x));
|
||||
import { of } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
// #enddocregion
|
||||
|
||||
export function docRegionDefault(console) {
|
||||
// #docregion
|
||||
const squareOdd = of(1, 2, 3, 4, 5)
|
||||
.pipe(
|
||||
filter(n => n % 2 !== 0),
|
||||
map(n => n * n)
|
||||
);
|
||||
|
||||
// Subscribe to get values
|
||||
squareOdd.subscribe(x => console.log(x));
|
||||
|
||||
// #enddocregion
|
||||
}
|
||||
|
14
aio/content/examples/rx-library/src/operators.spec.ts
Normal file
14
aio/content/examples/rx-library/src/operators.spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { docRegionDefault } from './operators';
|
||||
|
||||
describe('squaredNums - operators.ts', () => {
|
||||
it('should return square odds', () => {
|
||||
const console = {log: jasmine.createSpy('log')};
|
||||
docRegionDefault(console);
|
||||
expect(console.log).toHaveBeenCalledTimes(3);
|
||||
expect(console.log.calls.allArgs()).toEqual([
|
||||
[1],
|
||||
[4],
|
||||
[9],
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,20 +1,28 @@
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
const nums = of(1, 2, 3);
|
||||
|
||||
const squareValues = map((val: number) => val * val);
|
||||
const squaredNums = squareValues(nums);
|
||||
|
||||
squaredNums.subscribe(x => console.log(x));
|
||||
|
||||
// Logs
|
||||
// 1
|
||||
// 4
|
||||
// 9
|
||||
import { of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
// #enddocregion
|
||||
|
||||
export function docRegionDefault(console) {
|
||||
// #docregion
|
||||
const nums = of(1, 2, 3);
|
||||
|
||||
const squareValues = map((val: number) => val * val);
|
||||
const squaredNums = squareValues(nums);
|
||||
|
||||
squaredNums.subscribe(x => console.log(x));
|
||||
|
||||
// Logs
|
||||
// 1
|
||||
// 4
|
||||
// 9
|
||||
|
||||
// #enddocregion
|
||||
}
|
||||
|
69
aio/content/examples/rx-library/src/retry-on-error.spec.ts
Normal file
69
aio/content/examples/rx-library/src/retry-on-error.spec.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { mergeMap, tap } from 'rxjs/operators';
|
||||
import { docRegionDefault } from './retry-on-error';
|
||||
|
||||
describe('retry-on-error', () => {
|
||||
let mockConsole;
|
||||
beforeEach(() => mockConsole = { log: jasmine.createSpy('log') });
|
||||
|
||||
it('should return the response object', () => {
|
||||
const ajax = () => of({ response: { foo: 'bar' } });
|
||||
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['data: ', { foo: 'bar' }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array after 3 retries + 1 initial request', () => {
|
||||
const ajax = () => {
|
||||
return of({ noresponse: true }).pipe(tap(() => mockConsole.log('Subscribed to AJAX')));
|
||||
};
|
||||
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['data: ', []],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return the response if the request succeeds upon retrying', () => {
|
||||
// Fail on the first two requests, but succeed from the 3rd onwards.
|
||||
let failCount = 2;
|
||||
const ajax = () => of(null).pipe(
|
||||
tap(() => mockConsole.log('Subscribed to AJAX')),
|
||||
// Fail on the first 2 requests, but succeed from the 3rd onwards.
|
||||
mergeMap(() => {
|
||||
if (failCount > 0) {
|
||||
failCount--;
|
||||
return throwError('Test error');
|
||||
}
|
||||
return of({ response: { foo: 'bar' } });
|
||||
}),
|
||||
);
|
||||
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['Subscribed to AJAX'], // Initial request | 1st attempt overall
|
||||
['Subscribed to AJAX'], // 1st retry attempt | 2nd attempt overall
|
||||
['Subscribed to AJAX'], // 2nd retry attempt | 3rd attempt overall
|
||||
['data: ', { foo: 'bar' }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array when the ajax observable throws an error', () => {
|
||||
const ajax = () => throwError('Test Error');
|
||||
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['data: ', []],
|
||||
]);
|
||||
});
|
||||
});
|
@ -1,26 +1,35 @@
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:no-shadowed-variable */
|
||||
/* tslint:disable:align */
|
||||
// #docregion
|
||||
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { map, retry, catchError } from 'rxjs/operators';
|
||||
|
||||
const apiData = ajax('/api/data').pipe(
|
||||
retry(3), // Retry up to 3 times before failing
|
||||
map(res => {
|
||||
if (!res.response) {
|
||||
throw new Error('Value expected!');
|
||||
}
|
||||
return res.response;
|
||||
}),
|
||||
catchError(err => of([]))
|
||||
);
|
||||
|
||||
apiData.subscribe({
|
||||
next(x) { console.log('data: ', x); },
|
||||
error(err) { console.log('errors already caught... will not run'); }
|
||||
});
|
||||
import { of } from 'rxjs';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { map, retry, catchError } from 'rxjs/operators';
|
||||
|
||||
// #enddocregion
|
||||
|
||||
export function docRegionDefault(console, ajax) {
|
||||
// #docregion
|
||||
const apiData = ajax('/api/data').pipe(
|
||||
map((res: any) => {
|
||||
if (!res.response) {
|
||||
console.log('Error occured.');
|
||||
throw new Error('Value expected!');
|
||||
}
|
||||
return res.response;
|
||||
}),
|
||||
retry(3), // Retry up to 3 times before failing
|
||||
catchError(err => of([]))
|
||||
);
|
||||
|
||||
apiData.subscribe({
|
||||
next(x) { console.log('data: ', x); },
|
||||
error(err) { console.log('errors already caught... will not run'); }
|
||||
});
|
||||
|
||||
// #enddocregion
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { of } from 'rxjs';
|
||||
import { docRegionPromise } from './simple-creation.1';
|
||||
|
||||
describe('simple-creation.1', () => {
|
||||
it('should create a promise from an observable and return an empty object', () => {
|
||||
const console = {log: jasmine.createSpy('log')};
|
||||
const fetch = () => of({foo: 42});
|
||||
docRegionPromise(console, fetch);
|
||||
expect(console.log.calls.allArgs()).toEqual([
|
||||
[{foo: 42}],
|
||||
['Completed'],
|
||||
]);
|
||||
});
|
||||
});
|
24
aio/content/examples/rx-library/src/simple-creation.1.ts
Normal file
24
aio/content/examples/rx-library/src/simple-creation.1.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:align */
|
||||
// #docregion promise
|
||||
import { from } from 'rxjs';
|
||||
|
||||
// #enddocregion promise
|
||||
|
||||
export function docRegionPromise(console, fetch) {
|
||||
// #docregion promise
|
||||
// Create an Observable out of a promise
|
||||
const data = from(fetch('/api/endpoint'));
|
||||
// Subscribe to begin listening for async result
|
||||
data.subscribe({
|
||||
next(response) { console.log(response); },
|
||||
error(err) { console.error('Error: ' + err); },
|
||||
complete() { console.log('Completed'); }
|
||||
});
|
||||
|
||||
// #enddocregion promise
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { docRegionInterval } from './simple-creation.2';
|
||||
|
||||
describe('simple-creation.2', () => {
|
||||
beforeEach(() => jasmine.clock().install());
|
||||
afterEach(() => jasmine.clock().uninstall());
|
||||
|
||||
it('should create an Observable that will publish a value on an interval', () => {
|
||||
const console = {log: jasmine.createSpy('log')};
|
||||
const subscription = docRegionInterval(console);
|
||||
jasmine.clock().tick(1000);
|
||||
expect(console.log).toHaveBeenCalledWith('It\'s been 1 seconds since subscribing!');
|
||||
console.log.calls.reset();
|
||||
|
||||
jasmine.clock().tick(999);
|
||||
expect(console.log).not.toHaveBeenCalled();
|
||||
|
||||
jasmine.clock().tick(1);
|
||||
expect(console.log).toHaveBeenCalledWith('It\'s been 2 seconds since subscribing!');
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
});
|
22
aio/content/examples/rx-library/src/simple-creation.2.ts
Normal file
22
aio/content/examples/rx-library/src/simple-creation.2.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:align */
|
||||
// #docregion interval
|
||||
import { interval } from 'rxjs';
|
||||
|
||||
// #enddocregion interval
|
||||
|
||||
export function docRegionInterval(console) {
|
||||
// #docregion interval
|
||||
// Create an Observable that will publish a value on an interval
|
||||
const secondsCounter = interval(1000);
|
||||
// Subscribe to begin publishing values
|
||||
const subscription = secondsCounter.subscribe(n =>
|
||||
console.log(`It's been ${n + 1} seconds since subscribing!`));
|
||||
|
||||
// #enddocregion interval
|
||||
return subscription;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import { docRegionEvent } from './simple-creation.3';
|
||||
|
||||
describe('simple-creation.3', () => {
|
||||
let triggerMousemove;
|
||||
let mockConsole;
|
||||
let input;
|
||||
let mockDocument;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConsole = {log: jasmine.createSpy('log')};
|
||||
input = {
|
||||
addEventListener: jasmine
|
||||
.createSpy('addEventListener')
|
||||
.and.callFake((eventName, cb) => {
|
||||
if (eventName === 'mousemove') {
|
||||
triggerMousemove = cb;
|
||||
}
|
||||
}),
|
||||
removeEventListener: jasmine.createSpy('removeEventListener'),
|
||||
};
|
||||
mockDocument = { getElementById: () => input };
|
||||
});
|
||||
|
||||
it('should log coords when subscribing', () => {
|
||||
docRegionEvent(mockConsole, mockDocument);
|
||||
|
||||
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||
|
||||
triggerMousemove({ clientX: 50, clientY: 50 });
|
||||
triggerMousemove({ clientX: 30, clientY: 50 });
|
||||
triggerMousemove({ clientX: 50, clientY: 30 });
|
||||
expect(mockConsole.log).toHaveBeenCalledTimes(3);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['Coords: 50 X 50'],
|
||||
['Coords: 30 X 50'],
|
||||
['Coords: 50 X 30']
|
||||
]);
|
||||
});
|
||||
|
||||
it('should call unsubscribe when clientX and clientY are below < 40 ', () => {
|
||||
docRegionEvent(mockConsole, mockDocument);
|
||||
|
||||
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||
|
||||
// Ensure that we have unsubscribed.
|
||||
triggerMousemove({ clientX: 30, clientY: 30 });
|
||||
expect(input.removeEventListener).toHaveBeenCalledWith('mousemove', triggerMousemove, undefined);
|
||||
mockConsole.log.calls.reset();
|
||||
|
||||
triggerMousemove({ clientX: 50, clientY: 50 });
|
||||
expect(mockConsole.log).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
32
aio/content/examples/rx-library/src/simple-creation.3.ts
Normal file
32
aio/content/examples/rx-library/src/simple-creation.3.ts
Normal file
@ -0,0 +1,32 @@
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:align */
|
||||
// #docregion event
|
||||
import { fromEvent } from 'rxjs';
|
||||
|
||||
// #enddocregion event
|
||||
|
||||
export function docRegionEvent(console, document) {
|
||||
// #docregion event
|
||||
const el = document.getElementById('my-element');
|
||||
|
||||
// Create an Observable that will publish mouse movements
|
||||
const mouseMoves = fromEvent(el, 'mousemove');
|
||||
|
||||
// Subscribe to start listening for mouse-move events
|
||||
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
|
||||
// Log coords of mouse movements
|
||||
console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
|
||||
|
||||
// When the mouse is over the upper-left of the screen,
|
||||
// unsubscribe to stop listening for mouse movements
|
||||
if (evt.clientX < 40 && evt.clientY < 40) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
// #enddocregion event
|
||||
}
|
14
aio/content/examples/rx-library/src/simple-creation.spec.ts
Normal file
14
aio/content/examples/rx-library/src/simple-creation.spec.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { of } from 'rxjs';
|
||||
import { docRegionAjax } from './simple-creation';
|
||||
|
||||
describe('ajax', () => {
|
||||
it('should make a request and console log the status and response', () => {
|
||||
const console = {log: jasmine.createSpy('log')};
|
||||
const ajax = jasmine.createSpy('ajax').and.callFake((url: string) => {
|
||||
return of({status: 200, response: 'foo bar'});
|
||||
});
|
||||
|
||||
docRegionAjax(console, ajax);
|
||||
expect(console.log).toHaveBeenCalledWith(200, 'foo bar');
|
||||
});
|
||||
});
|
@ -1,65 +1,19 @@
|
||||
|
||||
// #docregion promise
|
||||
|
||||
import { from } from 'rxjs';
|
||||
|
||||
// Create an Observable out of a promise
|
||||
const data = from(fetch('/api/endpoint'));
|
||||
// Subscribe to begin listening for async result
|
||||
data.subscribe({
|
||||
next(response) { console.log(response); },
|
||||
error(err) { console.error('Error: ' + err); },
|
||||
complete() { console.log('Completed'); }
|
||||
});
|
||||
|
||||
// #enddocregion promise
|
||||
|
||||
// #docregion interval
|
||||
|
||||
import { interval } from 'rxjs';
|
||||
|
||||
// Create an Observable that will publish a value on an interval
|
||||
const secondsCounter = interval(1000);
|
||||
// Subscribe to begin publishing values
|
||||
secondsCounter.subscribe(n =>
|
||||
console.log(`It's been ${n} seconds since subscribing!`));
|
||||
|
||||
// #enddocregion interval
|
||||
|
||||
|
||||
// #docregion event
|
||||
|
||||
import { fromEvent } from 'rxjs';
|
||||
|
||||
const el = document.getElementById('my-element');
|
||||
|
||||
// Create an Observable that will publish mouse movements
|
||||
const mouseMoves = fromEvent(el, 'mousemove');
|
||||
|
||||
// Subscribe to start listening for mouse-move events
|
||||
const subscription = mouseMoves.subscribe((evt: MouseEvent) => {
|
||||
// Log coords of mouse movements
|
||||
console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);
|
||||
|
||||
// When the mouse is over the upper-left of the screen,
|
||||
// unsubscribe to stop listening for mouse movements
|
||||
if (evt.clientX < 40 && evt.clientY < 40) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
// #enddocregion event
|
||||
|
||||
|
||||
// #docplaster
|
||||
/*
|
||||
Because of how the code is merged together using the doc regions,
|
||||
we need to indent the imports with the function below.
|
||||
*/
|
||||
/* tslint:disable:no-shadowed-variable */
|
||||
/* tslint:disable:align */
|
||||
// #docregion ajax
|
||||
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
import { ajax } from 'rxjs/ajax';
|
||||
|
||||
// Create an Observable that will create an AJAX request
|
||||
const apiData = ajax('/api/data');
|
||||
// Subscribe to create the request
|
||||
apiData.subscribe(res => console.log(res.status, res.response));
|
||||
|
||||
// #enddocregion ajax
|
||||
|
||||
|
||||
export function docRegionAjax(console, ajax) {
|
||||
// #docregion ajax
|
||||
const apiData = ajax('/api/data');
|
||||
// Subscribe to create the request
|
||||
apiData.subscribe(res => console.log(res.status, res.response));
|
||||
// #enddocregion ajax
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
@ -13,22 +16,6 @@
|
||||
"deprecation": {
|
||||
"severity": "warning"
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||
"",
|
||||
"kebab-case"
|
||||
],
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
["app", "toh"],
|
||||
"camelCase"
|
||||
],
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
@ -56,6 +43,8 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "no-any": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
@ -95,6 +84,11 @@
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "typedef": [
|
||||
// true,
|
||||
// "call-signature"
|
||||
// ],
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
@ -130,6 +124,9 @@
|
||||
"check-typecast"
|
||||
]
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
@ -141,9 +138,19 @@
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
"use-pipe-transform-interface": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
["app", "toh"],
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||
"",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
import * as angular from 'angular';
|
||||
import 'angular-route';
|
||||
|
||||
const appName = 'myApp';
|
||||
|
||||
angular.module(appName, [
|
||||
const appModule = angular.module('myApp', [
|
||||
'ngRoute'
|
||||
])
|
||||
.config(['$routeProvider', '$locationProvider',
|
||||
@ -25,5 +23,5 @@ angular.module(appName, [
|
||||
);
|
||||
|
||||
export function bootstrap(el: HTMLElement) {
|
||||
return angular.bootstrap(el, [appName]);
|
||||
return angular.bootstrap(el, [appModule.name]);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ or in the `@NgModule()` or `@Component()` metadata
|
||||
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
|
||||
by removing the service from the compiled app if it isn't used.
|
||||
|
||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
|
||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator.
|
||||
|
||||
```
|
||||
@NgModule({
|
||||
|
@ -387,7 +387,7 @@ List the generated bundles in the `dist/` folder.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
|
||||
ls dist/*.bundle.js
|
||||
ls dist/*.js
|
||||
|
||||
</code-example>
|
||||
|
||||
@ -396,7 +396,7 @@ The following example displays the graph for the _main_ bundle.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
|
||||
node_modules/.bin/source-map-explorer dist/main.*.bundle.js
|
||||
node_modules/.bin/source-map-explorer dist/main*
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -603,18 +603,6 @@ In practical terms, the `package.json` of all `@angular` packages has changed in
|
||||
|
||||
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
||||
|
||||
{@a removed}
|
||||
## Removed APIs
|
||||
|
||||
The following APIs have been removed starting with version 10.0.0*:
|
||||
|
||||
| Package | API | Replacement | Notes |
|
||||
| ---------------- | -------------- | ----------- | ----- |
|
||||
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
||||
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
||||
|
||||
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
||||
|
||||
{@a style-sanitization}
|
||||
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
||||
Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular.
|
||||
|
@ -15,11 +15,11 @@ RxJS provides an implementation of the `Observable` type, which is needed until
|
||||
RxJS offers a number of functions that can be used to create new observables. These functions can simplify the process of creating observables from things such as events, timers, promises, and so on. For example:
|
||||
|
||||
|
||||
<code-example path="rx-library/src/simple-creation.ts" region="promise" header="Create an observable from a promise"></code-example>
|
||||
<code-example path="rx-library/src/simple-creation.1.ts" region="promise" header="Create an observable from a promise"></code-example>
|
||||
|
||||
<code-example path="rx-library/src/simple-creation.ts" region="interval" header="Create an observable from a counter"></code-example>
|
||||
<code-example path="rx-library/src/simple-creation.2.ts" region="interval" header="Create an observable from a counter"></code-example>
|
||||
|
||||
<code-example path="rx-library/src/simple-creation.ts" region="event" header="Create an observable from an event"></code-example>
|
||||
<code-example path="rx-library/src/simple-creation.3.ts" region="event" header="Create an observable from an event"></code-example>
|
||||
|
||||
<code-example path="rx-library/src/simple-creation.ts" region="ajax" header="Create an observable that creates an AJAX request"></code-example>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Template statements
|
||||
|
||||
A template **statement** responds to an **event** raised by a binding target
|
||||
such as an element, component, or directive.
|
||||
Template statements are methods or properties that you can use in your HTML to respond to user events.
|
||||
With template statements, your application can engage users through actions such as displaying dynamic content or submitting forms.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -10,24 +10,30 @@ the syntax and code snippets in this guide.
|
||||
|
||||
</div>
|
||||
|
||||
The following template statement appears in quotes to the right of the `=` symbol as in `(event)="statement"`.
|
||||
In the following example, the template statement `deleteHero()` appears in quotes to the right of the `=` symbol as in `(event)="statement"`.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||
|
||||
A template statement *has a side effect*.
|
||||
That's the whole point of an event.
|
||||
It's how you update application state from user action.
|
||||
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` function in the component class.
|
||||
|
||||
Responding to events is the other side of Angular's "unidirectional data flow".
|
||||
You're free to change anything, anywhere, during this turn of the event loop.
|
||||
You can use template statements with elements, components, or directives in response to events.
|
||||
|
||||
Like template expressions, template *statements* use a language that looks like JavaScript.
|
||||
The template statement parser differs from the template expression parser and
|
||||
specifically supports both basic assignment (`=`) and chaining expressions with <code>;</code>.
|
||||
<div class="alert is-helpful">
|
||||
|
||||
However, certain JavaScript and template expression syntax is not allowed:
|
||||
Responding to events is an aspect of Angular's [unidirectional data flow](guide/glossary#unidirectional-data-flow).
|
||||
You can change anything in your application during a single event loop.
|
||||
|
||||
* <code>new</code>
|
||||
</div>
|
||||
|
||||
## Syntax
|
||||
|
||||
Like [template expressions](guide/interpolation), template statements use a language that looks like JavaScript.
|
||||
However, the parser for template statements differs from the parser for template expressions.
|
||||
In addition, the template statements parser specifically supports both basic assignment, `=`, and chaining expressions with semicolons, `;`.
|
||||
|
||||
The following JavaScript and template expression syntax is not allowed:
|
||||
|
||||
* `new`
|
||||
* increment and decrement operators, `++` and `--`
|
||||
* operator assignment, such as `+=` and `-=`
|
||||
* the bitwise operators, such as `|` and `&`
|
||||
@ -35,31 +41,32 @@ However, certain JavaScript and template expression syntax is not allowed:
|
||||
|
||||
## Statement context
|
||||
|
||||
As with expressions, statements can refer only to what's in the statement context
|
||||
such as an event handling method of the component instance.
|
||||
Statements have a context—a particular part of the application to which the statement belongs.
|
||||
|
||||
The *statement context* is typically the component instance.
|
||||
The *deleteHero* in `(click)="deleteHero()"` is a method of the data-bound component.
|
||||
Statements can refer only to what's in the statement context, which is typically the component instance.
|
||||
For example, `deleteHero()` of `(click)="deleteHero()"` is a method of the component in the following snippet.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The statement context may also refer to properties of the template's own context.
|
||||
In the following examples, the template `$event` object,
|
||||
a [template input variable](guide/built-in-directives#template-input-variable) (`let hero`),
|
||||
and a [template reference variable](guide/template-reference-variables) (`#heroForm`)
|
||||
are passed to an event handling method of the component.
|
||||
In the following example, the component's event handling method, `onSave()` takes the template's own `$event` object as an argument.
|
||||
On the next two lines, the `deleteHero()` method takes a [template input variable](guide/built-in-directives#template-input-variable), `hero`, and `onSubmit()` takes a [template reference variable](guide/template-reference-variables), `#heroForm`.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="context-var-statement" header="src/app/app.component.html"></code-example>
|
||||
|
||||
In this example, the context of the `$event` object, `hero`, and `#heroForm` is the template.
|
||||
|
||||
Template context names take precedence over component context names.
|
||||
In `deleteHero(hero)` above, the `hero` is the template input variable,
|
||||
not the component's `hero` property.
|
||||
In the preceding `deleteHero(hero)`, the `hero` is the template input variable, not the component's `hero` property.
|
||||
|
||||
## Statement guidelines
|
||||
## Statement best practices
|
||||
|
||||
Template statements cannot refer to anything in the global namespace. They
|
||||
can't refer to `window` or `document`.
|
||||
They can't call `console.log` or `Math.max`.
|
||||
* **Conciseness**
|
||||
|
||||
As with expressions, avoid writing complex template statements.
|
||||
A method call or simple property assignment should be the norm.
|
||||
Keep template statements minimal by using method calls or basic property assignments.
|
||||
|
||||
* **Work within the context**
|
||||
|
||||
The context of a template statement can be the component class instance or the template.
|
||||
Because of this, template statements cannot refer to anything in the global namespace such as `window` or `document`.
|
||||
For example, template statements can't call `console.log()` or `Math.max()`.
|
||||
|
@ -12,7 +12,7 @@ include the following information:
|
||||
- Node and Angular CLI version (local version only).
|
||||
- How long each command took to initialize and execute.
|
||||
- Command name that was run.
|
||||
- For Schematics commands (add, generate, new and update), a list of whitelisted flags.
|
||||
- For Schematics commands (add, generate, new and update), a list of selected flags.
|
||||
- For build commands (build, serve), the number and size of bundles (initial and lazy),
|
||||
compilation units, the time it took to build and rebuild, and basic Angular-specific
|
||||
API usage.
|
||||
|
@ -26,6 +26,15 @@
|
||||
"end": "2020-10-20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DevReach",
|
||||
"location": "Online",
|
||||
"linkUrl": "https://www.telerik.com/devreach/online/agenda-thursday#sessions",
|
||||
"date": {
|
||||
"start": "2020-10-19",
|
||||
"end": "2020-10-23"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ngVikings",
|
||||
"location": "Oslo, Norway",
|
||||
|
@ -23,7 +23,7 @@
|
||||
"build-local-with-viewengine": "yarn ~~build",
|
||||
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
||||
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js ef770f1cb",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js ab97bc382",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
@ -87,28 +87,27 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "10.0.2",
|
||||
"@angular/cdk": "10.0.1",
|
||||
"@angular/common": "10.0.2",
|
||||
"@angular/compiler": "10.0.2",
|
||||
"@angular/core": "10.0.2",
|
||||
"@angular/elements": "10.0.2",
|
||||
"@angular/forms": "10.0.2",
|
||||
"@angular/material": "10.0.1",
|
||||
"@angular/platform-browser": "10.0.2",
|
||||
"@angular/platform-browser-dynamic": "10.0.2",
|
||||
"@angular/router": "10.0.2",
|
||||
"@angular/service-worker": "10.0.2",
|
||||
"@angular/animations": "10.1.3",
|
||||
"@angular/cdk": "10.2.2",
|
||||
"@angular/common": "10.1.3",
|
||||
"@angular/compiler": "10.1.3",
|
||||
"@angular/core": "10.1.3",
|
||||
"@angular/elements": "10.1.3",
|
||||
"@angular/forms": "10.1.3",
|
||||
"@angular/material": "10.2.2",
|
||||
"@angular/platform-browser": "10.1.3",
|
||||
"@angular/platform-browser-dynamic": "10.1.3",
|
||||
"@angular/router": "10.1.3",
|
||||
"@angular/service-worker": "10.1.3",
|
||||
"@webcomponents/custom-elements": "1.2.1",
|
||||
"rxjs": "^6.5.3",
|
||||
"tslib": "^1.10.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.1000.1",
|
||||
"@angular/cli": "10.0.1",
|
||||
"@angular/compiler-cli": "10.0.2",
|
||||
"@angular/language-service": "10.0.2",
|
||||
"@angular-devkit/build-angular": "0.1001.3",
|
||||
"@angular/cli": "10.1.3",
|
||||
"@angular/compiler-cli": "10.1.3",
|
||||
"@types/jasmine": "^3.4.2",
|
||||
"@types/jasminewd2": "^2.0.8",
|
||||
"@types/lunr": "^2.3.2",
|
||||
@ -119,7 +118,7 @@
|
||||
"canonical-path": "1.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "^6.0.0-next.1",
|
||||
"codelyzer": "^6.0.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.11",
|
||||
@ -136,8 +135,8 @@
|
||||
"html": "^1.0.0",
|
||||
"ignore": "^3.3.3",
|
||||
"image-size": "^0.5.1",
|
||||
"jasmine": "^3.4.0",
|
||||
"jasmine-core": "^3.4.0",
|
||||
"jasmine": "~3.6.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"jsdom": "^9.12.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
@ -145,7 +144,7 @@
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "^2.1.0",
|
||||
"karma-jasmine": "^3.1.1",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"light-server": "^2.6.2",
|
||||
"lighthouse": "6.1.0",
|
||||
@ -165,7 +164,7 @@
|
||||
"tree-kill": "^1.1.0",
|
||||
"ts-node": "^8.4.1",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.9.5",
|
||||
"typescript": "~4.0.2",
|
||||
"uglify-js": "^3.0.15",
|
||||
"unist-util-filter": "^0.2.1",
|
||||
"unist-util-source": "^1.0.1",
|
||||
|
@ -99,7 +99,7 @@ describe('ScrollService', () => {
|
||||
if (original !== undefined) {
|
||||
Object.defineProperty(window.history, 'scrollRestoration', original);
|
||||
} else {
|
||||
delete window.history.scrollRestoration;
|
||||
delete (window.history as any).scrollRestoration;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -39,31 +39,27 @@ See the [README.md](examples/README.md) for more details.
|
||||
|
||||
## example-zipper
|
||||
|
||||
In the AIO application, we offer the reader the option to download each example as a full self-contained
|
||||
runnable project packaged as a zip file. These zip files are generated by the utility in this folder.
|
||||
In the AIO application, we offer the reader the option to download each example as a full self-contained runnable project packaged as a zip file.
|
||||
These zip files are generated by the utility in this folder.
|
||||
|
||||
See the [README.md](example-zipper/README.md) for more details.
|
||||
|
||||
## stackblitz-builder
|
||||
|
||||
In the AIO application, we can embed a running version of the example as a [Stackblitz](https://stackblitz.com/) session.
|
||||
We can also provide a link to create a runnable version of the example in the [Stackblitz](https://stackblitz.com/)
|
||||
editor.
|
||||
We can also provide a link to create a runnable version of the example in the [Stackblitz](https://stackblitz.com/) editor.
|
||||
|
||||
See the [README.md](stackblitz-builder/README.md) for more details.
|
||||
|
||||
## transforms
|
||||
|
||||
All the content that is rendered by the AIO application, and some of its configuration files, are
|
||||
generated from source files by [Dgeni](https://github.com/angular/dgeni). Dgeni is a general purpose
|
||||
documentation generation tool.
|
||||
All the content that is rendered by the AIO application, and some of its configuration files, are generated from source files by [Dgeni](https://github.com/angular/dgeni).
|
||||
Dgeni is a general purpose documentation generation tool.
|
||||
|
||||
Markdown files in `/aio/content`, code comments in the core Angular source files and example files are
|
||||
processed and transformed into files that are consumed by the AIO application.
|
||||
Markdown files in `/aio/content`, code comments in the core Angular source files and example files are processed and transformed into files that are consumed by the AIO application.
|
||||
|
||||
Dgeni is configured by "packages", which contain services and processors. Some of these packages are
|
||||
installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and
|
||||
some are specific to the AIO project.
|
||||
Dgeni is configured by "packages", which contain services and processors.
|
||||
Some of these packages are installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and some are specific to the AIO project.
|
||||
|
||||
The project specific packages are stored in the `aio/tools/transforms` folder. See the
|
||||
[README.md](transforms/README.md) for more details.
|
||||
The project specific packages are stored in the `aio/tools/transforms` folder.
|
||||
See the [README.md](transforms/README.md) for more details.
|
||||
|
@ -1,16 +0,0 @@
|
||||
# Docs releases
|
||||
|
||||
This document explains how to update the documentation examples after an Angular release. This is only needed for major and minor versions.
|
||||
|
||||
All the packages for the docs' examples are specified in `/aio/tools/examples/shared/package.json`
|
||||
|
||||
**1)** So within the `shared` folder, you need to issue the following command:
|
||||
|
||||
```
|
||||
$ yarn upgrade-interactive --tilde
|
||||
```
|
||||
|
||||
There, select all the packages that are updated on the new Angular release.
|
||||
|
||||
**2)** Changes to the tsconfig.json? There are several files in `/aio/tools/examples/shared/boilerplate/*/tsconfig.json` (based on the example type).
|
||||
|
@ -1,7 +1,5 @@
|
||||
# Overview
|
||||
|
||||
The AIO application is built using the Angular CLI tool. We are often trialling new features for the CLI, which
|
||||
we apply to the library after it is installed. This folder contains git patch files that contain these new features
|
||||
and a utility to apply those patches to the CLI library.
|
||||
The AIO application is built using the Angular CLI tool. We are often trialling new features for the CLI, which we apply to the library after it is installed. This folder contains git patch files that contain these new features and a utility to apply those patches to the CLI library.
|
||||
|
||||
**Currently, we have no patches to run.**
|
||||
**Currently, we have no patches to run.**
|
||||
|
@ -1,13 +1,14 @@
|
||||
# Overview
|
||||
|
||||
In the AIO application, we offer the reader the option to download each example as a full self-contained
|
||||
runnable project packaged as a zip file. These zip files are generated by the utility in this folder.
|
||||
In the AIO application, we offer the reader the option to download each example as a full self-contained runnable project packaged as a zip file.
|
||||
These zip files are generated by the utility in this folder.
|
||||
|
||||
## Example zipper
|
||||
|
||||
The `exampleZipper.js` tool is very similar to the Stackblitz's `builder.js`. The latter creates an HTML file
|
||||
with all the examples' files and the former creates a zip file instead. They both use the `stackblitz.json` file
|
||||
to flag an example as something to stackblitz or zip. For example:
|
||||
The `exampleZipper.js` tool is very similar to the Stackblitz's `builder.js`.
|
||||
The latter creates an HTML file with all the examples' files and the former creates a zip file instead.
|
||||
They both use the `stackblitz.json` file to flag an example as something to stackblitz or zip.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -41,8 +42,7 @@ The `exampleZipper.js` won't include any `System.js` related files for CLI-based
|
||||
As mentioned, the tool uses the `stackblitz.json` as a flag and also a configuration file for the zipper.
|
||||
The problem is that not all examples have a stackblitz but they could offer a zip instead.
|
||||
|
||||
In those cases, you can create a `zipper.json` file with the same syntax. It will be ignored by the
|
||||
stackblitz tool.
|
||||
In those cases, you can create a `zipper.json` file with the same syntax. It will be ignored by the stackblitz tool.
|
||||
|
||||
## Executing the zip generation
|
||||
|
||||
@ -50,5 +50,4 @@ stackblitz tool.
|
||||
|
||||
Where? At `src/generated/zips/`.
|
||||
|
||||
Then the `<live-example>` embedded component will look at this folder to get the zip it needs for
|
||||
the example.
|
||||
Then the `<live-example>` embedded component will look at this folder to get the zip it needs for the example.
|
||||
|
@ -1,110 +1,157 @@
|
||||
# Overview
|
||||
|
||||
Many of the documentation pages contain snippets of code examples. Extract these snippets from
|
||||
real working example applications, which are stored in subfolders of the `/aio/content/examples`
|
||||
folder. Each example can be built and run independently. Each example also provides e2e specs, which
|
||||
are run as part of our CircleCI legacy build tasks, to verify that the examples continue to work as
|
||||
expected, as changes are made to the core Angular libraries.
|
||||
Many of the documentation pages contain snippets of code examples.
|
||||
These snippets are extracted from real working example applications, which are stored in sub-folders of the [aio/content/examples/](.) folder.
|
||||
Each example can be built and run independently.
|
||||
Each example also provides tests (mostly e2e and occasionally unit tests), which are run as part of our CircleCI `test_docs_examples*` jobs, to verify that the examples continue to work as expected, as changes are made to the core Angular libraries.
|
||||
|
||||
In order to build, run and test these examples independently you need to install dependencies into
|
||||
their sub-folder. Also there are a number of common boilerplate files that are needed to configure
|
||||
each example's project. Maintain these common boilerplate files centrally to reduce the amount
|
||||
of effort if one of them needs to change.
|
||||
In order to build, run and test these examples independently, you need to install dependencies into their sub-folder.
|
||||
Also there are a number of common boilerplate files that are needed to configure each example's project.
|
||||
These common boilerplate files are maintained centrally to reduce the amount of effort if one of them needs to change.
|
||||
|
||||
> **Note for Windows users**
|
||||
>
|
||||
> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
|
||||
> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](#symlinked-node_modules) for details).
|
||||
> On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
|
||||
|
||||
|
||||
## Boilerplate overview
|
||||
|
||||
As mentioned, many of the documentation pages contain snippets extracted from real example applications.
|
||||
To achieve that, all those applications needs to contain a basic boilerplate. E.g. a `node_modules`
|
||||
folder, `package.json` with scripts, etc.
|
||||
As mentioned above, many of the documentation pages contain snippets extracted from real example applications.
|
||||
To achieve that, all those applications need to contain some basic boilerplate, such as a `node_modules/` folder, a `package.json` file with scripts and dependencies, etc.
|
||||
|
||||
There are also different project types, each with its own boilerplate.
|
||||
For example, there are projects based on the Angular CLI, projects that use AngularJS, Custom Elements, i18n, server-side rendering, etc.
|
||||
(See the [example configuration section](#example-config) below for more info on how to specify the project type.)
|
||||
|
||||
To avoid having to maintain the boilerplate in each example, we use the [example-boilerplate-js](./example-boilerplate.js) script to provide a set of files that works across all the examples of a specific type.
|
||||
|
||||
No one wants to maintain the boilerplate on each example, so the goal of this tool is to provide a set of files that works across all the examples.
|
||||
|
||||
### Boilerplate files
|
||||
|
||||
Inside `/aio/tools/examples/shared/boilerplate` you will find a set of folders representing each project type.
|
||||
Inside [shared/boilerplate/](./shared/boilerplate) there is a sub-folder with boilerplate files for each of the different project types.
|
||||
|
||||
Currently you will find the next project types:
|
||||
Currently, the following project types are supported:
|
||||
|
||||
* cli - For CLI based examples. This is the default one, to be used in the majority of the examples.
|
||||
* getting-started - CLI-based with its own set of styles.
|
||||
* i18n - CLI-based with additional scripts for internationalization.
|
||||
* schematics - CLI-based with additional scripts for building schematics.
|
||||
* service-worker - CLI-based with additional packages and configuration for service workers.
|
||||
* systemjs - Currently in deprecation, only used in a few examples.
|
||||
* testing - CLI-based with additional styles for jasmine testing.
|
||||
* universal - CLI-based with an extra server target.
|
||||
* viewengine - Additional configuration for running CLI-/SystemJS-based examples with `ViewEngine` (the pre-Ivy compiler/renderer).
|
||||
- `cli`: For example apps based on the Angular CLI. This is the default type and is used in the majority of the examples.
|
||||
- `cli-ajs`: For CLI-based examples that also use AngularJS (but not via `@angular/upgrade`).
|
||||
- `elements`: For CLI-based examples that also use `@angular/elements`.
|
||||
- `getting-started`: For the "Getting started" tutorial. Essentially the same as `cli` but with custom CSS styles.
|
||||
- `i18n`: For CLI-based examples that also use internationalization.
|
||||
- `schematics`: For CLI-based examples that include a library with schematics.
|
||||
- `service-worker`: For CLI-based examples that also use `@angular/service-worker`.
|
||||
- `systemjs`: For non-CLI legacy examples using SystemJS. This is deprecated and only used in few examples.
|
||||
- `testing`: For CLI-based examples that are related to unit testing.
|
||||
- `universal`: For CLI-based examples that also use `@nguniversal/express-engine` for SSR.
|
||||
|
||||
There is also a `common` folder that contains files used in all different examples.
|
||||
There are also the following special folders:
|
||||
- `common`: Contains files used in many examples.
|
||||
(See the [next section](#example-config) for info on how to exclude common files in certain examples.)
|
||||
- `viewengine/cli`: Additional configuration for running CLI-based examples with `ViewEngine` (the pre-Ivy compiler/renderer).
|
||||
This applies to all CLI-based examples, such as `cli-ajs`, `elements`, `getting-started`, etc.
|
||||
- `viewengine/systemjs`: Additional configuration for running SystemJS-based examples with `ViewEngine` (the pre-Ivy compiler/renderer).
|
||||
|
||||
### The example-config.json
|
||||
|
||||
Each example is identified by an **example-config.json** configuration file in its root folder.
|
||||
This configuration file indicates what type of boilerplate this example needs. E.g.
|
||||
<a name="example-config"></a>
|
||||
### The `example-config.json`
|
||||
|
||||
Each example is identified by an `example-config.json` configuration file in its root folder.
|
||||
This configuration file indicates what type of boilerplate this example needs and how to test it.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"projectType": "cli",
|
||||
"useCommonBoilerplate": true
|
||||
"projectType": "cli"
|
||||
}
|
||||
```
|
||||
|
||||
If the file is empty then the default type of cli is assumed.
|
||||
When the boilerplate tooling runs, it will copy into the example folder all of the appropriate files based on the project type.
|
||||
The file is expected to contain a JSON object with zero or more of the following properties:
|
||||
|
||||
- `projectType: string`: One of the supported project types (see above).
|
||||
Default: `"cli"`
|
||||
- `useCommonBoilerplate: boolean`: Whether to include common boilerplate from the [common/](./shared/boilerplate/common) folder.
|
||||
Default: `true`
|
||||
|
||||
**SystemJS-only properties:**
|
||||
- `build: string`: The npm script to run in order to build the example app.
|
||||
Default: `"build"`
|
||||
- `run: string`: The npm script to run in order to serve the example app (so that e2e test can be run against it).
|
||||
Default `"serve:e2e"`
|
||||
|
||||
**CLI-only properties:**
|
||||
- `tests: object[]`: An array of objects, each specifying a test command. This can be used to run multiple test commands in series (for example, to run unit and e2e tests).
|
||||
The commands are specified as `{cmd: string, args: string[]}` and must be in a format that could be passed to Node.js' `child_process.spawn(cmd, args)`. You can use a special `{PORT}` placeholder, that will be replaced with the port on which the app is served during the actual test.
|
||||
Default:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"cmd": "yarn",
|
||||
"args": [
|
||||
"e2e",
|
||||
"--prod",
|
||||
"--protractor-config=e2e/protractor-puppeteer.conf.js",
|
||||
"--no-webdriver-update",
|
||||
"--port={PORT}"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
An empty `example-config.json` file is equivalent with `{"projectType": "cli"}`.
|
||||
|
||||
|
||||
<a name="symlinked-node_modules"></a>
|
||||
### A node_modules to share
|
||||
### A `node_modules/` to share
|
||||
|
||||
With all the boilerplate files in place, the only missing piece are the installed packages. For
|
||||
that you have a `/aio/tools/examples/shared/package.json` which contains **all** the packages
|
||||
needed to run all the examples through all different boilerplates.
|
||||
With all the boilerplate files in place, the only missing piece is the installed packages.
|
||||
For that we have [shared/package.json](./shared/package.json), which contains **all** the packages needed to run any example type.
|
||||
|
||||
After installing these dependencies, a `node_modules/` folder will be created at
|
||||
`/aio/tools/examples/shared/node_modules/`. This folder will be **symlinked** into each example.
|
||||
Upon installing these dependencies, a [shared/node_modules/](./shared/node_modules) folder is created.
|
||||
This folder will be **symlinked** into each example.
|
||||
So it is not a copy like the other boilerplate files.
|
||||
|
||||
### End to end tests
|
||||
|
||||
End to end changes between boilerplates.
|
||||
### End-to-end tests
|
||||
|
||||
For CLI applications, create a `app.e2e-spec.ts` inside the `e2e` folder. The tooling will run
|
||||
`ng e2e` for each CLI based examples.
|
||||
End-to-end infrastructure is slightly different between CLI- and SystemJS-based examples.
|
||||
|
||||
For SystemJS, each example contains an `e2e-spec.ts` file. You can find all the related configuration files
|
||||
in the `/aio/tools/examples/shared` folder.
|
||||
For CLI-based examples, create an `app.e2e-spec.ts` file inside the `e2e/` folder.
|
||||
This will be picked up by the default testing command (see the [example configuration section](#example-config) above).
|
||||
If you are using a custom test command, make sure e2e specs are picked up (if applicable).
|
||||
|
||||
### example-boilerplate.js
|
||||
For SystemJS-based examples, create an `e2e-spec.ts` file inside the example root folder.
|
||||
These apps will be tested with the following command:
|
||||
|
||||
This script installs all the dependencies that are shared among all the examples, creates the
|
||||
`node_modules` symlinks and copy all the boilerplate files where needed. It won't do anything
|
||||
about stackblitz nor e2e tests.
|
||||
```sh
|
||||
yarn protractor aio/tools/examples/shared/protractor.config.js \
|
||||
--specs=<example-folder>/e2e-spec.ts \
|
||||
--params.appDir=<example-folder>
|
||||
```
|
||||
|
||||
It also contains a function to remove all the boilerplate. It uses a `git clean -xdf` to do
|
||||
the job. It will remove all files that don't exist in the git repository, **including any
|
||||
new file that you are working on that hasn't been stage yet.** So be sure to save your work
|
||||
before removing the boilerplate.
|
||||
|
||||
### run-example-e2e.js
|
||||
### `example-boilerplate.js`
|
||||
|
||||
This script will find all the `e2e-spec.ts` files and run them.
|
||||
The [example-boilerplate.js](./example-boilerplate.js) script installs the dependencies for all examples, creates the `node_modules/` symlinks and copies the necessary boilerplate files into example folders.
|
||||
|
||||
To not run all tests, you can use the `--filter=name` flag to run the example's e2e that contains
|
||||
that name.
|
||||
It also contains a function to remove all the boilerplate.
|
||||
It uses `git clean -xdf` to do the job.
|
||||
It will remove all files that are not tracked by git, **including any new files that you are working on that haven't been stageg yet.**
|
||||
So, be sure to commit your work before removing the boilerplate.
|
||||
|
||||
It also has an optional `--setup` flag to run the `example-boilerplate.js` script and install
|
||||
the latest `webdriver`.
|
||||
|
||||
It will create a `/aio/protractor-results.txt` file when it finishes running tests.
|
||||
### `run-example-e2e.js`
|
||||
|
||||
The [run-example-e2e.js](./run-example-e2e.js) script will find and run the e2e tests for all examples.
|
||||
Although it only runs e2e tests by default, it can be configured to run any test command (for CLI-based examples) by using the `tests` property of the [example-config.json](#example-config) file.
|
||||
It is named `*-e2e` for historical reasons, but it is not limited to running e2e tests.
|
||||
|
||||
See [aio/README.md](../../README.md#developer-tasks) for the available command-line options.
|
||||
|
||||
Running the script will create an `aio/protractor-results.txt` file with the results of the tests.
|
||||
|
||||
|
||||
### Updating example dependencies
|
||||
|
||||
With every major release, we update the examples to be on the latest version. The following steps to update are:
|
||||
|
||||
* In the `shared/package.json` file, bump all the `@angular/*`, `@angular-devkit/*`, `rxjs`, `typescript`, and `zone.js` package versions to the version that corresponds with the [framework version](../../../package.json).
|
||||
* In the `shared` folder, run `yarn` to update the dependencies for the shared `node_modules` and the `yarn.lock` file.
|
||||
* In the `boilerplate` folder, go through each sub-folder and update the `package.json` dependencies if one is present.
|
||||
* Follow the [update guide](./shared/boilerplate/UPDATING_CLI.md) to update the common files used in the examples based on project type.
|
||||
With every major Angular release, we update the examples to be on the latest version.
|
||||
See [UPDATING.md](./UPDATING.md) for instructions.
|
||||
|
52
aio/tools/examples/UPDATING.md
Normal file
52
aio/tools/examples/UPDATING.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Update example dependencies
|
||||
|
||||
Follow these steps to update the examples to the latest versions of Angular (and related dependencies):
|
||||
|
||||
- In [shared/package.json](./shared/package.json), bump all the `@angular/*` and `@nguniversal/*` package versions to the version you want to update to and update their peer dependencies (such as `@angular-devkit/*`, `rxjs`, `typescript`, `zone.js`) and other dependencies (e.g. `@types/*`) to the latest compatible versions.
|
||||
|
||||
> NOTE:
|
||||
> The [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering what dependency versions are used for a basic CLI app at a specific CLI version.
|
||||
|
||||
- In the [shared/](./shared) folder, run `yarn` to update the dependencies in the [shared/node_modules/](./shared/node_modules) folder and the [shared/yarn.lock](./shared/yarn.lock) file.
|
||||
|
||||
- In the [shared/](./shared) folder, run `yarn sync-deps` to update the dependency versions of the `package.json` files in each sub-folder of [shared/boilerplate/](./shared/boilerplate) to match the ones in [shared/package.json](./shared/package.json).
|
||||
|
||||
- Follow the steps in the following section to update the rest of the boilerplate files.
|
||||
|
||||
|
||||
## Update other boilerplate files
|
||||
|
||||
The Angular CLI default setup is updated using `ng update`.
|
||||
Any necessary changes to boilerplate files will be done automatically through migration schematics.
|
||||
|
||||
> NOTE:
|
||||
> Migrations affecting source code files will not happen automatically, because `ng update` does not know about all the examples in `aio/content/examples/`.
|
||||
> You have to make these changes (if any) manually.
|
||||
> Again, the [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering changes between versions.
|
||||
|
||||
- In the [shared/boilerplate/cli/](./shared/boilerplate/cli) folder, run the following commands to migrate the the project to the current versions of Angular CLI and the Angular framework (updated in previous steps):
|
||||
```sh
|
||||
# Ensure dependencies are installed.
|
||||
yarn install
|
||||
|
||||
# Migrate project to new versions.
|
||||
yarn ng update @angular/cli --migrate-only --from=<previous-cli-version>
|
||||
yarn ng update @angular/core --migrate-only --from=<previous-core-version>
|
||||
```
|
||||
|
||||
> NOTE:
|
||||
> In order for `ng update` to work, there must be a `node_modules/` directory with installed dependencies inside the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory.
|
||||
> This `node_modules/` directory is only needed during the update operation and is otherwise ignored (both by git and by the [example-boilerplate.js](./example-boilerplate.js) script) by means of the [shared/boilerplate/.gitignore](./shared/boilerplate/.gitignore) file.
|
||||
|
||||
- The previous command made any necessary changes to boilerplate files inside the `cli/` folder, but the same changes need to be applied to the other CLI-based boilerplate folders.
|
||||
Inspect the changes in `cli/` and manually apply the necessary ones to other CLI-based boilerplate folders.
|
||||
|
||||
- Ensure any changes to [cli/tslint.json](./shared/boilerplate/cli/tslint.json) are ported over to [systemjs/tslint.json](./shared/boilerplate/systemjs/tslint.json) and also [aio/content/examples/tslint.json](../../content/examples/tslint.json).
|
||||
This last part is important, since this file is used to lint example code on CI.
|
||||
|
||||
- Inspect the changes and determine whether some of them need to be applied to the `systemjs` boilerplate files.
|
||||
|
||||
- Inspect the changes and determine whether any updates to guides are necessary.
|
||||
For example, if a file is renamed or moved, any guides mentioning that file may need updating to refer to the new name/location.
|
||||
|
||||
- Commit all changes to the repository.
|
@ -1,5 +1,6 @@
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('glob');
|
||||
const ignore = require('ignore');
|
||||
const path = require('canonical-path');
|
||||
const shelljs = require('shelljs');
|
||||
const yargs = require('yargs');
|
||||
@ -23,6 +24,8 @@ class ExampleBoilerPlate {
|
||||
// Get all the examples folders, indicated by those that contain a `example-config.json` file
|
||||
const exampleFolders =
|
||||
this.getFoldersContaining(EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, 'node_modules');
|
||||
const gitignore = ignore().add(fs.readFileSync(path.resolve(BOILERPLATE_BASE_PATH, '.gitignore'), 'utf8'));
|
||||
const isPathIgnored = absolutePath => gitignore.ignores(path.relative(BOILERPLATE_BASE_PATH, absolutePath));
|
||||
|
||||
if (!fs.existsSync(SHARED_NODE_MODULES_PATH)) {
|
||||
throw new Error(
|
||||
@ -48,22 +51,22 @@ class ExampleBoilerPlate {
|
||||
// boilerplate files first.
|
||||
// (Some of these files might be later overwritten by type-specific files.)
|
||||
if (boilerPlateType !== 'cli' && boilerPlateType !== 'systemjs') {
|
||||
this.copyDirectoryContents(BOILERPLATE_CLI_PATH, exampleFolder);
|
||||
this.copyDirectoryContents(BOILERPLATE_CLI_PATH, exampleFolder, isPathIgnored);
|
||||
}
|
||||
|
||||
// Copy the type-specific boilerplate files.
|
||||
this.copyDirectoryContents(boilerPlateBasePath, exampleFolder);
|
||||
this.copyDirectoryContents(boilerPlateBasePath, exampleFolder, isPathIgnored);
|
||||
|
||||
// Copy the common boilerplate files (unless explicitly not used).
|
||||
if (exampleConfig.useCommonBoilerplate !== false) {
|
||||
this.copyDirectoryContents(BOILERPLATE_COMMON_PATH, exampleFolder);
|
||||
this.copyDirectoryContents(BOILERPLATE_COMMON_PATH, exampleFolder, isPathIgnored);
|
||||
}
|
||||
|
||||
// Copy ViewEngine (pre-Ivy) specific files
|
||||
if (viewengine) {
|
||||
const veBoilerPlateType = boilerPlateType === 'systemjs' ? 'systemjs' : 'cli';
|
||||
const veBoilerPlateBasePath = path.resolve(BOILERPLATE_VIEWENGINE_PATH, veBoilerPlateType);
|
||||
this.copyDirectoryContents(veBoilerPlateBasePath, exampleFolder);
|
||||
this.copyDirectoryContents(veBoilerPlateBasePath, exampleFolder, isPathIgnored);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -89,25 +92,30 @@ class ExampleBoilerPlate {
|
||||
|
||||
loadJsonFile(filePath) { return fs.readJsonSync(filePath, {throws: false}) || {}; }
|
||||
|
||||
copyDirectoryContents(srcDir, dstDir) {
|
||||
copyDirectoryContents(srcDir, dstDir, isPathIgnored) {
|
||||
shelljs.ls('-Al', srcDir).forEach(stat => {
|
||||
const srcPath = path.resolve(srcDir, stat.name);
|
||||
const dstPath = path.resolve(dstDir, stat.name);
|
||||
|
||||
if (isPathIgnored(srcPath)) {
|
||||
// `srcPath` is ignored (e.g. by a `.gitignore` file): Ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// `srcPath` is a directory: Recursively copy it to `dstDir`.
|
||||
shelljs.mkdir('-p', dstPath);
|
||||
return this.copyDirectoryContents(srcPath, dstPath);
|
||||
} else {
|
||||
// `srcPath` is a file: Copy it to `dstDir`.
|
||||
// (Also make the file non-writable to avoid accidental editing of boilerplate files).
|
||||
if (shelljs.test('-f', dstPath)) {
|
||||
// If the file already exists, ensure it is writable (so it can be overwritten).
|
||||
shelljs.chmod(666, dstPath);
|
||||
}
|
||||
shelljs.cp(srcPath, dstDir);
|
||||
shelljs.chmod(444, dstPath);
|
||||
return this.copyDirectoryContents(srcPath, dstPath, isPathIgnored);
|
||||
}
|
||||
|
||||
// `srcPath` is a file: Copy it to `dstDir`.
|
||||
// (Also make the file non-writable to avoid accidental editing of boilerplate files).
|
||||
if (shelljs.test('-f', dstPath)) {
|
||||
// If the file already exists, ensure it is writable (so it can be overwritten).
|
||||
shelljs.chmod(666, dstPath);
|
||||
}
|
||||
shelljs.cp(srcPath, dstDir);
|
||||
shelljs.chmod(444, dstPath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -56,10 +56,10 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(4);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/systemjs`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/systemjs`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/systemjs`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/systemjs`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -71,10 +71,10 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(4);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -86,10 +86,10 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(4);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -101,12 +101,12 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/i18n`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/i18n`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/i18n`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/i18n`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -118,12 +118,12 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/universal`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/universal`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/universal`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/universal`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -148,12 +148,12 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/systemjs`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/viewengine/systemjs`, 'a/b'],
|
||||
[`${boilerplateDir}/systemjs`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/viewengine/systemjs`, 'c/d'],
|
||||
[`${boilerplateDir}/systemjs`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/viewengine/systemjs`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/systemjs`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/viewengine/systemjs`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -165,12 +165,12 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(6);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
|
||||
@ -182,14 +182,14 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
expect(exampleBoilerPlate.copyDirectoryContents).toHaveBeenCalledTimes(8);
|
||||
expect(exampleBoilerPlate.copyDirectoryContents.calls.allArgs()).toEqual([
|
||||
[`${boilerplateDir}/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/elements`, 'a/b'],
|
||||
[`${boilerplateDir}/common`, 'a/b'],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'a/b'],
|
||||
[`${boilerplateDir}/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/elements`, 'c/d'],
|
||||
[`${boilerplateDir}/common`, 'c/d'],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'c/d'],
|
||||
[`${boilerplateDir}/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/elements`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'a/b', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/cli`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/elements`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/common`, 'c/d', jasmine.any(Function)],
|
||||
[`${boilerplateDir}/viewengine/cli`, 'c/d', jasmine.any(Function)],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -214,10 +214,12 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
describe('copyDirectoryContents', () => {
|
||||
const spyFnFor = fnName => (...args) => { callLog.push(`${fnName}(${args.join(', ')})`); };
|
||||
let isPathIgnoredSpy;
|
||||
let callLog;
|
||||
|
||||
beforeEach(() => {
|
||||
callLog = [];
|
||||
isPathIgnoredSpy = jasmine.createSpy('isPathIgnored').and.returnValue(false);
|
||||
spyOn(shelljs, 'chmod').and.callFake(spyFnFor('chmod'));
|
||||
spyOn(shelljs, 'cp').and.callFake(spyFnFor('cp'));
|
||||
spyOn(shelljs, 'mkdir').and.callFake(spyFnFor('mkdir'));
|
||||
@ -226,17 +228,17 @@ describe('example-boilerplate tool', () => {
|
||||
|
||||
it('should list all contents of a directory', () => {
|
||||
const lsSpy = spyOn(shelljs, 'ls').and.returnValue([]);
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir');
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy);
|
||||
expect(lsSpy).toHaveBeenCalledWith('-Al', 'source/dir');
|
||||
});
|
||||
|
||||
it('should use copy files and make them read-only', () => {
|
||||
it('should copy files and make them read-only', () => {
|
||||
spyOn(shelljs, 'ls').and.returnValue([
|
||||
{name: 'file-1.txt', isDirectory: () => false},
|
||||
{name: 'file-2.txt', isDirectory: () => false},
|
||||
]);
|
||||
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir');
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy);
|
||||
|
||||
expect(callLog).toEqual([
|
||||
`test(-f, ${path.resolve('destination/dir/file-1.txt')})`,
|
||||
@ -249,6 +251,22 @@ describe('example-boilerplate tool', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should skip files that are ignored', () => {
|
||||
spyOn(shelljs, 'ls').and.returnValue([
|
||||
{name: 'file-1.txt', isDirectory: () => false},
|
||||
{name: 'file-2.txt', isDirectory: () => false},
|
||||
]);
|
||||
isPathIgnoredSpy.and.callFake(path => path.endsWith('file-1.txt'));
|
||||
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy);
|
||||
|
||||
expect(callLog).toEqual([
|
||||
`test(-f, ${path.resolve('destination/dir/file-2.txt')})`,
|
||||
`cp(${path.resolve('source/dir/file-2.txt')}, destination/dir)`,
|
||||
`chmod(444, ${path.resolve('destination/dir/file-2.txt')})`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should make existing files in destination writable before overwriting', () => {
|
||||
spyOn(shelljs, 'ls').and.returnValue([
|
||||
{name: 'new-file.txt', isDirectory: () => false},
|
||||
@ -256,7 +274,7 @@ describe('example-boilerplate tool', () => {
|
||||
]);
|
||||
shelljs.test.and.callFake((_, filePath) => filePath.endsWith('existing-file.txt'));
|
||||
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir');
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy);
|
||||
|
||||
expect(callLog).toEqual([
|
||||
`cp(${path.resolve('source/dir/new-file.txt')}, destination/dir)`,
|
||||
@ -283,7 +301,7 @@ describe('example-boilerplate tool', () => {
|
||||
{name: 'file-4.txt', isDirectory: () => false},
|
||||
]);
|
||||
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir');
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy);
|
||||
|
||||
expect(callLog).toEqual([
|
||||
// Copy `file-1.txt`.
|
||||
@ -317,6 +335,33 @@ describe('example-boilerplate tool', () => {
|
||||
`chmod(444, ${path.resolve('destination/dir/file-2.txt')})`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should skip ignored directories', () => {
|
||||
spyOn(shelljs, 'ls')
|
||||
.withArgs('-Al', 'source/dir').and.returnValue([
|
||||
{name: 'file-1.txt', isDirectory: () => false},
|
||||
{name: 'sub-dir-1', isDirectory: () => true},
|
||||
])
|
||||
.withArgs('-Al', path.resolve('source/dir/sub-dir-1')).and.returnValue([
|
||||
{name: 'file-2.txt', isDirectory: () => false},
|
||||
{name: 'sub-dir-2', isDirectory: () => true},
|
||||
])
|
||||
.withArgs('-Al', path.resolve('source/dir/sub-dir-1/sub-dir-2')).and.returnValue([
|
||||
{name: 'file-3.txt', isDirectory: () => false},
|
||||
]);
|
||||
isPathIgnoredSpy.and.callFake(path => path.endsWith('sub-dir-1'));
|
||||
|
||||
exampleBoilerPlate.copyDirectoryContents('source/dir', 'destination/dir', isPathIgnoredSpy);
|
||||
|
||||
expect(callLog).toEqual([
|
||||
// Copy `file-1.txt`.
|
||||
`test(-f, ${path.resolve('destination/dir/file-1.txt')})`,
|
||||
`cp(${path.resolve('source/dir/file-1.txt')}, destination/dir)`,
|
||||
`chmod(444, ${path.resolve('destination/dir/file-1.txt')})`,
|
||||
|
||||
// Skip `sub-dir-1` and all its contents.
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadJsonFile', () => {
|
||||
|
@ -1,2 +1,3 @@
|
||||
**/node_modules/
|
||||
**/package-lock.json
|
||||
**/yarn.lock
|
||||
**/package-lock.json
|
@ -1,50 +0,0 @@
|
||||
# How to update the CLI project
|
||||
|
||||
The Angular CLI default setup is updated using `ng update`. Any necessary file changes will be done automatically through migration schematics.
|
||||
|
||||
In the `cli` folder, update the Angular CLI depedencies to the latest version:
|
||||
|
||||
```
|
||||
ng update @angular/cli --next
|
||||
```
|
||||
|
||||
Then update the Angular Framework dependencies to the latest version:
|
||||
|
||||
```
|
||||
ng update @angular/core --next
|
||||
```
|
||||
|
||||
Commit any changes to the `cli` folder to the repository.
|
||||
|
||||
## Updating other CLI-based projects
|
||||
|
||||
Along with the boilerplate files for the `cli` folder, the other cli-based projects need to be updated also. Each cli-based project has slightly modified files specific to the project type. Make sure any necessary changes to these projects are made also to be in alignment with the `cli` project files.
|
||||
|
||||
The specific changes to each project type are listed below:
|
||||
|
||||
* i18n
|
||||
- angular.json
|
||||
- Includes additional configurations for `build`, `serve`, and `e2e` for different locales
|
||||
- package.json
|
||||
- Includes custom scripts for building and serving different locales
|
||||
* ivy
|
||||
- cli/tsconfig.app.json
|
||||
- Includes an `angularCompilerOptions` object with `enableIvy` set to `true`
|
||||
* schematics
|
||||
- angular.json
|
||||
- Includes a `my-lib` project that contains a library with example schematics
|
||||
* service-worker
|
||||
- angular.json
|
||||
- Has `serviceWorker` set to `true` in the `production` build target
|
||||
- package.json
|
||||
- Includes `@angular/service-worker` in `dependencies`
|
||||
* testing
|
||||
- angular.json
|
||||
- Includes `src/test.css` in the `styles` for the `test` target
|
||||
* universal
|
||||
- angular.json
|
||||
- Includes a `server` target in the `build` architect runners
|
||||
- package.json
|
||||
- Includes custom scripts for building the `server`
|
||||
- Includes additional `dependencies` on `@angular/platform-server`, `@nguniversal/express-engine`, and `express`
|
||||
- Includes additional `devDependencies` on `@nguniversal/builders` and `@types/express`
|
@ -13,43 +13,42 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"angular": "1.7.9",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"angular": "1.8.0",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"angular-route": "1.7.9",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"angular-route": "1.8.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@types/angular": "^1.6.57",
|
||||
"@types/angular-route": "^1.7.0",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@types/angular": "1.7.3",
|
||||
"@types/angular-route": "1.7.1",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
18
aio/tools/examples/shared/boilerplate/cli/.browserslistrc
Normal file
18
aio/tools/examples/shared/boilerplate/cli/.browserslistrc
Normal file
@ -0,0 +1,18 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
|
||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
@ -5,7 +5,11 @@
|
||||
"projects": {
|
||||
"angular.io-example": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -18,6 +22,7 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
@ -40,20 +45,19 @@
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
# Googlebot uses an older version of Chrome
|
||||
# For additional information see: https://developers.google.com/search/docs/guides/rendering
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
@ -2,7 +2,7 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
@ -27,6 +27,10 @@ exports.config = {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
jasmine.getEnv().addReporter(new SpecReporter({
|
||||
spec: {
|
||||
displayStacktrace: StacktraceOption.PRETTY
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
|
@ -13,39 +13,38 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "angular.io-example",
|
||||
"private": true,
|
||||
"description_1": "This is a special package.json file that is not used by package managers.",
|
||||
"description_2": "It is used to tell the tools and bundlers whether the code under this directory is free of code with non-local side-effect. Any code that does have non-local side-effects can't be well optimized (tree-shaken) and will result in unnecessary increased payload size.",
|
||||
"description_3": "It should be safe to set this option to 'false' for new applications, but existing code bases could be broken when built with the production config if the application code does contain non-local side-effects that the application depends on.",
|
||||
"description_4": "To learn more about this file see: https://angular.io/config/app-package-json.",
|
||||
"sideEffects": false
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
|
@ -1,26 +1,31 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
// TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`).
|
||||
"fullTemplateTypeCheck": true,// "strictTemplates": true
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
@ -13,22 +16,6 @@
|
||||
"deprecation": {
|
||||
"severity": "warning"
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||
"",
|
||||
"kebab-case"
|
||||
],
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
["app", "toh"],
|
||||
"camelCase"
|
||||
],
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
@ -56,6 +43,8 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "no-any": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
@ -95,6 +84,11 @@
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "typedef": [
|
||||
// true,
|
||||
// "call-signature"
|
||||
// ],
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
@ -130,6 +124,9 @@
|
||||
"check-typecast"
|
||||
]
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
@ -141,9 +138,19 @@
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
"use-pipe-transform-interface": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
["app", "toh"],
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||
"",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -13,41 +13,40 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/elements": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@webcomponents/custom-elements": "^1.4.1",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/elements": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"@webcomponents/custom-elements": "^1.4.2",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,11 @@
|
||||
"projects": {
|
||||
"angular.io-example": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -20,12 +24,12 @@
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"localize": true,
|
||||
"aot": true,
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
@ -48,20 +52,19 @@
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -16,40 +16,39 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/localize": "^9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/localize": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,11 @@
|
||||
"projects": {
|
||||
"angular.io-example": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -18,6 +22,7 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
@ -40,20 +45,19 @@
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -5,7 +5,11 @@
|
||||
"projects": {
|
||||
"angular.io-example": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -18,6 +22,7 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
@ -40,7 +45,6 @@
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
@ -48,13 +52,13 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -13,40 +13,39 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@angular/service-worker": "~9.1.4",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"@angular/service-worker": "~10.1.3",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -27,45 +27,44 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@angular/upgrade": "~9.1.4",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"@angular/upgrade": "~10.1.3",
|
||||
"core-js": "^2.5.4",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@types/angular": "1.6.47",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@types/angular": "1.7.3",
|
||||
"@types/angular-animate": "1.5.10",
|
||||
"@types/angular-mocks": "1.6.0",
|
||||
"@types/angular-resource": "1.5.14",
|
||||
"@types/angular-route": "1.3.5",
|
||||
"@types/angular-mocks": "1.7.0",
|
||||
"@types/angular-resource": "1.5.16",
|
||||
"@types/angular-route": "1.7.1",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"concurrently": "^5.0.1",
|
||||
"http-server": "^0.12.0",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"karma": "~4.3.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"lite-server": "^2.2.2",
|
||||
"protractor": "~5.4.3",
|
||||
"protractor": "~7.0.0",
|
||||
"rollup": "^1.1.0",
|
||||
"rollup-plugin-commonjs": "^9.2.1",
|
||||
"rollup-plugin-node-resolve": "^4.0.0",
|
||||
"rollup-plugin-terser": "^5.3.0",
|
||||
"tslint": "~5.18.0",
|
||||
"typescript": "~3.7.5"
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
],
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
@ -13,22 +16,6 @@
|
||||
"deprecation": {
|
||||
"severity": "warning"
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||
"",
|
||||
"kebab-case"
|
||||
],
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
["app", "toh"],
|
||||
"camelCase"
|
||||
],
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
@ -56,6 +43,8 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "no-any": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
@ -95,6 +84,11 @@
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "typedef": [
|
||||
// true,
|
||||
// "call-signature"
|
||||
// ],
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
@ -130,6 +124,9 @@
|
||||
"check-typecast"
|
||||
]
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
@ -141,9 +138,19 @@
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
"use-pipe-transform-interface": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
["app", "toh"],
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
// TODO: Fix the code and change the prefix to `"app"` (or whatever makes sense).
|
||||
"",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,11 @@
|
||||
"projects": {
|
||||
"angular.io-example": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -18,6 +22,7 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
@ -41,20 +46,19 @@
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
|
@ -1,10 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
|
@ -5,7 +5,11 @@
|
||||
"projects": {
|
||||
"angular.io-example": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
@ -18,6 +22,7 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
@ -40,20 +45,19 @@
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -17,44 +17,43 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/platform-server": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@nguniversal/express-engine": "~9.0.1",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/platform-server": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"@nguniversal/express-engine": "~10.1.0",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"express": "^4.15.2",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^1.10.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@nguniversal/builders": "^9.0.2",
|
||||
"@types/express": "^4.17.0",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@nguniversal/builders": "~10.1.0",
|
||||
"@types/express": "^4.17.8",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,22 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
// TODO(gkalpak): Fix the code and enable this.
|
||||
// "strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
@ -21,7 +24,9 @@
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false,
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
// TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`).
|
||||
"fullTemplateTypeCheck": true,// "strictTemplates": true
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "es2015",
|
||||
"module": "es2020",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
@ -8,7 +8,8 @@
|
||||
"protractor": "protractor",
|
||||
"webdriver:update": "node ../../../../scripts/webdriver-manager-update.js",
|
||||
"preinstall": "node ../../../../tools/yarn/check-yarn.js",
|
||||
"postinstall": "yarn webdriver:update"
|
||||
"postinstall": "yarn webdriver:update",
|
||||
"sync-deps": "node sync-boilerplate-dependencies"
|
||||
},
|
||||
"//engines-comment": "Keep this in sync with /package.json and /aio/package.json",
|
||||
"engines": {
|
||||
@ -20,62 +21,61 @@
|
||||
"license": "MIT",
|
||||
"repository": {},
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.4",
|
||||
"@angular/common": "~9.1.4",
|
||||
"@angular/compiler": "~9.1.4",
|
||||
"@angular/core": "~9.1.4",
|
||||
"@angular/elements": "~9.1.4",
|
||||
"@angular/forms": "~9.1.4",
|
||||
"@angular/localize": "~9.1.4",
|
||||
"@angular/platform-browser": "~9.1.4",
|
||||
"@angular/platform-browser-dynamic": "~9.1.4",
|
||||
"@angular/platform-server": "~9.1.4",
|
||||
"@angular/router": "~9.1.4",
|
||||
"@angular/service-worker": "~9.1.4",
|
||||
"@angular/upgrade": "~9.1.4",
|
||||
"@nguniversal/express-engine": "~9.0.1",
|
||||
"@webcomponents/custom-elements": "^1.4.1",
|
||||
"angular": "1.7.9",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/elements": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/localize": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/platform-server": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"@angular/service-worker": "~10.1.3",
|
||||
"@angular/upgrade": "~10.1.3",
|
||||
"@nguniversal/express-engine": "~10.1.0",
|
||||
"@webcomponents/custom-elements": "^1.4.2",
|
||||
"angular": "1.8.0",
|
||||
"angular-in-memory-web-api": "~0.11.0",
|
||||
"angular-route": "1.7.9",
|
||||
"angular-route": "1.8.0",
|
||||
"core-js": "^2.5.4",
|
||||
"express": "^4.15.2",
|
||||
"rxjs": "~6.5.4",
|
||||
"rxjs": "~6.6.0",
|
||||
"systemjs": "0.19.39",
|
||||
"tslib": "^1.10.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.4",
|
||||
"@angular/cli": "~9.1.4",
|
||||
"@angular/compiler-cli": "~9.1.4",
|
||||
"@angular/language-service": "~9.1.4",
|
||||
"@nguniversal/builders": "^9.0.2",
|
||||
"@types/angular": "1.6.47",
|
||||
"@angular-devkit/build-angular": "~0.1001.3",
|
||||
"@angular/cli": "~10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@nguniversal/builders": "~10.1.0",
|
||||
"@types/angular": "1.7.3",
|
||||
"@types/angular-animate": "1.5.10",
|
||||
"@types/angular-mocks": "1.6.0",
|
||||
"@types/angular-resource": "1.5.14",
|
||||
"@types/angular-route": "1.3.5",
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/angular-mocks": "1.7.0",
|
||||
"@types/angular-resource": "1.5.16",
|
||||
"@types/angular-route": "1.7.1",
|
||||
"@types/express": "^4.17.8",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/jquery": "3.3.28",
|
||||
"@types/jquery": "3.5.1",
|
||||
"@types/node": "^12.11.1",
|
||||
"canonical-path": "1.0.0",
|
||||
"codelyzer": "^5.1.2",
|
||||
"codelyzer": "^6.0.0",
|
||||
"concurrently": "^5.0.1",
|
||||
"http-server": "^0.12.0",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-marbles": "~0.6.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"lite-server": "^2.2.2",
|
||||
"lodash": "^4.16.2",
|
||||
"protractor": "~5.4.3",
|
||||
"protractor": "~7.0.0",
|
||||
"puppeteer": "3.3.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup": "^1.1.0",
|
||||
@ -85,6 +85,6 @@
|
||||
"source-map-explorer": "^1.3.2",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
64
aio/tools/examples/shared/sync-boilerplate-dependencies.js
Normal file
64
aio/tools/examples/shared/sync-boilerplate-dependencies.js
Normal file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* ```sh
|
||||
* node sync-boilerplate-dependencies
|
||||
* ```
|
||||
*
|
||||
* Updates the dependency versions of the top-level `package.json` files in each sub-folder of
|
||||
* `./boilerplate/` and `./boilerplate/viewengine/` to match the ones in `./package.json`.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
const BOILERPLATE_DIR = `${__dirname}/boilerplate`;
|
||||
const VIEWENGINE_DIR = `${BOILERPLATE_DIR}/viewengine`;
|
||||
const SHARED_PACKAGE_JSON_PATH = `${__dirname}/package.json`;
|
||||
|
||||
const sharedPkgJson = loadJsonFile(SHARED_PACKAGE_JSON_PATH);
|
||||
const boilerplatePkgJsonPaths = [
|
||||
...collectPackageJsonFiles(BOILERPLATE_DIR),
|
||||
...collectPackageJsonFiles(VIEWENGINE_DIR),
|
||||
];
|
||||
|
||||
boilerplatePkgJsonPaths.forEach(syncDependencies);
|
||||
|
||||
// Helpers
|
||||
function collectPackageJsonFiles(dirPath) {
|
||||
return fs.readdirSync(dirPath)
|
||||
.map(childName => `${dirPath}/${childName}`)
|
||||
.filter(childPath => fs.statSync(childPath).isDirectory())
|
||||
.map(subDirPath => `${subDirPath}/package.json`)
|
||||
.filter(pkgJsonPath => fs.existsSync(pkgJsonPath));
|
||||
}
|
||||
|
||||
function loadJsonFile(filePath) {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
function syncDependencies(boilerplatePkgJsonPath) {
|
||||
console.log(`Syncing '${path.relative(__dirname, boilerplatePkgJsonPath)}'...`);
|
||||
|
||||
const boilerplatePkgJson = loadJsonFile(boilerplatePkgJsonPath);
|
||||
|
||||
['dependencies', 'devDependencies', 'peerDependencies']
|
||||
.filter(depsProp => boilerplatePkgJson.hasOwnProperty(depsProp))
|
||||
.forEach(depsProp => {
|
||||
const srcDeps = sharedPkgJson[depsProp];
|
||||
const dstDeps = boilerplatePkgJson[depsProp];
|
||||
|
||||
for (const dep of Object.keys(dstDeps)) {
|
||||
if (!srcDeps.hasOwnProperty(dep)) {
|
||||
throw new Error(
|
||||
`Unable to update dependency '${dep}' in '${boilerplatePkgJsonPath} > ${depsProp}'. ` +
|
||||
`The dependency is missing from '${SHARED_PACKAGE_JSON_PATH}'.`);
|
||||
}
|
||||
|
||||
dstDeps[dep] = srcDeps[dep];
|
||||
}
|
||||
});
|
||||
|
||||
fs.writeFileSync(boilerplatePkgJsonPath, `${JSON.stringify(boilerplatePkgJson, null, 2)}\n`);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
# Overview
|
||||
|
||||
[Stackblitz](https://stackblitz.com/) is an online tool for creating, collaborating and sharing ideas.
|
||||
[Stackblitz](https://stackblitz.com/) is an online tool for creating, collaborating and sharing ideas.
|
||||
In AIO we use it to share one or more runnable versions of our examples.
|
||||
|
||||
Stackblitz can be used both on a separate page and in an embedded form.
|
||||
@ -8,24 +8,25 @@ Stackblitz can be used both on a separate page and in an embedded form.
|
||||
|
||||
## Stackblitz generation
|
||||
|
||||
Both forms are created within `builder.js`. How is a stackblitz created? What is the process from a
|
||||
directory with files to a link with a stackblitz.
|
||||
Both forms are created within `builder.js`.
|
||||
How is a stackblitz created?
|
||||
What is the process from a directory with files to a link with a stackblitz.
|
||||
|
||||
An "executable" stackblitz is an HTML file with a `<form>` that makes a post to stackblitz on submit. It
|
||||
contains an `<input>` element for each file we need in the stackblitz.
|
||||
An "executable" stackblitz is an HTML file with a `<form>` that makes a post to stackblitz on submit.
|
||||
It contains an `<input>` element for each file we need in the stackblitz.
|
||||
|
||||
The form will be submitted on load, so you can either double click the HTML file or open it with an
|
||||
anchor tag to open the stackblitz.
|
||||
The form will be submitted on load, so you can either double click the HTML file or open it with an anchor tag to open the stackblitz.
|
||||
|
||||
So the `builder.js` job is to get all the needed files from an example and build this HTML file for you.
|
||||
|
||||
## Customizing the generation per example basis
|
||||
|
||||
How does this tool know what is an example and what is not? It will look for all folders containing a
|
||||
`stackblitz.json` file. If found, all files within the folder and subfolders will be used in the stackblitz, with
|
||||
a few generic exceptions that you can find at `builder.js`.
|
||||
How does this tool know what is an example and what is not?
|
||||
It will look for all folders containing a `stackblitz.json` file.
|
||||
If found, all files within the folder and subfolders will be used in the stackblitz, with a few generic exceptions that you can find at `builder.js`.
|
||||
|
||||
You can use the `stackblitz.json` to customize the stackblitz generation. For example:
|
||||
You can use the `stackblitz.json` to customize the stackblitz generation.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -39,21 +40,21 @@ You can use the `stackblitz.json` to customize the stackblitz generation. For ex
|
||||
}
|
||||
```
|
||||
|
||||
Here you can specify a description for the stackblitz, some tags and also a files array where you
|
||||
can specify extra files to add or to ignore.
|
||||
Here you can specify a description for the stackblitz, some tags and also a files array where you can specify extra files to add or to ignore.
|
||||
|
||||
## Executing the stackblitz generation
|
||||
|
||||
`generateStackblitz.js` will create a stackblitz for each `stackblitz.json` it finds.
|
||||
|
||||
Where? At `src/generated/live-examples/`.
|
||||
Where?
|
||||
At `src/generated/live-examples/`.
|
||||
|
||||
Then the `<live-example>` embedded component will look at this folder to get the stackblitz it needs for the
|
||||
example.
|
||||
|
||||
## Appendix: Why not generating stackblitz at runtime?
|
||||
|
||||
At AngularJS, all the plunker examples were generated at runtime. The downside was that all the example code had to be
|
||||
deployed as well and would no longer be useful after the plunker was generated.
|
||||
At AngularJS, all the plunker examples were generated at runtime.
|
||||
The downside was that all the example code had to be deployed as well and would no longer be useful after the plunker was generated.
|
||||
|
||||
This `StackblitzBuilder` tool takes a few seconds to run, and the end result is only 3mb~.
|
||||
|
@ -1,36 +1,27 @@
|
||||
# Overview
|
||||
|
||||
All the content that is rendered by the AIO application, and some of its configuration files, are
|
||||
generated from source files by [Dgeni](https://github.com/angular/dgeni). Dgeni is a general purpose
|
||||
documentation generation tool.
|
||||
All the content that is rendered by the AIO application, and some of its configuration files, are generated from source files by [Dgeni](https://github.com/angular/dgeni).
|
||||
Dgeni is a general purpose documentation generation tool.
|
||||
|
||||
Markdown files in `/aio/content`, code comments in the core Angular source files and example files
|
||||
are processed and transformed into files that are consumed by the AIO application.
|
||||
Markdown files in `/aio/content`, code comments in the core Angular source files and example files are processed and transformed into files that are consumed by the AIO application.
|
||||
|
||||
Dgeni is configured by "packages", which contain services and processors. Some of these packages are
|
||||
installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and
|
||||
some are specific to the AIO project.
|
||||
Dgeni is configured by "packages", which contain services and processors.
|
||||
Some of these packages are installed as `node_modules` from the [dgeni-packages](https://github.com/angular/dgeni-packages) and some are specific to the AIO project.
|
||||
|
||||
The project specific packages are stored in this folder (`aio/tools/transforms`).
|
||||
|
||||
If you are an author and want to know how to generate the documentation, the steps are outlined in
|
||||
the top level [README.md](../../README.md#guide-to-authoring).
|
||||
If you are an author and want to know how to generate the documentation, the steps are outlined in the top level [README.md](../../README.md#guide-to-authoring).
|
||||
|
||||
## Root packages
|
||||
|
||||
To run Dgeni, you must specify a root package, which acts as the entry point to the documentation
|
||||
generation.
|
||||
This root package, in turn requires a number of other packages, some are defined locally in the
|
||||
`tools/transforms` folder, such as `tools/transforms/cheatsheet-package` and
|
||||
`tools/transforms/content-package`, etc. And some are brought in from the `dgeni-packages` node
|
||||
modules, such as `jsdoc` and `nunjucks`.
|
||||
To run Dgeni, you must specify a root package, which acts as the entry point to the documentation generation.
|
||||
This root package, in turn requires a number of other packages, some are defined locally in the `tools/transforms` folder, such as `tools/transforms/cheatsheet-package` and `tools/transforms/content-package`, etc.
|
||||
And some are brought in from the `dgeni-packages` node modules, such as `jsdoc` and `nunjucks`.
|
||||
|
||||
* The primary root package is defined in `tools/transforms/angular.io-package/index.js`. This package
|
||||
is used to run a full generation of all the documentation.
|
||||
* There are also root packages defined in `tools/transforms/authors-package/*-package.js`. These
|
||||
packages are used by the documentation authors when writing docs, since it allows them to run partial
|
||||
doc generation, which is not complete but is faster for quickly seeing changes to the document that
|
||||
you are working on.
|
||||
* The primary root package is defined in `tools/transforms/angular.io-package/index.js`.
|
||||
This package is used to run a full generation of all the documentation.
|
||||
* There are also root packages defined in `tools/transforms/authors-package/*-package.js`.
|
||||
These packages are used by the documentation authors when writing docs, since it allows them to run partial doc generation, which is not complete but is faster for quickly seeing changes to the document that you are working on.
|
||||
|
||||
## Other packages
|
||||
|
||||
@ -46,5 +37,5 @@ you are working on.
|
||||
|
||||
## Templates
|
||||
|
||||
All the templates for the angular.io dgeni transformations are stoted in the `tools/transforms/templates`
|
||||
folder. See the [README](./templates/README.md).
|
||||
All the templates for the angular.io dgeni transformations are stoted in the `tools/transforms/templates` folder.
|
||||
See the [README](./templates/README.md).
|
||||
|
@ -47,7 +47,7 @@ describe('createSitemap processor', () => {
|
||||
expect(docs.pop().urls).toEqual(['abc', 'fgh']);
|
||||
});
|
||||
|
||||
it('ignoring blacklisted doc types', () => {
|
||||
it('ignoring excluded doc types', () => {
|
||||
const docs = [
|
||||
{ path: 'abc', outputPath: 'abc', docType: 'good' },
|
||||
{ path: 'cde', outputPath: 'cde', docType: 'bad' },
|
||||
@ -58,7 +58,7 @@ describe('createSitemap processor', () => {
|
||||
expect(docs.pop().urls).toEqual(['abc', 'fgh']);
|
||||
});
|
||||
|
||||
it('ignoring blacklisted paths', () => {
|
||||
it('ignoring excluded paths', () => {
|
||||
const docs = [
|
||||
{ path: 'abc', outputPath: 'abc' },
|
||||
{ path: 'cde', outputPath: 'cde' },
|
||||
|
@ -1,25 +1,28 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"outDir": "./out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
// disabled because this is on by default in tsc 2.7 breaking our codebase - we need to refactor
|
||||
"strictPropertyInitialization": false
|
||||
},
|
||||
@ -35,6 +38,7 @@
|
||||
"angularCompilerOptions": {
|
||||
"disableTypeScriptVersionCheck": true,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
|
2288
aio/yarn.lock
2288
aio/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@
|
||||
|
||||
import {Arguments, Argv, CommandModule} from 'yargs';
|
||||
|
||||
import {addGithubTokenFlag} from '../../utils/yargs';
|
||||
import {addGithubTokenOption} from '../../utils/git/github-yargs';
|
||||
|
||||
import {checkServiceStatuses} from './check';
|
||||
|
||||
@ -17,12 +17,9 @@ export interface CaretakerCheckOptions {
|
||||
githubToken: string;
|
||||
}
|
||||
|
||||
/** URL to the Github page where personal access tokens can be generated. */
|
||||
export const GITHUB_TOKEN_GENERATE_URL = `https://github.com/settings/tokens`;
|
||||
|
||||
/** Builds the command. */
|
||||
function builder(yargs: Argv) {
|
||||
return addGithubTokenFlag(yargs);
|
||||
return addGithubTokenOption(yargs);
|
||||
}
|
||||
|
||||
/** Handles the command. */
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Arguments, Argv, CommandModule} from 'yargs';
|
||||
|
||||
import {addGithubTokenFlag} from '../../utils/yargs';
|
||||
import {addGithubTokenOption} from '../../utils/git/github-yargs';
|
||||
import {checkOutPullRequestLocally} from '../common/checkout-pr';
|
||||
|
||||
export interface CheckoutOptions {
|
||||
@ -18,7 +18,7 @@ export interface CheckoutOptions {
|
||||
|
||||
/** Builds the checkout pull request command. */
|
||||
function builder(yargs: Argv) {
|
||||
return addGithubTokenFlag(yargs).positional('prNumber', {type: 'number', demandOption: true});
|
||||
return addGithubTokenOption(yargs).positional('prNumber', {type: 'number', demandOption: true});
|
||||
}
|
||||
|
||||
/** Handles the checkout pull request command. */
|
||||
|
@ -7,10 +7,10 @@
|
||||
*/
|
||||
|
||||
import {types as graphQLTypes} from 'typed-graphqlify';
|
||||
import {URL} from 'url';
|
||||
|
||||
import {info} from '../../utils/console';
|
||||
import {GitClient} from '../../utils/git';
|
||||
import {addTokenToGitHttpsUrl} from '../../utils/git/github-urls';
|
||||
import {getPr} from '../../utils/github';
|
||||
|
||||
/* GraphQL schema for the response body for a pending PR. */
|
||||
@ -83,7 +83,7 @@ export async function checkOutPullRequestLocally(
|
||||
/** The full ref for the repository and branch the PR came from. */
|
||||
const fullHeadRef = `${pr.headRef.repository.nameWithOwner}:${headRefName}`;
|
||||
/** The full URL path of the repository the PR came from with github token as authentication. */
|
||||
const headRefUrl = addAuthenticationToUrl(pr.headRef.repository.url, githubToken);
|
||||
const headRefUrl = addTokenToGitHttpsUrl(pr.headRef.repository.url, githubToken);
|
||||
// Note: Since we use a detached head for rebasing the PR and therefore do not have
|
||||
// remote-tracking branches configured, we need to set our expected ref and SHA. This
|
||||
// allows us to use `--force-with-lease` for the detached head while ensuring that we
|
||||
@ -126,10 +126,3 @@ export async function checkOutPullRequestLocally(
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Adds the provided token as username to the provided url. */
|
||||
function addAuthenticationToUrl(urlString: string, token: string) {
|
||||
const url = new URL(urlString);
|
||||
url.username = token;
|
||||
return url.toString();
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ ts_library(
|
||||
visibility = ["//dev-infra:__subpackages__"],
|
||||
deps = [
|
||||
"//dev-infra/commit-message",
|
||||
"//dev-infra/release/config",
|
||||
"//dev-infra/release/versioning",
|
||||
"//dev-infra/utils",
|
||||
"@npm//@octokit/rest",
|
||||
"@npm//@types/inquirer",
|
||||
@ -28,6 +30,8 @@ ts_library(
|
||||
srcs = glob(["**/*.spec.ts"]),
|
||||
deps = [
|
||||
":merge",
|
||||
"//dev-infra/release/config",
|
||||
"//dev-infra/release/versioning",
|
||||
"//dev-infra/utils",
|
||||
"@npm//@types/jasmine",
|
||||
"@npm//@types/node",
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Arguments, Argv} from 'yargs';
|
||||
|
||||
import {addGithubTokenFlag} from '../../utils/yargs';
|
||||
import {addGithubTokenOption} from '../../utils/git/github-yargs';
|
||||
|
||||
import {mergePullRequest} from './index';
|
||||
|
||||
@ -20,7 +20,7 @@ export interface MergeCommandOptions {
|
||||
|
||||
/** Builds the options for the merge command. */
|
||||
export function buildMergeCommand(yargs: Argv): Argv<MergeCommandOptions> {
|
||||
return addGithubTokenFlag(yargs).help().strict().positional(
|
||||
return addGithubTokenOption(yargs).help().strict().positional(
|
||||
'pr-number', {demandOption: true, type: 'number'});
|
||||
}
|
||||
|
||||
|
@ -7,5 +7,4 @@
|
||||
*/
|
||||
|
||||
export * from './labels';
|
||||
export * from './branches';
|
||||
export * from './lts-branch';
|
||||
|
@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
import * as nock from 'nock';
|
||||
import * as nodeFetch from 'node-fetch';
|
||||
|
||||
import {ReleaseConfig} from '../../../release/config/index';
|
||||
import {_npmPackageInfoCache, NpmPackageInfo} from '../../../release/versioning/npm-registry';
|
||||
import {GithubConfig} from '../../../utils/config';
|
||||
import * as console from '../../../utils/console';
|
||||
import {GithubClient} from '../../../utils/git/github';
|
||||
@ -21,13 +22,17 @@ const API_ENDPOINT = `https://api.github.com`;
|
||||
|
||||
describe('default target labels', () => {
|
||||
let api: GithubClient;
|
||||
let config: GithubConfig;
|
||||
let npmPackageName: string;
|
||||
let githubConfig: GithubConfig;
|
||||
let releaseConfig: ReleaseConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
api = new GithubClient();
|
||||
config = {owner: 'angular', name: 'dev-infra-test'};
|
||||
npmPackageName = '@angular/dev-infra-test-pkg';
|
||||
githubConfig = {owner: 'angular', name: 'dev-infra-test'};
|
||||
releaseConfig = {
|
||||
npmPackages: ['@angular/dev-infra-test-pkg'],
|
||||
buildPackages: async () => [],
|
||||
generateReleaseNotesForHead: async () => {},
|
||||
};
|
||||
|
||||
// The label determination will print warn messages. These should not be
|
||||
// printed to the console, so we turn `console.warn` into a spy.
|
||||
@ -37,11 +42,11 @@ describe('default target labels', () => {
|
||||
afterEach(() => nock.cleanAll());
|
||||
|
||||
async function computeTargetLabels(): Promise<TargetLabel[]> {
|
||||
return getDefaultTargetLabelConfiguration(api, config, npmPackageName);
|
||||
return getDefaultTargetLabelConfiguration(api, githubConfig, releaseConfig);
|
||||
}
|
||||
|
||||
function getRepoApiRequestUrl(): string {
|
||||
return `${API_ENDPOINT}/repos/${config.owner}/${config.name}`;
|
||||
return `${API_ENDPOINT}/repos/${githubConfig.owner}/${githubConfig.name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,10 +66,9 @@ describe('default target labels', () => {
|
||||
}
|
||||
|
||||
/** Fakes a NPM package query API request. */
|
||||
function fakeNpmPackageQueryRequest(data: unknown) {
|
||||
// Note: We only need to mock the `json` function for a `Response`. Types
|
||||
// would expect us to mock more functions, so we need to cast to `any`.
|
||||
spyOn(nodeFetch, 'default').and.resolveTo({json: async () => data} as any);
|
||||
function fakeNpmPackageQueryRequest(data: Partial<NpmPackageInfo>) {
|
||||
_npmPackageInfoCache[releaseConfig.npmPackages[0]] =
|
||||
Promise.resolve({'dist-tags': {}, versions: {}, time: {}, ...data});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,7 +171,7 @@ describe('default target labels', () => {
|
||||
'time': {
|
||||
// v10 has been released at the given specified date. We pick a date that
|
||||
// guarantees that the version is no longer considered as active LTS version.
|
||||
'10.0.0': new Date(1912, 5, 23),
|
||||
'10.0.0': new Date(1912, 5, 23).toISOString(),
|
||||
}
|
||||
});
|
||||
|
||||
@ -234,7 +238,7 @@ describe('default target labels', () => {
|
||||
'time': {
|
||||
// v10 has been released at the given specified date. We pick a date that
|
||||
// guarantees that the version is no longer considered as active LTS version.
|
||||
'10.0.0': new Date(1912, 5, 23),
|
||||
'10.0.0': new Date(1912, 5, 23).toISOString(),
|
||||
}
|
||||
});
|
||||
|
||||
@ -247,16 +251,14 @@ describe('default target labels', () => {
|
||||
interceptBranchVersionRequest('10.5.x', '10.5.1');
|
||||
interceptBranchesListRequest(['10.5.x', '11.0.x']);
|
||||
|
||||
spyOn(require('node-fetch'), 'default').and.callFake(() => ({
|
||||
json: () => ({
|
||||
'dist-tags': {
|
||||
'v10-lts': '10.5.1',
|
||||
},
|
||||
'time': {
|
||||
'10.0.0': new Date().toISOString(),
|
||||
}
|
||||
}),
|
||||
}));
|
||||
fakeNpmPackageQueryRequest({
|
||||
'dist-tags': {
|
||||
'v10-lts': '10.5.1',
|
||||
},
|
||||
'time': {
|
||||
'10.0.0': new Date().toISOString(),
|
||||
}
|
||||
});
|
||||
|
||||
expect(await getBranchesForLabel('target: lts', '10.5.x')).toEqual(['10.5.x']);
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user