Compare commits
200 Commits
10.1.2
...
11.0.0-nex
Author | SHA1 | Date | |
---|---|---|---|
4baabf9cd3 | |||
7244e1b4e3 | |||
b1682526dd | |||
75610505c6 | |||
ba3f4c26bb | |||
c8f056beb6 | |||
a2068523fd | |||
145ab3d7e0 | |||
3082f7378b | |||
297b123151 | |||
a93605f2a4 | |||
15dfd3439a | |||
123bff7cb6 | |||
6158dc16b4 | |||
b0a43872a8 | |||
856e74ac98 | |||
6ae3b68acf | |||
49f27e31ed | |||
1a62f74496 | |||
2b1b7180db | |||
fdd4fa0009 | |||
984ed39195 | |||
0c0c54d615 | |||
dfbfabc052 | |||
f3f6a42342 | |||
e498ea9b5a | |||
a91f0f6b82 | |||
d5ddb9f340 | |||
b68fab547a | |||
16a560119a | |||
88d7bb8386 | |||
2d6105a784 | |||
95b8a8706a | |||
d92a0dd72f | |||
55485713a3 | |||
d3169c533e | |||
e4424863c2 | |||
d795a00137 | |||
7fb388f929 | |||
02c6bff9cf | |||
ef28e15884 | |||
a33d630a21 | |||
c4b8964424 | |||
dd8d8c8289 | |||
129107191c | |||
722699fb0c | |||
5614258cc7 | |||
e62a918542 | |||
49ee90b1b5 | |||
7849fdde09 | |||
97adc27207 | |||
41bc2701c4 | |||
a765530277 | |||
5b33798796 | |||
44074499dc | |||
ba54671993 | |||
a85109fd72 | |||
85a2626620 | |||
d192c87f6a | |||
3817e5f1df | |||
171a0d0696 | |||
a1c1c450dc | |||
fd44d84a33 | |||
f0688b4d18 | |||
b9ca6d20cc | |||
e00535a2a4 | |||
593bd594e3 | |||
284c70ee9d | |||
78e1ecb161 | |||
3934f0a833 | |||
297c060ae7 | |||
077f51685a | |||
e162da0753 | |||
354138eba9 | |||
6768fe9927 | |||
57c442f930 | |||
f667e374a9 | |||
15207e3c9c | |||
66129f8ea6 | |||
da14b72550 | |||
27cc56b359 | |||
6acea54f62 | |||
2d52c80332 | |||
03447ba52f | |||
8f349b2375 | |||
19598b47ca | |||
f56ece4fdc | |||
cf2e8b99a8 | |||
c4556db9f5 | |||
a46e0e48a3 | |||
9e77bd3087 | |||
26f28200bf | |||
ce1efc1af2 | |||
d1415162cb | |||
db21c4fb44 | |||
281865bbcf | |||
3406ec15a4 | |||
85951a0465 | |||
0ae00bb1f7 | |||
1373a98e25 | |||
1ed6913b8b | |||
3c4b8b97c1 | |||
f9421184ef | |||
c6ebb77cec | |||
a69507a0ad | |||
886f58d4fe | |||
18f84a0328 | |||
b0ca3cd0c4 | |||
3d77b64fc3 | |||
f645d26e3f | |||
6d9bfb8368 | |||
7997fc5f97 | |||
3cb2a79399 | |||
d896c33b0e | |||
19a484302d | |||
ded075a79c | |||
ba95b79a21 | |||
4faac78e32 | |||
4360eed9b7 | |||
c880e393e9 | |||
b0c79f2373 | |||
a32a317ea1 | |||
bfb7eec698 | |||
7e0b3fd953 | |||
7a6a061a9e | |||
109555b33a | |||
bf31ef29f6 | |||
40096bee00 | |||
45a73dddfd | |||
687477279b | |||
4007422cc6 | |||
1150649139 | |||
7869de6136 | |||
2c4a98a285 | |||
92ff6d93eb | |||
83ace4ed30 | |||
926ffcd8ac | |||
97475d7408 | |||
a29f9a9fb3 | |||
9f28f82598 | |||
261f689e9b | |||
1d9873c44c | |||
d9da7e5a18 | |||
79d8795633 | |||
3e57ca1d98 | |||
c2d017de83 | |||
7baa7ebfc4 | |||
4e5286180b | |||
73001b42fe | |||
c90eb5450d | |||
3e97435f1c | |||
1c7e5cef3e | |||
2cb3d58b42 | |||
44bb85ade4 | |||
50f4d8a1ce | |||
fdea1804d6 | |||
1d8c5d88cd | |||
a68f1a78a7 | |||
86e11f1110 | |||
5da1934115 | |||
86e7cd8117 | |||
e6ee7c2aeb | |||
54687f7765 | |||
59c234cfb4 | |||
a6f3cd93a9 | |||
d9fea857db | |||
03dbcc7a56 | |||
c142b071eb | |||
71acf9dd49 | |||
f5a148b1b7 | |||
4f28192d62 | |||
0fc2bef0cd | |||
f5d1e9a2d1 | |||
036a2faf02 | |||
5be4edfa17 | |||
38d6596742 | |||
0a7a5e3aff | |||
d5fabc303d | |||
ebc0e46501 | |||
3487b549fd | |||
52c7a4bfc6 | |||
827ba05914 | |||
b2857b4e3a | |||
5d5caf21b8 | |||
c1bc070b40 | |||
930eeaf177 | |||
2dd29fbae7 | |||
9613660fee | |||
c0523fc3b4 | |||
de1cffb23b | |||
31f4557621 | |||
7723bfd9ba | |||
e8ea839df8 | |||
90cec40cce | |||
4036281007 | |||
164cd274a4 | |||
fedcfec346 | |||
618cb32407 | |||
4aee0087ea | |||
0681a20d28 |
@ -653,8 +653,10 @@ jobs:
|
|||||||
name: Starting Saucelabs tunnel service
|
name: Starting Saucelabs tunnel service
|
||||||
command: ./tools/saucelabs/sauce-service.sh run
|
command: ./tools/saucelabs/sauce-service.sh run
|
||||||
background: true
|
background: true
|
||||||
- run: yarn tsc -p packages
|
# add module umd tsc compile option so the test can work
|
||||||
- run: yarn tsc -p modules
|
# properly in the legacy browsers
|
||||||
|
- run: yarn tsc -p packages --module UMD
|
||||||
|
- run: yarn tsc -p modules --module UMD
|
||||||
- run: yarn bazel build //packages/zone.js:npm_package
|
- run: yarn bazel build //packages/zone.js:npm_package
|
||||||
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
||||||
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
||||||
@ -747,6 +749,8 @@ jobs:
|
|||||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
|
||||||
yarn --cwd packages/zone.js electrontest
|
yarn --cwd packages/zone.js electrontest
|
||||||
- run: yarn --cwd packages/zone.js jesttest
|
- run: yarn --cwd packages/zone.js jesttest
|
||||||
|
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
|
||||||
|
- run: yarn --cwd packages/zone.js/test/typings test
|
||||||
|
|
||||||
# Windows jobs
|
# Windows jobs
|
||||||
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
|
||||||
|
1
.github/angular-robot.yml
vendored
1
.github/angular-robot.yml
vendored
@ -68,6 +68,7 @@ merge:
|
|||||||
- "packages/**/integrationtest/**"
|
- "packages/**/integrationtest/**"
|
||||||
- "packages/**/test/**"
|
- "packages/**/test/**"
|
||||||
- "packages/zone.js/*"
|
- "packages/zone.js/*"
|
||||||
|
- "packages/zone.js/dist/**"
|
||||||
- "packages/zone.js/doc/**"
|
- "packages/zone.js/doc/**"
|
||||||
- "packages/zone.js/example/**"
|
- "packages/zone.js/example/**"
|
||||||
- "packages/zone.js/scripts/**"
|
- "packages/zone.js/scripts/**"
|
||||||
|
2
.github/workflows/lock-closed.yml
vendored
2
.github/workflows/lock-closed.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
|||||||
if: github.repository == 'angular/angular'
|
if: github.repository == 'angular/angular'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: angular/dev-infra/github-actions/lock-closed@66462f6
|
- uses: angular/dev-infra/github-actions/lock-closed@414834b2b24dd2df37c6ed00808387ee6fd91b66
|
||||||
with:
|
with:
|
||||||
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}
|
||||||
|
@ -44654,7 +44654,7 @@ const FOLDERS_IGNORE = [
|
|||||||
const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE,
|
const DEFAULT_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([...FOLDERS_IGNORE,
|
||||||
|
|
||||||
// ignore cruft
|
// ignore cruft
|
||||||
'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.npmignore', '.gitignore', '.DS_Store']);
|
'yarn.lock', '.lock-wscript', '.wafpickle-{0..9}', '*.swp', '._*', 'npm-debug.log', 'yarn-error.log', '.npmrc', '.yarnrc', '.yarnrc.yml', '.npmignore', '.gitignore', '.DS_Store']);
|
||||||
|
|
||||||
const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
||||||
// never ignore these files
|
// never ignore these files
|
||||||
@ -44663,6 +44663,7 @@ const NEVER_IGNORE = (0, (_filter || _load_filter()).ignoreLinesToRegex)([
|
|||||||
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
function packWithIgnoreAndHeaders(cwd, ignoreFunction, { mapHeader } = {}) {
|
||||||
return tar.pack(cwd, {
|
return tar.pack(cwd, {
|
||||||
ignore: ignoreFunction,
|
ignore: ignoreFunction,
|
||||||
|
sort: true,
|
||||||
map: header => {
|
map: header => {
|
||||||
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
const suffix = header.name === '.' ? '' : `/${header.name}`;
|
||||||
header.name = `package${suffix}`;
|
header.name = `package${suffix}`;
|
||||||
@ -46678,7 +46679,7 @@ function mkdirfix (name, opts, cb) {
|
|||||||
/* 194 */
|
/* 194 */
|
||||||
/***/ (function(module, exports) {
|
/***/ (function(module, exports) {
|
||||||
|
|
||||||
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.4","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.22.5","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^3.1.0","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","cli-table3":"^0.4.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^6.2.0","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","js-yaml":"^3.13.1","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.4","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-inline-import":"^3.0.0","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.28","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.11.0","fancy-log":"^1.3.2","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^4.0.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","string-replace-loader":"^2.1.1","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}}
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
/* 195 */
|
/* 195 */
|
||||||
@ -98338,7 +98339,7 @@ var _buildSubCommands = (0, (_buildSubCommands2 || _load_buildSubCommands()).def
|
|||||||
|
|
||||||
const bundle = yield fetchBundle(config, bundleUrl);
|
const bundle = yield fetchBundle(config, bundleUrl);
|
||||||
|
|
||||||
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
|
const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.cjs`);
|
||||||
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
|
||||||
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
yield (_fs || _load_fs()).mkdirp(path.dirname(yarnPath));
|
||||||
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
yield (_fs || _load_fs()).writeFile(yarnPath, bundle);
|
||||||
@ -100190,7 +100191,7 @@ let main = exports.main = (() => {
|
|||||||
|
|
||||||
const config = new (_config || _load_config()).default(reporter);
|
const config = new (_config || _load_config()).default(reporter);
|
||||||
const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true);
|
const outputWrapperEnabled = (0, (_conversion || _load_conversion()).boolifyWithDefault)(process.env.YARN_WRAP_OUTPUT, true);
|
||||||
const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args);
|
const shouldWrapOutput = outputWrapperEnabled && !(_commander || _load_commander()).default.json && command.hasWrapper((_commander || _load_commander()).default, (_commander || _load_commander()).default.args) && !(commandName === 'init' && (_commander || _load_commander()).default[`2`]);
|
||||||
|
|
||||||
if (shouldWrapOutput) {
|
if (shouldWrapOutput) {
|
||||||
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
reporter.header(commandName, { name: 'yarn', version: (_yarnVersion || _load_yarnVersion()).version });
|
||||||
@ -100604,7 +100605,7 @@ let start = (() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (yarnPath.endsWith(`.js`)) {
|
if (/\.[cm]?js$/.test(yarnPath)) {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(process.execPath, [yarnPath, ...argv], opts);
|
||||||
} else {
|
} else {
|
||||||
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
exitCode = yield (0, (_child || _load_child()).spawnp)(yarnPath, argv, opts);
|
2
.yarnrc
2
.yarnrc
@ -2,4 +2,4 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
yarn-path ".yarn/releases/yarn-1.22.4.js"
|
yarn-path ".yarn/releases/yarn-1.22.5.js"
|
||||||
|
@ -34,7 +34,7 @@ filegroup(
|
|||||||
filegroup(
|
filegroup(
|
||||||
name = "angularjs_scripts",
|
name = "angularjs_scripts",
|
||||||
srcs = [
|
srcs = [
|
||||||
# We also declare the unminfied AngularJS files since these can be used for
|
# We also declare the unminified AngularJS files since these can be used for
|
||||||
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
||||||
"@npm//:node_modules/angular/angular.js",
|
"@npm//:node_modules/angular/angular.js",
|
||||||
"@npm//:node_modules/angular/angular.min.js",
|
"@npm//:node_modules/angular/angular.min.js",
|
||||||
|
142
CHANGELOG.md
142
CHANGELOG.md
@ -1,3 +1,102 @@
|
|||||||
|
<a name="11.0.0-next.3"></a>
|
||||||
|
# 11.0.0-next.3 (2020-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** add `params` and `reportProgress` options to `HttpClient.put()` overload ([#37873](https://github.com/angular/angular/issues/37873)) ([dd8d8c8](https://github.com/angular/angular/commit/dd8d8c8)), closes [#23600](https://github.com/angular/angular/issues/23600)
|
||||||
|
* **compiler-cli:** generate `let` statements in ES2015+ mode ([#38775](https://github.com/angular/angular/issues/38775)) ([123bff7](https://github.com/angular/angular/commit/123bff7))
|
||||||
|
* **core:** ensure TestBed is not instantiated before override provider ([#38717](https://github.com/angular/angular/issues/38717)) ([c8f056b](https://github.com/angular/angular/commit/c8f056b))
|
||||||
|
* **forms:** type NG_VALUE_ACCESSOR injection token as array ([#29723](https://github.com/angular/angular/issues/29723)) ([2b1b718](https://github.com/angular/angular/commit/2b1b718)), closes [#29351](https://github.com/angular/angular/issues/29351)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **common:** Add ISO week-numbering year formats support to formatDate ([#38828](https://github.com/angular/angular/issues/38828)) ([984ed39](https://github.com/angular/angular/commit/984ed39))
|
||||||
|
* **compiler:** Parse and recover on incomplete opening HTML tags ([#38681](https://github.com/angular/angular/issues/38681)) ([6ae3b68](https://github.com/angular/angular/commit/6ae3b68)), closes [#38596](https://github.com/angular/angular/issues/38596)
|
||||||
|
* **router:** add migration to update calls to navigateByUrl and createUrlTree with invalid parameters ([#38825](https://github.com/angular/angular/issues/38825)) ([7849fdd](https://github.com/angular/angular/commit/7849fdd)), closes [#38227](https://github.com/angular/angular/issues/38227)
|
||||||
|
* **service-worker:** add the option to prefer network for navigation requests ([#38565](https://github.com/angular/angular/issues/38565)) ([a206852](https://github.com/angular/angular/commit/a206852)), closes [#38194](https://github.com/angular/angular/issues/38194)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** If you call `TestBed.overrideProvider` after TestBed initialization, provider overrides are not applied. This
|
||||||
|
behavior is consistent with other override methods (such as `TestBed.overrideDirective`, etc) but they
|
||||||
|
throw an error to indicate that, when the check was missing in the `TestBed.overrideProvider` function.
|
||||||
|
Now calling `TestBed.overrideProvider` after TestBed initialization also triggers an
|
||||||
|
error, thus there is a chance that some tests (where `TestBed.overrideProvider` is
|
||||||
|
called after TestBed initialization) will start to fail and require updates to move `TestBed.overrideProvider` calls
|
||||||
|
before TestBed initialization is completed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.1.3"></a>
|
||||||
|
## 10.1.3 (2020-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **http:** Fix error message when we call jsonp without importing HttpClientJsonpModule ([#38756](https://github.com/angular/angular/issues/38756)) ([3902ec0](https://github.com/angular/angular/commit/3902ec0))
|
||||||
|
* **ngcc:** fix compilation of `ChangeDetectorRef` in pipe constructors ([#38892](https://github.com/angular/angular/issues/38892)) ([093c3a1](https://github.com/angular/angular/commit/093c3a1)), closes [#38666](https://github.com/angular/angular/issues/38666) [#38883](https://github.com/angular/angular/issues/38883)
|
||||||
|
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* feat(router): better warning message when a router outlet has not been instantiated ([#38920](https://github.com/angular/angular/issues/38920)) ([04d0aa6](https://github.com/angular/angular/commit/04d0aa6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.2"></a>
|
||||||
|
# 11.0.0-next.2 (2020-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** do not round up fractions of a millisecond in `DatePipe` ([#38009](https://github.com/angular/angular/issues/38009)) ([26f2820](https://github.com/angular/angular/commit/26f2820)), closes [/www.ecma-international.org/ecma-262/5.1/#sec-15](https://github.com//www.ecma-international.org/ecma-262/5.1//issues/sec-15) [#37989](https://github.com/angular/angular/issues/37989)
|
||||||
|
* **common:** mark locale data arrays as readonly ([#30397](https://github.com/angular/angular/issues/30397)) ([6acea54](https://github.com/angular/angular/commit/6acea54)), closes [#27003](https://github.com/angular/angular/issues/27003)
|
||||||
|
* **compiler:** source span for microsyntax text att should be key span ([#38766](https://github.com/angular/angular/issues/38766)) ([8f349b2](https://github.com/angular/angular/commit/8f349b2))
|
||||||
|
* **router:** Fix arguments order for call to shouldReuseRoute ([#26949](https://github.com/angular/angular/issues/26949)) ([3817e5f](https://github.com/angular/angular/commit/3817e5f)), closes [#16192](https://github.com/angular/angular/issues/16192) [#16192](https://github.com/angular/angular/issues/16192)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler-cli:** `TemplateTypeChecker` operation to get `Symbol` from a template node ([#38618](https://github.com/angular/angular/issues/38618)) ([c4556db](https://github.com/angular/angular/commit/c4556db))
|
||||||
|
* **compiler-cli:** Add ability to get `Symbol` of `Template`s and `Element`s in component template ([#38618](https://github.com/angular/angular/issues/38618)) ([cf2e8b9](https://github.com/angular/angular/commit/cf2e8b9))
|
||||||
|
* **compiler-cli:** Add ability to get `Symbol` of AST expression in component template ([#38618](https://github.com/angular/angular/issues/38618)) ([f56ece4](https://github.com/angular/angular/commit/f56ece4))
|
||||||
|
* **compiler-cli:** add ability to get symbol of reference or variable ([#38618](https://github.com/angular/angular/issues/38618)) ([19598b4](https://github.com/angular/angular/commit/19598b4))
|
||||||
|
* **compiler-cli:** define interfaces to be used for TemplateTypeChecker ([#38618](https://github.com/angular/angular/issues/38618)) ([9e77bd3](https://github.com/angular/angular/commit/9e77bd3))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **compiler-cli:** only emit directive/pipe references that are used ([#38539](https://github.com/angular/angular/issues/38539)) ([077f516](https://github.com/angular/angular/commit/077f516))
|
||||||
|
* **compiler-cli:** optimize computation of type-check scope information ([#38539](https://github.com/angular/angular/issues/38539)) ([297c060](https://github.com/angular/angular/commit/297c060))
|
||||||
|
* **router:** use `ngDevMode` to tree-shake error messages in router ([#38674](https://github.com/angular/angular/issues/38674)) ([db21c4f](https://github.com/angular/angular/commit/db21c4f))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **router:** This change corrects the argument order when calling
|
||||||
|
RouteReuseStrategy#shouldReuseRoute. Previously, when evaluating child
|
||||||
|
routes, they would be called with the future and current arguments would
|
||||||
|
be swapped. If your RouteReuseStrategy relies specifically on only the future
|
||||||
|
or current snapshot state, you may need to update the shouldReuseRoute
|
||||||
|
implementation's use of "future" and "current" ActivateRouteSnapshots.
|
||||||
|
* **common:** The locale data API has been marked as returning readonly arrays, rather
|
||||||
|
than mutable arrays, since these arrays are shared across calls to the
|
||||||
|
API. If you were mutating them (e.g. calling `sort()`, `push()`, `splice()`, etc)
|
||||||
|
then your code will not longer compile. If you need to mutate the array, you
|
||||||
|
should now take a copy (e.g. by calling `slice()`) and mutate the copy.
|
||||||
|
* **common:** When passing a date-time formatted string to the `DatePipe` in a format that contains
|
||||||
|
fractions of a millisecond, the milliseconds will now always be rounded down rather than
|
||||||
|
to the nearest millisecond.
|
||||||
|
|
||||||
|
Most applications will not be affected by this change. If this is not the desired behaviour
|
||||||
|
then consider pre-processing the string to round the millisecond part before passing
|
||||||
|
it to the `DatePipe`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.2"></a>
|
<a name="10.1.2"></a>
|
||||||
## 10.1.2 (2020-09-16)
|
## 10.1.2 (2020-09-16)
|
||||||
|
|
||||||
@ -20,6 +119,24 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.1"></a>
|
||||||
|
# 11.0.0-next.1 (2020-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** compute source-mappings for localized strings ([#38645](https://github.com/angular/angular/issues/38645)) ([7e0b3fd](https://github.com/angular/angular/commit/7e0b3fd)), closes [#38588](https://github.com/angular/angular/issues/38588)
|
||||||
|
* **core:** remove CollectionChangeRecord symbol ([#38668](https://github.com/angular/angular/issues/38668)) ([fdea180](https://github.com/angular/angular/commit/fdea180))
|
||||||
|
* **router:** support lazy loading for empty path named outlets ([#38379](https://github.com/angular/angular/issues/38379)) ([926ffcd](https://github.com/angular/angular/commit/926ffcd)), closes [#12842](https://github.com/angular/angular/issues/12842)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** CollectionChangeRecord has been removed, use IterableChangeRecord
|
||||||
|
instead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.1"></a>
|
<a name="10.1.1"></a>
|
||||||
## 10.1.1 (2020-09-09)
|
## 10.1.1 (2020-09-09)
|
||||||
|
|
||||||
@ -45,6 +162,31 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="11.0.0-next.0"></a>
|
||||||
|
# 11.0.0-next.0 (2020-09-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **forms:** ensure to emit `statusChanges` on subsequent value update/validations ([#38354](https://github.com/angular/angular/issues/38354)) ([d9fea85](https://github.com/angular/angular/commit/d9fea85)), closes [#20424](https://github.com/angular/angular/issues/20424) [#14542](https://github.com/angular/angular/issues/14542)
|
||||||
|
* **service-worker:** fix condition to check for a cache-busted request ([#36847](https://github.com/angular/angular/issues/36847)) ([5be4edf](https://github.com/angular/angular/commit/5be4edf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **service-worker:** add `UnrecoverableStateError` ([#36847](https://github.com/angular/angular/issues/36847)) ([036a2fa](https://github.com/angular/angular/commit/036a2fa)), closes [#36539](https://github.com/angular/angular/issues/36539)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **forms:** Previously if FormControl, FormGroup and FormArray class instances had async validators
|
||||||
|
defined at initialization time, the status change event was not emitted once async validator
|
||||||
|
completed. After this change the status event is emitted into the `statusChanges` observable.
|
||||||
|
If your code relies on the old behavior, you can filter/ignore this additional status change
|
||||||
|
event.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="10.1.0"></a>
|
<a name="10.1.0"></a>
|
||||||
# 10.1.0 (2020-09-02)
|
# 10.1.0 (2020-09-02)
|
||||||
|
|
||||||
|
@ -21,11 +21,13 @@ import { ItemDirective } from './item.directive';
|
|||||||
ItemDirective
|
ItemDirective
|
||||||
],
|
],
|
||||||
// #enddocregion declarations
|
// #enddocregion declarations
|
||||||
|
// #docregion imports
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpClientModule
|
HttpClientModule
|
||||||
],
|
],
|
||||||
|
// #enddocregion imports
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* This example project is special in that it is not a cli app. To run tests appropriate for this
|
||||||
|
* project, the test command is overwritten in `aio/content/examples/observables/example-config.json`.
|
||||||
|
*
|
||||||
|
* This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs
|
||||||
|
* tests for this project.
|
||||||
|
*
|
||||||
|
* TODO: Fix our infrastructure/tooling, so that this hack is not necessary.
|
||||||
|
*/
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { docRegionChain, docRegionObservable, docRegionUnsubscribe } from './observables';
|
||||||
|
|
||||||
|
describe('observables', () => {
|
||||||
|
it('should print 2', (doneFn: DoneFn) => {
|
||||||
|
const consoleLogSpy = spyOn(console, 'log');
|
||||||
|
const observable = docRegionObservable(console);
|
||||||
|
observable.subscribe(() => {
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledWith(2);
|
||||||
|
doneFn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the subscription', () => {
|
||||||
|
const subscription = docRegionUnsubscribe();
|
||||||
|
expect(subscription.closed).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should chain an observable', (doneFn: DoneFn) => {
|
||||||
|
const observable = docRegionChain();
|
||||||
|
observable.subscribe(value => {
|
||||||
|
expect(value).toBe(4);
|
||||||
|
doneFn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,20 +1,37 @@
|
|||||||
import { map } from 'rxjs/operators';
|
// #docplaster
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export function docRegionObservable(console: Console) {
|
||||||
// #docregion observable
|
// #docregion observable
|
||||||
|
|
||||||
// declare a publishing operation
|
// declare a publishing operation
|
||||||
const observable = new Observable<number>(observer => {
|
const observable = new Observable<number>(observer => {
|
||||||
// Subscriber fn...
|
// Subscriber fn...
|
||||||
|
// #enddocregion observable
|
||||||
|
// The below code is used for unit testing only
|
||||||
|
observer.next(2);
|
||||||
|
// #docregion observable
|
||||||
});
|
});
|
||||||
|
|
||||||
// initiate execution
|
// initiate execution
|
||||||
observable.subscribe(() => {
|
observable.subscribe(value => {
|
||||||
// observer handles notifications
|
// observer handles notifications
|
||||||
|
// #enddocregion observable
|
||||||
|
// The below code is used for unit testing only
|
||||||
|
console.log(value);
|
||||||
|
// #docregion observable
|
||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion observable
|
// #enddocregion observable
|
||||||
|
return observable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionUnsubscribe() {
|
||||||
|
const observable = new Observable<number>(() => {
|
||||||
|
// Subscriber fn...
|
||||||
|
});
|
||||||
// #docregion unsubscribe
|
// #docregion unsubscribe
|
||||||
|
|
||||||
const subscription = observable.subscribe(() => {
|
const subscription = observable.subscribe(() => {
|
||||||
@ -24,17 +41,32 @@ const subscription = observable.subscribe(() => {
|
|||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
|
|
||||||
// #enddocregion unsubscribe
|
// #enddocregion unsubscribe
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
// #docregion error
|
export function docRegionError() {
|
||||||
|
const observable = new Observable<number>(() => {
|
||||||
observable.subscribe(() => {
|
// Subscriber fn...
|
||||||
throw Error('my error');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// #docregion error
|
||||||
|
observable.subscribe(() => {
|
||||||
|
throw new Error('my error');
|
||||||
|
});
|
||||||
// #enddocregion error
|
// #enddocregion error
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionChain() {
|
||||||
|
let observable = new Observable<number>(observer => {
|
||||||
|
// Subscriber fn...
|
||||||
|
observer.next(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
observable =
|
||||||
// #docregion chain
|
// #docregion chain
|
||||||
|
|
||||||
observable.pipe(map(v => 2 * v));
|
observable.pipe(map(v => 2 * v));
|
||||||
|
|
||||||
// #enddocregion chain
|
// #enddocregion chain
|
||||||
|
return observable;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import { docRegionError, docRegionPromise } from './promises';
|
||||||
|
|
||||||
|
describe('promises', () => {
|
||||||
|
it('should print 2', (doneFn: DoneFn) => {
|
||||||
|
const consoleLogSpy = spyOn(console, 'log');
|
||||||
|
const pr = docRegionPromise(console, 2);
|
||||||
|
pr.then((value) => {
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledWith(2);
|
||||||
|
expect(value).toBe(4);
|
||||||
|
doneFn();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error', (doneFn: DoneFn) => {
|
||||||
|
const promise = docRegionError();
|
||||||
|
promise
|
||||||
|
.then(() => {
|
||||||
|
throw new Error('Promise should be rejected.');
|
||||||
|
},
|
||||||
|
() => doneFn());
|
||||||
|
});
|
||||||
|
});
|
@ -1,25 +1,44 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
|
export function docRegionPromise(console: Console, inputValue: number) {
|
||||||
// #docregion promise
|
// #docregion promise
|
||||||
// initiate execution
|
// initiate execution
|
||||||
const promise = new Promise<number>((resolve, reject) => {
|
let promise = new Promise<number>((resolve, reject) => {
|
||||||
// Executer fn...
|
// Executer fn...
|
||||||
|
// #enddocregion promise
|
||||||
|
// The below is used in the unit tests.
|
||||||
|
resolve(inputValue);
|
||||||
|
// #docregion promise
|
||||||
});
|
});
|
||||||
|
// #enddocregion promise
|
||||||
|
promise =
|
||||||
|
// #docregion promise
|
||||||
promise.then(value => {
|
promise.then(value => {
|
||||||
// handle result here
|
// handle result here
|
||||||
});
|
|
||||||
|
|
||||||
// #enddocregion promise
|
// #enddocregion promise
|
||||||
|
// The below is used in the unit tests.
|
||||||
|
console.log(value);
|
||||||
|
return value;
|
||||||
|
// #docregion promise
|
||||||
|
});
|
||||||
|
// #enddocregion promise
|
||||||
|
promise =
|
||||||
// #docregion chain
|
// #docregion chain
|
||||||
|
|
||||||
promise.then(v => 2 * v);
|
promise.then(v => 2 * v);
|
||||||
|
|
||||||
// #enddocregion chain
|
// #enddocregion chain
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionError() {
|
||||||
|
let promise = Promise.resolve();
|
||||||
|
promise =
|
||||||
// #docregion error
|
// #docregion error
|
||||||
|
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
throw Error('my error');
|
throw new Error('my error');
|
||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion error
|
// #enddocregion error
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
// tslint:disable: no-output-native
|
// tslint:disable: no-output-native
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core';
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
55
aio/content/examples/observables/src/creating.spec.ts
Normal file
55
aio/content/examples/observables/src/creating.spec.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { docRegionFromEvent, docRegionSubscriber } from './creating';
|
||||||
|
|
||||||
|
describe('observables', () => {
|
||||||
|
it('should create an observable using the constructor', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionSubscriber(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(4);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3],
|
||||||
|
['Finished sequence'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should listen to input changes', () => {
|
||||||
|
let triggerInputChange;
|
||||||
|
const input = {
|
||||||
|
value: 'Test',
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'keydown') {
|
||||||
|
triggerInputChange = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEventListener'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = { getElementById: () => input };
|
||||||
|
docRegionFromEvent(document);
|
||||||
|
triggerInputChange({keyCode: 65});
|
||||||
|
expect(input.value).toBe('Test');
|
||||||
|
|
||||||
|
triggerInputChange({keyCode: 27});
|
||||||
|
expect(input.value).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call removeEventListener when unsubscribing', (doneFn: DoneFn) => {
|
||||||
|
const input = {
|
||||||
|
addEventListener: jasmine.createSpy('addEvent'),
|
||||||
|
removeEventListener: jasmine
|
||||||
|
.createSpy('removeEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'keydown') {
|
||||||
|
doneFn();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = { getElementById: () => input };
|
||||||
|
const subscription = docRegionFromEvent(document);
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,9 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export function docRegionSubscriber(console) {
|
||||||
// #docregion subscriber
|
// #docregion subscriber
|
||||||
|
|
||||||
// This function runs when subscribe() is called
|
// This function runs when subscribe() is called
|
||||||
function sequenceSubscriber(observer) {
|
function sequenceSubscriber(observer) {
|
||||||
// synchronously deliver 1, 2, and 3, then complete
|
// synchronously deliver 1, 2, and 3, then complete
|
||||||
@ -30,8 +31,8 @@ sequence.subscribe({
|
|||||||
// 2
|
// 2
|
||||||
// 3
|
// 3
|
||||||
// Finished sequence
|
// Finished sequence
|
||||||
|
|
||||||
// #enddocregion subscriber
|
// #enddocregion subscriber
|
||||||
|
}
|
||||||
|
|
||||||
// #docregion fromevent
|
// #docregion fromevent
|
||||||
|
|
||||||
@ -51,16 +52,18 @@ function fromEvent(target, eventName) {
|
|||||||
|
|
||||||
// #enddocregion fromevent
|
// #enddocregion fromevent
|
||||||
|
|
||||||
|
export function docRegionFromEvent(document) {
|
||||||
// #docregion fromevent_use
|
// #docregion fromevent_use
|
||||||
|
|
||||||
const ESC_KEY = 27;
|
const ESC_KEY = 27;
|
||||||
const nameInput = document.getElementById('name') as HTMLInputElement;
|
const nameInput = document.getElementById('name') as HTMLInputElement;
|
||||||
|
|
||||||
const subscription = fromEvent(nameInput, 'keydown')
|
const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => {
|
||||||
.subscribe((e: KeyboardEvent) => {
|
|
||||||
if (e.keyCode === ESC_KEY) {
|
if (e.keyCode === ESC_KEY) {
|
||||||
nameInput.value = '';
|
nameInput.value = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// #enddocregion fromevent_use
|
// #enddocregion fromevent_use
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
// #docregion
|
// #docregion
|
||||||
|
|
||||||
// Create an Observable that will start listening to geolocation updates
|
// Create an Observable that will start listening to geolocation updates
|
||||||
|
48
aio/content/examples/observables/src/multicasting.spec.ts
Normal file
48
aio/content/examples/observables/src/multicasting.spec.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { docRegionDelaySequence, docRegionMulticastSequence } from './multicasting';
|
||||||
|
|
||||||
|
describe('multicasting', () => {
|
||||||
|
let console;
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
console = {log: jasmine.createSpy('log')};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jasmine.clock().uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an observable and emit in sequence', () => {
|
||||||
|
docRegionDelaySequence(console);
|
||||||
|
jasmine.clock().tick(10000);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(12);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
[1],
|
||||||
|
['1st subscribe: 1'],
|
||||||
|
['2nd subscribe: 1'],
|
||||||
|
[2],
|
||||||
|
['1st subscribe: 2'],
|
||||||
|
['2nd subscribe: 2'],
|
||||||
|
[3],
|
||||||
|
['Finished sequence'],
|
||||||
|
['1st subscribe: 3'],
|
||||||
|
['1st sequence finished.'],
|
||||||
|
['2nd subscribe: 3'],
|
||||||
|
['2nd sequence finished.']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an observable and multicast the emissions', () => {
|
||||||
|
docRegionMulticastSequence(console);
|
||||||
|
jasmine.clock().tick(10000);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(7);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
['1st subscribe: 1'],
|
||||||
|
['1st subscribe: 2'],
|
||||||
|
['2nd subscribe: 2'],
|
||||||
|
['1st subscribe: 3'],
|
||||||
|
['2nd subscribe: 3'],
|
||||||
|
['1st sequence finished.'],
|
||||||
|
['2nd sequence finished.']
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,9 @@
|
|||||||
|
// #docplaster
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export function docRegionDelaySequence(console) {
|
||||||
// #docregion delay_sequence
|
// #docregion delay_sequence
|
||||||
|
|
||||||
function sequenceSubscriber(observer) {
|
function sequenceSubscriber(observer) {
|
||||||
const seq = [1, 2, 3];
|
const seq = [1, 2, 3];
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
@ -23,9 +24,11 @@ function sequenceSubscriber(observer) {
|
|||||||
doInSequence(seq, 0);
|
doInSequence(seq, 0);
|
||||||
|
|
||||||
// Unsubscribe should clear the timeout to stop execution
|
// Unsubscribe should clear the timeout to stop execution
|
||||||
return {unsubscribe() {
|
return {
|
||||||
|
unsubscribe() {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
}};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Observable that will deliver the above sequence
|
// Create a new Observable that will deliver the above sequence
|
||||||
@ -71,9 +74,10 @@ setTimeout(() => {
|
|||||||
// (at 3.5 seconds): 2nd sequence finished
|
// (at 3.5 seconds): 2nd sequence finished
|
||||||
|
|
||||||
// #enddocregion subscribe_twice
|
// #enddocregion subscribe_twice
|
||||||
|
}
|
||||||
|
|
||||||
|
export function docRegionMulticastSequence(console) {
|
||||||
// #docregion multicast_sequence
|
// #docregion multicast_sequence
|
||||||
|
|
||||||
function multicastSequenceSubscriber() {
|
function multicastSequenceSubscriber() {
|
||||||
const seq = [1, 2, 3];
|
const seq = [1, 2, 3];
|
||||||
// Keep track of each observer (one for every active subscription)
|
// Keep track of each observer (one for every active subscription)
|
||||||
@ -84,7 +88,7 @@ function multicastSequenceSubscriber() {
|
|||||||
|
|
||||||
// Return the subscriber function (runs when subscribe()
|
// Return the subscriber function (runs when subscribe()
|
||||||
// function is invoked)
|
// function is invoked)
|
||||||
return (observer) => {
|
return observer => {
|
||||||
observers.push(observer);
|
observers.push(observer);
|
||||||
// When this is the first subscription, start the sequence
|
// When this is the first subscription, start the sequence
|
||||||
if (observers.length === 1) {
|
if (observers.length === 1) {
|
||||||
@ -153,3 +157,4 @@ setTimeout(() => {
|
|||||||
// (at 3 seconds): 2nd sequence finished
|
// (at 3 seconds): 2nd sequence finished
|
||||||
|
|
||||||
// #enddocregion multicast_sequence
|
// #enddocregion multicast_sequence
|
||||||
|
}
|
||||||
|
19
aio/content/examples/observables/src/subscribing.spec.ts
Normal file
19
aio/content/examples/observables/src/subscribing.spec.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { docRegionObserver } from './subscribing';
|
||||||
|
|
||||||
|
describe('subscribing', () => {
|
||||||
|
it('should subscribe and emit', () => {
|
||||||
|
const console = {log: jasmine.createSpy('log')};
|
||||||
|
docRegionObserver(console);
|
||||||
|
expect(console.log).toHaveBeenCalledTimes(8);
|
||||||
|
expect(console.log.calls.allArgs()).toEqual([
|
||||||
|
['Observer got a next value: 1'],
|
||||||
|
['Observer got a next value: 2'],
|
||||||
|
['Observer got a next value: 3'],
|
||||||
|
['Observer got a complete notification'],
|
||||||
|
['Observer got a next value: 1'],
|
||||||
|
['Observer got a next value: 2'],
|
||||||
|
['Observer got a next value: 3'],
|
||||||
|
['Observer got a complete notification'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,7 @@
|
|||||||
|
// #docplaster
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
import { Observable, of } from 'rxjs';
|
export function docRegionObserver(console) {
|
||||||
|
|
||||||
// #docregion observer
|
// #docregion observer
|
||||||
|
|
||||||
// Create simple observable that emits three values
|
// Create simple observable that emits three values
|
||||||
@ -15,6 +16,7 @@ const myObserver = {
|
|||||||
|
|
||||||
// Execute with the observer object
|
// Execute with the observer object
|
||||||
myObservable.subscribe(myObserver);
|
myObservable.subscribe(myObserver);
|
||||||
|
|
||||||
// Logs:
|
// Logs:
|
||||||
// Observer got a next value: 1
|
// Observer got a next value: 1
|
||||||
// Observer got a next value: 2
|
// Observer got a next value: 2
|
||||||
@ -30,3 +32,4 @@ myObservable.subscribe(
|
|||||||
() => console.log('Observer got a complete notification')
|
() => console.log('Observer got a complete notification')
|
||||||
);
|
);
|
||||||
// #enddocregion sub_fn
|
// #enddocregion sub_fn
|
||||||
|
}
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"cmd": "yarn",
|
"cmd": "yarn",
|
||||||
"args": [ "tsc", "--project", "./tsconfig.app.json" ]
|
"args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmd": "yarn",
|
||||||
|
"args": ["jasmine", "out-tsc/**/*.spec.js"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
|
// TODO: Add unit tests for this file.
|
||||||
import { pipe, range, timer, zip } from 'rxjs';
|
import { pipe, range, timer, zip } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { retryWhen, map, mergeMap } from 'rxjs/operators';
|
import { retryWhen, map, mergeMap } from 'rxjs/operators';
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
import { of } from 'rxjs';
|
||||||
|
import { docRegionTypeahead } from './typeahead';
|
||||||
|
|
||||||
|
describe('typeahead', () => {
|
||||||
|
let document;
|
||||||
|
let ajax;
|
||||||
|
let triggertInputChange;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
const input = {
|
||||||
|
addEventListener: jasmine
|
||||||
|
.createSpy('addEvent')
|
||||||
|
.and.callFake((eventName: string, cb: (e) => void) => {
|
||||||
|
if (eventName === 'input') {
|
||||||
|
triggertInputChange = cb;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
removeEventListener: jasmine.createSpy('removeEvent'),
|
||||||
|
};
|
||||||
|
|
||||||
|
document = { getElementById: (id: string) => input };
|
||||||
|
ajax = jasmine.createSpy('ajax').and.callFake((url: string) => of('foo bar'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jasmine.clock().uninstall();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make an ajax call to the corrent endpoint', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call, when the input length < 3', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: '' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
triggertInputChange({ target: { value: 'fo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call for intermediate values when debouncing', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'bar' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'baz' } });
|
||||||
|
jasmine.clock().tick(9);
|
||||||
|
triggertInputChange({ target: { value: 'qux' } });
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
jasmine.clock().tick(10);
|
||||||
|
expect(ajax).toHaveBeenCalledTimes(1);
|
||||||
|
expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=qux');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not make an ajax call, when the input value has not changed', () => {
|
||||||
|
docRegionTypeahead(document, ajax);
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).toHaveBeenCalled();
|
||||||
|
ajax.calls.reset();
|
||||||
|
triggertInputChange({ target: { value: 'foo' } });
|
||||||
|
jasmine.clock().tick(11);
|
||||||
|
expect(ajax).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Because of how the code is merged together using the doc regions,
|
||||||
|
we need to indent the imports with the function below.
|
||||||
|
*/
|
||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
import { fromEvent } from 'rxjs';
|
import { fromEvent } from 'rxjs';
|
||||||
import { ajax } from 'rxjs/ajax';
|
import { ajax } from 'rxjs/ajax';
|
||||||
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
/* tslint:disable:no-shadowed-variable */
|
||||||
|
/* tslint:disable:align */
|
||||||
|
export function docRegionTypeahead(document, ajax) {
|
||||||
|
// #docregion
|
||||||
const searchBox = document.getElementById('search-box');
|
const searchBox = document.getElementById('search-box');
|
||||||
|
|
||||||
const typeahead = fromEvent(searchBox, 'input').pipe(
|
const typeahead = fromEvent(searchBox, 'input').pipe(
|
||||||
@ -10,9 +20,13 @@ const typeahead = fromEvent(searchBox, 'input').pipe(
|
|||||||
filter(text => text.length > 2),
|
filter(text => text.length > 2),
|
||||||
debounceTime(10),
|
debounceTime(10),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap(() => ajax('/api/endpoint'))
|
switchMap(searchTerm => ajax(`/api/endpoint?search=${searchTerm}`))
|
||||||
);
|
);
|
||||||
|
|
||||||
typeahead.subscribe(data => {
|
typeahead.subscribe(data => {
|
||||||
// Handle the data from the API
|
// Handle the data from the API
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// #enddocregion
|
||||||
|
return typeahead;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { SwUpdate } from '@angular/service-worker';
|
||||||
|
|
||||||
|
function notifyUser(message: string): void { }
|
||||||
|
|
||||||
|
// #docregion sw-unrecoverable-state
|
||||||
|
@Injectable()
|
||||||
|
export class HandleUnrecoverableStateService {
|
||||||
|
constructor(updates: SwUpdate) {
|
||||||
|
updates.unrecoverable.subscribe(event => {
|
||||||
|
notifyUser(
|
||||||
|
`An error occurred that we cannot recover from:\n${event.reason}\n\n` +
|
||||||
|
'Please reload the page.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion sw-unrecoverable-state
|
@ -28,7 +28,7 @@ import {
|
|||||||
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
import { addMatchers, newEvent, click } from '../../testing';
|
import { addMatchers, click } from '../../testing';
|
||||||
|
|
||||||
export class NotProvided extends ValueService { /* example below */ }
|
export class NotProvided extends ValueService { /* example below */ }
|
||||||
beforeEach(addMatchers);
|
beforeEach(addMatchers);
|
||||||
@ -274,8 +274,10 @@ describe('demo (with TestBed):', () => {
|
|||||||
expect(comp.name).toBe(expectedOrigName,
|
expect(comp.name).toBe(expectedOrigName,
|
||||||
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait while ngModel pushes input.box value to comp.name
|
// then wait while ngModel pushes input.box value to comp.name
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
input.dispatchEvent(new Event('input'));
|
input.dispatchEvent(new Event('input'));
|
||||||
return fixture.whenStable();
|
return fixture.whenStable();
|
||||||
})
|
})
|
||||||
@ -312,8 +314,10 @@ describe('demo (with TestBed):', () => {
|
|||||||
expect(comp.name).toBe(expectedOrigName,
|
expect(comp.name).toBe(expectedOrigName,
|
||||||
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
`comp.name should still be ${expectedOrigName} after value change, before binding happens`);
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait a tick while ngModel pushes input.box value to comp.name
|
// then wait a tick while ngModel pushes input.box value to comp.name
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
input.dispatchEvent(new Event('input'));
|
input.dispatchEvent(new Event('input'));
|
||||||
tick();
|
tick();
|
||||||
expect(comp.name).toBe(expectedNewName,
|
expect(comp.name).toBe(expectedNewName,
|
||||||
@ -335,9 +339,11 @@ describe('demo (with TestBed):', () => {
|
|||||||
// simulate user entering new name in input
|
// simulate user entering new name in input
|
||||||
input.value = inputText;
|
input.value = inputText;
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// then wait a tick while ngModel pushes input.box value to comp.text
|
// then wait a tick while ngModel pushes input.box value to comp.text
|
||||||
// and Angular updates the output span
|
// and Angular updates the output span
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
input.dispatchEvent(new Event('input'));
|
input.dispatchEvent(new Event('input'));
|
||||||
tick();
|
tick();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActivatedRoute, ActivatedRouteStub, asyncData, click, newEvent
|
ActivatedRoute, ActivatedRouteStub, asyncData, click
|
||||||
} from '../../testing';
|
} from '../../testing';
|
||||||
|
|
||||||
import { Hero } from '../model/hero';
|
import { Hero } from '../model/hero';
|
||||||
@ -99,6 +99,9 @@ function overrideSetup() {
|
|||||||
const newName = 'New Name';
|
const newName = 'New Name';
|
||||||
|
|
||||||
page.nameInput.value = newName;
|
page.nameInput.value = newName;
|
||||||
|
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
page.nameInput.dispatchEvent(new Event('input')); // tell Angular
|
page.nameInput.dispatchEvent(new Event('input')); // tell Angular
|
||||||
|
|
||||||
expect(component.hero.name).toBe(newName, 'component hero has new name');
|
expect(component.hero.name).toBe(newName, 'component hero has new name');
|
||||||
@ -197,8 +200,9 @@ function heroModuleSetup() {
|
|||||||
// simulate user entering a new name into the input box
|
// simulate user entering a new name into the input box
|
||||||
nameInput.value = 'quick BROWN fOx';
|
nameInput.value = 'quick BROWN fOx';
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular learns of input value change.
|
// Dispatch a DOM event so that Angular learns of input value change.
|
||||||
// use newEvent utility function (not provided by Angular) for better browser compatibility
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
nameInput.dispatchEvent(new Event('input'));
|
nameInput.dispatchEvent(new Event('input'));
|
||||||
|
|
||||||
// Tell Angular to update the display binding through the title pipe
|
// Tell Angular to update the display binding through the title pipe
|
||||||
|
@ -6,7 +6,7 @@ import { DebugElement } from '@angular/core';
|
|||||||
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { addMatchers, newEvent } from '../../testing';
|
import { addMatchers } from '../../testing';
|
||||||
import { HeroService } from '../model/hero.service';
|
import { HeroService } from '../model/hero.service';
|
||||||
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
import { getTestHeroes, TestHeroService } from '../model/testing/test-hero.service';
|
||||||
|
|
||||||
@ -53,6 +53,9 @@ describe('HeroListComponent', () => {
|
|||||||
it('should select hero on click', fakeAsync(() => {
|
it('should select hero on click', fakeAsync(() => {
|
||||||
const expectedHero = HEROES[1];
|
const expectedHero = HEROES[1];
|
||||||
const li = page.heroRows[1];
|
const li = page.heroRows[1];
|
||||||
|
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
li.dispatchEvent(new Event('click'));
|
li.dispatchEvent(new Event('click'));
|
||||||
tick();
|
tick();
|
||||||
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
// `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
|
||||||
@ -62,6 +65,9 @@ describe('HeroListComponent', () => {
|
|||||||
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
it('should navigate to selected hero detail on click', fakeAsync(() => {
|
||||||
const expectedHero = HEROES[1];
|
const expectedHero = HEROES[1];
|
||||||
const li = page.heroRows[1];
|
const li = page.heroRows[1];
|
||||||
|
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
li.dispatchEvent(new Event('click'));
|
li.dispatchEvent(new Event('click'));
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { HighlightDirective } from './highlight.directive';
|
import { HighlightDirective } from './highlight.directive';
|
||||||
import { newEvent } from '../../testing';
|
|
||||||
|
|
||||||
// #docregion test-component
|
// #docregion test-component
|
||||||
@Component({
|
@Component({
|
||||||
@ -59,8 +58,11 @@ describe('HighlightDirective', () => {
|
|||||||
const input = des[2].nativeElement as HTMLInputElement;
|
const input = des[2].nativeElement as HTMLInputElement;
|
||||||
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
|
||||||
|
|
||||||
// dispatch a DOM event so that Angular responds to the input value change.
|
|
||||||
input.value = 'green';
|
input.value = 'green';
|
||||||
|
|
||||||
|
// Dispatch a DOM event so that Angular responds to the input value change.
|
||||||
|
// In older browsers, such as IE, you might need a CustomEvent instead. See
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
|
||||||
input.dispatchEvent(new Event('input'));
|
input.dispatchEvent(new Event('input'));
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
@ -14,18 +14,6 @@ export function advance(f: ComponentFixture<any>): void {
|
|||||||
f.detectChanges();
|
f.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create custom DOM event the old fashioned way
|
|
||||||
*
|
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent
|
|
||||||
* Although officially deprecated, some browsers (phantom) don't accept the preferred "new Event(eventName)"
|
|
||||||
*/
|
|
||||||
export function newEvent(eventName: string, bubbles = false, cancelable = false) {
|
|
||||||
const evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
|
|
||||||
evt.initCustomEvent(eventName, bubbles, cancelable, null);
|
|
||||||
return evt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||||
// #docregion click-event
|
// #docregion click-event
|
||||||
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
|
||||||
|
@ -18,8 +18,6 @@ When you use the [Angular CLI](cli) command `ng new` to generate an app, the def
|
|||||||
/* JavaScript imports */
|
/* JavaScript imports */
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
@ -29,9 +27,7 @@ import { AppComponent } from './app.component';
|
|||||||
AppComponent
|
AppComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule
|
||||||
FormsModule,
|
|
||||||
HttpClientModule
|
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
@ -120,9 +116,6 @@ Now you could use your `ItemDirective` in a component. This example uses `AppMod
|
|||||||
|
|
||||||
Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean.
|
Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a imports}
|
{@a imports}
|
||||||
|
|
||||||
## The `imports` array
|
## The `imports` array
|
||||||
@ -130,6 +123,12 @@ Remember, components, directives, and pipes belong to one module only. You only
|
|||||||
The module's `imports` array appears exclusively in the `@NgModule` metadata object.
|
The module's `imports` array appears exclusively in the `@NgModule` metadata object.
|
||||||
It tells Angular about other NgModules that this particular module needs to function properly.
|
It tells Angular about other NgModules that this particular module needs to function properly.
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="bootstrapping/src/app/app.module.ts"
|
||||||
|
region="imports"
|
||||||
|
header="src/app/app.module.ts (excerpt)">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
This list of modules are those that export components, directives, or pipes
|
This list of modules are those that export components, directives, or pipes
|
||||||
that component templates in this module reference. In this case, the component is
|
that component templates in this module reference. In this case, the component is
|
||||||
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
|
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
|
||||||
@ -138,6 +137,8 @@ A component template can reference another component, directive,
|
|||||||
or pipe when the referenced class is declared in this module or
|
or pipe when the referenced class is declared in this module or
|
||||||
the class was imported from another module.
|
the class was imported from another module.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a bootstrap-array}
|
{@a bootstrap-array}
|
||||||
|
|
||||||
## The `providers` array
|
## The `providers` array
|
||||||
|
@ -38,7 +38,6 @@ v9 - v12
|
|||||||
| `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 |
|
| `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 |
|
||||||
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
||||||
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
||||||
| `@angular/core` | [`CollectionChangeRecord`](#core) | <!--v7--> v11 |
|
|
||||||
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
||||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
||||||
@ -89,7 +88,6 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
|
|
||||||
| API | Replacement | Deprecation announced | Notes |
|
| API | Replacement | Deprecation announced | Notes |
|
||||||
| --- | ----------- | --------------------- | ----- |
|
| --- | ----------- | --------------------- | ----- |
|
||||||
| [`CollectionChangeRecord`](api/core/CollectionChangeRecord) | [`IterableChangeRecord`](api/core/IterableChangeRecord) | v4 | none |
|
|
||||||
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
| [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. |
|
||||||
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) |
|
||||||
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
| [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none |
|
||||||
|
@ -67,6 +67,33 @@ Therefore, it is recommended to reload the page once the promise returned by `ac
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
### Handling an unrecoverable state
|
||||||
|
|
||||||
|
In some cases, the version of the app used by the service worker to serve a client might be in a broken state that cannot be recovered from without a full page reload.
|
||||||
|
|
||||||
|
For example, imagine the following scenario:
|
||||||
|
- A user opens the app for the first time and the service worker caches the latest version of the app.
|
||||||
|
Let's assume the app's cached assets include `index.html`, `main.<main-hash-1>.js` and `lazy-chunk.<lazy-hash-1>.js`.
|
||||||
|
- The user closes the app and does not open it for a while.
|
||||||
|
- After some time, a new version of the app is deployed to the server.
|
||||||
|
This newer version includes the files `index.html`, `main.<main-hash-2>.js` and `lazy-chunk.<lazy-hash-2>.js` (note that the hashes are different now, because the content of the files has changed).
|
||||||
|
The old version is no longer available on the server.
|
||||||
|
- In the meantime, the user's browser decides to evict `lazy-chunk.<lazy-hash-1>.js` from its cache.
|
||||||
|
Browsers may decide to evict specific (or all) resources from a cache in order to reclaim disk space.
|
||||||
|
- The user opens the app again.
|
||||||
|
The service worker serves the latest version known to it at this point, namely the old version (`index.html` and `main.<main-hash-1>.js`).
|
||||||
|
- At some later point, the app requests the lazy bundle, `lazy-chunk.<lazy-hash-1>.js`.
|
||||||
|
- The service worker is unable to find the asset in the cache (remember that the browser evicted it).
|
||||||
|
Nor is it able to retrieve it from the server (since the server now only has `lazy-chunk.<lazy-hash-2>.js` from the newer version).
|
||||||
|
|
||||||
|
In the above scenario, the service worker is not able to serve an asset that would normally be cached.
|
||||||
|
That particular app version is broken and there is no way to fix the state of the client without reloading the page.
|
||||||
|
In such cases, the service worker notifies the client by sending an `UnrecoverableStateEvent` event.
|
||||||
|
You can subscribe to `SwUpdate#unrecoverable` to be notified and handle these errors.
|
||||||
|
|
||||||
|
<code-example path="service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts" header="handle-unrecoverable-state.service.ts" region="sw-unrecoverable-state"></code-example>
|
||||||
|
|
||||||
|
|
||||||
## More on Angular service workers
|
## More on Angular service workers
|
||||||
|
|
||||||
You may also be interested in the following:
|
You may also be interested in the following:
|
||||||
|
@ -267,6 +267,12 @@ By default, these criteria are:
|
|||||||
1. The URL must not contain a file extension (i.e. a `.`) in the last path segment.
|
1. The URL must not contain a file extension (i.e. a `.`) in the last path segment.
|
||||||
2. The URL must not contain `__`.
|
2. The URL must not contain `__`.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
To configure whether navigation requests are sent through to the network or not, see the [navigationRequestStrategy](#navigation-request-strategy) section.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
### Matching navigation request URLs
|
### Matching navigation request URLs
|
||||||
|
|
||||||
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules. For example, you may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server.
|
While these default criteria are fine in most cases, it is sometimes desirable to configure different rules. For example, you may want to ignore specific routes (that are not part of the Angular app) and pass them through to the server.
|
||||||
@ -285,3 +291,32 @@ If the field is omitted, it defaults to:
|
|||||||
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
|
'!/**/*__*/**', // Exclude URLs containing `__` in any other segment.
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
{@a navigation-request-strategy}
|
||||||
|
|
||||||
|
## `navigationRequestStrategy`
|
||||||
|
|
||||||
|
This optional property enables you to configure how the service worker handles navigation requests:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"navigationRequestStrategy": "freshness"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible values:
|
||||||
|
|
||||||
|
- `'performance'`: The default setting. Serves the specified [index file](#index-file), which is typically cached.
|
||||||
|
- `'freshness'`: Passes the requests through to the network and falls back to the `performance` behavior when offline.
|
||||||
|
This value is useful when the server redirects the navigation requests elsewhere using an HTTP redirect (3xx status code).
|
||||||
|
Reasons for using this value include:
|
||||||
|
- Redirecting to an authentication website when authentication is not handled by the application.
|
||||||
|
- Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign.
|
||||||
|
- Redirecting to a different website, such as a server-status page, while a page is temporarily down.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
The `freshness` strategy usually results in more requests sent to the server, which can increase response latency.
|
||||||
|
It is recommended that you use the default performance strategy whenever possible.
|
||||||
|
|
||||||
|
</div>
|
@ -324,5 +324,5 @@ These techniques are useful for small-scale demonstrations, but they
|
|||||||
quickly become verbose and clumsy when handling large amounts of user input.
|
quickly become verbose and clumsy when handling large amounts of user input.
|
||||||
Two-way data binding is a more elegant and compact way to move
|
Two-way data binding is a more elegant and compact way to move
|
||||||
values between data entry fields and model properties.
|
values between data entry fields and model properties.
|
||||||
The next page, `Forms`, explains how to write
|
The [`Forms`](guide/forms-overview) page explains how to write
|
||||||
two-way bindings with `NgModel`.
|
two-way bindings with `NgModel`.
|
||||||
|
@ -32,7 +32,7 @@ To do this:
|
|||||||
|
|
||||||
1. Create a `typings.d.ts` file in your `src/` folder. This file is automatically included as global type definition.
|
1. Create a `typings.d.ts` file in your `src/` folder. This file is automatically included as global type definition.
|
||||||
|
|
||||||
2. Add the following code in `src/typings.d.ts`.
|
2. Add the following code in `src/typings.d.ts`:
|
||||||
|
|
||||||
```
|
```
|
||||||
declare module 'host' {
|
declare module 'host' {
|
||||||
@ -45,7 +45,7 @@ declare module 'host' {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
3. In the component or file that uses the library, add the following code.
|
3. In the component or file that uses the library, add the following code:
|
||||||
|
|
||||||
```
|
```
|
||||||
import * as host from 'host';
|
import * as host from 'host';
|
||||||
@ -129,7 +129,7 @@ interface JQuery {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If don't add the interface for the script-defined extension, your IDE shows an error:
|
If you don't add the interface for the script-defined extension, your IDE shows an error:
|
||||||
|
|
||||||
```
|
```
|
||||||
[TS][Error] Property 'myPlugin' does not exist on type 'JQuery'
|
[TS][Error] Property 'myPlugin' does not exist on type 'JQuery'
|
||||||
|
@ -53,6 +53,9 @@
|
|||||||
},
|
},
|
||||||
"kyliau": {
|
"kyliau": {
|
||||||
"name": "Keen Yee Liau",
|
"name": "Keen Yee Liau",
|
||||||
|
"twitter": "liauky",
|
||||||
|
"website": "https://github.com/kyliau",
|
||||||
|
"bio": "Keen works on language service and CLI. He also maintains Karma and Protractor.",
|
||||||
"groups": ["Angular"],
|
"groups": ["Angular"],
|
||||||
"lead": "igorminar",
|
"lead": "igorminar",
|
||||||
"picture": "kyliau.jpg"
|
"picture": "kyliau.jpg"
|
||||||
|
@ -3,162 +3,5 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<article class="events-container">
|
<article class="events-container">
|
||||||
<p>Where we'll be presenting:</p>
|
<aio-events></aio-events>
|
||||||
<table class="is-full-width">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Event</th>
|
|
||||||
<th>Location</th>
|
|
||||||
<th>Date</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p>Where we already presented:</p>
|
|
||||||
<table class="is-full-width">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Event</th>
|
|
||||||
<th>Location</th>
|
|
||||||
<th>Date</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<!-- ng-vikings 2020 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
|
||||||
<td>Oslo, Norway</td>
|
|
||||||
<td>May 25-26 conference, 27 workshops, 2020</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ng-conf 2020 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
|
||||||
<td>Salt Lake City, Utah</td>
|
|
||||||
<td>April 1-3, 2020</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngIndia 2020 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.ng-ind.com/" title="ngIndia">ngIndia</a></th>
|
|
||||||
<td>Delhi, India</td>
|
|
||||||
<td>Feb 29, 2020</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ReactiveConf 2019 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
|
||||||
<td>Prague, Czech Republic</td>
|
|
||||||
<td>October 30 - November 1, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- NG Rome 2019-->
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a href="https://ngrome.io" title="NG Rome MMXIX - The Italian Angular Conference">NG Rome MMXIX</a>
|
|
||||||
</th>
|
|
||||||
<td>Rome, Italy</td>
|
|
||||||
<td>Oct 6th workshops, 7th conference, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- AngularConnect 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral"
|
|
||||||
title="AngularConnect">AngularConnect</a></th>
|
|
||||||
<td>London, UK</td>
|
|
||||||
<td>September 19-20, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- NG-DE 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-de.org/" title="NG-DE">NG-DE</a></th>
|
|
||||||
<td>Berlin, Germany</td>
|
|
||||||
<td>August 29th workshops, 30-31 conference, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngJapan-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngjapan.org" title="ng-japan">ng-japan</a></th>
|
|
||||||
<td>Tokyo, Japan</td>
|
|
||||||
<td>July 13, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngVikings 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
|
||||||
<td>Copenhagen, Denmark</td>
|
|
||||||
<td>May 26 (workshops), 27-28 (conference), 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ng-conf 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
|
||||||
<td>Salt Lake City, Utah</td>
|
|
||||||
<td>May 1-3, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ng-India 2019-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.ng-ind.com/" title="ng-India">ng-India</a></th>
|
|
||||||
<td>Gurgaon, India</td>
|
|
||||||
<td>February 23, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngAtlanta 2019 -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ng-atl.org/" title="ngAtlanta">ngAtlanta</a></th>
|
|
||||||
<td>Atlanta, Georgia</td>
|
|
||||||
<td>January 9-12, 2019</td>
|
|
||||||
</tr>
|
|
||||||
<!-- AngularConnect-->
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a href="https://past.angularconnect.com/2018" title="AngularConnect">AngularConnect</a>
|
|
||||||
</th>
|
|
||||||
<td>London, United Kingdom</td>
|
|
||||||
<td>November 5-7, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ReactiveConf -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
|
||||||
<td>Prague, Czech Republic</td>
|
|
||||||
<td>October 29-31, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- AngularMix -->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
|
||||||
<td>Orlando, Florida</td>
|
|
||||||
<td>October 10-12, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- Angular Conf Australia-->
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<a href="https://www.angularconf.com.au/" title="Angular Conf Australia">Angular Conf Australia</a>
|
|
||||||
</th>
|
|
||||||
<td>Melbourne, Australia</td>
|
|
||||||
<td>Jun 22, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngJapan-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngjapan.org/en.html" title="ng-japan">ng-japan</a></th>
|
|
||||||
<td>Tokyo, Japan</td>
|
|
||||||
<td>Jun 16, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- WeRDevs-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
|
|
||||||
<td>Vienna, Austria</td>
|
|
||||||
<td>May 16-18, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngconf 2018-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
|
||||||
<td>Salt Lake City, Utah</td>
|
|
||||||
<td>April 18-20, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngVikings-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
|
||||||
<td>Helsinki, Finland</td>
|
|
||||||
<td>March 1-2, 2018</td>
|
|
||||||
</tr>
|
|
||||||
<!-- ngAtlanta-->
|
|
||||||
<tr>
|
|
||||||
<th><a href="http://ng-atl.org/" title="ngAtlanta">ngAtlanta</a></th>
|
|
||||||
<td>Atlanta, Georgia</td>
|
|
||||||
<td>January 30, 2018</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</article>
|
</article>
|
||||||
|
236
aio/content/marketing/events.json
Normal file
236
aio/content/marketing/events.json
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "ng-china",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://ng-china.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-11-21",
|
||||||
|
"end": "2020-11-22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EnterpriseNG",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://www.ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-11-19",
|
||||||
|
"end": "2020-11-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngrome",
|
||||||
|
"location": "Online",
|
||||||
|
"linkUrl": "https://ngrome.io/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-10-20",
|
||||||
|
"end": "2020-10-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngVikings",
|
||||||
|
"location": "Oslo, Norway",
|
||||||
|
"linkUrl": "https://ngvikings.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-05-25",
|
||||||
|
"end": "2020-05-26"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2020-05-27",
|
||||||
|
"end": "2020-05-27"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-conf",
|
||||||
|
"location": "Salt Lake City, Utah",
|
||||||
|
"linkUrl": "https://ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-04-01",
|
||||||
|
"end": "2020-04-03"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngIndia",
|
||||||
|
"location": "Delhi, India",
|
||||||
|
"linkUrl": "https://www.ng-ind.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2020-02-29",
|
||||||
|
"end": "2020-02-29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ReactiveConf",
|
||||||
|
"location": "Prague, Czech Republic",
|
||||||
|
"linkUrl": "https://reactiveconf.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-10-30",
|
||||||
|
"end": "2019-11-01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NG Rome MMXIX",
|
||||||
|
"location": "Rome, Italy",
|
||||||
|
"linkUrl": "https://ngrome.io",
|
||||||
|
"tooltip": "NG Rome MMXIX - The Italian Angular Conference",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-10-07",
|
||||||
|
"end": "2019-10-07"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2019-10-06",
|
||||||
|
"end": "2019-10-06"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AngularConnect",
|
||||||
|
"location": "London, UK",
|
||||||
|
"linkUrl": "https://www.angularconnect.com/?utm_source=angular.io&utm_medium=referral",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-09-19",
|
||||||
|
"end": "2019-09-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "NG-DE",
|
||||||
|
"location": "Berlin, Germany",
|
||||||
|
"linkUrl": "https://ng-de.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-08-30",
|
||||||
|
"end": "2019-08-31"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2019-08-29",
|
||||||
|
"end": "2019-08-29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-japan",
|
||||||
|
"location": "Tokyo, Japan",
|
||||||
|
"linkUrl": "https://ngjapan.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-07-13",
|
||||||
|
"end": "2019-07-13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngVikings",
|
||||||
|
"location": "Copenhagen, Denmark",
|
||||||
|
"linkUrl": "https://ngvikings.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-05-27",
|
||||||
|
"end": "2019-05-28"
|
||||||
|
},
|
||||||
|
"workshopsDate": {
|
||||||
|
"start": "2019-05-26",
|
||||||
|
"end": "2019-05-26"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-conf",
|
||||||
|
"location": "Salt Lake City, Utah",
|
||||||
|
"linkUrl": "https://ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-05-01",
|
||||||
|
"end": "2019-05-03"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-India",
|
||||||
|
"location": "Gurgaon, India",
|
||||||
|
"linkUrl": "https://www.ng-ind.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-02-23",
|
||||||
|
"end": "2019-02-23"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngAtlanta",
|
||||||
|
"location": "Atlanta, Georgia",
|
||||||
|
"linkUrl": "https://ng-atl.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2019-01-09",
|
||||||
|
"end": "2019-01-12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AngularConnect",
|
||||||
|
"location": "London, United Kingdom",
|
||||||
|
"linkUrl": "https://past.angularconnect.com/2018",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-11-05",
|
||||||
|
"end": "2018-11-07"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ReactiveConf",
|
||||||
|
"location": "Prague, Czech Republic",
|
||||||
|
"linkUrl": "https://reactiveconf.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-10-29",
|
||||||
|
"end": "2018-10-31"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AngularMix",
|
||||||
|
"location": "Orlando, Florida",
|
||||||
|
"linkUrl": "https://angularmix.com/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-10-10",
|
||||||
|
"end": "2018-10-12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Angular Conf Australia",
|
||||||
|
"location": "Melbourne, Australia",
|
||||||
|
"linkUrl": "https://www.angularconf.com.au/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-06-22",
|
||||||
|
"end": "2018-06-22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-japan",
|
||||||
|
"location": "Tokyo, Japan",
|
||||||
|
"linkUrl": "https://ngjapan.org/en.html",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-06-16",
|
||||||
|
"end": "2018-06-16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WeAreDevelopers",
|
||||||
|
"location": "Vienna, Austria",
|
||||||
|
"linkUrl": "https://www.wearedevelopers.com/",
|
||||||
|
"tooltip": "WeAreDevs",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-05-16",
|
||||||
|
"end": "2018-05-18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ng-conf",
|
||||||
|
"location": "Salt Lake City, Utah",
|
||||||
|
"linkUrl": "https://ng-conf.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-04-18",
|
||||||
|
"end": "2018-04-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngVikings",
|
||||||
|
"location": "Helsinki, Finland",
|
||||||
|
"linkUrl": "https://ngvikings.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-03-01",
|
||||||
|
"end": "2018-03-02"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ngAtlanta",
|
||||||
|
"location": "Atlanta, Georgia",
|
||||||
|
"linkUrl": "https://ng-atl.org/",
|
||||||
|
"date": {
|
||||||
|
"start": "2018-01-30",
|
||||||
|
"end": "2018-01-30"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@ -61,27 +61,27 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"url": "start",
|
"url": "start",
|
||||||
"title": "A Sample App",
|
"title": "Getting started",
|
||||||
"tooltip": "Take a look at Angular's component model, template syntax, and component communication."
|
"tooltip": "Take a look at Angular's component model, template syntax, and component communication."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-routing",
|
"url": "start/start-routing",
|
||||||
"title": "In-app Navigation",
|
"title": "Adding navigation",
|
||||||
"tooltip": "Navigate among different page views using the browser's URL."
|
"tooltip": "Navigate among different page views using the browser's URL."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-data",
|
"url": "start/start-data",
|
||||||
"title": "Manage Data",
|
"title": "Managing Data",
|
||||||
"tooltip": "Use services and access external data via HTTP."
|
"tooltip": "Use services and access external data via HTTP."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-forms",
|
"url": "start/start-forms",
|
||||||
"title": "Forms for User Input",
|
"title": "Using Forms for User Input",
|
||||||
"tooltip": "Learn about fetching and managing data from users with forms."
|
"tooltip": "Learn about fetching and managing data from users with forms."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "start/start-deployment",
|
"url": "start/start-deployment",
|
||||||
"title": "Deployment",
|
"title": "Deploying an application",
|
||||||
"tooltip": "Move to local development, or deploy your application to Firebase or your own server."
|
"tooltip": "Move to local development, or deploy your application to Firebase or your own server."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Part 1: Getting started with a basic Angular app
|
# Getting started with a basic Angular app
|
||||||
|
|
||||||
Welcome to Angular!
|
Welcome to Angular!
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Try it: Manage data
|
# Managing data
|
||||||
|
|
||||||
At the end of [In-app Navigation](start/start-routing "Try it: In-app Navigation"), the online store application has a product catalog with two views: a product list and product details.
|
At the end of [In-app Navigation](start/start-routing "Try it: In-app Navigation"), the online store application has a product catalog with two views: a product list and product details.
|
||||||
Users can click on a product name from the list to see details in a new view, with a distinct URL, or route.
|
Users can click on a product name from the list to see details in a new view, with a distinct URL, or route.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Try it: Deployment
|
# Deploying an application
|
||||||
|
|
||||||
|
|
||||||
To deploy your application, you have to compile it, and then host the JavaScript, CSS, and HTML on a web server. Built Angular applications are very portable and can live in any environment or served by any technology, such as Node, Java, .NET, PHP, and many others.
|
To deploy your application, you have to compile it, and then host the JavaScript, CSS, and HTML on a web server. Built Angular applications are very portable and can live in any environment or served by any technology, such as Node, Java, .NET, PHP, and many others.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Try it: Use forms for user input
|
# Using forms for user input
|
||||||
|
|
||||||
At the end of [Managing Data](start/start-data "Try it: Managing Data"), the online store application has a product catalog and a shopping cart.
|
At the end of [Managing Data](start/start-data "Try it: Managing Data"), the online store application has a product catalog and a shopping cart.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# In-app navigation
|
# Adding navigation
|
||||||
|
|
||||||
At the end of [part 1](start "Get started with a basic Angular app"), the online store application has a basic product catalog.
|
At the end of [part 1](start "Get started with a basic Angular app"), the online store application has a basic product catalog.
|
||||||
The app doesn't have any variable states or navigation.
|
The app doesn't have any variable states or navigation.
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
"build-local-with-viewengine": "yarn ~~build",
|
"build-local-with-viewengine": "yarn ~~build",
|
||||||
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
||||||
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
|
"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 800ba9271",
|
||||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||||
"test": "yarn check-env && ng test",
|
"test": "yarn check-env && ng test",
|
||||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||||
|
@ -40,6 +40,10 @@ export const ELEMENT_MODULE_LOAD_CALLBACKS_AS_ROUTES = [
|
|||||||
{
|
{
|
||||||
selector: 'live-example',
|
selector: 'live-example',
|
||||||
loadChildren: () => import('./live-example/live-example.module').then(m => m.LiveExampleModule)
|
loadChildren: () => import('./live-example/live-example.module').then(m => m.LiveExampleModule)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'aio-events',
|
||||||
|
loadChildren: () => import('./events/events.module').then(m => m.EventsModule)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
43
aio/src/app/custom-elements/events/events.component.html
Normal file
43
aio/src/app/custom-elements/events/events.component.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<p>Where we'll be presenting:</p>
|
||||||
|
<table class="is-full-width">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Event</th>
|
||||||
|
<th>Location</th>
|
||||||
|
<th>Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody >
|
||||||
|
<tr *ngFor="let event of upcomingEvents">
|
||||||
|
<th><a href="{{event.linkUrl}}" title="{{event.tooltip}}">{{event.name}}</a></th>
|
||||||
|
<td>{{event.location}}</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
{{getEventDates(event)}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Where we already presented:</p>
|
||||||
|
<table class="is-full-width">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Event</th>
|
||||||
|
<th>Location</th>
|
||||||
|
<th>Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let event of pastEvents">
|
||||||
|
<th><a href="{{event.linkUrl}}" title="{{event.tooltip}}">{{event.name}}</a></th>
|
||||||
|
<td>{{event.location}}</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
{{getEventDates(event)}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
231
aio/src/app/custom-elements/events/events.component.spec.ts
Normal file
231
aio/src/app/custom-elements/events/events.component.spec.ts
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import { Injector } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { Duration, Event, EventsComponent } from './events.component';
|
||||||
|
import { EventsService } from './events.service';
|
||||||
|
|
||||||
|
describe('EventsComponent', () => {
|
||||||
|
let component: EventsComponent;
|
||||||
|
let injector: Injector;
|
||||||
|
let eventsService: TestEventsService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
injector = Injector.create({
|
||||||
|
providers: [
|
||||||
|
{ provide: EventsComponent, deps: [EventsService] } ,
|
||||||
|
{ provide: EventsService, useClass: TestEventsService, deps: [] },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
eventsService = injector.get(EventsService) as unknown as TestEventsService;
|
||||||
|
component = injector.get(EventsComponent) as unknown as EventsComponent;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have no pastEvents when first created', () => {
|
||||||
|
expect(component.pastEvents).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have no upcoming when first created', () => {
|
||||||
|
expect(component.upcomingEvents).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ngOnInit()', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jasmine.clock().install();
|
||||||
|
jasmine.clock().mockDate(new Date(2020, 5, 15, 23, 59, 59));
|
||||||
|
component.ngOnInit();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => jasmine.clock().uninstall());
|
||||||
|
|
||||||
|
it('should separate past and upcoming events', () => {
|
||||||
|
eventsService.events.next([
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 1',
|
||||||
|
{start: '2020-06-16', end: '2020-06-17'},
|
||||||
|
{start: '2020-06-18', end: '2020-06-18'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 3',
|
||||||
|
{start: '2222-01-01', end: '2222-01-02'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 2',
|
||||||
|
{start: '2020-06-13', end: '2020-06-14'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 2',
|
||||||
|
{start: '2020-06-17', end: '2020-06-18'},
|
||||||
|
{start: '2020-06-16', end: '2020-06-16'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 1',
|
||||||
|
{start: '2020-05-30', end: '2020-05-31'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 3',
|
||||||
|
{start: '2020-06-14', end: '2020-06-14'},
|
||||||
|
{start: '2020-06-16', end: '2020-06-17'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(component.pastEvents.map(evt => evt.name)).toEqual(jasmine.arrayWithExactContents(
|
||||||
|
['Past event 1', 'Past event 2', 'Past event 3']));
|
||||||
|
|
||||||
|
expect(component.upcomingEvents.map(evt => evt.name)).toEqual(jasmine.arrayWithExactContents(
|
||||||
|
['Upcoming event 1', 'Upcoming event 2', 'Upcoming event 3']));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should order past events in reverse chronological order (ignoring workshops dates)', () => {
|
||||||
|
eventsService.events.next([
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 2',
|
||||||
|
{start: '1999-12-13', end: '1999-12-14'},
|
||||||
|
{start: '1999-12-11', end: '1999-12-11'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 4',
|
||||||
|
{start: '2020-01-16', end: '2020-01-17'},
|
||||||
|
{start: '2020-01-14', end: '2020-01-15'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 3',
|
||||||
|
{start: '2020-01-15', end: '2020-01-16'},
|
||||||
|
{start: '2020-01-17', end: '2020-01-18'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Past event 1',
|
||||||
|
{start: '1999-12-12', end: '1999-12-15'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(component.pastEvents.map(evt => evt.name)).toEqual(
|
||||||
|
['Past event 4', 'Past event 3', 'Past event 2', 'Past event 1']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should order upcoming events in chronological order (ignoring workshops dates)', () => {
|
||||||
|
eventsService.events.next([
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 2',
|
||||||
|
{start: '2020-12-13', end: '2020-12-14'},
|
||||||
|
{start: '2020-12-11', end: '2020-12-11'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 4',
|
||||||
|
{start: '2021-01-16', end: '2021-01-17'},
|
||||||
|
{start: '2021-01-14', end: '2021-01-15'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 3',
|
||||||
|
{start: '2021-01-15', end: '2021-01-16'},
|
||||||
|
{start: '2021-01-17', end: '2021-01-18'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Upcoming event 1',
|
||||||
|
{start: '2020-12-12', end: '2020-12-15'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(component.upcomingEvents.map(evt => evt.name)).toEqual(
|
||||||
|
['Upcoming event 1', 'Upcoming event 2', 'Upcoming event 3', 'Upcoming event 4']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should treat ongoing events as upcoming', () => {
|
||||||
|
eventsService.events.next([
|
||||||
|
createMockEvent(
|
||||||
|
'Ongoing event 1',
|
||||||
|
{start: '2020-06-14', end: '2020-06-16'}),
|
||||||
|
createMockEvent(
|
||||||
|
'Ongoing event 2',
|
||||||
|
{start: '2020-06-14', end: '2020-06-15'},
|
||||||
|
{start: '2020-06-13', end: '2020-06-13'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(component.pastEvents).toEqual([]);
|
||||||
|
expect(component.upcomingEvents.map(evt => evt.name)).toEqual(jasmine.arrayWithExactContents(
|
||||||
|
['Ongoing event 1', 'Ongoing event 2']));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getEventDates()', () => {
|
||||||
|
describe('(without workshops)', () => {
|
||||||
|
it('should correctly format the main event date', () => {
|
||||||
|
const testEvent = createMockEvent('Test', {start: '2020-06-20', end: '2020-06-20'});
|
||||||
|
expect(component.getEventDates(testEvent)).toBe('June 20, 2020');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format the main event date spanning mupliple days', () => {
|
||||||
|
const testEvent = createMockEvent('Test', {start: '2019-09-19', end: '2019-09-21'});
|
||||||
|
expect(component.getEventDates(testEvent)).toBe('September 19-21, 2019');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format the main event date spanning mupliple months', () => {
|
||||||
|
const testEvent = createMockEvent('Test', {start: '2019-10-30', end: '2019-11-01'});
|
||||||
|
expect(component.getEventDates(testEvent)).toBe('October 30 - November 1, 2019');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('(with workshops)', () => {
|
||||||
|
it('should correctly format event dates with workshops after main event', () => {
|
||||||
|
const testEvent = createMockEvent(
|
||||||
|
'Test',
|
||||||
|
{start: '2020-07-25', end: '2020-07-26'},
|
||||||
|
{start: '2020-07-27', end: '2020-07-27'});
|
||||||
|
|
||||||
|
expect(component.getEventDates(testEvent))
|
||||||
|
.toBe('July 25-26 (conference), July 27 (workshops), 2020');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format event dates with workshops before main event', () => {
|
||||||
|
const testEvent = createMockEvent(
|
||||||
|
'Test',
|
||||||
|
{start: '2019-10-07', end: '2019-10-07'},
|
||||||
|
{start: '2019-10-06', end: '2019-10-06'});
|
||||||
|
|
||||||
|
expect(component.getEventDates(testEvent))
|
||||||
|
.toBe('October 6 (workshops), October 7 (conference), 2019');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format event dates spanning multiple days', () => {
|
||||||
|
const testEvent = createMockEvent(
|
||||||
|
'Test',
|
||||||
|
{start: '2019-08-30', end: '2019-08-31'},
|
||||||
|
{start: '2019-08-28', end: '2019-08-29'});
|
||||||
|
|
||||||
|
expect(component.getEventDates(testEvent))
|
||||||
|
.toBe('August 28-29 (workshops), August 30-31 (conference), 2019');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format event dates with workshops on different month before the main event',
|
||||||
|
() => {
|
||||||
|
const testEvent = createMockEvent(
|
||||||
|
'Test',
|
||||||
|
{start: '2020-08-01', end: '2020-08-02'},
|
||||||
|
{start: '2020-07-30', end: '2020-07-31'});
|
||||||
|
|
||||||
|
expect(component.getEventDates(testEvent))
|
||||||
|
.toBe('July 30-31 (workshops), August 1-2 (conference), 2020');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format event dates with workshops on different month after the main event',
|
||||||
|
() => {
|
||||||
|
const testEvent = createMockEvent(
|
||||||
|
'Test',
|
||||||
|
{start: '2020-07-30', end: '2020-07-31'},
|
||||||
|
{start: '2020-08-01', end: '2020-08-02'});
|
||||||
|
|
||||||
|
expect(component.getEventDates(testEvent))
|
||||||
|
.toBe('July 30-31 (conference), August 1-2 (workshops), 2020');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly format event dates spanning multiple months', () => {
|
||||||
|
const testEvent = createMockEvent(
|
||||||
|
'Test',
|
||||||
|
{start: '2020-07-31', end: '2020-08-01'},
|
||||||
|
{start: '2020-07-30', end: '2020-08-01'});
|
||||||
|
|
||||||
|
expect(component.getEventDates(testEvent))
|
||||||
|
.toBe('July 30 - August 1 (workshops), July 31 - August 1 (conference), 2020');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
class TestEventsService {
|
||||||
|
events = new Subject<Event[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMockEvent(name: string, date: Duration, workshopsDate?: Duration): Event {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
location: '',
|
||||||
|
linkUrl: '',
|
||||||
|
date,
|
||||||
|
workshopsDate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
102
aio/src/app/custom-elements/events/events.component.ts
Normal file
102
aio/src/app/custom-elements/events/events.component.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { EventsService } from './events.service';
|
||||||
|
|
||||||
|
const DAY = 24 * 60 * 60 * 1000;
|
||||||
|
const MONTHS = [
|
||||||
|
'January',
|
||||||
|
'February',
|
||||||
|
'March',
|
||||||
|
'April',
|
||||||
|
'May',
|
||||||
|
'June',
|
||||||
|
'July',
|
||||||
|
'August',
|
||||||
|
'September',
|
||||||
|
'October',
|
||||||
|
'November',
|
||||||
|
'December',
|
||||||
|
];
|
||||||
|
|
||||||
|
export type date = string; // of the format `YYYY-MM-DD`.
|
||||||
|
export interface Duration {
|
||||||
|
start: date;
|
||||||
|
end: date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Event {
|
||||||
|
name: string;
|
||||||
|
location: string;
|
||||||
|
linkUrl: string;
|
||||||
|
tooltip?: string;
|
||||||
|
date: Duration;
|
||||||
|
workshopsDate?: Duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-events',
|
||||||
|
templateUrl: 'events.component.html'
|
||||||
|
})
|
||||||
|
export class EventsComponent implements OnInit {
|
||||||
|
|
||||||
|
pastEvents: Event[];
|
||||||
|
upcomingEvents: Event[];
|
||||||
|
|
||||||
|
constructor(private eventsService: EventsService) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.eventsService.events.subscribe(events => {
|
||||||
|
this.pastEvents = events
|
||||||
|
.filter(event => new Date(event.date.end).getTime() < Date.now() - DAY)
|
||||||
|
.sort((l: Event, r: Event) => isBefore(l.date, r.date) ? 1 : -1);
|
||||||
|
|
||||||
|
this.upcomingEvents = events
|
||||||
|
.filter(event => new Date(event.date.end).getTime() >= Date.now() - DAY)
|
||||||
|
.sort((l: Event, r: Event) => isBefore(l.date, r.date) ? -1 : 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventDates(event: Event) {
|
||||||
|
let dateString;
|
||||||
|
|
||||||
|
// Check if there is a workshop
|
||||||
|
if (event.workshopsDate) {
|
||||||
|
const mainEventDateString = `${processDate(event.date)} (conference)`;
|
||||||
|
const workshopsDateString = `${processDate(event.workshopsDate)} (workshops)`;
|
||||||
|
const areWorkshopsBeforeEvent = isBefore(event.workshopsDate, event.date);
|
||||||
|
|
||||||
|
dateString = areWorkshopsBeforeEvent ?
|
||||||
|
`${workshopsDateString}, ${mainEventDateString}` :
|
||||||
|
`${mainEventDateString}, ${workshopsDateString}`;
|
||||||
|
} else {
|
||||||
|
// If no work shop date create conference date string
|
||||||
|
dateString = processDate(event.date);
|
||||||
|
}
|
||||||
|
dateString = `${dateString}, ${new Date(event.date.end).getFullYear()}`;
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processDate(dates: Duration) {
|
||||||
|
// Covert Date sting to date object for comparisons
|
||||||
|
const startDate = new Date(dates.start);
|
||||||
|
const endDate = new Date(dates.end);
|
||||||
|
|
||||||
|
// Create a date string in the start like January 31
|
||||||
|
let processedDate = `${MONTHS[startDate.getMonth()]} ${startDate.getDate()}`;
|
||||||
|
|
||||||
|
// If they are in different months add the string '- February 2' Making the final string January 31 - February 2
|
||||||
|
if (startDate.getMonth() !== endDate.getMonth()) {
|
||||||
|
processedDate = `${processedDate} - ${MONTHS[endDate.getMonth()]} ${endDate.getDate()}`;
|
||||||
|
} else if (startDate.getDate() !== endDate.getDate()) {
|
||||||
|
// If not add - date eg it will make // January 30-31
|
||||||
|
processedDate = `${processedDate}-${endDate.getDate()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBefore(duration1: Duration, duration2: Duration): boolean {
|
||||||
|
return (duration1.start < duration2.start) ||
|
||||||
|
(duration1.start === duration2.start && duration1.end < duration2.end);
|
||||||
|
}
|
15
aio/src/app/custom-elements/events/events.module.ts
Normal file
15
aio/src/app/custom-elements/events/events.module.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { NgModule, Type } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { EventsComponent } from './events.component';
|
||||||
|
import { EventsService } from './events.service';
|
||||||
|
import { WithCustomElementComponent } from '../element-registry';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule ],
|
||||||
|
declarations: [ EventsComponent ],
|
||||||
|
entryComponents: [ EventsComponent ],
|
||||||
|
providers: [ EventsService]
|
||||||
|
})
|
||||||
|
export class EventsModule implements WithCustomElementComponent {
|
||||||
|
customElementComponent: Type<any> = EventsComponent;
|
||||||
|
}
|
56
aio/src/app/custom-elements/events/events.service.spec.ts
Normal file
56
aio/src/app/custom-elements/events/events.service.spec.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||||
|
import { Injector } from '@angular/core';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EventsService } from './events.service';
|
||||||
|
import { Logger } from 'app/shared/logger.service';
|
||||||
|
import { MockLogger } from 'testing/logger.service';
|
||||||
|
|
||||||
|
describe('EventsService', () => {
|
||||||
|
|
||||||
|
let injector: Injector;
|
||||||
|
let eventsService: EventsService;
|
||||||
|
let httpMock: HttpTestingController;
|
||||||
|
let mockLogger: MockLogger;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
injector = TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
providers: [
|
||||||
|
EventsService,
|
||||||
|
{ provide: Logger, useClass: MockLogger }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
eventsService = injector.get<EventsService>(EventsService);
|
||||||
|
mockLogger = injector.get(Logger) as any;
|
||||||
|
httpMock = injector.get(HttpTestingController);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => httpMock.verify());
|
||||||
|
|
||||||
|
it('should make a single connection to the server', () => {
|
||||||
|
eventsService.events.subscribe();
|
||||||
|
eventsService.events.subscribe();
|
||||||
|
httpMock.expectOne('generated/events.json');
|
||||||
|
expect().nothing(); // Prevent jasmine from complaining about no expectations.
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle a failed request for `events.json`', () => {
|
||||||
|
const request = httpMock.expectOne('generated/events.json');
|
||||||
|
request.error(new ErrorEvent('404'));
|
||||||
|
expect(mockLogger.output.error).toEqual([
|
||||||
|
[jasmine.any(Error)]
|
||||||
|
]);
|
||||||
|
expect(mockLogger.output.error[0][0].message).toMatch(/^generated\/events\.json request failed:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array on a failed request for `events.json`', done => {
|
||||||
|
const request = httpMock.expectOne('generated/events.json');
|
||||||
|
request.error(new ErrorEvent('404'));
|
||||||
|
eventsService.events.subscribe(results => {
|
||||||
|
expect(results).toEqual([]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
32
aio/src/app/custom-elements/events/events.service.ts
Normal file
32
aio/src/app/custom-elements/events/events.service.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { ConnectableObservable, Observable, of } from 'rxjs';
|
||||||
|
import { catchError, publishLast } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { Event } from './events.component';
|
||||||
|
import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||||
|
import { Logger } from 'app/shared/logger.service';
|
||||||
|
|
||||||
|
const eventsPath = CONTENT_URL_PREFIX + 'events.json';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EventsService {
|
||||||
|
events: Observable<Event[]>;
|
||||||
|
|
||||||
|
constructor(private http: HttpClient, private logger: Logger) {
|
||||||
|
this.events = this.getEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEvents() {
|
||||||
|
const events = this.http.get<any>(eventsPath).pipe(
|
||||||
|
catchError(error => {
|
||||||
|
this.logger.error(new Error(`${eventsPath} request failed: ${error.message}`));
|
||||||
|
return of([]);
|
||||||
|
}),
|
||||||
|
publishLast()
|
||||||
|
);
|
||||||
|
(events as ConnectableObservable<Event[]>).connect();
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,6 @@
|
|||||||
@import 'progress-bar';
|
@import 'progress-bar';
|
||||||
@import 'presskit';
|
@import 'presskit';
|
||||||
@import 'resources';
|
@import 'resources';
|
||||||
@import 'scrollbar';
|
|
||||||
@import 'search-results';
|
@import 'search-results';
|
||||||
@import 'select-menu';
|
@import 'select-menu';
|
||||||
@import 'table';
|
@import 'table';
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
body::-webkit-scrollbar, mat-sidenav.sidenav::-webkit-scrollbar, .mat-sidenav-content::-webkit-scrollbar {
|
|
||||||
height: 6px;
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body::-webkit-scrollbar-track, mat-sidenav.sidenav::-webkit-scrollbar-track, .mat-sidenav-content::-webkit-scrollbar-track {
|
|
||||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
body::-webkit-scrollbar-thumb, mat-sidenav.sidenav::-webkit-scrollbar-thumb, .mat-sidenav-content::-webkit-scrollbar-thumb {
|
|
||||||
background-color: $mediumgray;
|
|
||||||
outline: 1px solid $darkgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-results::-webkit-scrollbar, .toc-container::-webkit-scrollbar {
|
|
||||||
height: 4px;
|
|
||||||
width: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-results::-webkit-scrollbar-track, .toc-container::-webkit-scrollbar-track {
|
|
||||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-results::-webkit-scrollbar-thumb, .toc-container::-webkit-scrollbar-thumb {
|
|
||||||
background-color: $mediumgray;
|
|
||||||
outline: 1px solid slategrey;
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ module.exports = function createSitemap() {
|
|||||||
'contributors-json',
|
'contributors-json',
|
||||||
'navigation-json',
|
'navigation-json',
|
||||||
'resources-json',
|
'resources-json',
|
||||||
|
'events-json'
|
||||||
],
|
],
|
||||||
ignoredPaths: [
|
ignoredPaths: [
|
||||||
'file-not-found',
|
'file-not-found',
|
||||||
|
@ -82,6 +82,11 @@ module.exports = new Package('angular-content', [basePackage, contentPackage])
|
|||||||
include: CONTENTS_PATH + '/marketing/resources.json',
|
include: CONTENTS_PATH + '/marketing/resources.json',
|
||||||
fileReader: 'jsonFileReader'
|
fileReader: 'jsonFileReader'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
basePath: CONTENTS_PATH,
|
||||||
|
include: CONTENTS_PATH + '/marketing/events.json',
|
||||||
|
fileReader: 'jsonFileReader'
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
collectExamples.exampleFolders.push('examples');
|
collectExamples.exampleFolders.push('examples');
|
||||||
@ -110,7 +115,8 @@ module.exports = new Package('angular-content', [basePackage, contentPackage])
|
|||||||
{docTypes: ['navigation-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
{docTypes: ['navigation-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
||||||
{docTypes: ['contributors-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
{docTypes: ['contributors-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
||||||
{docTypes: ['announcements-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
{docTypes: ['announcements-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
||||||
{docTypes: ['resources-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'}
|
{docTypes: ['resources-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'},
|
||||||
|
{docTypes: ['events-json'], pathTemplate: '${id}', outputPathTemplate: '../${id}.json'}
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
{%- macro renderHeritage(exportDoc) -%}
|
{%- macro renderHeritage(exportDoc) -%}
|
||||||
{%- if exportDoc.extendsClauses.length %} extends {% for clause in exportDoc.extendsClauses -%}
|
{%- if exportDoc.extendsClauses.length %} extends {% for clause in exportDoc.extendsClauses -%}
|
||||||
<a class="code-anchor" href="{$ clause.doc.path $}">{$ clause.text $}</a>{% if not loop.last %}, {% endif -%}
|
{% if clause.doc.path %}<a class="code-anchor" href="{$ clause.doc.path $}">{$ clause.text $}</a>{% else %}{$ clause.text $}{% endif %}{% if not loop.last %}, {% endif -%}
|
||||||
{% endfor %}{% endif %}
|
{% endfor %}{% endif %}
|
||||||
{%- if exportDoc.implementsClauses.length %} implements {% for clause in exportDoc.implementsClauses -%}
|
{%- if exportDoc.implementsClauses.length %} implements {% for clause in exportDoc.implementsClauses -%}
|
||||||
<a class="code-anchor" href="{$ clause.doc.path $}">{$ clause.text $}</a>{% if not loop.last %}, {% endif -%}
|
<a class="code-anchor" href="{$ clause.doc.path $}">{$ clause.text $}</a>{% if not loop.last %}, {% endif -%}
|
||||||
|
@ -13,7 +13,7 @@ import * as webdriver from 'selenium-webdriver';
|
|||||||
declare var expect: any;
|
declare var expect: any;
|
||||||
|
|
||||||
export function openBrowser(config: {
|
export function openBrowser(config: {
|
||||||
url: string,
|
url?: string,
|
||||||
params?: {name: string, value: any}[],
|
params?: {name: string, value: any}[],
|
||||||
ignoreBrowserSynchronization?: boolean
|
ignoreBrowserSynchronization?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
@ -34,8 +34,8 @@ export async function runBenchmark({
|
|||||||
setup,
|
setup,
|
||||||
}: {
|
}: {
|
||||||
id: string,
|
id: string,
|
||||||
url: string,
|
url?: string,
|
||||||
params: {name: string, value: any}[],
|
params?: {name: string, value: any}[],
|
||||||
ignoreBrowserSynchronization?: boolean,
|
ignoreBrowserSynchronization?: boolean,
|
||||||
microMetrics?: {[key: string]: string},
|
microMetrics?: {[key: string]: string},
|
||||||
work?: (() => void)|(() => Promise<unknown>),
|
work?: (() => void)|(() => Promise<unknown>),
|
||||||
@ -46,8 +46,6 @@ export async function runBenchmark({
|
|||||||
if (setup) {
|
if (setup) {
|
||||||
await setup();
|
await setup();
|
||||||
}
|
}
|
||||||
const description: {[key: string]: any} = {};
|
|
||||||
params.forEach((param) => description[param.name] = param.value);
|
|
||||||
return runner.sample({
|
return runner.sample({
|
||||||
id,
|
id,
|
||||||
execute: work,
|
execute: work,
|
||||||
|
@ -13,15 +13,9 @@ import {GitClient} from '../../utils/git';
|
|||||||
import {CaretakerConfig} from '../config';
|
import {CaretakerConfig} from '../config';
|
||||||
|
|
||||||
|
|
||||||
interface GithubInfoQuery {
|
|
||||||
[key: string]: {
|
|
||||||
issueCount: number,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retrieve the number of matching issues for each github query. */
|
/** Retrieve the number of matching issues for each github query. */
|
||||||
export async function printGithubTasks(git: GitClient, config: CaretakerConfig) {
|
export async function printGithubTasks(git: GitClient, config?: CaretakerConfig) {
|
||||||
if (!config.githubQueries?.length) {
|
if (!config?.githubQueries?.length) {
|
||||||
debug('No github queries defined in the configuration, skipping.');
|
debug('No github queries defined in the configuration, skipping.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,10 @@ import {MergeResult, MergeStatus, PullRequestMergeTask} from './task';
|
|||||||
export async function mergePullRequest(
|
export async function mergePullRequest(
|
||||||
prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(),
|
prNumber: number, githubToken: string, projectRoot: string = getRepoBaseDir(),
|
||||||
config?: MergeConfigWithRemote) {
|
config?: MergeConfigWithRemote) {
|
||||||
|
// Set the environment variable to skip all git commit hooks triggered by husky. We are unable to
|
||||||
|
// rely on `---no-verify` as some hooks still run, notably the `prepare-commit-msg` hook.
|
||||||
|
process.env['HUSKY_SKIP_HOOKS'] = '1';
|
||||||
|
|
||||||
const api = await createPullRequestMergeTask(githubToken, projectRoot, config);
|
const api = await createPullRequestMergeTask(githubToken, projectRoot, config);
|
||||||
|
|
||||||
// Perform the merge. Force mode can be activated through a command line flag.
|
// Perform the merge. Force mode can be activated through a command line flag.
|
||||||
|
@ -112,7 +112,8 @@ export function main(
|
|||||||
if (fixedCircularDeps.length !== 0) {
|
if (fixedCircularDeps.length !== 0) {
|
||||||
error(yellow(` Fixed circular dependencies that need to be removed from the golden:`));
|
error(yellow(` Fixed circular dependencies that need to be removed from the golden:`));
|
||||||
fixedCircularDeps.forEach(c => error(` • ${convertReferenceChainToString(c)}`));
|
fixedCircularDeps.forEach(c => error(` • ${convertReferenceChainToString(c)}`));
|
||||||
error();
|
info(yellow(`\n Total: ${newCircularDeps.length} new cycle(s), ${
|
||||||
|
fixedCircularDeps.length} fixed cycle(s). \n`));
|
||||||
if (approveCommand) {
|
if (approveCommand) {
|
||||||
info(yellow(` Please approve the new golden with: ${approveCommand}`));
|
info(yellow(` Please approve the new golden with: ${approveCommand}`));
|
||||||
} else {
|
} else {
|
||||||
|
@ -154,9 +154,7 @@ available as a long-term distribution mechanism, but they are guaranteed to be a
|
|||||||
time of the build.
|
time of the build.
|
||||||
|
|
||||||
You can access the artifacts for a specific CI run by going to the workflow page, clicking on the
|
You can access the artifacts for a specific CI run by going to the workflow page, clicking on the
|
||||||
`publish_packages_as_artifacts` job and then switching to the "Artifacts" tab.
|
`publish_packages_as_artifacts` job and then switching to the "ARTIFACTS" tab.
|
||||||
(If you happen to know the build number of the job, the URL will be something like:
|
|
||||||
`https://circleci.com/gh/angular/angular/<build-number>#artifacts`)
|
|
||||||
|
|
||||||
#### Archives for each Package
|
#### Archives for each Package
|
||||||
On the "Artifacts" tab, there is a list of links to compressed archives for Angular packages. The
|
On the "Artifacts" tab, there is a list of links to compressed archives for Angular packages. The
|
||||||
|
File diff suppressed because it is too large
Load Diff
8
goldens/public-api/common/common.d.ts
vendored
8
goldens/public-api/common/common.d.ts
vendored
@ -61,13 +61,13 @@ export declare function getLocaleDateFormat(locale: string, width: FormatWidth):
|
|||||||
|
|
||||||
export declare function getLocaleDateTimeFormat(locale: string, width: FormatWidth): string;
|
export declare function getLocaleDateTimeFormat(locale: string, width: FormatWidth): string;
|
||||||
|
|
||||||
export declare function getLocaleDayNames(locale: string, formStyle: FormStyle, width: TranslationWidth): string[];
|
export declare function getLocaleDayNames(locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string>;
|
||||||
|
|
||||||
export declare function getLocaleDayPeriods(locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string];
|
export declare function getLocaleDayPeriods(locale: string, formStyle: FormStyle, width: TranslationWidth): Readonly<[string, string]>;
|
||||||
|
|
||||||
export declare function getLocaleDirection(locale: string): 'ltr' | 'rtl';
|
export declare function getLocaleDirection(locale: string): 'ltr' | 'rtl';
|
||||||
|
|
||||||
export declare function getLocaleEraNames(locale: string, width: TranslationWidth): [string, string];
|
export declare function getLocaleEraNames(locale: string, width: TranslationWidth): Readonly<[string, string]>;
|
||||||
|
|
||||||
export declare function getLocaleExtraDayPeriodRules(locale: string): (Time | [Time, Time])[];
|
export declare function getLocaleExtraDayPeriodRules(locale: string): (Time | [Time, Time])[];
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ export declare function getLocaleFirstDayOfWeek(locale: string): WeekDay;
|
|||||||
|
|
||||||
export declare function getLocaleId(locale: string): string;
|
export declare function getLocaleId(locale: string): string;
|
||||||
|
|
||||||
export declare function getLocaleMonthNames(locale: string, formStyle: FormStyle, width: TranslationWidth): string[];
|
export declare function getLocaleMonthNames(locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string>;
|
||||||
|
|
||||||
export declare function getLocaleNumberFormat(locale: string, type: NumberFormatStyle): string;
|
export declare function getLocaleNumberFormat(locale: string, type: NumberFormatStyle): string;
|
||||||
|
|
||||||
|
4
goldens/public-api/common/http/http.d.ts
vendored
4
goldens/public-api/common/http/http.d.ts
vendored
@ -1177,6 +1177,10 @@ export declare class HttpClient {
|
|||||||
[header: string]: string | string[];
|
[header: string]: string | string[];
|
||||||
};
|
};
|
||||||
observe: 'events';
|
observe: 'events';
|
||||||
|
params?: HttpParams | {
|
||||||
|
[param: string]: string | string[];
|
||||||
|
};
|
||||||
|
reportProgress?: boolean;
|
||||||
responseType?: 'json';
|
responseType?: 'json';
|
||||||
withCredentials?: boolean;
|
withCredentials?: boolean;
|
||||||
}): Observable<HttpEvent<T>>;
|
}): Observable<HttpEvent<T>>;
|
||||||
|
4
goldens/public-api/core/core.d.ts
vendored
4
goldens/public-api/core/core.d.ts
vendored
@ -85,10 +85,6 @@ export declare interface ClassSansProvider {
|
|||||||
useClass: Type<any>;
|
useClass: Type<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated */
|
|
||||||
export declare interface CollectionChangeRecord<V> extends IterableChangeRecord<V> {
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare class Compiler {
|
export declare class Compiler {
|
||||||
compileModuleAndAllComponentsAsync: <T>(moduleType: Type<T>) => Promise<ModuleWithComponentFactories<T>>;
|
compileModuleAndAllComponentsAsync: <T>(moduleType: Type<T>) => Promise<ModuleWithComponentFactories<T>>;
|
||||||
compileModuleAndAllComponentsSync: <T>(moduleType: Type<T>) => ModuleWithComponentFactories<T>;
|
compileModuleAndAllComponentsSync: <T>(moduleType: Type<T>) => ModuleWithComponentFactories<T>;
|
||||||
|
2
goldens/public-api/forms/forms.d.ts
vendored
2
goldens/public-api/forms/forms.d.ts
vendored
@ -349,7 +349,7 @@ export declare const NG_ASYNC_VALIDATORS: InjectionToken<(Function | Validator)[
|
|||||||
|
|
||||||
export declare const NG_VALIDATORS: InjectionToken<(Function | Validator)[]>;
|
export declare const NG_VALIDATORS: InjectionToken<(Function | Validator)[]>;
|
||||||
|
|
||||||
export declare const NG_VALUE_ACCESSOR: InjectionToken<ControlValueAccessor>;
|
export declare const NG_VALUE_ACCESSOR: InjectionToken<readonly ControlValueAccessor[]>;
|
||||||
|
|
||||||
export declare abstract class NgControl extends AbstractControlDirective {
|
export declare abstract class NgControl extends AbstractControlDirective {
|
||||||
get asyncValidator(): AsyncValidatorFn | null;
|
get asyncValidator(): AsyncValidatorFn | null;
|
||||||
|
@ -14,6 +14,7 @@ export declare interface Config {
|
|||||||
assetGroups?: AssetGroup[];
|
assetGroups?: AssetGroup[];
|
||||||
dataGroups?: DataGroup[];
|
dataGroups?: DataGroup[];
|
||||||
index: string;
|
index: string;
|
||||||
|
navigationRequestStrategy?: 'freshness' | 'performance';
|
||||||
navigationUrls?: string[];
|
navigationUrls?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,11 +29,17 @@ export declare class SwUpdate {
|
|||||||
readonly activated: Observable<UpdateActivatedEvent>;
|
readonly activated: Observable<UpdateActivatedEvent>;
|
||||||
readonly available: Observable<UpdateAvailableEvent>;
|
readonly available: Observable<UpdateAvailableEvent>;
|
||||||
get isEnabled(): boolean;
|
get isEnabled(): boolean;
|
||||||
|
readonly unrecoverable: Observable<UnrecoverableStateEvent>;
|
||||||
constructor(sw: ɵangular_packages_service_worker_service_worker_a);
|
constructor(sw: ɵangular_packages_service_worker_service_worker_a);
|
||||||
activateUpdate(): Promise<void>;
|
activateUpdate(): Promise<void>;
|
||||||
checkForUpdate(): Promise<void>;
|
checkForUpdate(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export declare interface UnrecoverableStateEvent {
|
||||||
|
reason: string;
|
||||||
|
type: 'UNRECOVERABLE_STATE';
|
||||||
|
}
|
||||||
|
|
||||||
export declare interface UpdateActivatedEvent {
|
export declare interface UpdateActivatedEvent {
|
||||||
current: {
|
current: {
|
||||||
hash: string;
|
hash: string;
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
"aio": {
|
"aio": {
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2987,
|
"runtime-es2015": 3037,
|
||||||
"main-es2015": 450880,
|
"main-es2015": 450952,
|
||||||
"polyfills-es2015": 52685
|
"polyfills-es2015": 52685
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,18 +11,18 @@
|
|||||||
"aio-local": {
|
"aio-local": {
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2987,
|
"runtime-es2015": 3037,
|
||||||
"main-es2015": 448419,
|
"main-es2015": 448493,
|
||||||
"polyfills-es2015": 52630
|
"polyfills-es2015": 52415
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"aio-local-viewengine": {
|
"aio-local-viewengine": {
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 3097,
|
"runtime-es2015": 3157,
|
||||||
"main-es2015": 430239,
|
"main-es2015": 430008,
|
||||||
"polyfills-es2015": 52195
|
"polyfills-es2015": 52415
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2289,
|
"runtime-es2015": 2289,
|
||||||
"main-es2015": 245351,
|
"main-es2015": 242351,
|
||||||
"polyfills-es2015": 36938,
|
"polyfills-es2015": 36938,
|
||||||
"5-es2015": 751
|
"5-es2015": 751
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@
|
|||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2289,
|
"runtime-es2015": 2289,
|
||||||
"main-es2015": 221939,
|
"main-es2015": 218961,
|
||||||
"polyfills-es2015": 36723,
|
"polyfills-es2015": 36723,
|
||||||
"5-es2015": 781
|
"5-es2015": 781
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "angular-srcs",
|
"name": "angular-srcs",
|
||||||
"version": "10.1.2",
|
"version": "11.0.0-next.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Angular - a web framework for modern web apps",
|
"description": "Angular - a web framework for modern web apps",
|
||||||
"homepage": "https://github.com/angular/angular",
|
"homepage": "https://github.com/angular/angular",
|
||||||
|
@ -2448,6 +2448,8 @@ export class HttpClient {
|
|||||||
*/
|
*/
|
||||||
put<T>(url: string, body: any|null, options: {
|
put<T>(url: string, body: any|null, options: {
|
||||||
headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events',
|
headers?: HttpHeaders|{[header: string]: string | string[]}, observe: 'events',
|
||||||
|
params?: HttpParams|{[param: string]: string | string[]},
|
||||||
|
reportProgress?: boolean,
|
||||||
responseType?: 'json',
|
responseType?: 'json',
|
||||||
withCredentials?: boolean,
|
withCredentials?: boolean,
|
||||||
}): Observable<HttpEvent<T>>;
|
}): Observable<HttpEvent<T>>;
|
||||||
|
@ -79,9 +79,10 @@ export class HttpXhrBackend implements HttpBackend {
|
|||||||
*/
|
*/
|
||||||
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
|
handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
|
||||||
// Quick check to give a better error message when a user attempts to use
|
// Quick check to give a better error message when a user attempts to use
|
||||||
// HttpClient.jsonp() without installing the JsonpClientModule
|
// HttpClient.jsonp() without installing the HttpClientJsonpModule
|
||||||
if (req.method === 'JSONP') {
|
if (req.method === 'JSONP') {
|
||||||
throw new Error(`Attempted to construct Jsonp request without JsonpClientModule installed.`);
|
throw new Error(
|
||||||
|
`Attempted to construct Jsonp request without HttpClientJsonpModule installed.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything happens on Observable subscription.
|
// Everything happens on Observable subscription.
|
||||||
|
@ -13,7 +13,7 @@ export const ISO8601_DATE_REGEX =
|
|||||||
// 1 2 3 4 5 6 7 8 9 10 11
|
// 1 2 3 4 5 6 7 8 9 10 11
|
||||||
const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {};
|
const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {};
|
||||||
const DATE_FORMATS_SPLIT =
|
const DATE_FORMATS_SPLIT =
|
||||||
/((?:[^GyMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/;
|
/((?:[^GyrMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|r{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/;
|
||||||
|
|
||||||
enum ZoneWidth {
|
enum ZoneWidth {
|
||||||
Short,
|
Short,
|
||||||
@ -394,6 +394,18 @@ function weekGetter(size: number, monthBased = false): DateFormatter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a date formatter that provides the week-numbering year for the input date.
|
||||||
|
*/
|
||||||
|
function weekNumberingYearGetter(size: number, trim = false): DateFormatter {
|
||||||
|
return function(date: Date, locale: string) {
|
||||||
|
const thisThurs = getThursdayThisWeek(date);
|
||||||
|
const weekNumberingYear = thisThurs.getFullYear();
|
||||||
|
return padNumber(
|
||||||
|
weekNumberingYear, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign), trim);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
type DateFormatter = (date: Date, locale: string, offset: number) => string;
|
type DateFormatter = (date: Date, locale: string, offset: number) => string;
|
||||||
|
|
||||||
const DATE_FORMATS: {[format: string]: DateFormatter} = {};
|
const DATE_FORMATS: {[format: string]: DateFormatter} = {};
|
||||||
@ -438,6 +450,25 @@ function getDateFormatter(format: string): DateFormatter|null {
|
|||||||
formatter = dateGetter(DateType.FullYear, 4, 0, false, true);
|
formatter = dateGetter(DateType.FullYear, 4, 0, false, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199)
|
||||||
|
case 'r':
|
||||||
|
formatter = weekNumberingYearGetter(1);
|
||||||
|
break;
|
||||||
|
// 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD
|
||||||
|
// 2010 => 10)
|
||||||
|
case 'rr':
|
||||||
|
formatter = weekNumberingYearGetter(2, true);
|
||||||
|
break;
|
||||||
|
// 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD
|
||||||
|
// 2010 => 2010)
|
||||||
|
case 'rrr':
|
||||||
|
formatter = weekNumberingYearGetter(3);
|
||||||
|
break;
|
||||||
|
// 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010)
|
||||||
|
case 'rrrr':
|
||||||
|
formatter = weekNumberingYearGetter(4);
|
||||||
|
break;
|
||||||
|
|
||||||
// Month of the year (1-12), numeric
|
// Month of the year (1-12), numeric
|
||||||
case 'M':
|
case 'M':
|
||||||
case 'L':
|
case 'L':
|
||||||
@ -734,7 +765,10 @@ export function isoStringToDate(match: RegExpMatchArray): Date {
|
|||||||
const h = Number(match[4] || 0) - tzHour;
|
const h = Number(match[4] || 0) - tzHour;
|
||||||
const m = Number(match[5] || 0) - tzMin;
|
const m = Number(match[5] || 0) - tzMin;
|
||||||
const s = Number(match[6] || 0);
|
const s = Number(match[6] || 0);
|
||||||
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
|
// The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11)
|
||||||
|
// defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms`
|
||||||
|
// becomes `999ms`.
|
||||||
|
const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000);
|
||||||
timeSetter.call(date, h, m, s, ms);
|
timeSetter.call(date, h, m, s, ms);
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ export function getLocaleId(locale: string): string {
|
|||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function getLocaleDayPeriods(
|
export function getLocaleDayPeriods(
|
||||||
locale: string, formStyle: FormStyle, width: TranslationWidth): [string, string] {
|
locale: string, formStyle: FormStyle, width: TranslationWidth): Readonly<[string, string]> {
|
||||||
const data = ɵfindLocaleData(locale);
|
const data = ɵfindLocaleData(locale);
|
||||||
const amPmData = <[string, string][][]>[
|
const amPmData = <[string, string][][]>[
|
||||||
data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone]
|
data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone]
|
||||||
@ -255,7 +255,7 @@ export function getLocaleDayPeriods(
|
|||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function getLocaleDayNames(
|
export function getLocaleDayNames(
|
||||||
locale: string, formStyle: FormStyle, width: TranslationWidth): string[] {
|
locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string> {
|
||||||
const data = ɵfindLocaleData(locale);
|
const data = ɵfindLocaleData(locale);
|
||||||
const daysData =
|
const daysData =
|
||||||
<string[][][]>[data[ɵLocaleDataIndex.DaysFormat], data[ɵLocaleDataIndex.DaysStandalone]];
|
<string[][][]>[data[ɵLocaleDataIndex.DaysFormat], data[ɵLocaleDataIndex.DaysStandalone]];
|
||||||
@ -276,7 +276,7 @@ export function getLocaleDayNames(
|
|||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function getLocaleMonthNames(
|
export function getLocaleMonthNames(
|
||||||
locale: string, formStyle: FormStyle, width: TranslationWidth): string[] {
|
locale: string, formStyle: FormStyle, width: TranslationWidth): ReadonlyArray<string> {
|
||||||
const data = ɵfindLocaleData(locale);
|
const data = ɵfindLocaleData(locale);
|
||||||
const monthsData =
|
const monthsData =
|
||||||
<string[][][]>[data[ɵLocaleDataIndex.MonthsFormat], data[ɵLocaleDataIndex.MonthsStandalone]];
|
<string[][][]>[data[ɵLocaleDataIndex.MonthsFormat], data[ɵLocaleDataIndex.MonthsStandalone]];
|
||||||
@ -287,7 +287,6 @@ export function getLocaleMonthNames(
|
|||||||
/**
|
/**
|
||||||
* Retrieves Gregorian-calendar eras for the given locale.
|
* Retrieves Gregorian-calendar eras for the given locale.
|
||||||
* @param locale A locale code for the locale format rules to use.
|
* @param locale A locale code for the locale format rules to use.
|
||||||
* @param formStyle The required grammatical form.
|
|
||||||
* @param width The required character width.
|
* @param width The required character width.
|
||||||
|
|
||||||
* @returns An array of localized era strings.
|
* @returns An array of localized era strings.
|
||||||
@ -296,7 +295,8 @@ export function getLocaleMonthNames(
|
|||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function getLocaleEraNames(locale: string, width: TranslationWidth): [string, string] {
|
export function getLocaleEraNames(
|
||||||
|
locale: string, width: TranslationWidth): Readonly<[string, string]> {
|
||||||
const data = ɵfindLocaleData(locale);
|
const data = ɵfindLocaleData(locale);
|
||||||
const erasData = <[string, string][]>data[ɵLocaleDataIndex.Eras];
|
const erasData = <[string, string][]>data[ɵLocaleDataIndex.Eras];
|
||||||
return getLastDefinedValue(erasData, width);
|
return getLastDefinedValue(erasData, width);
|
||||||
|
@ -65,6 +65,10 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
* | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
||||||
* | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
* | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
||||||
* | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
* | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
||||||
|
* | Week-numbering year| r | Numeric: minimum digits | 2, 20, 201, 2017, 20173 |
|
||||||
|
* | | rr | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
||||||
|
* | | rrr | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
||||||
|
* | | rrrr | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
||||||
* | Month | M | Numeric: 1 digit | 9, 12 |
|
* | Month | M | Numeric: 1 digit | 9, 12 |
|
||||||
* | | MM | Numeric: 2 digits + zero padded | 09, 12 |
|
* | | MM | Numeric: 2 digits + zero padded | 09, 12 |
|
||||||
* | | MMM | Abbreviated | Sep |
|
* | | MMM | Abbreviated | Sep |
|
||||||
|
@ -95,6 +95,10 @@ describe('Format date', () => {
|
|||||||
yy: '15',
|
yy: '15',
|
||||||
yyy: '2015',
|
yyy: '2015',
|
||||||
yyyy: '2015',
|
yyyy: '2015',
|
||||||
|
r: '2015',
|
||||||
|
rr: '15',
|
||||||
|
rrr: '2015',
|
||||||
|
rrrr: '2015',
|
||||||
M: '6',
|
M: '6',
|
||||||
MM: '06',
|
MM: '06',
|
||||||
MMM: 'Jun',
|
MMM: 'Jun',
|
||||||
@ -153,6 +157,10 @@ describe('Format date', () => {
|
|||||||
yy: '15',
|
yy: '15',
|
||||||
yyy: '2015',
|
yyy: '2015',
|
||||||
yyyy: '2015',
|
yyyy: '2015',
|
||||||
|
r: '2015',
|
||||||
|
rr: '15',
|
||||||
|
rrr: '2015',
|
||||||
|
rrrr: '2015',
|
||||||
M: '1',
|
M: '1',
|
||||||
MM: '01',
|
MM: '01',
|
||||||
MMM: 'Jan',
|
MMM: 'Jan',
|
||||||
@ -361,5 +369,14 @@ describe('Format date', () => {
|
|||||||
expect(formatDate(3001, 'm:ss.SS', 'en')).toEqual('0:03.00');
|
expect(formatDate(3001, 'm:ss.SS', 'en')).toEqual('0:03.00');
|
||||||
expect(formatDate(3001, 'm:ss.SSS', 'en')).toEqual('0:03.001');
|
expect(formatDate(3001, 'm:ss.SSS', 'en')).toEqual('0:03.001');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/angular/angular/issues/38739
|
||||||
|
it('should return correct ISO 8601 week-numbering year for dates close to year end/beginning',
|
||||||
|
() => {
|
||||||
|
expect(formatDate('2013-12-27', 'rrrr', 'en')).toEqual('2013');
|
||||||
|
expect(formatDate('2013-12-29', 'rrrr', 'en')).toEqual('2014');
|
||||||
|
expect(formatDate('2010-01-02', 'rrrr', 'en')).toEqual('2009');
|
||||||
|
expect(formatDate('2010-01-04', 'rrrr', 'en')).toEqual('2010');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ import localeHe from '@angular/common/locales/he';
|
|||||||
import localeZh from '@angular/common/locales/zh';
|
import localeZh from '@angular/common/locales/zh';
|
||||||
import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
|
import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
|
||||||
|
|
||||||
import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection, getNumberOfCurrencyDigits} from '../../src/i18n/locale_data_api';
|
import {FormatWidth, FormStyle, getCurrencySymbol, getLocaleDateFormat, getLocaleDayNames, getLocaleDirection, getLocaleMonthNames, getNumberOfCurrencyDigits, TranslationWidth} from '../../src/i18n/locale_data_api';
|
||||||
|
|
||||||
{
|
{
|
||||||
describe('locale data api', () => {
|
describe('locale data api', () => {
|
||||||
@ -71,5 +71,96 @@ import {FormatWidth, getCurrencySymbol, getLocaleDateFormat, getLocaleDirection,
|
|||||||
expect(getLocaleDirection('en')).toEqual('ltr');
|
expect(getLocaleDirection('en')).toEqual('ltr');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getLocaleDayNames', () => {
|
||||||
|
it('should return english short list of days', () => {
|
||||||
|
expect(
|
||||||
|
getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short),
|
||||||
|
)
|
||||||
|
.toEqual(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return french short list of days', () => {
|
||||||
|
expect(
|
||||||
|
getLocaleDayNames('fr-CA', FormStyle.Format, TranslationWidth.Short),
|
||||||
|
)
|
||||||
|
.toEqual(['di', 'lu', 'ma', 'me', 'je', 've', 'sa']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return english wide list of days', () => {
|
||||||
|
expect(
|
||||||
|
getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Wide),
|
||||||
|
)
|
||||||
|
.toEqual(
|
||||||
|
['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return french wide list of days', () => {
|
||||||
|
expect(
|
||||||
|
getLocaleDayNames('fr-CA', FormStyle.Format, TranslationWidth.Wide),
|
||||||
|
)
|
||||||
|
.toEqual(['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the full short list of days after manipulations', () => {
|
||||||
|
const days =
|
||||||
|
Array.from(getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short));
|
||||||
|
|
||||||
|
days.splice(2);
|
||||||
|
days.push('unexisting_day');
|
||||||
|
|
||||||
|
const newDays = getLocaleDayNames('en-US', FormStyle.Format, TranslationWidth.Short);
|
||||||
|
|
||||||
|
expect(newDays.length).toBe(7);
|
||||||
|
|
||||||
|
expect(newDays).toEqual(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLocaleMonthNames', () => {
|
||||||
|
it('should return english abbreviated list of month', () => {
|
||||||
|
expect(getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated))
|
||||||
|
.toEqual([
|
||||||
|
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return french abbreviated list of month', () => {
|
||||||
|
expect(getLocaleMonthNames('fr-CA', FormStyle.Format, TranslationWidth.Abbreviated))
|
||||||
|
.toEqual([
|
||||||
|
'janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||||
|
'nov.', 'déc.'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return english wide list of month', () => {
|
||||||
|
expect(getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Wide)).toEqual([
|
||||||
|
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
|
||||||
|
'October', 'November', 'December'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return french wide list of month', () => {
|
||||||
|
expect(getLocaleMonthNames('fr-CA', FormStyle.Format, TranslationWidth.Wide)).toEqual([
|
||||||
|
'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||||
|
'octobre', 'novembre', 'décembre'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the full abbreviated list of month after manipulations', () => {
|
||||||
|
const month = Array.from(
|
||||||
|
getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated));
|
||||||
|
month.splice(2);
|
||||||
|
month.push('unexisting_month');
|
||||||
|
|
||||||
|
const newMonth =
|
||||||
|
getLocaleMonthNames('en-US', FormStyle.Format, TranslationWidth.Abbreviated);
|
||||||
|
|
||||||
|
expect(newMonth.length).toBe(12);
|
||||||
|
|
||||||
|
expect(newMonth).toEqual(
|
||||||
|
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,13 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle
|
|||||||
expect(pipe.transform('2012-12-30T00:00:00', 'w')).toEqual('1');
|
expect(pipe.transform('2012-12-30T00:00:00', 'w')).toEqual('1');
|
||||||
expect(pipe.transform('2012-12-31T00:00:00', 'w')).toEqual('1');
|
expect(pipe.transform('2012-12-31T00:00:00', 'w')).toEqual('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should round milliseconds down to the nearest millisecond', () => {
|
||||||
|
expect(pipe.transform('2020-08-01T23:59:59.999', 'yyyy-MM-dd')).toEqual('2020-08-01');
|
||||||
|
expect(pipe.transform('2020-08-01T23:59:59.9999', 'yyyy-MM-dd, h:mm:ss SSS'))
|
||||||
|
.toEqual('2020-08-01, 11:59:59 999');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ ts_library(
|
|||||||
"//packages/compiler-cli/src/ngtsc/perf",
|
"//packages/compiler-cli/src/ngtsc/perf",
|
||||||
"//packages/compiler-cli/src/ngtsc/reflection",
|
"//packages/compiler-cli/src/ngtsc/reflection",
|
||||||
"//packages/compiler-cli/src/ngtsc/shims",
|
"//packages/compiler-cli/src/ngtsc/shims",
|
||||||
|
"//packages/compiler-cli/src/ngtsc/translator",
|
||||||
"//packages/compiler-cli/src/ngtsc/typecheck",
|
"//packages/compiler-cli/src/ngtsc/typecheck",
|
||||||
"@npm//@bazel/typescript",
|
"@npm//@bazel/typescript",
|
||||||
"@npm//@types/node",
|
"@npm//@types/node",
|
||||||
|
@ -10,7 +10,7 @@ import * as ts from 'typescript';
|
|||||||
|
|
||||||
import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system';
|
import {absoluteFromSourceFile} from '../../../src/ngtsc/file_system';
|
||||||
import {Logger} from '../../../src/ngtsc/logging';
|
import {Logger} from '../../../src/ngtsc/logging';
|
||||||
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection';
|
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, EnumMember, Import, isDecoratorIdentifier, isNamedClassDeclaration, isNamedFunctionDeclaration, isNamedVariableDeclaration, KnownDeclaration, reflectObjectLiteral, SpecialDeclarationKind, TypeScriptReflectionHost, TypeValueReference, TypeValueReferenceKind, ValueUnavailableKind} from '../../../src/ngtsc/reflection';
|
||||||
import {isWithinPackage} from '../analysis/util';
|
import {isWithinPackage} from '../analysis/util';
|
||||||
import {BundleProgram} from '../packages/bundle_program';
|
import {BundleProgram} from '../packages/bundle_program';
|
||||||
import {findAll, getNameText, hasNameIdentifier, isDefined, stripDollarSuffix} from '../utils';
|
import {findAll, getNameText, hasNameIdentifier, isDefined, stripDollarSuffix} from '../utils';
|
||||||
@ -1608,10 +1608,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
/**
|
/**
|
||||||
* Compute the `TypeValueReference` for the given `typeExpression`.
|
* Compute the `TypeValueReference` for the given `typeExpression`.
|
||||||
*
|
*
|
||||||
* In ngcc, all the `typeExpression` are guaranteed to be "values" because it is working in JS and
|
* Although `typeExpression` is a valid `ts.Expression` that could be emitted directly into the
|
||||||
* not TS. This means that the TS compiler is not going to remove the "type" import and so we can
|
* generated code, ngcc still needs to resolve the declaration and create an `IMPORTED` type
|
||||||
* always use a LOCAL `TypeValueReference` kind, rather than trying to force an additional import
|
* value reference as the compiler has specialized handling for some symbols, for example
|
||||||
* for non-local expressions.
|
* `ChangeDetectorRef` from `@angular/core`. Such an `IMPORTED` type value reference will result
|
||||||
|
* in a newly generated namespace import, instead of emitting the original `typeExpression` as is.
|
||||||
*/
|
*/
|
||||||
private typeToValue(typeExpression: ts.Expression|null): TypeValueReference {
|
private typeToValue(typeExpression: ts.Expression|null): TypeValueReference {
|
||||||
if (typeExpression === null) {
|
if (typeExpression === null) {
|
||||||
@ -1621,6 +1622,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imp = this.getImportOfExpression(typeExpression);
|
||||||
|
const decl = this.getDeclarationOfExpression(typeExpression);
|
||||||
|
if (imp === null || decl === null || decl.node === null) {
|
||||||
return {
|
return {
|
||||||
kind: TypeValueReferenceKind.LOCAL,
|
kind: TypeValueReferenceKind.LOCAL,
|
||||||
expression: typeExpression,
|
expression: typeExpression,
|
||||||
@ -1628,6 +1632,32 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: TypeValueReferenceKind.IMPORTED,
|
||||||
|
valueDeclaration: decl.node,
|
||||||
|
moduleName: imp.from,
|
||||||
|
importedName: imp.name,
|
||||||
|
nestedPath: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines where the `expression` is imported from.
|
||||||
|
*
|
||||||
|
* @param expression the expression to determine the import details for.
|
||||||
|
* @returns the `Import` for the expression, or `null` if the expression is not imported or the
|
||||||
|
* expression syntax is not supported.
|
||||||
|
*/
|
||||||
|
private getImportOfExpression(expression: ts.Expression): Import|null {
|
||||||
|
if (ts.isIdentifier(expression)) {
|
||||||
|
return this.getImportOfIdentifier(expression);
|
||||||
|
} else if (ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.name)) {
|
||||||
|
return this.getImportOfIdentifier(expression.name);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parameter type and decorators for the constructor of a class,
|
* Get the parameter type and decorators for the constructor of a class,
|
||||||
* where the information is stored on a static property of the class.
|
* where the information is stored on a static property of the class.
|
||||||
|
@ -55,7 +55,7 @@ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter {
|
|||||||
const namedImport = entryPointBasePath !== basePath ?
|
const namedImport = entryPointBasePath !== basePath ?
|
||||||
importManager.generateNamedImport(relativePath, e.identifier) :
|
importManager.generateNamedImport(relativePath, e.identifier) :
|
||||||
{symbol: e.identifier, moduleImport: null};
|
{symbol: e.identifier, moduleImport: null};
|
||||||
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
|
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : '';
|
||||||
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
|
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
|
||||||
output.append(exportStr);
|
output.append(exportStr);
|
||||||
});
|
});
|
||||||
@ -66,7 +66,7 @@ export class CommonJsRenderingFormatter extends Esm5RenderingFormatter {
|
|||||||
file: ts.SourceFile): void {
|
file: ts.SourceFile): void {
|
||||||
for (const e of exports) {
|
for (const e of exports) {
|
||||||
const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName);
|
const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName);
|
||||||
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
|
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : '';
|
||||||
const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`;
|
const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`;
|
||||||
output.append(exportStr);
|
output.append(exportStr);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import {Statement} from '@angular/compiler';
|
|||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../../src/ngtsc/imports';
|
|
||||||
import {ImportManager, translateStatement} from '../../../src/ngtsc/translator';
|
import {ImportManager, translateStatement} from '../../../src/ngtsc/translator';
|
||||||
import {CompiledClass} from '../analysis/types';
|
import {CompiledClass} from '../analysis/types';
|
||||||
import {getContainingStatement} from '../host/esm2015_host';
|
import {getContainingStatement} from '../host/esm2015_host';
|
||||||
@ -65,8 +64,9 @@ export class Esm5RenderingFormatter extends EsmRenderingFormatter {
|
|||||||
* @return The JavaScript code corresponding to `stmt` (in the appropriate format).
|
* @return The JavaScript code corresponding to `stmt` (in the appropriate format).
|
||||||
*/
|
*/
|
||||||
printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string {
|
printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string {
|
||||||
const node =
|
const node = translateStatement(
|
||||||
translateStatement(stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES5);
|
stmt, importManager,
|
||||||
|
{downlevelLocalizedStrings: true, downlevelVariableDeclarations: true});
|
||||||
const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
|
@ -10,7 +10,7 @@ import MagicString from 'magic-string';
|
|||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {absoluteFromSourceFile, AbsoluteFsPath, dirname, relative, toRelativeImport} from '../../../src/ngtsc/file_system';
|
import {absoluteFromSourceFile, AbsoluteFsPath, dirname, relative, toRelativeImport} from '../../../src/ngtsc/file_system';
|
||||||
import {NOOP_DEFAULT_IMPORT_RECORDER, Reexport} from '../../../src/ngtsc/imports';
|
import {Reexport} from '../../../src/ngtsc/imports';
|
||||||
import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator';
|
import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator';
|
||||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||||
import {ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer';
|
import {ModuleWithProvidersInfo} from '../analysis/module_with_providers_analyzer';
|
||||||
@ -247,8 +247,7 @@ export class EsmRenderingFormatter implements RenderingFormatter {
|
|||||||
* @return The JavaScript code corresponding to `stmt` (in the appropriate format).
|
* @return The JavaScript code corresponding to `stmt` (in the appropriate format).
|
||||||
*/
|
*/
|
||||||
printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string {
|
printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string {
|
||||||
const node = translateStatement(
|
const node = translateStatement(stmt, importManager);
|
||||||
stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015);
|
|
||||||
const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
@ -264,8 +263,6 @@ export class EsmRenderingFormatter implements RenderingFormatter {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the given type is the core Angular `ModuleWithProviders` interface.
|
* Check whether the given type is the core Angular `ModuleWithProviders` interface.
|
||||||
* @param typeName The type to check.
|
* @param typeName The type to check.
|
||||||
@ -292,7 +289,8 @@ function findStatement(node: ts.Node): ts.Statement|undefined {
|
|||||||
function generateImportString(
|
function generateImportString(
|
||||||
importManager: ImportManager, importPath: string|null, importName: string) {
|
importManager: ImportManager, importPath: string|null, importName: string) {
|
||||||
const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null;
|
const importAs = importPath ? importManager.generateNamedImport(importPath, importName) : null;
|
||||||
return importAs ? `${importAs.moduleImport}.${importAs.symbol}` : `${importName}`;
|
return importAs && importAs.moduleImport ? `${importAs.moduleImport.text}.${importAs.symbol}` :
|
||||||
|
`${importName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextSiblingInArray<T extends ts.Node>(node: T, array: ts.NodeArray<T>): T|null {
|
function getNextSiblingInArray<T extends ts.Node>(node: T, array: ts.NodeArray<T>): T|null {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {CommentStmt, ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
import {ConstantPool, Expression, jsDocComment, LeadingComment, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -166,11 +166,11 @@ export class Renderer {
|
|||||||
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager,
|
sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: ImportManager,
|
||||||
annotateForClosureCompiler: boolean): string {
|
annotateForClosureCompiler: boolean): string {
|
||||||
const name = this.host.getInternalNameOfClass(compiledClass.declaration);
|
const name = this.host.getInternalNameOfClass(compiledClass.declaration);
|
||||||
const statements: Statement[][] = compiledClass.compilation.map(c => {
|
const leadingComment =
|
||||||
return createAssignmentStatements(
|
annotateForClosureCompiler ? jsDocComment([{tagName: 'nocollapse'}]) : undefined;
|
||||||
name, c.name, c.initializer, annotateForClosureCompiler ? '* @nocollapse ' : undefined);
|
const statements: Statement[] = compiledClass.compilation.map(
|
||||||
});
|
c => createAssignmentStatement(name, c.name, c.initializer, leadingComment));
|
||||||
return this.renderStatements(sourceFile, Array.prototype.concat.apply([], statements), imports);
|
return this.renderStatements(sourceFile, statements, imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -213,16 +213,16 @@ export function renderConstantPool(
|
|||||||
* compiled decorator to be applied to the class.
|
* compiled decorator to be applied to the class.
|
||||||
* @param analyzedClass The info about the class whose statement we want to create.
|
* @param analyzedClass The info about the class whose statement we want to create.
|
||||||
*/
|
*/
|
||||||
function createAssignmentStatements(
|
function createAssignmentStatement(
|
||||||
receiverName: ts.DeclarationName, propName: string, initializer: Expression,
|
receiverName: ts.DeclarationName, propName: string, initializer: Expression,
|
||||||
leadingComment?: string): Statement[] {
|
leadingComment?: LeadingComment): Statement {
|
||||||
const receiver = new WrappedNodeExpr(receiverName);
|
const receiver = new WrappedNodeExpr(receiverName);
|
||||||
const statements =
|
const statement =
|
||||||
[new WritePropExpr(
|
new WritePropExpr(
|
||||||
receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined)
|
receiver, propName, initializer, /* type */ undefined, /* sourceSpan */ undefined)
|
||||||
.toStmt()];
|
.toStmt();
|
||||||
if (leadingComment !== undefined) {
|
if (leadingComment !== undefined) {
|
||||||
statements.unshift(new CommentStmt(leadingComment, true));
|
statement.addLeadingComment(leadingComment);
|
||||||
}
|
}
|
||||||
return statements;
|
return statement;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter {
|
|||||||
const namedImport = entryPointBasePath !== basePath ?
|
const namedImport = entryPointBasePath !== basePath ?
|
||||||
importManager.generateNamedImport(relativePath, e.identifier) :
|
importManager.generateNamedImport(relativePath, e.identifier) :
|
||||||
{symbol: e.identifier, moduleImport: null};
|
{symbol: e.identifier, moduleImport: null};
|
||||||
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
|
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : '';
|
||||||
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
|
const exportStr = `\nexports.${e.identifier} = ${importNamespace}${namedImport.symbol};`;
|
||||||
output.appendRight(insertionPoint, exportStr);
|
output.appendRight(insertionPoint, exportStr);
|
||||||
});
|
});
|
||||||
@ -111,7 +111,7 @@ export class UmdRenderingFormatter extends Esm5RenderingFormatter {
|
|||||||
lastStatement ? lastStatement.getEnd() : factoryFunction.body.getEnd() - 1;
|
lastStatement ? lastStatement.getEnd() : factoryFunction.body.getEnd() - 1;
|
||||||
for (const e of exports) {
|
for (const e of exports) {
|
||||||
const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName);
|
const namedImport = importManager.generateNamedImport(e.fromModule, e.symbolName);
|
||||||
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport}.` : '';
|
const importNamespace = namedImport.moduleImport ? `${namedImport.moduleImport.text}.` : '';
|
||||||
const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`;
|
const exportStr = `\nexports.${e.asAlias} = ${importNamespace}${namedImport.symbol};`;
|
||||||
output.appendRight(insertionPoint, exportStr);
|
output.appendRight(insertionPoint, exportStr);
|
||||||
}
|
}
|
||||||
|
@ -1211,7 +1211,7 @@ exports.MissingClass2 = MissingClass2;
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getConstructorParameters', () => {
|
describe('getConstructorParameters', () => {
|
||||||
it('should always specify LOCAL type value references for decorated constructor parameter types',
|
it('should retain imported name for type value references for decorated constructor parameter types',
|
||||||
() => {
|
() => {
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
@ -1271,7 +1271,7 @@ exports.MissingClass2 = MissingClass2;
|
|||||||
|
|
||||||
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
||||||
expectTypeValueReferencesForParameters(
|
expectTypeValueReferencesForParameters(
|
||||||
parameters, ['shared.Baz', 'local.External', 'SameFile']);
|
parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the decorated constructor parameters', () => {
|
it('should find the decorated constructor parameters', () => {
|
||||||
|
@ -1140,7 +1140,7 @@ runInEachFileSystem(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getConstructorParameters()', () => {
|
describe('getConstructorParameters()', () => {
|
||||||
it('should always specify LOCAL type value references for decorated constructor parameter types',
|
it('should retain imported name for type value references for decorated constructor parameter types',
|
||||||
() => {
|
() => {
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
@ -1188,7 +1188,8 @@ runInEachFileSystem(() => {
|
|||||||
const parameters = host.getConstructorParameters(classNode)!;
|
const parameters = host.getConstructorParameters(classNode)!;
|
||||||
|
|
||||||
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
||||||
expectTypeValueReferencesForParameters(parameters, ['Baz', 'External', 'SameFile']);
|
expectTypeValueReferencesForParameters(
|
||||||
|
parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the decorated constructor parameters', () => {
|
it('should find the decorated constructor parameters', () => {
|
||||||
@ -1205,7 +1206,8 @@ runInEachFileSystem(() => {
|
|||||||
'_viewContainer', '_template', 'injected'
|
'_viewContainer', '_template', 'injected'
|
||||||
]);
|
]);
|
||||||
expectTypeValueReferencesForParameters(
|
expectTypeValueReferencesForParameters(
|
||||||
parameters, ['ViewContainerRef', 'TemplateRef', null]);
|
parameters, ['ViewContainerRef', 'TemplateRef', null],
|
||||||
|
['@angular/core', '@angular/core', null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept `ctorParameters` as an array', () => {
|
it('should accept `ctorParameters` as an array', () => {
|
||||||
|
@ -1252,7 +1252,7 @@ runInEachFileSystem(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getConstructorParameters()', () => {
|
describe('getConstructorParameters()', () => {
|
||||||
it('should always specify LOCAL type value references for decorated constructor parameter types',
|
it('should retain imported name for type value references for decorated constructor parameter types',
|
||||||
() => {
|
() => {
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
@ -1310,7 +1310,8 @@ runInEachFileSystem(() => {
|
|||||||
const parameters = host.getConstructorParameters(classNode)!;
|
const parameters = host.getConstructorParameters(classNode)!;
|
||||||
|
|
||||||
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
||||||
expectTypeValueReferencesForParameters(parameters, ['Baz', 'External', 'SameFile']);
|
expectTypeValueReferencesForParameters(
|
||||||
|
parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the decorated constructor parameters', () => {
|
it('should find the decorated constructor parameters', () => {
|
||||||
|
@ -1332,7 +1332,7 @@ runInEachFileSystem(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getConstructorParameters', () => {
|
describe('getConstructorParameters', () => {
|
||||||
it('should always specify LOCAL type value references for decorated constructor parameter types',
|
it('should retain imported name for type value references for decorated constructor parameter types',
|
||||||
() => {
|
() => {
|
||||||
const files = [
|
const files = [
|
||||||
{
|
{
|
||||||
@ -1369,7 +1369,7 @@ runInEachFileSystem(() => {
|
|||||||
name: _('/main.js'),
|
name: _('/main.js'),
|
||||||
contents: `
|
contents: `
|
||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib), require('./local')) :
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('shared-lib'), require('./local')) :
|
||||||
typeof define === 'function' && define.amd ? define('main', ['exports', 'shared-lib', './local'], factory) :
|
typeof define === 'function' && define.amd ? define('main', ['exports', 'shared-lib', './local'], factory) :
|
||||||
(factory(global.main, global.shared, global.local));
|
(factory(global.main, global.shared, global.local));
|
||||||
}(this, (function (exports, shared, local) { 'use strict';
|
}(this, (function (exports, shared, local) { 'use strict';
|
||||||
@ -1401,7 +1401,7 @@ runInEachFileSystem(() => {
|
|||||||
|
|
||||||
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
expect(parameters.map(p => p.name)).toEqual(['arg1', 'arg2', 'arg3']);
|
||||||
expectTypeValueReferencesForParameters(
|
expectTypeValueReferencesForParameters(
|
||||||
parameters, ['shared.Baz', 'local.External', 'SameFile']);
|
parameters, ['Baz', 'External', 'SameFile'], ['shared-lib', './local', null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find the decorated constructor parameters', () => {
|
it('should find the decorated constructor parameters', () => {
|
||||||
|
@ -576,6 +576,35 @@ runInEachFileSystem(() => {
|
|||||||
`TestClass.ɵprov = ɵngcc0.ɵɵdefineInjectable({`);
|
`TestClass.ɵprov = ɵngcc0.ɵɵdefineInjectable({`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/angular/angular/issues/38883
|
||||||
|
it('should recognize ChangeDetectorRef as special symbol for pipes', () => {
|
||||||
|
compileIntoFlatEs2015Package('test-package', {
|
||||||
|
'/index.ts': `
|
||||||
|
import {ChangeDetectorRef, Pipe, PipeTransform} from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'myTestPipe'
|
||||||
|
})
|
||||||
|
export class TestClass implements PipeTransform {
|
||||||
|
constructor(cdr: ChangeDetectorRef) {}
|
||||||
|
transform(value: any) { return value; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
mainNgcc({
|
||||||
|
basePath: '/node_modules',
|
||||||
|
targetEntryPointPath: 'test-package',
|
||||||
|
propertiesToConsider: ['esm2015'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const jsContents = fs.readFile(_(`/node_modules/test-package/index.js`));
|
||||||
|
expect(jsContents)
|
||||||
|
.toContain(
|
||||||
|
`TestClass.ɵfac = function TestClass_Factory(t) { ` +
|
||||||
|
`return new (t || TestClass)(ɵngcc0.ɵɵinjectPipeChangeDetectorRef()); };`);
|
||||||
|
});
|
||||||
|
|
||||||
it('should use the correct type name in typings files when an export has a different name in source files',
|
it('should use the correct type name in typings files when an export has a different name in source files',
|
||||||
() => {
|
() => {
|
||||||
// We need to make sure that changes to the typings files use the correct name
|
// We need to make sure that changes to the typings files use the correct name
|
||||||
@ -1668,7 +1697,7 @@ runInEachFileSystem(() => {
|
|||||||
it('should default to not give closure annotated output', () => {
|
it('should default to not give closure annotated output', () => {
|
||||||
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']});
|
||||||
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
const jsContents = fs.readFile(_(`/dist/local-package/index.js`));
|
||||||
expect(jsContents).not.toContain('/** @nocollapse */');
|
expect(jsContents).not.toContain('@nocollapse');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,7 +99,14 @@ function compileIntoFlatPackage(
|
|||||||
program.emit();
|
program.emit();
|
||||||
};
|
};
|
||||||
|
|
||||||
emit({declaration: true, module: options.module, target: options.target, lib: []});
|
emit({
|
||||||
|
declaration: true,
|
||||||
|
emitDecoratorMetadata: true,
|
||||||
|
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||||
|
module: options.module,
|
||||||
|
target: options.target,
|
||||||
|
lib: [],
|
||||||
|
});
|
||||||
|
|
||||||
// Copy over the JS and .d.ts files, and add a .metadata.json for each .d.ts file.
|
// Copy over the JS and .d.ts files, and add a .metadata.json for each .d.ts file.
|
||||||
for (const file of rootNames) {
|
for (const file of rootNames) {
|
||||||
@ -152,7 +159,9 @@ export function compileIntoApf(
|
|||||||
compileFs.ensureDir(compileFs.resolve('esm2015'));
|
compileFs.ensureDir(compileFs.resolve('esm2015'));
|
||||||
emit({
|
emit({
|
||||||
declaration: true,
|
declaration: true,
|
||||||
|
emitDecoratorMetadata: true,
|
||||||
outDir: './esm2015',
|
outDir: './esm2015',
|
||||||
|
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||||
module: ts.ModuleKind.ESNext,
|
module: ts.ModuleKind.ESNext,
|
||||||
target: ts.ScriptTarget.ES2015,
|
target: ts.ScriptTarget.ES2015,
|
||||||
lib: [],
|
lib: [],
|
||||||
@ -178,7 +187,9 @@ export function compileIntoApf(
|
|||||||
compileFs.ensureDir(compileFs.resolve('esm5'));
|
compileFs.ensureDir(compileFs.resolve('esm5'));
|
||||||
emit({
|
emit({
|
||||||
declaration: false,
|
declaration: false,
|
||||||
|
emitDecoratorMetadata: true,
|
||||||
outDir: './esm5',
|
outDir: './esm5',
|
||||||
|
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||||
module: ts.ModuleKind.ESNext,
|
module: ts.ModuleKind.ESNext,
|
||||||
target: ts.ScriptTarget.ES5,
|
target: ts.ScriptTarget.ES5,
|
||||||
lib: [],
|
lib: [],
|
||||||
|
@ -69,7 +69,7 @@ B.decorators = [
|
|||||||
{ type: OtherB },
|
{ type: OtherB },
|
||||||
{ type: Directive, args: [{ selector: '[b]' }] }
|
{ type: Directive, args: [{ selector: '[b]' }] }
|
||||||
];
|
];
|
||||||
var C_1;
|
let C_1;
|
||||||
let C = C_1 = class C {};
|
let C = C_1 = class C {};
|
||||||
C.decorators = [
|
C.decorators = [
|
||||||
{ type: Directive, args: [{ selector: '[c]' }] },
|
{ type: Directive, args: [{ selector: '[c]' }] },
|
||||||
@ -111,7 +111,7 @@ B.decorators = [
|
|||||||
];
|
];
|
||||||
return B;
|
return B;
|
||||||
})();
|
})();
|
||||||
var C_1;
|
let C_1;
|
||||||
let C = C_1 = /** @class */ (() => {
|
let C = C_1 = /** @class */ (() => {
|
||||||
class C {}
|
class C {}
|
||||||
C.decorators = [
|
C.decorators = [
|
||||||
@ -432,7 +432,7 @@ A.decorators = [
|
|||||||
name: _('/node_modules/test-package/some/file.js'),
|
name: _('/node_modules/test-package/some/file.js'),
|
||||||
contents: `
|
contents: `
|
||||||
import * as tslib_1 from "tslib";
|
import * as tslib_1 from "tslib";
|
||||||
var D_1;
|
let D_1;
|
||||||
/* A copyright notice */
|
/* A copyright notice */
|
||||||
import { Directive } from '@angular/core';
|
import { Directive } from '@angular/core';
|
||||||
const OtherA = () => (node) => { };
|
const OtherA = () => (node) => { };
|
||||||
@ -681,9 +681,9 @@ export { D };
|
|||||||
const stmt3 = new DeclareVarStmt('baz', new LiteralExpr('qux'), undefined, []);
|
const stmt3 = new DeclareVarStmt('baz', new LiteralExpr('qux'), undefined, []);
|
||||||
|
|
||||||
expect(renderer.printStatement(stmt1, sourceFile, importManager)).toBe('const foo = 42;');
|
expect(renderer.printStatement(stmt1, sourceFile, importManager)).toBe('const foo = 42;');
|
||||||
expect(renderer.printStatement(stmt2, sourceFile, importManager)).toBe('var bar = true;');
|
expect(renderer.printStatement(stmt2, sourceFile, importManager)).toBe('let bar = true;');
|
||||||
expect(renderer.printStatement(stmt3, sourceFile, importManager))
|
expect(renderer.printStatement(stmt3, sourceFile, importManager))
|
||||||
.toBe('var baz = "qux";');
|
.toBe('let baz = "qux";');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ import * as ts from 'typescript';
|
|||||||
|
|
||||||
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||||
import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing';
|
import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing';
|
||||||
import {NOOP_DEFAULT_IMPORT_RECORDER, Reexport} from '../../../src/ngtsc/imports';
|
import {Reexport} from '../../../src/ngtsc/imports';
|
||||||
import {MockLogger} from '../../../src/ngtsc/logging/testing';
|
import {MockLogger} from '../../../src/ngtsc/logging/testing';
|
||||||
import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator';
|
import {Import, ImportManager, translateStatement} from '../../../src/ngtsc/translator';
|
||||||
import {loadTestFiles} from '../../../test/helpers';
|
import {loadTestFiles} from '../../../test/helpers';
|
||||||
@ -32,6 +32,8 @@ import {getRootFiles, makeTestEntryPointBundle} from '../helpers/utils';
|
|||||||
class TestRenderingFormatter implements RenderingFormatter {
|
class TestRenderingFormatter implements RenderingFormatter {
|
||||||
private printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed});
|
private printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed});
|
||||||
|
|
||||||
|
constructor(private isEs5: boolean) {}
|
||||||
|
|
||||||
addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) {
|
addImports(output: MagicString, imports: Import[], sf: ts.SourceFile) {
|
||||||
output.prepend('\n// ADD IMPORTS\n');
|
output.prepend('\n// ADD IMPORTS\n');
|
||||||
}
|
}
|
||||||
@ -63,7 +65,8 @@ class TestRenderingFormatter implements RenderingFormatter {
|
|||||||
}
|
}
|
||||||
printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string {
|
printStatement(stmt: Statement, sourceFile: ts.SourceFile, importManager: ImportManager): string {
|
||||||
const node = translateStatement(
|
const node = translateStatement(
|
||||||
stmt, importManager, NOOP_DEFAULT_IMPORT_RECORDER, ts.ScriptTarget.ES2015);
|
stmt, importManager,
|
||||||
|
{downlevelLocalizedStrings: this.isEs5, downlevelVariableDeclarations: this.isEs5});
|
||||||
const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
const code = this.printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
||||||
|
|
||||||
return `// TRANSPILED\n${code}`;
|
return `// TRANSPILED\n${code}`;
|
||||||
@ -94,7 +97,7 @@ function createTestRenderer(
|
|||||||
.analyzeProgram(bundle.src.program);
|
.analyzeProgram(bundle.src.program);
|
||||||
const privateDeclarationsAnalyses =
|
const privateDeclarationsAnalyses =
|
||||||
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program);
|
||||||
const testFormatter = new TestRenderingFormatter();
|
const testFormatter = new TestRenderingFormatter(isEs5);
|
||||||
spyOn(testFormatter, 'addExports').and.callThrough();
|
spyOn(testFormatter, 'addExports').and.callThrough();
|
||||||
spyOn(testFormatter, 'addImports').and.callThrough();
|
spyOn(testFormatter, 'addImports').and.callThrough();
|
||||||
spyOn(testFormatter, 'addDefinitions').and.callThrough();
|
spyOn(testFormatter, 'addDefinitions').and.callThrough();
|
||||||
@ -569,7 +572,7 @@ UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || U
|
|||||||
UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) {
|
UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) {
|
||||||
ɵngcc0.ɵɵstaticViewQuery(_c0, true);
|
ɵngcc0.ɵɵstaticViewQuery(_c0, true);
|
||||||
} if (rf & 2) {
|
} if (rf & 2) {
|
||||||
var _t;
|
let _t;
|
||||||
ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first);
|
ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first);
|
||||||
} } });`);
|
} } });`);
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user