Compare commits
36 Commits
7.1.2
...
7.2.0-beta
Author | SHA1 | Date | |
---|---|---|---|
57e1576ff4 | |||
e6b1f7e395 | |||
39e426cde3 | |||
60e403bf6d | |||
23b06af940 | |||
78e3a4c97c | |||
5d82c73da6 | |||
1cca27d823 | |||
d35d164ece | |||
f7ba4b2ff9 | |||
572fd7a79a | |||
04f902fca5 | |||
d1227ec800 | |||
198797bcf6 | |||
4622d0b23a | |||
0487fbe236 | |||
d0e8020506 | |||
a7ba05ad82 | |||
d62da4da12 | |||
b09e03b28d | |||
2fce701ced | |||
32157115da | |||
c2f30542e7 | |||
d767e0b2c0 | |||
1db53da0d3 | |||
f45aedcbf0 | |||
4001bb44d2 | |||
30f12f2887 | |||
89b8c88389 | |||
330ecfbed0 | |||
bc0fbfc93e | |||
a4462c24fa | |||
69c7488927 | |||
9e5223eaba | |||
3c9ad1d231 | |||
dc300c5c41 |
@ -1,3 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
aio/node_modules
|
aio/node_modules
|
||||||
|
aio/tools/examples/shared/node_modules
|
||||||
|
14
.bazelrc
14
.bazelrc
@ -1,11 +1,3 @@
|
|||||||
# Load any settings specific to the current user
|
|
||||||
try-import .bazelrc.user
|
|
||||||
################################
|
|
||||||
# Settings for Angular team members only
|
|
||||||
################################
|
|
||||||
# To enable this feature check the "Remote caching" section in docs/BAZEL.md.
|
|
||||||
build:angular-team --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Typescript / Angular / Sass #
|
# Typescript / Angular / Sass #
|
||||||
###############################
|
###############################
|
||||||
@ -38,12 +30,10 @@ test --nolegacy_external_runfiles
|
|||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Release support #
|
# Release support #
|
||||||
# Turn on these settings with #
|
|
||||||
# --config=release #
|
|
||||||
###############################
|
###############################
|
||||||
|
|
||||||
# Releases should always be stamped with version control info
|
# Releases should always be stamped with version control info
|
||||||
build:release --workspace_status_command=./tools/bazel_stamp_vars.sh
|
build --workspace_status_command=./tools/bazel_stamp_vars.sh
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Output #
|
# Output #
|
||||||
@ -71,6 +61,6 @@ test --experimental_ui
|
|||||||
################################
|
################################
|
||||||
# Temporary Settings for Ivy #
|
# Temporary Settings for Ivy #
|
||||||
################################
|
################################
|
||||||
# to determine if the compiler used should be Ivy or ViewEngine one can use `--define=compile=local` on
|
# to determine if the compiler used should be Ivy or ViewEngine one can use `--define=compile=aot` on
|
||||||
# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
||||||
build --define=compile=legacy
|
build --define=compile=legacy
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
# Heavily based on https://github.com/StefanScherer/dockerfiles-windows/ images.
|
|
||||||
# Combines the node windowsservercore image with the Bazel Prerequisites (https://docs.bazel.build/versions/master/install-windows.html).
|
|
||||||
# msys install taken from https://github.com/StefanScherer/dockerfiles-windows/issues/30
|
|
||||||
# VS redist install taken from https://github.com/StefanScherer/dockerfiles-windows/blob/master/apache/Dockerfile
|
|
||||||
# The nanoserver image won't work because MSYS2 does not run in it https://github.com/Alexpux/MSYS2-packages/issues/1493
|
|
||||||
|
|
||||||
# Before building this image, you must locally build node-windows:10.13.0-windowsservercore-1803.
|
|
||||||
# Clone https://github.com/StefanScherer/dockerfiles-windows/commit/4ce7101a766b9b880ac262479dd9126b64d656cf and build using
|
|
||||||
# docker build -t node-windows:10.13.0-windowsservercore-1803 --build-arg core=microsoft/windowsservercore:1803 --build-arg target=microsoft/windowsservercore:1803 .
|
|
||||||
FROM node-windows:10.13.0-windowsservercore-1803
|
|
||||||
|
|
||||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
|
||||||
|
|
||||||
# Install 7zip to extract msys2
|
|
||||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe
|
|
||||||
# For some reason the last letter in the destination directory is lost. So '/D=C:\\7zip0' will extract to '/D=C:\\7zip'.
|
|
||||||
RUN Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait
|
|
||||||
|
|
||||||
# Extract msys2
|
|
||||||
RUN Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz
|
|
||||||
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait
|
|
||||||
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait
|
|
||||||
RUN Remove-Item msys2.tar.xz
|
|
||||||
RUN Remove-Item msys2.tar
|
|
||||||
RUN Remove-Item 7z.exe
|
|
||||||
RUN Remove-Item -Recurse 7zip
|
|
||||||
|
|
||||||
# Add MSYS2 to PATH, and set BAZEL_SH
|
|
||||||
RUN [Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine)
|
|
||||||
RUN [Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
|
|
||||||
|
|
||||||
# Install Microsoft Visual C++ Redistributable for Visual Studio 2015
|
|
||||||
RUN Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe
|
|
||||||
RUN Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait
|
|
||||||
RUN Remove-Item vc_redist.x64.exe
|
|
||||||
|
|
||||||
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
|
|
||||||
SHELL ["cmd", "/c"]
|
|
||||||
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
|
|
||||||
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
|
|
||||||
|
|
||||||
CMD ["cmd.exe"]
|
|
@ -1,96 +0,0 @@
|
|||||||
# BuildKite configuration
|
|
||||||
|
|
||||||
This folder contains configuration for the [BuildKite](https://buildkite.com) based CI checks for
|
|
||||||
this repository.
|
|
||||||
|
|
||||||
BuildKite is a CI provider that provides build coordination and reports while we provide the
|
|
||||||
infrastructure.
|
|
||||||
|
|
||||||
CI runs are triggered by new PRs and will show up on the GitHub checks interface, along with the
|
|
||||||
other current CI solutions.
|
|
||||||
|
|
||||||
Currently it is only used for tests on Windows platforms.
|
|
||||||
|
|
||||||
|
|
||||||
## The build pipeline
|
|
||||||
|
|
||||||
BuildKite uses a pipeline for each repository. The `pipeline.yml` file defines pipeline
|
|
||||||
[build steps](https://buildkite.com/docs/pipelines/defining-steps) for this repository.
|
|
||||||
|
|
||||||
Run results can be seen in the GitHub checks interface and in the
|
|
||||||
[pipeline dashboard](https://buildkite.com/angular/angular).
|
|
||||||
|
|
||||||
Although most configuration is done via `pipeline.yml`, some options are only available
|
|
||||||
in the online [pipeline settings](https://buildkite.com/angular/angular/settings).
|
|
||||||
|
|
||||||
|
|
||||||
## Infrastructure
|
|
||||||
|
|
||||||
BuildKite does not provide the host machines where the builds runs, providing instead the
|
|
||||||
[BuildKite Agent](https://buildkite.com/docs/agent/v3) that should be run our own infrastructure.
|
|
||||||
|
|
||||||
|
|
||||||
### Agents
|
|
||||||
|
|
||||||
This agent polls the BuildKite API for builds, runs them, and reports back the results.
|
|
||||||
Agents are the unit of concurrency: each agent can run one build at any given time.
|
|
||||||
Adding agents allows more builds to be ran at the same time.
|
|
||||||
|
|
||||||
Individual agents can have tags, and pipeline steps can target only agents with certain tags via the
|
|
||||||
`agents` field in `pipeline.yml`.
|
|
||||||
For example: agents on Windows machines are tagged as `windows`, and the Windows specific build
|
|
||||||
steps list `windows: true` in their `agents` field.
|
|
||||||
|
|
||||||
You can see the current agent pool, along with their tags, in the
|
|
||||||
[agents list](https://buildkite.com/organizations/angular/agents).
|
|
||||||
|
|
||||||
|
|
||||||
### Our host machines
|
|
||||||
|
|
||||||
We use [Google Cloud](https://cloud.google.com/) as our cloud provider, under the
|
|
||||||
[Angular project](https://console.cloud.google.com/home/dashboard?project=internal-200822).
|
|
||||||
To access this project you need need to be logged in with a Google account that's a member of
|
|
||||||
team@angular.io.
|
|
||||||
For googlers this may be your google.com account, for others it is an angular.io account.
|
|
||||||
|
|
||||||
In this project we have a number of Windows VMs running, each of them with several agents.
|
|
||||||
The `provision-windows-buildkite.ps1` file contains instructions on how to create new host VMs that
|
|
||||||
are fully configured to run the BuildKite agents as services.
|
|
||||||
|
|
||||||
Our pipeline uses [docker-buildkite-plugin](https://github.com/buildkite-plugins/docker-buildkite-plugin)
|
|
||||||
to run build steps inside docker containers.
|
|
||||||
This way we achieve isolation and hermeticity.
|
|
||||||
|
|
||||||
The `Dockerfile` file describes a custom Docker image that includes NodeJs, Yarn, and the Bazel
|
|
||||||
pre-requisites on Windows.
|
|
||||||
|
|
||||||
To upload a new version of the docker image, follow any build instructions in `Dockerfile` and then
|
|
||||||
run `docker build -t angular/node-bazel-windows:NEW_VERSION`, followed by
|
|
||||||
`docker push angular/node-bazel-windows:NEW_VERSION`.
|
|
||||||
After being pushed it should be available online, and you can use the new version in `pipeline.yml`.
|
|
||||||
|
|
||||||
|
|
||||||
## Caretaker
|
|
||||||
|
|
||||||
BuildKite status can be found at https://www.buildkitestatus.com/.
|
|
||||||
|
|
||||||
Issues related to the BuildKite setup should be escalated to the Tools Team via the current
|
|
||||||
caretaker, followed by Alex Eagle and Filipe Silva.
|
|
||||||
|
|
||||||
Support requests should be submitted via email to support@buildkite.com and cc Igor, Misko, Alex,
|
|
||||||
Jeremy and Manu
|
|
||||||
|
|
||||||
|
|
||||||
## Rollout strategy
|
|
||||||
|
|
||||||
At the moment our BuildKite CI uses 1 host VM running 4 agents, thus being capable of 4 concurrent
|
|
||||||
builds.
|
|
||||||
The only test running is `bazel test //tools/ts-api-guardian:all`, and the PR check is not
|
|
||||||
mandatory.
|
|
||||||
|
|
||||||
In the future we should add cache support to speed up the initial `yarn` install, and also Bazel
|
|
||||||
remote caching to speed up Bazel builds.
|
|
||||||
|
|
||||||
After the current setup is verified as stable and reliable the GitHub PR check can become mandatory.
|
|
||||||
|
|
||||||
The tests ran should also be expanded to cover most, if not all, of the Bazel tests.
|
|
@ -1,6 +0,0 @@
|
|||||||
@echo off
|
|
||||||
REM Fix for https://github.com/docker/for-win/issues/2920
|
|
||||||
REM echo "Fixing msys64 folder..."
|
|
||||||
REM Touch all .dll files inside C:\msys64\
|
|
||||||
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
|
|
||||||
REM echo "Fixed msys64 folder."
|
|
@ -1,10 +0,0 @@
|
|||||||
steps:
|
|
||||||
- label: windows-test
|
|
||||||
commands:
|
|
||||||
- "yarn install --frozen-lockfile --non-interactive --network-timeout 100000"
|
|
||||||
- "yarn bazel test //tools/ts-api-guardian:all --noshow_progress"
|
|
||||||
plugins:
|
|
||||||
- docker#v2.1.0:
|
|
||||||
image: "filipesilva/node-bazel-windows:0.0.2"
|
|
||||||
agents:
|
|
||||||
windows: true
|
|
@ -1,92 +0,0 @@
|
|||||||
# PowerShell script to provision a Windows Server with BuildKite
|
|
||||||
# This script follows https://buildkite.com/docs/agent/v3/windows.
|
|
||||||
|
|
||||||
# Instructions
|
|
||||||
|
|
||||||
# VM creation:
|
|
||||||
# In Google Cloud Platform, create a Compute Engine instance.
|
|
||||||
# We recommend machine type n1-highcpu-16 (16 vCPUs, 14.4 GB memory).
|
|
||||||
# Use a windows boot disk with container support such as
|
|
||||||
# "Windows Server version 1803 Datacenter Core for Containers".
|
|
||||||
# Give it a name, then click "Create".
|
|
||||||
|
|
||||||
# VM setup:
|
|
||||||
# In the Compute Engine menu, select "VM Instances". Click on the VM name you chose before.
|
|
||||||
# Click "Set Windows Password" to choose a username and password.
|
|
||||||
# Click RDP to open a remote desktop via browser, using the username and password.
|
|
||||||
# In the Windows command prompt start an elevated powershell by inputing
|
|
||||||
# "powershell -Command "Start-Process PowerShell -Verb RunAs" followed by Enter.
|
|
||||||
# Download and execute this script from GitHub, passing the token (mandatory), tags (optional)
|
|
||||||
# and number of agents (optional) as args:
|
|
||||||
# ```
|
|
||||||
# Invoke-WebRequest -Uri https://raw.githubusercontent.com/angular/angular/master/.buildkite/provision-windows-buildkite.ps1 -OutFile provision.ps1
|
|
||||||
# .\provision.ps1 -token "MY_TOKEN" -tags "windows=true,another_tag=true" -agents 4
|
|
||||||
# ```
|
|
||||||
# The VM should restart and be fully configured.
|
|
||||||
|
|
||||||
# Creating extra VMs
|
|
||||||
# You can create an image of the current VM by following the instructions below.
|
|
||||||
# https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image
|
|
||||||
# Then create a new VM and choose "Custom images".
|
|
||||||
|
|
||||||
|
|
||||||
# Script proper.
|
|
||||||
|
|
||||||
# Get the token and tags from arguments.
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory=$true)][string]$token,
|
|
||||||
[string]$tags = ""
|
|
||||||
[Int]$agents = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Allow HTTPS
|
|
||||||
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
|
|
||||||
|
|
||||||
# Helper to add to PATH.
|
|
||||||
# Will take current PATH so avoid running it after anything to modifies only the powershell session path.
|
|
||||||
function Add-Path ([string]$newPathItem) {
|
|
||||||
$Env:Path+= ";" + $newPathItem + ";"
|
|
||||||
[Environment]::SetEnvironmentVariable("Path",$env:Path, [System.EnvironmentVariableTarget]::Machine)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Install Git for Windows
|
|
||||||
Write-Host "Installing Git for Windows."
|
|
||||||
Invoke-WebRequest -Uri https://github.com/git-for-windows/git/releases/download/v2.19.1.windows.1/Git-2.19.1-64-bit.exe -OutFile git.exe
|
|
||||||
.\git.exe /VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /COMPONENTS="icons,ext\reg\shellhere,assoc,assoc_sh" /DIR="C:\git"
|
|
||||||
Add-Path "C:\git\bin"
|
|
||||||
Remove-Item git.exe
|
|
||||||
|
|
||||||
# Download NSSM (https://nssm.cc/) to run the BuildKite agent as a service.
|
|
||||||
Write-Host "Downloading NSSM."
|
|
||||||
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -OutFile nssm.zip
|
|
||||||
Expand-Archive -Path nssm.zip -DestinationPath C:\nssm
|
|
||||||
Add-Path "C:\nssm\nssm-2.24-101-g897c7ad\win64"
|
|
||||||
Remove-Item nssm.zip
|
|
||||||
|
|
||||||
# Run the BuildKite agent install script
|
|
||||||
Write-Host "Installing BuildKite agent."
|
|
||||||
$env:buildkiteAgentToken = $token
|
|
||||||
$env:buildkiteAgentTags = $tags
|
|
||||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
|
||||||
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/buildkite/agent/master/install.ps1'))
|
|
||||||
|
|
||||||
# Configure the BuildKite agent clone and timestamp behavior
|
|
||||||
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ngit-clone-flags=--config core.autocrlf=input --config core.eol=lf --config core.longpaths=true --config core.symlinks=true`n"
|
|
||||||
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ntimestamp-lines=true`n"
|
|
||||||
|
|
||||||
# Register the BuildKite agent service using NSSM, so that it persists through restarts and is
|
|
||||||
# restarted if the process dies.
|
|
||||||
for ($i=1; $i -le $agents; $i++)
|
|
||||||
{
|
|
||||||
$agentName = "buildkite-agent-$i"
|
|
||||||
Write-Host "Registering $agentName as a service."
|
|
||||||
nssm.exe install $agentName "C:\buildkite-agent\bin\buildkite-agent.exe" "start"
|
|
||||||
nssm.exe set $agentName AppStdout "C:\buildkite-agent\$agentName.log"
|
|
||||||
nssm.exe set $agentName AppStderr "C:\buildkite-agent\$agentName.log"
|
|
||||||
nssm.exe status $agentName
|
|
||||||
nssm.exe start $agentName
|
|
||||||
nssm.exe status $agentName
|
|
||||||
}
|
|
||||||
|
|
||||||
# Restart the machine.
|
|
||||||
Restart-Computer
|
|
@ -275,7 +275,6 @@ jobs:
|
|||||||
# The `destination` needs to be kept in synch with the value of
|
# The `destination` needs to be kept in synch with the value of
|
||||||
# `AIO_ARTIFACT_PATH` in `aio/aio-builds-setup/Dockerfile`
|
# `AIO_ARTIFACT_PATH` in `aio/aio-builds-setup/Dockerfile`
|
||||||
destination: aio/dist/aio-snapshot.tgz
|
destination: aio/dist/aio-snapshot.tgz
|
||||||
- run: node ./aio/scripts/create-preview $CIRCLE_BUILD_NUM
|
|
||||||
|
|
||||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||||
test_aio_preview:
|
test_aio_preview:
|
||||||
@ -322,7 +321,6 @@ jobs:
|
|||||||
root: dist
|
root: dist
|
||||||
paths:
|
paths:
|
||||||
- packages-dist
|
- packages-dist
|
||||||
- packages-dist-ivy-jit
|
|
||||||
- packages-dist-ivy-aot
|
- packages-dist-ivy-aot
|
||||||
|
|
||||||
# We run the integration tests outside of Bazel for now.
|
# We run the integration tests outside of Bazel for now.
|
||||||
@ -462,3 +460,7 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
notify:
|
||||||
|
webhooks:
|
||||||
|
- url: https://ngbuilds.io/circle-build
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage (cli):
|
|
||||||
* ```
|
|
||||||
* node create-preview <build-number> <job-name> <webhook-url>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Usage (JS):
|
|
||||||
* ```js
|
|
||||||
* require('./trigger-webhook').
|
|
||||||
* triggerWebhook(buildNumber, jobName, webhookUrl).
|
|
||||||
* then(...);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Triggers a notification webhook with CircleCI specific info.
|
|
||||||
*
|
|
||||||
* It can be used for notifying external servers and trigger operations based on CircleCI job status
|
|
||||||
* (e.g. triggering the creation of a preview based on previously stored build atrifacts).
|
|
||||||
*
|
|
||||||
* The body of the sent payload is of the form:
|
|
||||||
* ```json
|
|
||||||
* {
|
|
||||||
* "payload": {
|
|
||||||
* "build_num": ${buildNumber}
|
|
||||||
* "build_parameters": {
|
|
||||||
* "CIRCLE_JOB": "${jobName}"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* When used from JS, it returns a promise which resolves to an object of the form:
|
|
||||||
* ```json
|
|
||||||
* {
|
|
||||||
* "statucCode": ${statusCode},
|
|
||||||
* "responseText": "${responseText}"
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* NOTE:
|
|
||||||
* - When used from the cli, the command will exit with an error code if the response's status code
|
|
||||||
* is outside the [200, 400) range.
|
|
||||||
* - When used from JS, the returned promise will be resolved, even if the response's status code is
|
|
||||||
* outside the [200, 400) range. It is up to the caller to decide how this should be handled.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Imports
|
|
||||||
const {request} = require('https');
|
|
||||||
|
|
||||||
// Exports
|
|
||||||
module.exports = {
|
|
||||||
triggerWebhook,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run
|
|
||||||
if (require.resolve === module) {
|
|
||||||
_main(process.argv.slice(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
function _main(args) {
|
|
||||||
triggerWebhook(...args).
|
|
||||||
then(({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
|
||||||
console.log(`Status: ${statusCode}\n${responseText}`) :
|
|
||||||
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
|
|
||||||
catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function postJson(url, data) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const opts = {method: 'post', headers: {'Content-Type': 'application/json'}};
|
|
||||||
const onResponse = res => {
|
|
||||||
const statusCode = res.statusCode || -1;
|
|
||||||
let responseText = '';
|
|
||||||
|
|
||||||
res.
|
|
||||||
on('error', reject).
|
|
||||||
on('data', d => responseText += d).
|
|
||||||
on('end', () => resolve({statusCode, responseText}));
|
|
||||||
};
|
|
||||||
|
|
||||||
request(url, opts, onResponse).
|
|
||||||
on('error', reject).
|
|
||||||
end(JSON.stringify(data));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function triggerWebhook(buildNumber, jobName, webhookUrl) {
|
|
||||||
if (!buildNumber || !jobName || !webhookUrl || isNaN(buildNumber)) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing or invalid arguments.\n' +
|
|
||||||
'Expected: buildNumber (number), jobName (string), webhookUrl (string)');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
payload: {
|
|
||||||
build_num: +buildNumber,
|
|
||||||
build_parameters: {CIRCLE_JOB: jobName},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return postJson(webhookUrl, data);
|
|
||||||
}
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
/dist/
|
/dist/
|
||||||
/bazel-*
|
/bazel-*
|
||||||
/integration/bazel/bazel-*
|
|
||||||
e2e_test.*
|
e2e_test.*
|
||||||
node_modules
|
node_modules
|
||||||
bower_components
|
bower_components
|
||||||
@ -30,7 +29,3 @@ yarn-error.log
|
|||||||
|
|
||||||
# rollup-test output
|
# rollup-test output
|
||||||
/modules/rollup-test/dist/
|
/modules/rollup-test/dist/
|
||||||
|
|
||||||
# User specific bazel settings
|
|
||||||
.bazelrc.user
|
|
||||||
|
|
||||||
|
@ -98,7 +98,6 @@ groups:
|
|||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery
|
- mhevery
|
||||||
- vikerman #fallback
|
- vikerman #fallback
|
||||||
- kara
|
|
||||||
|
|
||||||
build-and-ci:
|
build-and-ci:
|
||||||
conditions:
|
conditions:
|
||||||
@ -212,7 +211,6 @@ groups:
|
|||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- jenniferfell #docs only
|
- jenniferfell #docs only
|
||||||
- kara
|
|
||||||
|
|
||||||
compiler/i18n:
|
compiler/i18n:
|
||||||
conditions:
|
conditions:
|
||||||
@ -225,7 +223,6 @@ groups:
|
|||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- jenniferfell #docs only
|
- jenniferfell #docs only
|
||||||
- kara
|
|
||||||
|
|
||||||
compiler:
|
compiler:
|
||||||
conditions:
|
conditions:
|
||||||
@ -237,7 +234,6 @@ groups:
|
|||||||
- mhevery
|
- mhevery
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- jenniferfell #docs only
|
- jenniferfell #docs only
|
||||||
- kara
|
|
||||||
|
|
||||||
compiler-cli/ngtools:
|
compiler-cli/ngtools:
|
||||||
conditions:
|
conditions:
|
||||||
@ -247,7 +243,6 @@ groups:
|
|||||||
- hansl
|
- hansl
|
||||||
- filipesilva #fallback
|
- filipesilva #fallback
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- kara
|
|
||||||
|
|
||||||
compiler-cli:
|
compiler-cli:
|
||||||
conditions:
|
conditions:
|
||||||
@ -262,7 +257,6 @@ groups:
|
|||||||
- alxhub
|
- alxhub
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- kara
|
|
||||||
|
|
||||||
common:
|
common:
|
||||||
conditions:
|
conditions:
|
||||||
@ -275,7 +269,6 @@ groups:
|
|||||||
- pkozlowski-opensource #primary
|
- pkozlowski-opensource #primary
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- kara
|
|
||||||
|
|
||||||
forms:
|
forms:
|
||||||
conditions:
|
conditions:
|
||||||
@ -341,7 +334,6 @@ groups:
|
|||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- jenniferfell #docs only
|
- jenniferfell #docs only
|
||||||
- kara
|
|
||||||
|
|
||||||
testing:
|
testing:
|
||||||
conditions:
|
conditions:
|
||||||
@ -375,7 +367,6 @@ groups:
|
|||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- jenniferfell #docs only
|
- jenniferfell #docs only
|
||||||
- kara
|
|
||||||
|
|
||||||
platform-browser:
|
platform-browser:
|
||||||
conditions:
|
conditions:
|
||||||
@ -385,7 +376,6 @@ groups:
|
|||||||
- mhevery #primary
|
- mhevery #primary
|
||||||
# needs secondary
|
# needs secondary
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- kara
|
|
||||||
|
|
||||||
platform-server:
|
platform-server:
|
||||||
conditions:
|
conditions:
|
||||||
@ -408,7 +398,6 @@ groups:
|
|||||||
- mhevery #primary
|
- mhevery #primary
|
||||||
# needs secondary
|
# needs secondary
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- kara
|
|
||||||
|
|
||||||
service-worker:
|
service-worker:
|
||||||
conditions:
|
conditions:
|
||||||
@ -442,7 +431,6 @@ groups:
|
|||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- jenniferfell #docs only
|
- jenniferfell #docs only
|
||||||
- kara
|
|
||||||
|
|
||||||
benchpress:
|
benchpress:
|
||||||
conditions:
|
conditions:
|
||||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,17 +1,11 @@
|
|||||||
<a name="7.1.2"></a>
|
<a name="7.2.0-beta.0"></a>
|
||||||
## [7.1.2](https://github.com/angular/angular/compare/7.1.1...7.1.2) (2018-12-06)
|
# [7.2.0-beta.0](https://github.com/angular/angular/compare/7.1.0...7.2.0-beta.0) (2018-11-28)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **bazel:** do not throw error when writing tsickle externs ([#27200](https://github.com/angular/angular/issues/27200)) ([079c4b3](https://github.com/angular/angular/commit/079c4b3))
|
* **common:** expose request url in network error ([#27143](https://github.com/angular/angular/issues/27143)) ([1db53da](https://github.com/angular/angular/commit/1db53da)), closes [#27029](https://github.com/angular/angular/issues/27029)
|
||||||
* **bazel:** do not throw if ts compile action does not create esm5 outputs ([#27401](https://github.com/angular/angular/issues/27401)) ([9b4d959](https://github.com/angular/angular/commit/9b4d959))
|
* **upgrade:** don't rely upon the runtime to resolve forward refs ([#27132](https://github.com/angular/angular/issues/27132)) ([a4462c2](https://github.com/angular/angular/commit/a4462c2))
|
||||||
* **bazel:** ng_package cannot be run multiple times without clean ([#27200](https://github.com/angular/angular/issues/27200)) ([1ca2923](https://github.com/angular/angular/commit/1ca2923))
|
|
||||||
* **bazel:** ng_package not generating UMD bundles on windows ([#27200](https://github.com/angular/angular/issues/27200)) ([e476c38](https://github.com/angular/angular/commit/e476c38))
|
|
||||||
* **bazel:** ng_package should correctly map to source maps in secondary entry-points ([#27313](https://github.com/angular/angular/issues/27313)) ([fc2c23e](https://github.com/angular/angular/commit/fc2c23e)), closes [#25510](https://github.com/angular/angular/issues/25510)
|
|
||||||
* **compiler-cli:** flatModuleIndex files not generated on windows with multiple input files ([#27200](https://github.com/angular/angular/issues/27200)) ([8087b6b](https://github.com/angular/angular/commit/8087b6b))
|
|
||||||
* **compiler-cli:** ngtsc shim files not being generated on case-insensitive platforms ([#27466](https://github.com/angular/angular/issues/27466)) ([84f2928](https://github.com/angular/angular/commit/84f2928)), closes [/github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts#L681-L682](https://github.com//github.com/Microsoft/TypeScript/blob/3e4c5c95abd515eb9713b881d27ab3a93cc00461/src/compiler/sys.ts/issues/L681-L682)
|
|
||||||
* **platform-server:** add [@angular](https://github.com/angular)/http to the list of peerDependencies ([#27307](https://github.com/angular/angular/issues/27307)) ([236ac06](https://github.com/angular/angular/commit/236ac06)), closes [#26154](https://github.com/angular/angular/issues/26154)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
13
WORKSPACE
13
WORKSPACE
@ -21,14 +21,6 @@ rules_angular_dependencies()
|
|||||||
# These are the dependencies only for us
|
# These are the dependencies only for us
|
||||||
rules_angular_dev_dependencies()
|
rules_angular_dev_dependencies()
|
||||||
|
|
||||||
# Install transitive deps of rules_typescript
|
|
||||||
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
|
|
||||||
rules_typescript_dependencies()
|
|
||||||
|
|
||||||
# Install transitive deps of rules_nodejs
|
|
||||||
load("@build_bazel_rules_nodejs//:package.bzl", "rules_nodejs_dependencies")
|
|
||||||
rules_nodejs_dependencies()
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
# Point Bazel to WORKSPACEs that live in subdirectories
|
||||||
#
|
#
|
||||||
@ -64,9 +56,10 @@ node_repositories(
|
|||||||
yarn_version = "1.12.1",
|
yarn_version = "1.12.1",
|
||||||
)
|
)
|
||||||
|
|
||||||
local_repository(
|
yarn_install(
|
||||||
name = "npm",
|
name = "npm",
|
||||||
path = "tools/npm_workspace",
|
package_json = "//tools:npm/package.json",
|
||||||
|
yarn_lock = "//tools:npm/yarn.lock",
|
||||||
)
|
)
|
||||||
|
|
||||||
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||||
|
3
aio/.gitignore
vendored
3
aio/.gitignore
vendored
@ -44,3 +44,6 @@ protractor-results*.txt
|
|||||||
# System Files
|
# System Files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# copied dependencies
|
||||||
|
src/assets/js/lunr*
|
||||||
|
@ -41,6 +41,8 @@ Here are the most important tasks you might need to use:
|
|||||||
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
||||||
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
||||||
|
|
||||||
|
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
|
||||||
|
|
||||||
## Developing on Windows
|
## Developing on Windows
|
||||||
The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
|
The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
|
||||||
These unresolved links cause the docs generation process to fail because it cannot locate certain files.
|
These unresolved links cause the docs generation process to fail because it cannot locate certain files.
|
||||||
|
@ -7,7 +7,7 @@ import {CircleCiApi} from '../common/circle-ci-api';
|
|||||||
import {GithubApi} from '../common/github-api';
|
import {GithubApi} from '../common/github-api';
|
||||||
import {GithubPullRequests} from '../common/github-pull-requests';
|
import {GithubPullRequests} from '../common/github-pull-requests';
|
||||||
import {GithubTeams} from '../common/github-teams';
|
import {GithubTeams} from '../common/github-teams';
|
||||||
import {assert, assertNotMissingOrEmpty, computeShortSha, Logger} from '../common/utils';
|
import {assert, assertNotMissingOrEmpty, Logger} from '../common/utils';
|
||||||
import {BuildCreator} from './build-creator';
|
import {BuildCreator} from './build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
|
||||||
import {BuildRetriever} from './build-retriever';
|
import {BuildRetriever} from './build-retriever';
|
||||||
@ -144,10 +144,7 @@ export class PreviewServerFactory {
|
|||||||
const artifactPath = await buildRetriever.downloadBuildArtifact(buildNum, pr, sha, cfg.buildArtifactPath);
|
const artifactPath = await buildRetriever.downloadBuildArtifact(buildNum, pr, sha, cfg.buildArtifactPath);
|
||||||
const isPublic = await buildVerifier.getPrIsTrusted(pr);
|
const isPublic = await buildVerifier.getPrIsTrusted(pr);
|
||||||
await buildCreator.create(pr, sha, artifactPath, isPublic);
|
await buildCreator.create(pr, sha, artifactPath, isPublic);
|
||||||
|
|
||||||
res.sendStatus(isPublic ? 201 : 202);
|
res.sendStatus(isPublic ? 201 : 202);
|
||||||
logger.log(`PR:${pr}, SHA:${computeShortSha(sha)}, Build:${buildNum} - ` +
|
|
||||||
`Successfully created ${isPublic ? 'public' : 'non-public'} preview.`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('CircleCI webhook error', err);
|
logger.error('CircleCI webhook error', err);
|
||||||
respondWithError(res, err);
|
respondWithError(res, err);
|
||||||
|
@ -9,7 +9,7 @@ import { AdComponent } from './ad.component';
|
|||||||
selector: 'app-ad-banner',
|
selector: 'app-ad-banner',
|
||||||
// #docregion ad-host
|
// #docregion ad-host
|
||||||
template: `
|
template: `
|
||||||
<div class="ad-banner-example">
|
<div class="ad-banner">
|
||||||
<h3>Advertisements</h3>
|
<h3>Advertisements</h3>
|
||||||
<ng-template ad-host></ng-template>
|
<ng-template ad-host></ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,6 +18,6 @@
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ad-banner-example {
|
.ad-banner {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
@ -7,8 +7,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
text-align: center;
|
text-align: center; margin-bottom: 0;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
[class*='col-'] {
|
[class*='col-'] {
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
|
@ -17,8 +17,7 @@ button {
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer; cursor: hand;
|
||||||
cursor: hand;
|
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #cfd8dc;
|
background-color: #cfd8dc;
|
||||||
|
@ -16,8 +16,7 @@ a {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
text-align: center;
|
text-align: center; margin-bottom: 0;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
h4 {
|
h4 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -18,8 +18,7 @@ button {
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer; cursor: hand;
|
||||||
cursor: hand;
|
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #cfd8dc;
|
background-color: #cfd8dc;
|
||||||
|
@ -16,8 +16,7 @@ a {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
text-align: center;
|
text-align: center; margin-bottom: 0;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
h4 {
|
h4 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -16,8 +16,7 @@ a {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
text-align: center;
|
text-align: center; margin-bottom: 0;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
h4 {
|
h4 {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -18,8 +18,7 @@ button {
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer; cursor: hand;
|
||||||
cursor: hand;
|
|
||||||
}
|
}
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #cfd8dc;
|
background-color: #cfd8dc;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="nf-container l-flex-wrap flex-center">
|
<div class="nf-container l-flex-wrap flex-center">
|
||||||
<img src="assets/images/support/angular-404.svg" width="300" height="300"/>
|
<img src="assets/images/support/angular-404.svg" width="300" height="300"/>
|
||||||
<div class="nf-response l-flex-wrap">
|
<div class="nf-response l-flex-wrap">
|
||||||
<h1 class="no-anchor no-toc">Page Not Found</h1>
|
<h1 class="no-toc">Page Not Found</h1>
|
||||||
<p>We're sorry. The page you are looking for cannot be found.</p>
|
<p>We're sorry. The page you are looking for cannot be found.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -192,7 +192,7 @@ Here are two sample components and the `AdComponent` interface for reference:
|
|||||||
The final ad banner looks like this:
|
The final ad banner looks like this:
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="generated/images/guide/dynamic-component-loader/ads-example.gif" alt="Ads">
|
<img src="generated/images/guide/dynamic-component-loader/ads.gif" alt="Ads">
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ which you must adjust to start Chrome without sandboxing.
|
|||||||
|
|
||||||
We'll be using [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome#cli) in these examples.
|
We'll be using [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome#cli) in these examples.
|
||||||
|
|
||||||
* In the Karma configuration file, `karma.conf.js`, add a custom launcher called ChromeHeadlessCI below browsers:
|
* In the Karma configuration file, `karma.conf.js`, add a custom launcher called ChromeNoSandbox below browsers:
|
||||||
```
|
```
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome'],
|
||||||
customLaunchers: {
|
customLaunchers: {
|
||||||
|
Before Width: | Height: | Size: 851 KiB After Width: | Height: | Size: 851 KiB |
@ -422,8 +422,8 @@
|
|||||||
"picture": "minko.jpg",
|
"picture": "minko.jpg",
|
||||||
"twitter": "mgechev",
|
"twitter": "mgechev",
|
||||||
"website": "http://blog.mgechev.com",
|
"website": "http://blog.mgechev.com",
|
||||||
"bio": "Software engineer who enjoys theoretical computer science and its practical applications. Speaker, author of the book 'Switching to Angular', codelyzer, Guess.js, and the Go linter revive. Working for faster and more reliable software.",
|
"bio": "Software engineer who enjoys theoretical computer science and its practical applications. Author of the books 'Switching to Angular 2' and 'Getting Started with Angular', speaker and blogger. Working on tooling for Angular and TypeScript.",
|
||||||
"group": "Angular"
|
"group": "GDE"
|
||||||
},
|
},
|
||||||
|
|
||||||
"uri": {
|
"uri": {
|
||||||
|
@ -4,19 +4,6 @@
|
|||||||
|
|
||||||
<article>
|
<article>
|
||||||
<p>Where we'll be presenting:</p>
|
<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>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p>Where we already presented:</p>
|
|
||||||
<table class="is-full-width">
|
<table class="is-full-width">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -38,6 +25,19 @@
|
|||||||
<td>London, United Kingdom</td>
|
<td>London, United Kingdom</td>
|
||||||
<td>November 5-7, 2018</td>
|
<td>November 5-7, 2018</td>
|
||||||
</tr>
|
</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>
|
||||||
<!-- AngularMix -->
|
<!-- AngularMix -->
|
||||||
<tr>
|
<tr>
|
||||||
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<h1>Test Code Page</h1>
|
<h1>Test Code Page</h1>
|
||||||
|
|
||||||
|
<p>Current location is <current-location></current-location></p>
|
||||||
|
|
||||||
<h2><code-tabs></h2>
|
<h2><code-tabs></h2>
|
||||||
|
|
||||||
<p>No linenums at code-tabs level</p>
|
<p>No linenums at code-tabs level</p>
|
||||||
|
@ -251,7 +251,7 @@ Here is the final version of `getHeroes` with the `tap` that logs the operation.
|
|||||||
|
|
||||||
Most web APIs support a _get by id_ request in the form `:baseURL/:id`.
|
Most web APIs support a _get by id_ request in the form `:baseURL/:id`.
|
||||||
|
|
||||||
Here, the _base URL_ is the `heroesURL` defined in the [Heroes and HTTP](tutorial/toh-pt6#heroes-and-http) section (`api/heroes`) and _id_ is
|
Here, the _base URL_ is the `heroesURL` defined in the [Heroes and HTTP](http://localhost:4800/tutorial/toh-pt6#heroes-and-http) section (`api/heroes`) and _id_ is
|
||||||
the number of the hero that you want to retrieve. For example, `api/heroes/11`.
|
the number of the hero that you want to retrieve. For example, `api/heroes/11`.
|
||||||
|
|
||||||
Add a `HeroService.getHero()` method to make that request:
|
Add a `HeroService.getHero()` method to make that request:
|
||||||
|
@ -12,13 +12,13 @@
|
|||||||
"/app/search/search-worker.js",
|
"/app/search/search-worker.js",
|
||||||
"/assets/images/favicons/favicon.ico",
|
"/assets/images/favicons/favicon.ico",
|
||||||
"/assets/js/*.js",
|
"/assets/js/*.js",
|
||||||
"/generated/lunr.min.js",
|
|
||||||
"/*.css",
|
"/*.css",
|
||||||
"/*.js"
|
"/*.js"
|
||||||
],
|
],
|
||||||
"urls": [
|
"urls": [
|
||||||
"https://fonts.googleapis.com/**",
|
"https://fonts.googleapis.com/**",
|
||||||
"https://fonts.gstatic.com/s/**"
|
"https://fonts.gstatic.com/s/**",
|
||||||
|
"https://maxcdn.bootstrapcdn.com/**"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "node ../tools/yarn/check-yarn.js",
|
"preinstall": "node ../tools/yarn/check-yarn.js",
|
||||||
"postinstall": "node tools/cli-patches/patch.js",
|
"postinstall": "node tools/cli-patches/patch.js && uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
|
||||||
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug",
|
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug",
|
||||||
"aio-use-npm": "node tools/ng-packages-installer restore .",
|
"aio-use-npm": "node tools/ng-packages-installer restore .",
|
||||||
"aio-check-local": "node tools/ng-packages-installer check .",
|
"aio-check-local": "node tools/ng-packages-installer check .",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"e2e": "ng e2e --no-webdriver-update",
|
"e2e": "ng e2e --no-webdriver-update",
|
||||||
"presetup": "yarn --cwd .. install && yarn install --frozen-lockfile && yarn ~~check-env && yarn ~~clean-generated && yarn boilerplate:remove",
|
"presetup": "yarn --cwd .. install && yarn install --frozen-lockfile && yarn ~~check-env && yarn ~~clean-generated && yarn boilerplate:remove",
|
||||||
"setup": "yarn aio-use-npm && yarn example-use-npm",
|
"setup": "yarn aio-use-npm && yarn example-use-npm",
|
||||||
"postsetup": "yarn ~~build-ie-polyfills && yarn ~~minify-lunr && yarn boilerplate:add && yarn extract-cli-command-docs && yarn docs",
|
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn extract-cli-command-docs && yarn docs",
|
||||||
"presetup-local": "yarn presetup",
|
"presetup-local": "yarn presetup",
|
||||||
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
||||||
"postsetup-local": "yarn postsetup",
|
"postsetup-local": "yarn postsetup",
|
||||||
@ -61,14 +61,13 @@
|
|||||||
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
|
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
|
||||||
"generate-zips": "node ./tools/example-zipper/generateZips",
|
"generate-zips": "node ./tools/example-zipper/generateZips",
|
||||||
"build-404-page": "node scripts/build-404-page",
|
"build-404-page": "node scripts/build-404-page",
|
||||||
|
"build-ie-polyfills": "yarn webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production",
|
||||||
"update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG",
|
"update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG",
|
||||||
"~~check-env": "node scripts/check-environment",
|
"~~check-env": "node scripts/check-environment",
|
||||||
"~~clean-generated": "node --eval \"require('shelljs').rm('-rf', 'src/generated')\"",
|
"~~clean-generated": "node --eval \"require('shelljs').rm('-rf', 'src/generated')\"",
|
||||||
"~~build": "ng build",
|
"~~build": "ng build",
|
||||||
"post~~build": "yarn build-404-page",
|
"post~~build": "yarn build-404-page",
|
||||||
"~~build-ie-polyfills": "webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production",
|
"~~http-server": "http-server"
|
||||||
"~~http-server": "http-server",
|
|
||||||
"~~minify-lunr": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/generated/lunr.min.js --source-map"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.9.0 <11.0.0",
|
"node": ">=10.9.0 <11.0.0",
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
"aio": {
|
"aio": {
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime": 3713,
|
"runtime": 3881,
|
||||||
"main": 500343,
|
"main": 499953,
|
||||||
"polyfills": 53926
|
"polyfills": 53926,
|
||||||
|
"prettify": 14917
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage:
|
|
||||||
* node create-preview <build-number>
|
|
||||||
*
|
|
||||||
* Triggers the preview server to initiate the preview creation process for the specified CircleCI
|
|
||||||
* build number. It must be called _after_ the build artifacts have been created and stored on
|
|
||||||
* CircleCI.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Imports
|
|
||||||
const {triggerWebhook} = require('../../.circleci/trigger-webhook');
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const JOB_NAME = 'aio_preview';
|
|
||||||
const WEBHOOK_URL = 'https://ngbuilds.io/circle-build';
|
|
||||||
|
|
||||||
// Input
|
|
||||||
const buildNumber = process.argv[2];
|
|
||||||
|
|
||||||
// Run
|
|
||||||
triggerWebhook(buildNumber, JOB_NAME, WEBHOOK_URL).
|
|
||||||
then(({statusCode, responseText}) => isSuccess(statusCode) ?
|
|
||||||
console.log(`Status: ${statusCode}\n${responseText}`) :
|
|
||||||
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
|
|
||||||
catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
function isSuccess(statusCode) {
|
|
||||||
// Getting a 409 response from the preview server means that the preview has already been created
|
|
||||||
// for the corresponding PR/SHA, so our objective has been accomplished.
|
|
||||||
return (200 <= statusCode && statusCode < 400) || (statusCode === 409);
|
|
||||||
}
|
|
@ -29,12 +29,10 @@
|
|||||||
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
|
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
|
||||||
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
||||||
<div class="toolbar-external-icons-container">
|
<div class="toolbar-external-icons-container">
|
||||||
<a href="https://twitter.com/angular" title="Twitter" aria-label="Angular on twitter">
|
<a href="https://twitter.com/angular" title="Twitter">
|
||||||
<mat-icon svgIcon="logos:twitter"></mat-icon>
|
<img src="assets/images/logos/twitter-icon.svg"></a>
|
||||||
</a>
|
<a href="https://github.com/angular/angular" title="GitHub">
|
||||||
<a href="https://github.com/angular/angular" title="GitHub" aria-label="Angular on github">
|
<img src="assets/images/logos/github-icon.svg"></a>
|
||||||
<mat-icon svgIcon="logos:github"></mat-icon>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</mat-toolbar-row>
|
</mat-toolbar-row>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
@ -74,10 +72,3 @@
|
|||||||
<aio-footer [nodes]="footerNodes" [versionInfo]="versionInfo" ></aio-footer>
|
<aio-footer [nodes]="footerNodes" [versionInfo]="versionInfo" ></aio-footer>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!--
|
|
||||||
Work-around to ensure that the `.woff2` file for the 'Material Icons' font will always be fetched.
|
|
||||||
This allows the SW to cache the file (even if there are no icons in the current page) and ensure
|
|
||||||
material icons on other pages are displayed correctly in offline mode.
|
|
||||||
(Note: Instantiate lazily to avoid affecting the initial rendering.)
|
|
||||||
-->
|
|
||||||
<mat-icon class="cdk-visually-hidden" *ngIf="!isStarting"> </mat-icon>
|
|
||||||
|
@ -51,7 +51,7 @@ export const svgIconProviders = [
|
|||||||
useValue: {
|
useValue: {
|
||||||
name: 'close',
|
name: 'close',
|
||||||
svgSource:
|
svgSource:
|
||||||
'<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
|
'<svg fill="#ffffff" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
|
||||||
'<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />' +
|
'<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />' +
|
||||||
'<path d="M0 0h24v24H0z" fill="none" />' +
|
'<path d="M0 0h24v24H0z" fill="none" />' +
|
||||||
'</svg>',
|
'</svg>',
|
||||||
@ -61,9 +61,21 @@ export const svgIconProviders = [
|
|||||||
{
|
{
|
||||||
provide: SVG_ICONS,
|
provide: SVG_ICONS,
|
||||||
useValue: {
|
useValue: {
|
||||||
name: 'insert_comment',
|
name: 'error_outline',
|
||||||
svgSource:
|
svgSource:
|
||||||
'<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
|
'<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
|
||||||
|
'<path d="M0 0h24v24H0V0z" fill="none" />' +
|
||||||
|
'<path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />' +
|
||||||
|
'</svg>',
|
||||||
|
},
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SVG_ICONS,
|
||||||
|
useValue: {
|
||||||
|
name: 'insert_comment',
|
||||||
|
svgSource:
|
||||||
|
'<svg fill="#ffffff" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
|
||||||
'<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z" />' +
|
'<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z" />' +
|
||||||
'<path d="M0 0h24v24H0z" fill="none" />' +
|
'<path d="M0 0h24v24H0z" fill="none" />' +
|
||||||
'</svg>',
|
'</svg>',
|
||||||
@ -92,41 +104,6 @@ export const svgIconProviders = [
|
|||||||
},
|
},
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Namespace: logos
|
|
||||||
{
|
|
||||||
provide: SVG_ICONS,
|
|
||||||
useValue: {
|
|
||||||
namespace: 'logos',
|
|
||||||
name: 'github',
|
|
||||||
svgSource:
|
|
||||||
'<svg focusable="false" viewBox="0 0 51.8 50.4" xmlns="http://www.w3.org/2000/svg">' +
|
|
||||||
'<path d="M25.9,0.2C11.8,0.2,0.3,11.7,0.3,25.8c0,11.3,7.3,20.9,17.5,24.3c1.3,0.2,1.7-0.6,1.7-1.2c0-0.6,0-2.6,0-4.8' +
|
|
||||||
'c-7.1,1.5-8.6-3-8.6-3c-1.2-3-2.8-3.7-2.8-3.7c-2.3-1.6,0.2-1.6,0.2-1.6c2.6,0.2,3.9,2.6,3.9,2.6c2.3,3.9,6,2.8,7.5,2.1' +
|
|
||||||
'c0.2-1.7,0.9-2.8,1.6-3.4c-5.7-0.6-11.7-2.8-11.7-12.7c0-2.8,1-5.1,2.6-6.9c-0.3-0.7-1.1-3.3,0.3-6.8c0,0,2.1-0.7,7,2.6' +
|
|
||||||
'c2-0.6,4.2-0.9,6.4-0.9c2.2,0,4.4,0.3,6.4,0.9c4.9-3.3,7-2.6,7-2.6c1.4,3.5,0.5,6.1,0.3,6.8c1.6,1.8,2.6,4.1,2.6,6.9' +
|
|
||||||
'c0,9.8-6,12-11.7,12.6c0.9,0.8,1.7,2.4,1.7,4.7c0,3.4,0,6.2,0,7c0,0.7,0.5,1.5,1.8,1.2c10.2-3.4,17.5-13,17.5-24.3' +
|
|
||||||
'C51.5,11.7,40.1,0.2,25.9,0.2z" />' +
|
|
||||||
'</svg>',
|
|
||||||
},
|
|
||||||
multi: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: SVG_ICONS,
|
|
||||||
useValue: {
|
|
||||||
namespace: 'logos',
|
|
||||||
name: 'twitter',
|
|
||||||
svgSource:
|
|
||||||
'<svg focusable="false" viewBox="0 0 50 59" xmlns="http://www.w3.org/2000/svg">' +
|
|
||||||
'<path d="M50,9.3c-1.8,0.8-3.8,1.4-5.9,1.6c2.1-1.3,3.7-3.3,4.5-5.7c-2,1.2-4.2,2-6.5,2.5c-1.9-2-4.5-3.2-7.5-3.2' +
|
|
||||||
'c-5.7,0-10.3,4.6-10.3,10.3c0,0.8,0.1,1.6,0.3,2.3C16.1,16.7,8.5,12.6,3.5,6.4c-0.9,1.5-1.4,3.3-1.4,5.2c0,3.6,1.8,6.7,4.6,8.5' +
|
|
||||||
'C5,20,3.4,19.6,2,18.8c0,0,0,0.1,0,0.1c0,5,3.5,9.1,8.2,10.1c-0.9,0.2-1.8,0.4-2.7,0.4c-0.7,0-1.3-0.1-1.9-0.2' +
|
|
||||||
'c1.3,4.1,5.1,7,9.6,7.1c-3.5,2.8-7.9,4.4-12.7,4.4c-0.8,0-1.6,0-2.4-0.1c4.5,2.9,9.9,4.6,15.7,4.6c18.9,0,29.2-15.6,29.2-29.2' +
|
|
||||||
'c0-0.4,0-0.9,0-1.3C46.9,13.2,48.6,11.4,50,9.3z" />' +
|
|
||||||
'</svg>',
|
|
||||||
},
|
|
||||||
multi: true,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
// tslint:enable: max-line-length
|
// tslint:enable: max-line-length
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import { NgModule, Type } from '@angular/core';
|
import { NgModule, Type } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
|
||||||
import { ContributorListComponent } from './contributor-list.component';
|
import { ContributorListComponent } from './contributor-list.component';
|
||||||
import { ContributorService } from './contributor.service';
|
import { ContributorService } from './contributor.service';
|
||||||
import { ContributorComponent } from './contributor.component';
|
import { ContributorComponent } from './contributor.component';
|
||||||
import { WithCustomElementComponent } from '../element-registry';
|
import { WithCustomElementComponent } from '../element-registry';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [ CommonModule, MatIconModule ],
|
imports: [ CommonModule ],
|
||||||
declarations: [ ContributorListComponent, ContributorComponent ],
|
declarations: [ ContributorListComponent, ContributorComponent ],
|
||||||
entryComponents: [ ContributorListComponent ],
|
entryComponents: [ ContributorListComponent ],
|
||||||
providers: [ ContributorService ]
|
providers: [ ContributorService ]
|
||||||
|
@ -13,16 +13,16 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
|||||||
|
|
||||||
<div class="contributor-image" [style.background-image]="'url('+pictureBase+(person.picture || noPicture)+')'">
|
<div class="contributor-image" [style.background-image]="'url('+pictureBase+(person.picture || noPicture)+')'">
|
||||||
<div class="contributor-info">
|
<div class="contributor-info">
|
||||||
<a *ngIf="person.bio" mat-button class="info-item">
|
<a *ngIf="person.bio" mat-button>
|
||||||
View Bio
|
View Bio
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="person.twitter" mat-icon-button class="info-item icon"
|
<a *ngIf="person.twitter" mat-button class="icon"
|
||||||
href="https://twitter.com/{{person.twitter}}" target="_blank" (click)="$event.stopPropagation()">
|
href="https://twitter.com/{{person.twitter}}" target="_blank" (click)="$event.stopPropagation()">
|
||||||
<mat-icon svgIcon="logos:twitter"></mat-icon>
|
<span class="fa fa-twitter fa-2x"></span>
|
||||||
</a>
|
</a>
|
||||||
<a *ngIf="person.website" mat-icon-button class="info-item icon"
|
<a *ngIf="person.website" mat-button class="icon"
|
||||||
href="{{person.website}}" target="_blank" (click)="$event.stopPropagation()">
|
href="{{person.website}}" target="_blank" (click)="$event.stopPropagation()">
|
||||||
<mat-icon class="link-icon">link</mat-icon>
|
<span class="fa fa-link fa-2x"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { LocationService } from 'app/shared/location.service';
|
||||||
|
import { MockLocationService } from 'testing/location.service';
|
||||||
|
import { CurrentLocationComponent } from './current-location.component';
|
||||||
|
|
||||||
|
|
||||||
|
describe('CurrentLocationComponent', () => {
|
||||||
|
let element: HTMLElement;
|
||||||
|
let fixture: ComponentFixture<CurrentLocationComponent>;
|
||||||
|
let locationService: MockLocationService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
locationService = new MockLocationService('initial/url');
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CurrentLocationComponent ],
|
||||||
|
providers: [
|
||||||
|
{ provide: LocationService, useValue: locationService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(CurrentLocationComponent);
|
||||||
|
element = fixture.nativeElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the current location', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.textContent).toEqual('initial/url');
|
||||||
|
|
||||||
|
locationService.urlSubject.next('next/url');
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(element.textContent).toEqual('next/url');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,12 @@
|
|||||||
|
/* tslint:disable component-selector */
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { LocationService } from 'app/shared/location.service';
|
||||||
|
|
||||||
|
/** Renders the current location path. */
|
||||||
|
@Component({
|
||||||
|
selector: 'current-location',
|
||||||
|
template: '{{ location.currentPath | async }}'
|
||||||
|
})
|
||||||
|
export class CurrentLocationComponent {
|
||||||
|
constructor(public location: LocationService) { }
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule, Type } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CurrentLocationComponent } from './current-location.component';
|
||||||
|
import { WithCustomElementComponent } from '../element-registry';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ CommonModule ],
|
||||||
|
declarations: [ CurrentLocationComponent ],
|
||||||
|
entryComponents: [ CurrentLocationComponent ]
|
||||||
|
})
|
||||||
|
export class CurrentLocationModule implements WithCustomElementComponent {
|
||||||
|
customElementComponent: Type<any> = CurrentLocationComponent;
|
||||||
|
}
|
@ -36,6 +36,14 @@ export const ELEMENT_MODULE_PATHS_AS_ROUTES = [
|
|||||||
selector: 'code-tabs',
|
selector: 'code-tabs',
|
||||||
loadChildren: './code/code-tabs.module#CodeTabsModule'
|
loadChildren: './code/code-tabs.module#CodeTabsModule'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selector: 'current-location',
|
||||||
|
loadChildren: './current-location/current-location.module#CurrentLocationModule'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'expandable-section',
|
||||||
|
loadChildren: './expandable-section/expandable-section.module#ExpandableSectionModule'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
selector: 'live-example',
|
selector: 'live-example',
|
||||||
loadChildren: './live-example/live-example.module#LiveExampleModule'
|
loadChildren: './live-example/live-example.module#LiveExampleModule'
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
<mat-expansion-panel style="background: inherit">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
{{title}}
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</mat-expansion-panel>
|
@ -0,0 +1,11 @@
|
|||||||
|
/* tslint:disable component-selector */
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
|
||||||
|
/** Custom element wrapper for the material expansion panel with a title input. */
|
||||||
|
@Component({
|
||||||
|
selector: 'aio-expandable-section',
|
||||||
|
templateUrl: 'expandable-section.component.html',
|
||||||
|
})
|
||||||
|
export class ExpandableSectionComponent {
|
||||||
|
@Input() title;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
import { NgModule, Type } from '@angular/core';
|
||||||
|
import { ExpandableSectionComponent } from './expandable-section.component';
|
||||||
|
import { WithCustomElementComponent } from '../element-registry';
|
||||||
|
import { MatExpansionModule } from '@angular/material';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ MatExpansionModule ],
|
||||||
|
declarations: [ ExpandableSectionComponent, ],
|
||||||
|
entryComponents: [ ExpandableSectionComponent ]
|
||||||
|
})
|
||||||
|
export class ExpandableSectionModule implements WithCustomElementComponent {
|
||||||
|
customElementComponent: Type<any> = ExpandableSectionComponent;
|
||||||
|
}
|
@ -113,7 +113,6 @@ describe('DocumentService', () => {
|
|||||||
|
|
||||||
httpMock.expectOne({}).flush(null, {status: 500, statusText: 'Server Error'});
|
httpMock.expectOne({}).flush(null, {status: 500, statusText: 'Server Error'});
|
||||||
expect(latestDocument.id).toEqual(FETCHING_ERROR_ID);
|
expect(latestDocument.id).toEqual(FETCHING_ERROR_ID);
|
||||||
expect(latestDocument.contents).toContain('We are unable to retrieve the "initial/doc" page at this time.');
|
|
||||||
expect(logger.output.error).toEqual([
|
expect(logger.output.error).toEqual([
|
||||||
[jasmine.any(Error)]
|
[jasmine.any(Error)]
|
||||||
]);
|
]);
|
||||||
|
@ -15,13 +15,13 @@ export const FETCHING_ERROR_ID = 'fetching-error';
|
|||||||
|
|
||||||
export const CONTENT_URL_PREFIX = 'generated/';
|
export const CONTENT_URL_PREFIX = 'generated/';
|
||||||
export const DOC_CONTENT_URL_PREFIX = CONTENT_URL_PREFIX + 'docs/';
|
export const DOC_CONTENT_URL_PREFIX = CONTENT_URL_PREFIX + 'docs/';
|
||||||
const FETCHING_ERROR_CONTENTS = (path: string) => `
|
const FETCHING_ERROR_CONTENTS = `
|
||||||
<div class="nf-container l-flex-wrap flex-center">
|
<div class="nf-container l-flex-wrap flex-center">
|
||||||
<div class="nf-icon material-icons">error_outline</div>
|
<div class="nf-icon material-icons">error_outline</div>
|
||||||
<div class="nf-response l-flex-wrap">
|
<div class="nf-response l-flex-wrap">
|
||||||
<h1 class="no-toc">Request for document failed.</h1>
|
<h1 class="no-toc">Request for document failed.</h1>
|
||||||
<p>
|
<p>
|
||||||
We are unable to retrieve the "${path}" page at this time.
|
We are unable to retrieve the "<current-location></current-location>" page at this time.
|
||||||
Please check your connection and try again later.
|
Please check your connection and try again later.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -93,7 +93,7 @@ export class DocumentService {
|
|||||||
this.cache.delete(id);
|
this.cache.delete(id);
|
||||||
return of({
|
return of({
|
||||||
id: FETCHING_ERROR_ID,
|
id: FETCHING_ERROR_ID,
|
||||||
contents: FETCHING_ERROR_CONTENTS(id),
|
contents: FETCHING_ERROR_CONTENTS
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
var SEARCH_TERMS_URL = '/generated/docs/app/search-data.json';
|
var SEARCH_TERMS_URL = '/generated/docs/app/search-data.json';
|
||||||
|
|
||||||
// NOTE: This needs to be kept in sync with `ngsw-config.json`.
|
// NOTE: This needs to be kept in sync with `ngsw-config.json`.
|
||||||
importScripts('/generated/lunr.min.js');
|
importScripts('/assets/js/lunr.min.js');
|
||||||
|
|
||||||
var index;
|
var index;
|
||||||
var pages /* : SearchInfo */ = {};
|
var pages /* : SearchInfo */ = {};
|
||||||
|
@ -17,27 +17,6 @@ describe('CustomIconRegistry', () => {
|
|||||||
expect(svgElement).toEqual(createSvg(svgSrc));
|
expect(svgElement).toEqual(createSvg(svgSrc));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support caching icons with a namespace', () => {
|
|
||||||
const mockHttp: any = {};
|
|
||||||
const mockSanitizer: any = {};
|
|
||||||
const mockDocument: any = {};
|
|
||||||
|
|
||||||
const svgSrc1 = '<svg xmlns="http://www.w3.org/2000/svg"><path d="h100" /></svg>';
|
|
||||||
const svgSrc2 = '<svg xmlns="http://www.w3.org/2000/svg"><path d="h200" /></svg>';
|
|
||||||
const svgSrc3 = '<svg xmlns="http://www.w3.org/2000/svg"><path d="h300" /></svg>';
|
|
||||||
const svgIcons: SvgIconInfo[] = [
|
|
||||||
{ name: 'test_icon', svgSource: svgSrc1 },
|
|
||||||
{ namespace: 'foo', name: 'test_icon', svgSource: svgSrc2 },
|
|
||||||
{ namespace: 'bar', name: 'test_icon', svgSource: svgSrc3 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, mockDocument, svgIcons);
|
|
||||||
let svgElement: SVGElement|undefined;
|
|
||||||
registry.getNamedSvgIcon('test_icon', 'foo').subscribe(el => svgElement = el);
|
|
||||||
|
|
||||||
expect(svgElement).toEqual(createSvg(svgSrc2));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call through to the MdIconRegistry if the icon name is not in the preloaded cache', () => {
|
it('should call through to the MdIconRegistry if the icon name is not in the preloaded cache', () => {
|
||||||
const mockHttp: any = {};
|
const mockHttp: any = {};
|
||||||
const mockSanitizer: any = {};
|
const mockSanitizer: any = {};
|
||||||
|
@ -20,26 +20,21 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|||||||
*/
|
*/
|
||||||
export const SVG_ICONS = new InjectionToken<Array<SvgIconInfo>>('SvgIcons');
|
export const SVG_ICONS = new InjectionToken<Array<SvgIconInfo>>('SvgIcons');
|
||||||
export interface SvgIconInfo {
|
export interface SvgIconInfo {
|
||||||
namespace?: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
svgSource: string;
|
svgSource: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SvgIconMap {
|
interface SvgIconMap {
|
||||||
[namespace: string]: {
|
|
||||||
[iconName: string]: SVGElement;
|
[iconName: string]: SVGElement;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_NS = '$$default';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom replacement for Angular Material's `MdIconRegistry`, which allows
|
* A custom replacement for Angular Material's `MdIconRegistry`, which allows
|
||||||
* us to provide preloaded icon SVG sources.
|
* us to provide preloaded icon SVG sources.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CustomIconRegistry extends MatIconRegistry {
|
export class CustomIconRegistry extends MatIconRegistry {
|
||||||
private preloadedSvgElements: SvgIconMap = {[DEFAULT_NS]: {}};
|
private preloadedSvgElements: SvgIconMap = {};
|
||||||
|
|
||||||
constructor(http: HttpClient, sanitizer: DomSanitizer, @Optional() @Inject(DOCUMENT) document,
|
constructor(http: HttpClient, sanitizer: DomSanitizer, @Optional() @Inject(DOCUMENT) document,
|
||||||
@Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
|
@Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
|
||||||
@ -48,24 +43,18 @@ export class CustomIconRegistry extends MatIconRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNamedSvgIcon(iconName: string, namespace?: string) {
|
getNamedSvgIcon(iconName: string, namespace?: string) {
|
||||||
const nsIconMap = this.preloadedSvgElements[namespace || DEFAULT_NS];
|
if (this.preloadedSvgElements[iconName]) {
|
||||||
const preloadedElement = nsIconMap && nsIconMap[iconName];
|
return of(this.preloadedSvgElements[iconName].cloneNode(true) as SVGElement);
|
||||||
|
}
|
||||||
return preloadedElement
|
return super.getNamedSvgIcon(iconName, namespace);
|
||||||
? of(preloadedElement.cloneNode(true) as SVGElement)
|
|
||||||
: super.getNamedSvgIcon(iconName, namespace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadSvgElements(svgIcons: SvgIconInfo[]) {
|
private loadSvgElements(svgIcons: SvgIconInfo[]) {
|
||||||
const div = document.createElement('DIV');
|
const div = document.createElement('DIV');
|
||||||
svgIcons.forEach(icon => {
|
svgIcons.forEach(icon => {
|
||||||
const ns = icon.namespace || DEFAULT_NS;
|
|
||||||
const nsIconMap = this.preloadedSvgElements[ns] || (this.preloadedSvgElements[ns] = {});
|
|
||||||
|
|
||||||
// SECURITY: the source for the SVG icons is provided in code by trusted developers
|
// SECURITY: the source for the SVG icons is provided in code by trusted developers
|
||||||
div.innerHTML = icon.svgSource;
|
div.innerHTML = icon.svgSource;
|
||||||
|
this.preloadedSvgElements[icon.name] = div.querySelector('svg')!;
|
||||||
nsIconMap[icon.name] = div.querySelector('svg')!;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
aio/src/assets/images/logos/github-icon.svg
Normal file
14
aio/src/assets/images/logos/github-icon.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 51.8 50.4" style="enable-background:new 0 0 51.8 50.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M25.9,0.2C11.8,0.2,0.3,11.7,0.3,25.8c0,11.3,7.3,20.9,17.5,24.3c1.3,0.2,1.7-0.6,1.7-1.2c0-0.6,0-2.6,0-4.8
|
||||||
|
c-7.1,1.5-8.6-3-8.6-3c-1.2-3-2.8-3.7-2.8-3.7c-2.3-1.6,0.2-1.6,0.2-1.6c2.6,0.2,3.9,2.6,3.9,2.6c2.3,3.9,6,2.8,7.5,2.1
|
||||||
|
c0.2-1.7,0.9-2.8,1.6-3.4c-5.7-0.6-11.7-2.8-11.7-12.7c0-2.8,1-5.1,2.6-6.9c-0.3-0.7-1.1-3.3,0.3-6.8c0,0,2.1-0.7,7,2.6
|
||||||
|
c2-0.6,4.2-0.9,6.4-0.9c2.2,0,4.4,0.3,6.4,0.9c4.9-3.3,7-2.6,7-2.6c1.4,3.5,0.5,6.1,0.3,6.8c1.6,1.8,2.6,4.1,2.6,6.9
|
||||||
|
c0,9.8-6,12-11.7,12.6c0.9,0.8,1.7,2.4,1.7,4.7c0,3.4,0,6.2,0,7c0,0.7,0.5,1.5,1.8,1.2c10.2-3.4,17.5-13,17.5-24.3
|
||||||
|
C51.5,11.7,40.1,0.2,25.9,0.2z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
13
aio/src/assets/images/logos/twitter-icon.svg
Normal file
13
aio/src/assets/images/logos/twitter-icon.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 50 49.7" style="enable-background:new 0 0 50 49.7;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M50,9.3c-1.8,0.8-3.8,1.4-5.9,1.6c2.1-1.3,3.7-3.3,4.5-5.7c-2,1.2-4.2,2-6.5,2.5c-1.9-2-4.5-3.2-7.5-3.2
|
||||||
|
c-5.7,0-10.3,4.6-10.3,10.3c0,0.8,0.1,1.6,0.3,2.3C16.1,16.7,8.5,12.6,3.5,6.4c-0.9,1.5-1.4,3.3-1.4,5.2c0,3.6,1.8,6.7,4.6,8.5
|
||||||
|
C5,20,3.4,19.6,2,18.8c0,0,0,0.1,0,0.1c0,5,3.5,9.1,8.2,10.1c-0.9,0.2-1.8,0.4-2.7,0.4c-0.7,0-1.3-0.1-1.9-0.2
|
||||||
|
c1.3,4.1,5.1,7,9.6,7.1c-3.5,2.8-7.9,4.4-12.7,4.4c-0.8,0-1.6,0-2.4-0.1c4.5,2.9,9.9,4.6,15.7,4.6c18.9,0,29.2-15.6,29.2-29.2
|
||||||
|
c0-0.4,0-0.9,0-1.3C46.9,13.2,48.6,11.4,50,9.3z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 937 B |
@ -21,9 +21,9 @@
|
|||||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/images/favicons/favicon-144x144.png">
|
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/images/favicons/favicon-144x144.png">
|
||||||
|
|
||||||
<!-- NOTE: These need to be kept in sync with `ngsw-config.json`. -->
|
<!-- NOTE: These need to be kept in sync with `ngsw-config.json`. -->
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono">
|
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
|
||||||
<link rel="manifest" href="pwa-manifest.json">
|
<link rel="manifest" href="pwa-manifest.json">
|
||||||
|
@ -103,8 +103,8 @@ footer::after {
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background:
|
background:
|
||||||
url('/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 0 left 0 repeat,
|
url('../src/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 0 left 0 repeat,
|
||||||
url('/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 80px left 160px repeat;
|
url('../src/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 80px left 160px repeat;
|
||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
background-size: 320px auto;
|
background-size: 320px auto;
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,21 @@ a.button.mat-button {
|
|||||||
background: $darkgray;
|
background: $darkgray;
|
||||||
color: rgba($white, .87);
|
color: rgba($white, .87);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.button-shield,
|
||||||
|
&.button-shield.mat-button {
|
||||||
|
background-color: $blue;
|
||||||
|
background: $blue url('assets/images/logos/angular/angular_whiteTransparent.svg') 24px 13px no-repeat;
|
||||||
|
color: rgba($white, .87);
|
||||||
|
padding-left: 54px;
|
||||||
|
background-size: 22px 22px;
|
||||||
|
|
||||||
|
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2) {
|
||||||
|
background: $blue url('assets/images/logos/angular/angular_whiteTransparent.svg') 24px 13px no-repeat;
|
||||||
|
background-size: 22px 22px;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta-bar {
|
.cta-bar {
|
||||||
|
@ -70,7 +70,7 @@ aio-contributor {
|
|||||||
@media (hover) { opacity: 0; }
|
@media (hover) { opacity: 0; }
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
.info-item {
|
[mat-button] {
|
||||||
color: $white;
|
color: $white;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -85,14 +85,8 @@ aio-contributor {
|
|||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
|
||||||
mat-icon {
|
.fa-2x {
|
||||||
height: 20px;
|
font-size: 20px;
|
||||||
width: 20px;
|
|
||||||
|
|
||||||
&.link-icon {
|
|
||||||
margin-top: -7px;
|
|
||||||
transform: rotateZ(45deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ aio-notification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mat-sidenav.mat-sidenav.sidenav {
|
mat-sidenav.mat-sidenav.sidenav {
|
||||||
top: 64px + $notificationHeight;
|
top: 56px + $notificationHeight;
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
top: 56px + $notificationHeight;
|
top: 56px + $notificationHeight;
|
||||||
|
@ -65,14 +65,11 @@ See also: [`//.bazelrc`](https://github.com/angular/angular/blob/master/.bazelrc
|
|||||||
- `--test_arg=--node_options=--inspect=9228`: change the inspector port.
|
- `--test_arg=--node_options=--inspect=9228`: change the inspector port.
|
||||||
- `--define=compile=<option>` Controls if ivy or legacy mode is enabled. This switches which compiler is used (ngc, ngtsc, or a tsc pass-through mode).
|
- `--define=compile=<option>` Controls if ivy or legacy mode is enabled. This switches which compiler is used (ngc, ngtsc, or a tsc pass-through mode).
|
||||||
- `legacy`: (default behavior) compile against View Engine, e.g. `--define=compile=legacy`
|
- `legacy`: (default behavior) compile against View Engine, e.g. `--define=compile=legacy`
|
||||||
- `jit`: Compile in ivy JIT mode, e.g. `--define=compile=jit`
|
|
||||||
- `aot`: Compile in ivy AOT move, e.g. `--define=compile=aot`
|
- `aot`: Compile in ivy AOT move, e.g. `--define=compile=aot`
|
||||||
- `--test_tag_filters=<tag>`: filter tests down to tags defined in the `tag` config of your rules in any given `BUILD.bazel`.
|
- `--test_tag_filters=<tag>`: filter tests down to tags defined in the `tag` config of your rules in any given `BUILD.bazel`.
|
||||||
- `no-ivy-aot`: Useful for excluding build and test targets that are not meant to be executed in Ivy AOT mode (`--define=compile=aot`).
|
- `no-ivy-aot`: Useful for excluding build and test targets that are not meant to be executed in Ivy AOT mode (`--define=compile=aot`).
|
||||||
- `no-ivy-jit`: Useful for excluding build and test targets that are not meant to be executed in Ivy JIT mode (`--define=compile=jit`).
|
|
||||||
- `ivy-only`: Useful for excluding all Ivy build and tests targets with `--define=compile=legacy`.
|
- `ivy-only`: Useful for excluding all Ivy build and tests targets with `--define=compile=legacy`.
|
||||||
- `fixme-ivy-aot`: Useful for including/excluding build and test targets that are currently broken in Ivy AOT mode (`--define=compile=aot`).
|
- `fixme-ivy-aot`: Useful for including/excluding build and test targets that are currently broken in Ivy AOT mode (`--define=compile=aot`).
|
||||||
- `fixme-ivy-jit`: Useful for including/excluding build and test targets that are currently broken in Ivy JIT mode (`--define=compile=jit`).
|
|
||||||
|
|
||||||
|
|
||||||
### Debugging a Node Test
|
### Debugging a Node Test
|
||||||
@ -168,23 +165,8 @@ Of course, non-hermeticity in an action can cause problems.
|
|||||||
At worst, you can fetch a broken artifact from the cache, making your build non-reproducible.
|
At worst, you can fetch a broken artifact from the cache, making your build non-reproducible.
|
||||||
For this reason, we are careful to implement our Bazel rules to depend only on their inputs.
|
For this reason, we are careful to implement our Bazel rules to depend only on their inputs.
|
||||||
|
|
||||||
Currently we only use remote caching on CircleCI and we let Angular core developers enable remote caching to speed up their builds.
|
Currently we only use remote caching on CircleCI.
|
||||||
|
We could enable it for developer builds as well, which would make initial builds much faster for developers by fetching already-built artifacts from the cache.
|
||||||
### Remote cache in development
|
|
||||||
|
|
||||||
To enable remote caching for your build:
|
|
||||||
|
|
||||||
1. Go to the service accounts for the ["internal" project](https://console.cloud.google.com/iam-admin/serviceaccounts?project=internal-200822)
|
|
||||||
1. Select "Angular local dev", click on "Edit", scroll to the bottom, and click "Create key"
|
|
||||||
1. When the pop-up shows, select "JSON" for "Key type" and click "Create"
|
|
||||||
1. Save the key in a secure location
|
|
||||||
1. Create a file called `.bazelrc.user` in the root directory of the workspace, and add the following content:
|
|
||||||
|
|
||||||
```
|
|
||||||
build --config=angular-team --google_credentials=[ABSOLUTE_PATH_TO_SERVICE_KEY]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remote cache for Circle CI
|
|
||||||
|
|
||||||
This feature is experimental, and developed by the CircleCI team with guidance from Angular.
|
This feature is experimental, and developed by the CircleCI team with guidance from Angular.
|
||||||
Contact Alex Eagle with questions.
|
Contact Alex Eagle with questions.
|
||||||
@ -196,75 +178,6 @@ Contact Alex Eagle with questions.
|
|||||||
1. Bazel must be configured to connect to the proxy on a local port. This configuration lives in `.circleci/bazel.rc` and is enabled because we overwrite the system Bazel settings in /etc/bazel.bazelrc with this file.
|
1. Bazel must be configured to connect to the proxy on a local port. This configuration lives in `.circleci/bazel.rc` and is enabled because we overwrite the system Bazel settings in /etc/bazel.bazelrc with this file.
|
||||||
1. Each `bazel` command in `.circleci/config.yml` picks up and uses the caching flags.
|
1. Each `bazel` command in `.circleci/config.yml` picks up and uses the caching flags.
|
||||||
|
|
||||||
## Diagnosing slow builds
|
|
||||||
|
|
||||||
If a build seems slow you can use Bazel to diagnose where time is spent.
|
|
||||||
|
|
||||||
The first step is to generate a profile of the build using the `--profile filename_name.profile` flag.
|
|
||||||
```
|
|
||||||
yarn bazel build //packages/compiler --profile filename_name.profile
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate a `filename_name.profile` that you can then analyse using [analyze-profile](https://docs.bazel.build/versions/master/user-manual.html#analyze-profile) command.
|
|
||||||
|
|
||||||
## Using the console profile report
|
|
||||||
|
|
||||||
You can obtain a simple report directly in the console by running:
|
|
||||||
```
|
|
||||||
yarn bazel analyze-profile filename_name.profile
|
|
||||||
```
|
|
||||||
|
|
||||||
This will show the phase summary, individual phase information and critical path.
|
|
||||||
|
|
||||||
You can also list all individual tasks and the time they took using `--task_tree`.
|
|
||||||
```
|
|
||||||
yarn bazel analyze-profile filename_name.profile --task_tree ".*"
|
|
||||||
```
|
|
||||||
|
|
||||||
To show all tasks that take longer than a certain threshold, use the `--task_tree_threshold` flag.
|
|
||||||
The default behaviour is to use a 50ms threshold.
|
|
||||||
```
|
|
||||||
yarn bazel analyze-profile filename_name.profile --task_tree ".*" --task_tree_threshold 5000
|
|
||||||
```
|
|
||||||
|
|
||||||
`--task_tree` takes a regexp as argument that filters by the text shown after the time taken.
|
|
||||||
|
|
||||||
Compiling TypeScript shows as:
|
|
||||||
```
|
|
||||||
70569 ACTION_EXECUTE (10974.826 ms) Compiling TypeScript (devmode) //packages/compiler:compiler []
|
|
||||||
```
|
|
||||||
|
|
||||||
To filter all tasks by TypeScript compilations that took more than 5 seconds, use:
|
|
||||||
|
|
||||||
```
|
|
||||||
yarn bazel analyze-profile filename_name.profile --task_tree "Compiling TypeScript" --task_tree_threshold 5000
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the HTML profile report
|
|
||||||
|
|
||||||
A more comprehensive way to visualize the profile information is through the HTML report:
|
|
||||||
|
|
||||||
```
|
|
||||||
yarn bazel analyze-profile filename_name.profile --html --html_details --html_histograms
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate a `filename_name.profile.html` file that you can open in your browser.
|
|
||||||
|
|
||||||
On the upper right corner that is a small table of contents with links to three areas: Tasks, Legend and Statistics.
|
|
||||||
|
|
||||||
In the Tasks section you will find a graph of where time is spent. Legend shows what the colors in the Tasks graph mean.
|
|
||||||
Hovering over the background will show what phase that is, while hovering over bars will show more details about that specific action.
|
|
||||||
|
|
||||||
The Statistics section shows how long each phase took and how time was spent in that phase.
|
|
||||||
Usually the longest one is the execution phase, which also includes critical path information.
|
|
||||||
|
|
||||||
Also in the Statistics section are the Skylark statistic, split in User-Defined and Builtin function execution time.
|
|
||||||
You can click the "self" header twice to order the table by functions where the most time (in ms) is spent.
|
|
||||||
|
|
||||||
When diagnosing slow builds you should focus on the top time spenders across all phases and functions.
|
|
||||||
Usually there is a single item (or multiple items of the same kind) where the overwhelming majority of time is spent.
|
|
||||||
|
|
||||||
|
|
||||||
## Known issues
|
## Known issues
|
||||||
|
|
||||||
### Webstorm
|
### Webstorm
|
||||||
|
@ -29,6 +29,8 @@ following products on your development machine:
|
|||||||
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||||
to execute the selenium standalone server for e2e testing.
|
to execute the selenium standalone server for e2e testing.
|
||||||
|
|
||||||
|
* (Optional for now) [Bazel](https://bazel.build/), please follow instructions in [BAZEL.md](https://github.com/angular/angular/blob/master/docs/BAZEL.md)
|
||||||
|
|
||||||
## Getting the Sources
|
## Getting the Sources
|
||||||
|
|
||||||
Fork and clone the Angular repository:
|
Fork and clone the Angular repository:
|
||||||
|
@ -32,6 +32,8 @@ gulp.task('format', loadTask('format', 'format'));
|
|||||||
gulp.task('format:untracked', loadTask('format', 'format-untracked'));
|
gulp.task('format:untracked', loadTask('format', 'format-untracked'));
|
||||||
gulp.task('format:diff', loadTask('format', 'format-diff'));
|
gulp.task('format:diff', loadTask('format', 'format-diff'));
|
||||||
gulp.task('format:changed', ['format:untracked', 'format:diff']);
|
gulp.task('format:changed', ['format:untracked', 'format:diff']);
|
||||||
|
gulp.task('build.sh', loadTask('build', 'all'));
|
||||||
|
gulp.task('build.sh:no-bundle', loadTask('build', 'no-bundle'));
|
||||||
gulp.task('lint', ['format:enforce', 'validate-commit-messages', 'tslint']);
|
gulp.task('lint', ['format:enforce', 'validate-commit-messages', 'tslint']);
|
||||||
gulp.task('tslint', ['tools:build'], loadTask('lint'));
|
gulp.task('tslint', ['tools:build'], loadTask('lint'));
|
||||||
gulp.task('validate-commit-messages', loadTask('validate-commit-message'));
|
gulp.task('validate-commit-messages', loadTask('validate-commit-message'));
|
||||||
|
@ -20,14 +20,6 @@ load("@angular//packages/bazel:package.bzl", "rules_angular_dependencies")
|
|||||||
|
|
||||||
rules_angular_dependencies()
|
rules_angular_dependencies()
|
||||||
|
|
||||||
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
|
|
||||||
|
|
||||||
rules_typescript_dependencies()
|
|
||||||
|
|
||||||
load("@build_bazel_rules_nodejs//:package.bzl", "rules_nodejs_dependencies")
|
|
||||||
|
|
||||||
rules_nodejs_dependencies()
|
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "io_bazel_rules_sass",
|
name = "io_bazel_rules_sass",
|
||||||
sha256 = "dbe9fb97d5a7833b2a733eebc78c9c1e3880f676ac8af16e58ccf2139cbcad03",
|
sha256 = "dbe9fb97d5a7833b2a733eebc78c9c1e3880f676ac8af16e58ccf2139cbcad03",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "angular-srcs",
|
"name": "angular-srcs",
|
||||||
"version": "7.1.2",
|
"version": "7.2.0-beta.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"branchPattern": "2.0.*",
|
"branchPattern": "2.0.*",
|
||||||
"description": "Angular - a web framework for modern web apps",
|
"description": "Angular - a web framework for modern web apps",
|
||||||
@ -36,7 +36,6 @@
|
|||||||
"@angular-devkit/architect": "^0.10.6",
|
"@angular-devkit/architect": "^0.10.6",
|
||||||
"@angular-devkit/core": "^7.0.4",
|
"@angular-devkit/core": "^7.0.4",
|
||||||
"@angular-devkit/schematics": "^7.0.4",
|
"@angular-devkit/schematics": "^7.0.4",
|
||||||
"@bazel/karma": "0.21.0",
|
|
||||||
"@bazel/typescript": "0.21.0",
|
"@bazel/typescript": "0.21.0",
|
||||||
"@schematics/angular": "^7.0.4",
|
"@schematics/angular": "^7.0.4",
|
||||||
"@types/chokidar": "1.7.3",
|
"@types/chokidar": "1.7.3",
|
||||||
@ -86,12 +85,13 @@
|
|||||||
"zone.js": "^0.8.26"
|
"zone.js": "^0.8.26"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "2.0.1"
|
"fsevents": "1.1.2"
|
||||||
},
|
},
|
||||||
"// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.",
|
"// 2": "devDependencies are not used under Bazel. Many can be removed after test.sh is deleted.",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@bazel/bazel": "^0.18.1",
|
"@bazel/bazel": "^0.18.1",
|
||||||
"@bazel/ibazel": "~0.8.2",
|
"@bazel/ibazel": "^0.1.1",
|
||||||
|
"@bazel/karma": "0.21.0",
|
||||||
"@types/angular": "^1.6.47",
|
"@types/angular": "^1.6.47",
|
||||||
"@types/base64-js": "1.2.5",
|
"@types/base64-js": "1.2.5",
|
||||||
"@types/jasminewd2": "^2.0.4",
|
"@types/jasminewd2": "^2.0.4",
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
load(":rules_nodejs_package.bzl", "rules_nodejs_dependencies")
|
||||||
|
load(":rules_typescript_package.bzl", "rules_typescript_dependencies")
|
||||||
|
|
||||||
def rules_angular_dependencies():
|
def rules_angular_dependencies():
|
||||||
"""
|
"""
|
||||||
@ -18,13 +20,13 @@ def rules_angular_dependencies():
|
|||||||
|
|
||||||
#
|
#
|
||||||
# Download Bazel toolchain dependencies as needed by build actions
|
# Download Bazel toolchain dependencies as needed by build actions
|
||||||
# Use a SHA to get fix for needing symlink_prefix during npm publishing
|
#
|
||||||
# TODO(alexeagle): updated to next tagged rules_typescript release
|
# TODO(gmagolan): updated to next tagged rules_typescript release
|
||||||
_maybe(
|
_maybe(
|
||||||
http_archive,
|
http_archive,
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/ee218e2a98b9f09ba07cecac8496a5918c47bc5d.zip",
|
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.16.2.zip",
|
||||||
strip_prefix = "rules_nodejs-ee218e2a98b9f09ba07cecac8496a5918c47bc5d",
|
strip_prefix = "rules_nodejs-0.16.2",
|
||||||
)
|
)
|
||||||
|
|
||||||
_maybe(
|
_maybe(
|
||||||
@ -46,6 +48,9 @@ def rules_angular_dependencies():
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rules_typescript_dependencies()
|
||||||
|
rules_nodejs_dependencies()
|
||||||
|
|
||||||
def rules_angular_dev_dependencies():
|
def rules_angular_dev_dependencies():
|
||||||
"""
|
"""
|
||||||
Fetch dependencies needed for local development, but not needed by users.
|
Fetch dependencies needed for local development, but not needed by users.
|
||||||
|
56
packages/bazel/rules_nodejs_package.bzl
Normal file
56
packages/bazel/rules_nodejs_package.bzl
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright 2018 The Bazel Authors. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Dependency-related rules defining our version and dependency versions.
|
||||||
|
|
||||||
|
Fulfills similar role as the package.json file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
# This file mirrored from https://raw.githubusercontent.com/bazelbuild/rules_nodejs/0.15.3/package.bzl
|
||||||
|
VERSION = "0.15.3"
|
||||||
|
|
||||||
|
def rules_nodejs_dependencies():
|
||||||
|
"""
|
||||||
|
Fetch our transitive dependencies.
|
||||||
|
|
||||||
|
If the user wants to get a different version of these, they can just fetch it
|
||||||
|
from their WORKSPACE before calling this function, or not call this function at all.
|
||||||
|
"""
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "bazel_skylib",
|
||||||
|
url = "https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip",
|
||||||
|
strip_prefix = "bazel-skylib-0.3.1",
|
||||||
|
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Needed for Remote Build Execution
|
||||||
|
# See https://releases.bazel.build/bazel-toolchains.html
|
||||||
|
# Not strictly a dependency for all users, but it is convenient for them to have this repository
|
||||||
|
# defined to reduce the effort required to on-board to remote execution.
|
||||||
|
http_archive(
|
||||||
|
name = "bazel_toolchains",
|
||||||
|
urls = [
|
||||||
|
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/cdea5b8675914d0a354d89f108de5d28e54e0edc.tar.gz",
|
||||||
|
"https://github.com/bazelbuild/bazel-toolchains/archive/cdea5b8675914d0a354d89f108de5d28e54e0edc.tar.gz",
|
||||||
|
],
|
||||||
|
strip_prefix = "bazel-toolchains-cdea5b8675914d0a354d89f108de5d28e54e0edc",
|
||||||
|
sha256 = "cefb6ccf86ca592baaa029bcef04148593c0efe8f734542f10293ea58f170715",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _maybe(repo_rule, name, **kwargs):
|
||||||
|
if name not in native.existing_rules():
|
||||||
|
repo_rule(name = name, **kwargs)
|
94
packages/bazel/rules_typescript_package.bzl
Normal file
94
packages/bazel/rules_typescript_package.bzl
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Copyright 2018 The Bazel Authors. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Package file which defines build_bazel_rules_typescript version in skylark
|
||||||
|
|
||||||
|
check_rules_typescript_version can be used in downstream WORKSPACES to check
|
||||||
|
against a minimum dependent build_bazel_rules_typescript version.
|
||||||
|
"""
|
||||||
|
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
# This file mirrored from https://raw.githubusercontent.com/bazelbuild/rules_typescript/0.20.3/package.bzl
|
||||||
|
VERSION = "0.20.3"
|
||||||
|
|
||||||
|
def rules_typescript_dependencies():
|
||||||
|
"""
|
||||||
|
Fetch our transitive dependencies.
|
||||||
|
|
||||||
|
If the user wants to get a different version of these, they can just fetch it
|
||||||
|
from their WORKSPACE before calling this function, or not call this function at all.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TypeScript compiler runs on node.js runtime
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "build_bazel_rules_nodejs",
|
||||||
|
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.15.3.zip"],
|
||||||
|
strip_prefix = "rules_nodejs-0.15.3",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ts_web_test depends on the web testing rules to provision browsers.
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "io_bazel_rules_webtesting",
|
||||||
|
urls = ["https://github.com/bazelbuild/rules_webtesting/archive/111d792b9a5b17f87b6e177e274dbbee46094791.zip"],
|
||||||
|
strip_prefix = "rules_webtesting-111d792b9a5b17f87b6e177e274dbbee46094791",
|
||||||
|
sha256 = "a13af63e928c34eff428d47d31bafeec4e38ee9b6940e70bf2c9cd47184c5c16",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ts_devserver depends on the Go rules.
|
||||||
|
# See https://github.com/bazelbuild/rules_go#setup for the latest version.
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "io_bazel_rules_go",
|
||||||
|
urls = ["https://github.com/bazelbuild/rules_go/archive/cbc1e32fba771845305f15e341fa26595d4a136d.zip"],
|
||||||
|
strip_prefix = "rules_go-cbc1e32fba771845305f15e341fa26595d4a136d",
|
||||||
|
sha256 = "d02b1d8d11fb67fb1e451645256e58a1542170eedd6e2ba160c8540c96f659da",
|
||||||
|
)
|
||||||
|
|
||||||
|
# go_repository is defined in bazel_gazelle
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "bazel_gazelle",
|
||||||
|
urls = ["https://github.com/bazelbuild/bazel-gazelle/archive/109bcfd6880aac2517a1a2d48987226da6337e11.zip"],
|
||||||
|
strip_prefix = "bazel-gazelle-109bcfd6880aac2517a1a2d48987226da6337e11",
|
||||||
|
sha256 = "8f80ce0f7a6f8a3fee1fb863c9a23e1de99d678c1cf3c6f0a128f3b883168208",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ts_auto_deps depends on com_github_bazelbuild_buildtools
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "com_github_bazelbuild_buildtools",
|
||||||
|
url = "https://github.com/bazelbuild/buildtools/archive/0.12.0.zip",
|
||||||
|
strip_prefix = "buildtools-0.12.0",
|
||||||
|
sha256 = "ec495cbd19238c9dc488fd65ca1fee56dcb1a8d6d56ee69a49f2ebe69826c261",
|
||||||
|
)
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Repeat the dependencies of rules_nodejs here!
|
||||||
|
# We can't load() from rules_nodejs yet, because we've only just fetched it.
|
||||||
|
# But we also don't want to make users load and call the rules_nodejs_dependencies
|
||||||
|
# function because we can do that for them, mostly hiding the transitive dependency.
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "bazel_skylib",
|
||||||
|
url = "https://github.com/bazelbuild/bazel-skylib/archive/0.5.0.zip",
|
||||||
|
strip_prefix = "bazel-skylib-0.5.0",
|
||||||
|
sha256 = "ca4e3b8e4da9266c3a9101c8f4704fe2e20eb5625b2a6a7d2d7d45e3dd4efffd",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _maybe(repo_rule, name, **kwargs):
|
||||||
|
if name not in native.existing_rules():
|
||||||
|
repo_rule(name = name, **kwargs)
|
@ -51,10 +51,6 @@ def _esm5_outputs_aspect(target, ctx):
|
|||||||
if not hasattr(target.typescript, "replay_params"):
|
if not hasattr(target.typescript, "replay_params"):
|
||||||
print("WARNING: no esm5 output from target %s//%s:%s available" % (target.label.workspace_root, target.label.package, target.label.name))
|
print("WARNING: no esm5 output from target %s//%s:%s available" % (target.label.workspace_root, target.label.package, target.label.name))
|
||||||
return []
|
return []
|
||||||
elif not target.typescript.replay_params:
|
|
||||||
# In case there are "replay_params" specified but the compile action didn't generate any
|
|
||||||
# outputs (e.g. only "d.ts" files), we cannot create ESM5 outputs for this target either.
|
|
||||||
return []
|
|
||||||
|
|
||||||
# We create a new tsconfig.json file that will have our compilation settings
|
# We create a new tsconfig.json file that will have our compilation settings
|
||||||
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)
|
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)
|
||||||
@ -169,7 +165,7 @@ def flatten_esm5(ctx):
|
|||||||
ctx: the skylark rule execution context
|
ctx: the skylark rule execution context
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
depset of flattened files
|
list of flattened files
|
||||||
"""
|
"""
|
||||||
esm5_sources = []
|
esm5_sources = []
|
||||||
result = []
|
result = []
|
||||||
@ -190,4 +186,4 @@ def flatten_esm5(ctx):
|
|||||||
template = f,
|
template = f,
|
||||||
substitutions = {},
|
substitutions = {},
|
||||||
)
|
)
|
||||||
return depset(result)
|
return result
|
||||||
|
@ -18,33 +18,18 @@ function main(args) {
|
|||||||
if (args.length < 3) {
|
if (args.length < 3) {
|
||||||
console.error('Usage: $0 input.tsconfig.json output.tsconfig.json newRoot binDir');
|
console.error('Usage: $0 input.tsconfig.json output.tsconfig.json newRoot binDir');
|
||||||
}
|
}
|
||||||
|
[input, output, newRoot, binDir] = args;
|
||||||
|
|
||||||
const [input, output, newRoot, binDir] = args;
|
|
||||||
const data = JSON.parse(fs.readFileSync(input, {encoding: 'utf-8'}));
|
const data = JSON.parse(fs.readFileSync(input, {encoding: 'utf-8'}));
|
||||||
const {compilerOptions, bazelOptions} = data;
|
data['compilerOptions']['target'] = 'es5';
|
||||||
|
data['bazelOptions']['es5Mode'] = true;
|
||||||
// Relative path to the execroot that refers to the directory for the ES5 output files.
|
data['compilerOptions']['outDir'] = path.join(data['compilerOptions']['outDir'], newRoot);
|
||||||
const newOutputBase = path.posix.join(binDir, newRoot);
|
|
||||||
|
|
||||||
// Update the compiler options to produce ES5 output. Also ensure that the new ES5 output
|
|
||||||
// directory is used.
|
|
||||||
compilerOptions['target'] = 'es5';
|
|
||||||
compilerOptions['outDir'] = path.posix.join(compilerOptions['outDir'], newRoot);
|
|
||||||
|
|
||||||
bazelOptions['es5Mode'] = true;
|
|
||||||
bazelOptions['tsickleExternsPath'] =
|
|
||||||
bazelOptions['tsickleExternsPath'].replace(binDir, newOutputBase);
|
|
||||||
|
|
||||||
if (data['angularCompilerOptions']) {
|
if (data['angularCompilerOptions']) {
|
||||||
const {angularCompilerOptions} = data;
|
|
||||||
// Don't enable tsickle's closure conversions
|
// Don't enable tsickle's closure conversions
|
||||||
angularCompilerOptions['annotateForClosureCompiler'] = false;
|
data['angularCompilerOptions']['annotateForClosureCompiler'] = false;
|
||||||
// Note: It's important that the "expectedOut" is only modified in a way that still
|
data['angularCompilerOptions']['expectedOut'] =
|
||||||
// keeps posix normalized paths. Otherwise this could cause unexpected behavior because
|
data['angularCompilerOptions']['expectedOut'].map(
|
||||||
// ngc-wrapped is expecting POSIX paths and the TypeScript Bazel rules by default only pass
|
f => f.replace(/\.closure\.js$/, '.js').replace(binDir, path.join(binDir, newRoot)));
|
||||||
// POSIX paths as well.
|
|
||||||
angularCompilerOptions['expectedOut'] = angularCompilerOptions['expectedOut'].map(
|
|
||||||
f => f.replace(/\.closure\.js$/, '.js').replace(binDir, newOutputBase));
|
|
||||||
}
|
}
|
||||||
fs.writeFileSync(output, JSON.stringify(data));
|
fs.writeFileSync(output, JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ load(
|
|||||||
def compile_strategy(ctx):
|
def compile_strategy(ctx):
|
||||||
"""Detect which strategy should be used to implement ng_module.
|
"""Detect which strategy should be used to implement ng_module.
|
||||||
|
|
||||||
Depending on the value of the 'compile' define flag or the '_global_mode' attribute, ng_module
|
Depending on the value of the 'compile' define flag, ng_module
|
||||||
can be implemented in various ways. This function reads the configuration passed by the user and
|
can be implemented in various ways. This function reads the configuration passed by the user and
|
||||||
determines which mode is active.
|
determines which mode is active.
|
||||||
|
|
||||||
@ -30,19 +30,16 @@ def compile_strategy(ctx):
|
|||||||
ctx: skylark rule execution context
|
ctx: skylark rule execution context
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
one of 'legacy', 'aot', 'jit', or 'global' depending on the configuration in ctx
|
one of 'legacy' or 'aot' depending on the configuration in ctx
|
||||||
"""
|
"""
|
||||||
|
|
||||||
strategy = "legacy"
|
strategy = "legacy"
|
||||||
if "compile" in ctx.var:
|
if "compile" in ctx.var:
|
||||||
strategy = ctx.var["compile"]
|
strategy = ctx.var["compile"]
|
||||||
|
|
||||||
if strategy not in ["legacy", "aot", "jit"]:
|
if strategy not in ["legacy", "aot"]:
|
||||||
fail("Unknown --define=compile value '%s'" % strategy)
|
fail("Unknown --define=compile value '%s'" % strategy)
|
||||||
|
|
||||||
if strategy == "legacy" and hasattr(ctx.attr, "_global_mode") and ctx.attr._global_mode:
|
|
||||||
strategy = "global"
|
|
||||||
|
|
||||||
return strategy
|
return strategy
|
||||||
|
|
||||||
def _compiler_name(ctx):
|
def _compiler_name(ctx):
|
||||||
@ -58,12 +55,8 @@ def _compiler_name(ctx):
|
|||||||
strategy = compile_strategy(ctx)
|
strategy = compile_strategy(ctx)
|
||||||
if strategy == "legacy":
|
if strategy == "legacy":
|
||||||
return "ngc"
|
return "ngc"
|
||||||
elif strategy == "global":
|
|
||||||
return "ngc.ivy"
|
|
||||||
elif strategy == "aot":
|
elif strategy == "aot":
|
||||||
return "ngtsc"
|
return "ngtsc"
|
||||||
elif strategy == "jit":
|
|
||||||
return "tsc"
|
|
||||||
else:
|
else:
|
||||||
fail("unreachable")
|
fail("unreachable")
|
||||||
|
|
||||||
@ -80,12 +73,8 @@ def _enable_ivy_value(ctx):
|
|||||||
strategy = compile_strategy(ctx)
|
strategy = compile_strategy(ctx)
|
||||||
if strategy == "legacy":
|
if strategy == "legacy":
|
||||||
return False
|
return False
|
||||||
elif strategy == "global":
|
|
||||||
return True
|
|
||||||
elif strategy == "aot":
|
elif strategy == "aot":
|
||||||
return "ngtsc"
|
return "ngtsc"
|
||||||
elif strategy == "jit":
|
|
||||||
return "tsc"
|
|
||||||
else:
|
else:
|
||||||
fail("unreachable")
|
fail("unreachable")
|
||||||
|
|
||||||
@ -101,7 +90,7 @@ def _include_ng_files(ctx):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
strategy = compile_strategy(ctx)
|
strategy = compile_strategy(ctx)
|
||||||
return strategy == "legacy" or strategy == "global"
|
return strategy == "legacy"
|
||||||
|
|
||||||
def _basename_of(ctx, file):
|
def _basename_of(ctx, file):
|
||||||
ext_len = len(".ts")
|
ext_len = len(".ts")
|
||||||
@ -169,7 +158,7 @@ def _expected_outs(ctx):
|
|||||||
|
|
||||||
if short_path.endswith(".ts") and not short_path.endswith(".d.ts"):
|
if short_path.endswith(".ts") and not short_path.endswith(".d.ts"):
|
||||||
basename = short_path[len(package_prefix):-len(".ts")]
|
basename = short_path[len(package_prefix):-len(".ts")]
|
||||||
if include_ng_files and (len(factory_basename_set.to_list()) == 0 or basename in factory_basename_set.to_list()):
|
if include_ng_files and (len(factory_basename_set) == 0 or basename in factory_basename_set):
|
||||||
devmode_js = [
|
devmode_js = [
|
||||||
".ngfactory.js",
|
".ngfactory.js",
|
||||||
".ngsummary.js",
|
".ngsummary.js",
|
||||||
@ -636,16 +625,3 @@ This rule extends the [ts_library] rule.
|
|||||||
|
|
||||||
[ts_library]: http://tsetse.info/api/build_defs.html#ts_library
|
[ts_library]: http://tsetse.info/api/build_defs.html#ts_library
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO(alxhub): this rule causes legacy ngc to produce Ivy outputs from global analysis information.
|
|
||||||
# It exists to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used
|
|
||||||
# instead, and should be removed once ngtsc is capable of fulfilling the same requirements.
|
|
||||||
internal_global_ng_module = rule(
|
|
||||||
implementation = _ng_module_impl,
|
|
||||||
attrs = dict(NG_MODULE_RULE_ATTRS, **{
|
|
||||||
"_global_mode": attr.bool(
|
|
||||||
default = True,
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
outputs = COMMON_OUTPUTS,
|
|
||||||
)
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
# Copyright Google LLC. All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Use of this source code is governed by an MIT-style license that can be
|
|
||||||
# found in the LICENSE file at https://angular.io/license
|
|
||||||
|
|
||||||
"""Collect TypeScript definition files from a rule context.
|
|
||||||
|
|
||||||
This is used to find all files that will be copied into a "ng_package".
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _filter_typing_files(files):
|
|
||||||
return [file for file in files if file.path.endswith(".d.ts")]
|
|
||||||
|
|
||||||
def collect_type_definitions(ctx):
|
|
||||||
"""Returns a file tree containing only TypeScript definition files.
|
|
||||||
|
|
||||||
This is useful when packaging a "ng_package" where we only want to package specified
|
|
||||||
definition files.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
ctx: ctx.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A file tree containing only TypeScript definition files.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Add all source files and filter for TypeScript definition files
|
|
||||||
# See: https://docs.bazel.build/versions/master/skylark/lib/File.html#is_source
|
|
||||||
collected_files = _filter_typing_files([d for d in ctx.files.deps if d.is_source])
|
|
||||||
|
|
||||||
# In case source files have been explicitly specified in the attributes, just collect
|
|
||||||
# them and filter for definition files.
|
|
||||||
if hasattr(ctx.attr, "srcs"):
|
|
||||||
collected_files += _filter_typing_files(ctx.files.srcs)
|
|
||||||
|
|
||||||
# Collect all TypeScript definition files from the specified dependencies.
|
|
||||||
for dep in ctx.attr.deps:
|
|
||||||
if hasattr(dep, "typescript"):
|
|
||||||
collected_files += dep.typescript.transitive_declarations.to_list()
|
|
||||||
|
|
||||||
return collected_files
|
|
@ -30,7 +30,6 @@ load(
|
|||||||
load("@build_bazel_rules_nodejs//:internal/node.bzl", "sources_aspect")
|
load("@build_bazel_rules_nodejs//:internal/node.bzl", "sources_aspect")
|
||||||
load("@build_bazel_rules_nodejs//internal/common:node_module_info.bzl", "NodeModuleInfo")
|
load("@build_bazel_rules_nodejs//internal/common:node_module_info.bzl", "NodeModuleInfo")
|
||||||
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
|
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
|
||||||
load("//packages/bazel/src/ng_package:collect-type-definitions.bzl", "collect_type_definitions")
|
|
||||||
|
|
||||||
_DEFAULT_NG_PACKAGER = "@npm//@angular/bazel/bin:packager"
|
_DEFAULT_NG_PACKAGER = "@npm//@angular/bazel/bin:packager"
|
||||||
|
|
||||||
@ -154,20 +153,11 @@ def _flatten_paths(directory):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
# takes an depset of files and returns an array that doesn't contain any generated files by ngc
|
# takes an depset of files and returns an array that doesn't contain any generated files by ngc
|
||||||
def _filter_out_generated_files(files, extension, filter_external_files):
|
def _filter_out_generated_files(files):
|
||||||
result = []
|
result = []
|
||||||
for file in files:
|
for file in files:
|
||||||
# If the "filter_external_files" parameter has been set to true, filter out files
|
if (not (file.path.endswith(".ngfactory.js") or file.path.endswith(".ngsummary.js") or file.path.endswith(".ngstyle.js"))):
|
||||||
# that refer to external workspaces.
|
|
||||||
if filter_external_files and file.short_path.startswith("../"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Filter out files that are generated by the Angular Compiler CLI.
|
|
||||||
if (not (file.path.endswith(".ngfactory.%s" % extension) or
|
|
||||||
file.path.endswith(".ngsummary.%s" % extension) or
|
|
||||||
file.path.endswith(".ngstyle.%s" % extension))):
|
|
||||||
result.append(file)
|
result.append(file)
|
||||||
|
|
||||||
return depset(result)
|
return depset(result)
|
||||||
|
|
||||||
def _esm2015_root_dir(ctx):
|
def _esm2015_root_dir(ctx):
|
||||||
@ -184,9 +174,8 @@ def _filter_js_inputs(all_inputs):
|
|||||||
def _ng_package_impl(ctx):
|
def _ng_package_impl(ctx):
|
||||||
npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name)
|
npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name)
|
||||||
|
|
||||||
esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx), "js", False)
|
esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx))
|
||||||
esm5_sources = _filter_out_generated_files(flatten_esm5(ctx), "js", False)
|
esm5_sources = _filter_out_generated_files(flatten_esm5(ctx))
|
||||||
type_definitions = _filter_out_generated_files(collect_type_definitions(ctx), "d.ts", True)
|
|
||||||
|
|
||||||
# These accumulators match the directory names where the files live in the
|
# These accumulators match the directory names where the files live in the
|
||||||
# Angular package format.
|
# Angular package format.
|
||||||
@ -295,7 +284,11 @@ def _ng_package_impl(ctx):
|
|||||||
ctx.files.srcs +
|
ctx.files.srcs +
|
||||||
ctx.files.data +
|
ctx.files.data +
|
||||||
esm5_sources.to_list() +
|
esm5_sources.to_list() +
|
||||||
type_definitions.to_list() +
|
depset(transitive = [
|
||||||
|
d.typescript.transitive_declarations
|
||||||
|
for d in ctx.attr.deps
|
||||||
|
if hasattr(d, "typescript")
|
||||||
|
]).to_list() +
|
||||||
[f.js for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles] +
|
[f.js for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles] +
|
||||||
[f.map for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles if f.map]
|
[f.map for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles if f.map]
|
||||||
)
|
)
|
||||||
@ -328,16 +321,15 @@ def _ng_package_impl(ctx):
|
|||||||
# placeholder
|
# placeholder
|
||||||
packager_args.add("")
|
packager_args.add("")
|
||||||
|
|
||||||
packager_args.add_joined(_flatten_paths(fesm2015), join_with = ",", omit_if_empty = False)
|
packager_args.add_joined(_flatten_paths(fesm2015), join_with = ",")
|
||||||
packager_args.add_joined(_flatten_paths(fesm5), join_with = ",", omit_if_empty = False)
|
packager_args.add_joined(_flatten_paths(fesm5), join_with = ",")
|
||||||
packager_args.add_joined(_flatten_paths(esm2015), join_with = ",", omit_if_empty = False)
|
packager_args.add_joined(_flatten_paths(esm2015), join_with = ",")
|
||||||
packager_args.add_joined(_flatten_paths(esm5), join_with = ",", omit_if_empty = False)
|
packager_args.add_joined(_flatten_paths(esm5), join_with = ",")
|
||||||
packager_args.add_joined(_flatten_paths(bundles), join_with = ",", omit_if_empty = False)
|
packager_args.add_joined(_flatten_paths(bundles), join_with = ",")
|
||||||
packager_args.add_joined([s.path for s in ctx.files.srcs], join_with = ",", omit_if_empty = False)
|
packager_args.add_joined([s.path for s in ctx.files.srcs], join_with = ",")
|
||||||
packager_args.add_joined([s.path for s in type_definitions], join_with = ",", omit_if_empty = False)
|
|
||||||
|
|
||||||
# TODO: figure out a better way to gather runfiles providers from the transitive closure.
|
# TODO: figure out a better way to gather runfiles providers from the transitive closure.
|
||||||
packager_args.add_joined([d.path for d in ctx.files.data], join_with = ",", omit_if_empty = False)
|
packager_args.add_joined([d.path for d in ctx.files.data], join_with = ",")
|
||||||
|
|
||||||
if ctx.file.license_banner:
|
if ctx.file.license_banner:
|
||||||
packager_inputs.append(ctx.file.license_banner)
|
packager_inputs.append(ctx.file.license_banner)
|
||||||
|
@ -73,9 +73,6 @@ function main(args: string[]): number {
|
|||||||
// List of all files in the ng_package rule's srcs.
|
// List of all files in the ng_package rule's srcs.
|
||||||
srcsArg,
|
srcsArg,
|
||||||
|
|
||||||
// List of all type definitions that need to packaged into the ng_package.
|
|
||||||
typeDefinitionsArg,
|
|
||||||
|
|
||||||
// List of all files in the ng_package rule's data.
|
// List of all files in the ng_package rule's data.
|
||||||
dataArg,
|
dataArg,
|
||||||
|
|
||||||
@ -88,7 +85,6 @@ function main(args: string[]): number {
|
|||||||
const esm2015 = esm2015Arg.split(',').filter(s => !!s);
|
const esm2015 = esm2015Arg.split(',').filter(s => !!s);
|
||||||
const esm5 = esm5Arg.split(',').filter(s => !!s);
|
const esm5 = esm5Arg.split(',').filter(s => !!s);
|
||||||
const bundles = bundlesArg.split(',').filter(s => !!s);
|
const bundles = bundlesArg.split(',').filter(s => !!s);
|
||||||
const typeDefinitions = typeDefinitionsArg.split(',').filter(s => !!s);
|
|
||||||
const srcs = srcsArg.split(',').filter(s => !!s);
|
const srcs = srcsArg.split(',').filter(s => !!s);
|
||||||
const dataFiles: string[] = dataArg.split(',').filter(s => !!s);
|
const dataFiles: string[] = dataArg.split(',').filter(s => !!s);
|
||||||
const modulesManifest = JSON.parse(modulesManifestArg);
|
const modulesManifest = JSON.parse(modulesManifestArg);
|
||||||
@ -138,8 +134,7 @@ function main(args: string[]): number {
|
|||||||
* @param outDir path where we copy the file, relative to the out
|
* @param outDir path where we copy the file, relative to the out
|
||||||
*/
|
*/
|
||||||
function writeEsmFile(file: string, suffix: string, outDir: string) {
|
function writeEsmFile(file: string, suffix: string, outDir: string) {
|
||||||
// Note that the specified file path is always using the posix path delimiter.
|
const root = file.substr(0, file.lastIndexOf(suffix + path.sep) + suffix.length + 1);
|
||||||
const root = file.substr(0, file.lastIndexOf(`${suffix}/`) + suffix.length + 1);
|
|
||||||
const rel = path.dirname(path.relative(path.join(root, srcDir), file));
|
const rel = path.dirname(path.relative(path.join(root, srcDir), file));
|
||||||
if (!rel.startsWith('..')) {
|
if (!rel.startsWith('..')) {
|
||||||
copyFile(file, path.join(out, outDir), rel);
|
copyFile(file, path.join(out, outDir), rel);
|
||||||
@ -153,9 +148,8 @@ function main(args: string[]): number {
|
|||||||
fesm2015.forEach(file => { copyFile(file, out, 'fesm2015'); });
|
fesm2015.forEach(file => { copyFile(file, out, 'fesm2015'); });
|
||||||
fesm5.forEach(file => { copyFile(file, out, 'fesm5'); });
|
fesm5.forEach(file => { copyFile(file, out, 'fesm5'); });
|
||||||
|
|
||||||
// Copy all type definitions into the package. This is necessary so that developers can use
|
const allsrcs = shx.find('-R', binDir);
|
||||||
// the package with type definitions.
|
allsrcs.filter(hasFileExtension('.d.ts')).forEach((f: string) => {
|
||||||
typeDefinitions.forEach((f: string) => {
|
|
||||||
const content = fs.readFileSync(f, 'utf-8')
|
const content = fs.readFileSync(f, 'utf-8')
|
||||||
// Strip the named AMD module for compatibility with non-bazel users
|
// Strip the named AMD module for compatibility with non-bazel users
|
||||||
.replace(/^\/\/\/ <amd-module name=.*\/>\n/gm, '');
|
.replace(/^\/\/\/ <amd-module name=.*\/>\n/gm, '');
|
||||||
@ -241,6 +235,12 @@ function main(args: string[]): number {
|
|||||||
return `./${result}`;
|
return `./${result}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a predicate function to filter non-generated files with a specified extension. */
|
||||||
|
function hasFileExtension(ext: string): (path: string) => boolean {
|
||||||
|
return f => f.endsWith(ext) && !f.endsWith(`.ngfactory${ext}`) &&
|
||||||
|
!f.endsWith(`.ngsummary${ext}`);
|
||||||
|
}
|
||||||
|
|
||||||
function copyFile(file: string, baseDir: string, relative = '.') {
|
function copyFile(file: string, baseDir: string, relative = '.') {
|
||||||
const dir = path.join(baseDir, relative);
|
const dir = path.join(baseDir, relative);
|
||||||
shx.mkdir('-p', dir);
|
shx.mkdir('-p', dir);
|
||||||
@ -253,12 +253,6 @@ function main(args: string[]): number {
|
|||||||
const outputPath = path.join(dir, ...path.basename(file).split('__'));
|
const outputPath = path.join(dir, ...path.basename(file).split('__'));
|
||||||
shx.mkdir('-p', path.dirname(outputPath));
|
shx.mkdir('-p', path.dirname(outputPath));
|
||||||
shx.mv(path.join(dir, path.basename(file)), outputPath);
|
shx.mv(path.join(dir, path.basename(file)), outputPath);
|
||||||
|
|
||||||
// if we are renaming the .js file, we'll also need to update the sourceMappingURL in the file
|
|
||||||
if (file.endsWith('.js')) {
|
|
||||||
shx.chmod('+w', outputPath);
|
|
||||||
shx.sed('-i', `${path.basename(file)}.map`, `${path.basename(outputPath)}.map`, outputPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ def _protractor_web_test_impl(ctx):
|
|||||||
|
|
||||||
specs = [
|
specs = [
|
||||||
expand_path_into_runfiles(ctx, f.short_path)
|
expand_path_into_runfiles(ctx, f.short_path)
|
||||||
for f in files.to_list()
|
for f in files
|
||||||
]
|
]
|
||||||
|
|
||||||
configuration_sources = []
|
configuration_sources = []
|
||||||
|
@ -70,26 +70,6 @@ describe('@angular/common ng_package', () => {
|
|||||||
expect(shx.ls('-R', 'fesm2015').stdout.split('\n').filter(n => !!n).sort()).toEqual(expected);
|
expect(shx.ls('-R', 'fesm2015').stdout.split('\n').filter(n => !!n).sort()).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have the correct source map paths', () => {
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm5/common.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=common.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm2015/common.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=common.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm5/http.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=http.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm2015/http.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=http.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm5/http/testing.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=testing.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm2015/http/testing.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=testing.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm5/testing.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=testing.js.map');
|
|
||||||
expect(shx.grep('sourceMappingURL', 'fesm2015/testing.js'))
|
|
||||||
.toMatch('//# sourceMappingURL=testing.js.map');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('should have module resolution properties in the package.json file for', () => {
|
describe('should have module resolution properties in the package.json file for', () => {
|
||||||
// https://github.com/angular/common-builds/blob/master/package.json
|
// https://github.com/angular/common-builds/blob/master/package.json
|
||||||
it('/', () => {
|
it('/', () => {
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
||||||
|
|
||||||
ts_library(
|
# TODO: these tests don't even compile under bazel right now
|
||||||
name = "test_lib",
|
#
|
||||||
testonly = True,
|
# ts_library(
|
||||||
srcs = glob(["**/*.ts"]),
|
# name = "test_lib",
|
||||||
)
|
# testonly = True,
|
||||||
|
# srcs = glob(["**/*.ts"]),
|
||||||
|
# tags = ["manual"]
|
||||||
|
# )
|
||||||
|
|
||||||
jasmine_node_test(
|
# jasmine_node_test(
|
||||||
name = "test",
|
# name = "test",
|
||||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
# deps = [
|
||||||
deps = [
|
# ":test_lib",
|
||||||
":test_lib",
|
# "//packages/benchpress"
|
||||||
"//packages/benchpress",
|
# # "//tools/testing:node",
|
||||||
"//packages/core/testing",
|
# ],
|
||||||
"//tools/testing:node",
|
# tags = ["manual"]
|
||||||
"@ngdeps//protractor",
|
# )
|
||||||
],
|
|
||||||
)
|
|
||||||
|
@ -9,14 +9,11 @@
|
|||||||
import {$, browser} from 'protractor';
|
import {$, browser} from 'protractor';
|
||||||
|
|
||||||
const benchpress = require('../../index.js');
|
const benchpress = require('../../index.js');
|
||||||
|
|
||||||
// TODO: this test is currnetly failing. it seems that it didn't run on the ci for a while
|
|
||||||
xdescribe('deep tree baseline', function() {
|
|
||||||
const runner = new benchpress.Runner([
|
const runner = new benchpress.Runner([
|
||||||
// use protractor as Webdriver client
|
// use protractor as Webdriver client
|
||||||
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
benchpress.SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
|
||||||
// use RegressionSlopeValidator to validate samples
|
// use RegressionSlopeValidator to validate samples
|
||||||
benchpress.Validator.bind(benchpress.RegressionSlopeValidator),
|
benchpress.Validator.bindTo(benchpress.RegressionSlopeValidator),
|
||||||
// use 10 samples to calculate slope regression
|
// use 10 samples to calculate slope regression
|
||||||
benchpress.bind(benchpress.RegressionSlopeValidator.SAMPLE_SIZE).toValue(20),
|
benchpress.bind(benchpress.RegressionSlopeValidator.SAMPLE_SIZE).toValue(20),
|
||||||
// use the script metric to calculate slope regression
|
// use the script metric to calculate slope regression
|
||||||
@ -24,7 +21,7 @@ xdescribe('deep tree baseline', function() {
|
|||||||
benchpress.bind(benchpress.Options.FORCE_GC).toValue(true)
|
benchpress.bind(benchpress.Options.FORCE_GC).toValue(true)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
describe('deep tree baseline', function() {
|
||||||
it('should be fast!', function(done) {
|
it('should be fast!', function(done) {
|
||||||
browser.ignoreSynchronization = true;
|
browser.ignoreSynchronization = true;
|
||||||
browser.get('http://localhost:8001/playground/src/benchpress/');
|
browser.get('http://localhost:8001/playground/src/benchpress/');
|
@ -20,8 +20,7 @@ const assertEventsContainsName = function(events: any[], eventName: string) {
|
|||||||
expect(found).toBeTruthy();
|
expect(found).toBeTruthy();
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this test is currnetly failing. it seems that it didn't run on the ci for a while
|
describe('firefox extension', function() {
|
||||||
xdescribe('firefox extension', function() {
|
|
||||||
const TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
const TEST_URL = 'http://localhost:8001/playground/src/hello_world/index.html';
|
||||||
|
|
||||||
it('should measure performance', function() {
|
it('should measure performance', function() {
|
||||||
|
@ -232,10 +232,12 @@ export class HttpXhrBackend implements HttpBackend {
|
|||||||
// Connection timeout, DNS error, offline, etc. These are actual errors, and are
|
// Connection timeout, DNS error, offline, etc. These are actual errors, and are
|
||||||
// transmitted on the error channel.
|
// transmitted on the error channel.
|
||||||
const onError = (error: ErrorEvent) => {
|
const onError = (error: ErrorEvent) => {
|
||||||
|
const {url} = partialFromXhr();
|
||||||
const res = new HttpErrorResponse({
|
const res = new HttpErrorResponse({
|
||||||
error,
|
error,
|
||||||
status: xhr.status || 0,
|
status: xhr.status || 0,
|
||||||
statusText: xhr.statusText || 'Unknown Error',
|
statusText: xhr.statusText || 'Unknown Error',
|
||||||
|
url: url || undefined,
|
||||||
});
|
});
|
||||||
observer.error(res);
|
observer.error(res);
|
||||||
};
|
};
|
||||||
|
@ -142,6 +142,7 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||||||
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
|
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
|
||||||
expect(err instanceof HttpErrorResponse).toBe(true);
|
expect(err instanceof HttpErrorResponse).toBe(true);
|
||||||
expect(err.error instanceof Error);
|
expect(err.error instanceof Error);
|
||||||
|
expect(err.url).toBe('/test');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
factory.mock.mockErrorEvent(new Error('blah'));
|
factory.mock.mockErrorEvent(new Error('blah'));
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library", "ts_web_test_suite")
|
|
||||||
|
|
||||||
ts_library(
|
|
||||||
name = "test_lib",
|
|
||||||
testonly = True,
|
|
||||||
srcs = glob(
|
|
||||||
["**/*.ts"],
|
|
||||||
),
|
|
||||||
deps = [
|
|
||||||
"//packages/common/http",
|
|
||||||
"//packages/common/http/testing",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
jasmine_node_test(
|
|
||||||
name = "test",
|
|
||||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
|
||||||
deps = [
|
|
||||||
":test_lib",
|
|
||||||
"//tools/testing:node",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
ts_web_test_suite(
|
|
||||||
name = "test_web",
|
|
||||||
deps = [
|
|
||||||
":test_lib",
|
|
||||||
],
|
|
||||||
)
|
|
@ -6,8 +6,10 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {HttpClient} from '@angular/common/http';
|
import {ddescribe, describe, fit, it} from '@angular/core/testing/src/testing_internal';
|
||||||
import {HttpClientTestingBackend} from '@angular/common/http/testing/src/backend';
|
|
||||||
|
import {HttpClient} from '../../src/client';
|
||||||
|
import {HttpClientTestingBackend} from '../src/backend';
|
||||||
|
|
||||||
describe('HttpClient TestRequest', () => {
|
describe('HttpClient TestRequest', () => {
|
||||||
it('accepts a null body', () => {
|
it('accepts a null body', () => {
|
||||||
|
@ -114,7 +114,7 @@ export class PercentPipe implements PipeTransform {
|
|||||||
* - `minFractionDigits`: The minimum number of digits after the decimal point.
|
* - `minFractionDigits`: The minimum number of digits after the decimal point.
|
||||||
* Default is `0`.
|
* Default is `0`.
|
||||||
* - `maxFractionDigits`: The maximum number of digits after the decimal point.
|
* - `maxFractionDigits`: The maximum number of digits after the decimal point.
|
||||||
* Default is `0`.
|
* Default is `3`.
|
||||||
* @param locale A locale code for the locale format rules to use.
|
* @param locale A locale code for the locale format rules to use.
|
||||||
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
|
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
|
||||||
* See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app).
|
* See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app).
|
||||||
|
@ -144,7 +144,6 @@ describe('insert/remove', () => {
|
|||||||
expect(fixture.nativeElement).toHaveText('projected foo');
|
expect(fixture.nativeElement).toHaveText('projected foo');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
|
||||||
it('should resolve components from other modules, if supplied', async(() => {
|
it('should resolve components from other modules, if supplied', async(() => {
|
||||||
const compiler = TestBed.get(Compiler) as Compiler;
|
const compiler = TestBed.get(Compiler) as Compiler;
|
||||||
let fixture = TestBed.createComponent(TestComponent);
|
let fixture = TestBed.createComponent(TestComponent);
|
||||||
@ -159,7 +158,7 @@ describe('insert/remove', () => {
|
|||||||
expect(fixture.nativeElement).toHaveText('baz');
|
expect(fixture.nativeElement).toHaveText('baz');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
fixmeIvy('FW-739: destroy on NgModuleRef is not being called') &&
|
||||||
it('should clean up moduleRef, if supplied', async(() => {
|
it('should clean up moduleRef, if supplied', async(() => {
|
||||||
let destroyed = false;
|
let destroyed = false;
|
||||||
const compiler = TestBed.get(Compiler) as Compiler;
|
const compiler = TestBed.get(Compiler) as Compiler;
|
||||||
@ -176,7 +175,6 @@ describe('insert/remove', () => {
|
|||||||
expect(moduleRef.destroy).toHaveBeenCalled();
|
expect(moduleRef.destroy).toHaveBeenCalled();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
|
||||||
it('should not re-create moduleRef when it didn\'t actually change', async(() => {
|
it('should not re-create moduleRef when it didn\'t actually change', async(() => {
|
||||||
const compiler = TestBed.get(Compiler) as Compiler;
|
const compiler = TestBed.get(Compiler) as Compiler;
|
||||||
const fixture = TestBed.createComponent(TestComponent);
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
@ -194,7 +192,6 @@ describe('insert/remove', () => {
|
|||||||
expect(moduleRef).toBe(fixture.componentInstance.ngComponentOutlet['_moduleRef']);
|
expect(moduleRef).toBe(fixture.componentInstance.ngComponentOutlet['_moduleRef']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
|
||||||
it('should re-create moduleRef when changed', async(() => {
|
it('should re-create moduleRef when changed', async(() => {
|
||||||
const compiler = TestBed.get(Compiler) as Compiler;
|
const compiler = TestBed.get(Compiler) as Compiler;
|
||||||
const fixture = TestBed.createComponent(TestComponent);
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
@ -68,8 +68,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
|||||||
describe('transform', () => {
|
describe('transform', () => {
|
||||||
it('should return correct value for numbers', () => {
|
it('should return correct value for numbers', () => {
|
||||||
expect(pipe.transform(1.23)).toEqual('123%');
|
expect(pipe.transform(1.23)).toEqual('123%');
|
||||||
expect(pipe.transform(1.234)).toEqual('123%');
|
|
||||||
expect(pipe.transform(1.236)).toEqual('124%');
|
|
||||||
expect(pipe.transform(12.3456, '0.0-10')).toEqual('1,234.56%');
|
expect(pipe.transform(12.3456, '0.0-10')).toEqual('1,234.56%');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ jasmine_node_test(
|
|||||||
":test_lib",
|
":test_lib",
|
||||||
"//packages/platform-server",
|
"//packages/platform-server",
|
||||||
"//packages/platform-server/testing",
|
"//packages/platform-server/testing",
|
||||||
|
"//packages/private/testing",
|
||||||
"//tools/testing:node",
|
"//tools/testing:node",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import {Component, INJECTOR, Injectable, NgModule} from '@angular/core';
|
import {Component, INJECTOR, Injectable, NgModule} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {renderModuleFactory} from '@angular/platform-server';
|
import {renderModuleFactory} from '@angular/platform-server';
|
||||||
|
import {fixmeIvy} from '@angular/private/testing';
|
||||||
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
|
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
|
||||||
import {DepAppModuleNgFactory} from 'app_built/src/dep.ngfactory';
|
import {DepAppModuleNgFactory} from 'app_built/src/dep.ngfactory';
|
||||||
import {HierarchyAppModuleNgFactory} from 'app_built/src/hierarchy.ngfactory';
|
import {HierarchyAppModuleNgFactory} from 'app_built/src/hierarchy.ngfactory';
|
||||||
@ -167,6 +168,7 @@ describe('ngInjectableDef Bazel Integration', () => {
|
|||||||
expect(TestBed.get(INJECTOR).get('foo')).toEqual('bar');
|
expect(TestBed.get(INJECTOR).get('foo')).toEqual('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fixmeIvy('FW-646: Directive providers don\'t support primitive types') &&
|
||||||
it('Component injector understands requests for INJECTABLE', () => {
|
it('Component injector understands requests for INJECTABLE', () => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'test-cmp',
|
selector: 'test-cmp',
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
load("//tools:defaults.bzl", "ivy_ng_module")
|
load("//tools:defaults.bzl", "ng_module")
|
||||||
load("//packages/bazel/src:ng_rollup_bundle.bzl", "ng_rollup_bundle")
|
load("//packages/bazel/src:ng_rollup_bundle.bzl", "ng_rollup_bundle")
|
||||||
|
|
||||||
ivy_ng_module(
|
ng_module(
|
||||||
name = "app",
|
name = "app",
|
||||||
srcs = glob(
|
srcs = glob(
|
||||||
[
|
[
|
||||||
@ -11,6 +11,7 @@ ivy_ng_module(
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
module_name = "app_built",
|
module_name = "app_built",
|
||||||
|
tags = ["ivy-only"],
|
||||||
deps = [
|
deps = [
|
||||||
"//packages/core",
|
"//packages/core",
|
||||||
"@rxjs",
|
"@rxjs",
|
||||||
|
@ -10,9 +10,13 @@ ts_library(
|
|||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
tags = [
|
||||||
|
"ivy-only",
|
||||||
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app",
|
"//packages/compiler-cli/integrationtest/bazel/injector_def/ivy_build/app",
|
||||||
"//packages/core",
|
"//packages/core",
|
||||||
|
"//packages/private/testing",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,6 +25,7 @@ jasmine_node_test(
|
|||||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
||||||
tags = [
|
tags = [
|
||||||
"fixme-ivy-aot",
|
"fixme-ivy-aot",
|
||||||
|
"ivy-only",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
":test_lib",
|
":test_lib",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable, InjectionToken, Injector, NgModule, createInjector, forwardRef} from '@angular/core';
|
import {Injectable, InjectionToken, Injector, NgModule, createInjector, forwardRef} from '@angular/core';
|
||||||
|
import {fixmeIvy} from '@angular/private/testing';
|
||||||
import {AOT_TOKEN, AotModule, AotService} from 'app_built/src/module';
|
import {AOT_TOKEN, AotModule, AotService} from 'app_built/src/module';
|
||||||
|
|
||||||
describe('Ivy NgModule', () => {
|
describe('Ivy NgModule', () => {
|
||||||
@ -40,6 +41,7 @@ describe('Ivy NgModule', () => {
|
|||||||
|
|
||||||
it('works', () => { createInjector(JitAppModule); });
|
it('works', () => { createInjector(JitAppModule); });
|
||||||
|
|
||||||
|
fixmeIvy('FW-645: jit doesn\'t support forwardRefs') &&
|
||||||
it('throws an error on circular module dependencies', () => {
|
it('throws an error on circular module dependencies', () => {
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [forwardRef(() => BModule)],
|
imports: [forwardRef(() => BModule)],
|
||||||
|
@ -71,10 +71,8 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
|
|||||||
indexFile = files[0];
|
indexFile = files[0];
|
||||||
} else {
|
} else {
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
// Assume the shortest file path called index.ts is the entry point. Note that we
|
// Assume the shortest file path called index.ts is the entry point
|
||||||
// need to use the posix path delimiter here because TypeScript internally only
|
if (f.endsWith(path.sep + 'index.ts')) {
|
||||||
// passes posix paths.
|
|
||||||
if (f.endsWith('/index.ts')) {
|
|
||||||
if (!indexFile || indexFile.length > f.length) {
|
if (!indexFile || indexFile.length > f.length) {
|
||||||
indexFile = f;
|
indexFile = f;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ConstantPool, CssSelector, Expression, R3ComponentMetadata, R3DirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
|
import {ConstantPool, CssSelector, DomElementSchemaRegistry, ElementSchemaRegistry, Expression, R3ComponentMetadata, R3DirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ export class ComponentDecoratorHandler implements
|
|||||||
private resourceLoader: ResourceLoader, private rootDirs: string[]) {}
|
private resourceLoader: ResourceLoader, private rootDirs: string[]) {}
|
||||||
|
|
||||||
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
|
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
|
||||||
|
private elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||||
|
|
||||||
|
|
||||||
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined {
|
||||||
@ -74,8 +75,9 @@ export class ComponentDecoratorHandler implements
|
|||||||
|
|
||||||
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
|
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building
|
||||||
// on it.
|
// on it.
|
||||||
const directiveResult =
|
const directiveResult = extractDirectiveMetadata(
|
||||||
extractDirectiveMetadata(node, decorator, this.checker, this.reflector, this.isCore);
|
node, decorator, this.checker, this.reflector, this.isCore,
|
||||||
|
this.elementSchemaRegistry.getDefaultComponentElementName());
|
||||||
if (directiveResult === undefined) {
|
if (directiveResult === undefined) {
|
||||||
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
|
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
|
||||||
// case, compilation of the decorator is skipped. Returning an empty object signifies
|
// case, compilation of the decorator is skipped. Returning an empty object signifies
|
||||||
|
@ -93,7 +93,7 @@ export class DirectiveDecoratorHandler implements
|
|||||||
*/
|
*/
|
||||||
export function extractDirectiveMetadata(
|
export function extractDirectiveMetadata(
|
||||||
clazz: ts.ClassDeclaration, decorator: Decorator, checker: ts.TypeChecker,
|
clazz: ts.ClassDeclaration, decorator: Decorator, checker: ts.TypeChecker,
|
||||||
reflector: ReflectionHost, isCore: boolean): {
|
reflector: ReflectionHost, isCore: boolean, defaultSelector: string | null = null): {
|
||||||
decorator: Map<string, ts.Expression>,
|
decorator: Map<string, ts.Expression>,
|
||||||
metadata: R3DirectiveMetadata,
|
metadata: R3DirectiveMetadata,
|
||||||
decoratedElements: ClassMember[],
|
decoratedElements: ClassMember[],
|
||||||
@ -154,7 +154,7 @@ export function extractDirectiveMetadata(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the selector.
|
// Parse the selector.
|
||||||
let selector = '';
|
let selector = defaultSelector;
|
||||||
if (directive.has('selector')) {
|
if (directive.has('selector')) {
|
||||||
const expr = directive.get('selector') !;
|
const expr = directive.get('selector') !;
|
||||||
const resolved = staticallyResolve(expr, reflector, checker);
|
const resolved = staticallyResolve(expr, reflector, checker);
|
||||||
|
@ -48,9 +48,10 @@ export class GeneratedShimsHostWrapper implements ts.CompilerHost {
|
|||||||
fileName: string, languageVersion: ts.ScriptTarget,
|
fileName: string, languageVersion: ts.ScriptTarget,
|
||||||
onError?: ((message: string) => void)|undefined,
|
onError?: ((message: string) => void)|undefined,
|
||||||
shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined {
|
shouldCreateNewSourceFile?: boolean|undefined): ts.SourceFile|undefined {
|
||||||
|
const canonical = this.getCanonicalFileName(fileName);
|
||||||
for (let i = 0; i < this.shimGenerators.length; i++) {
|
for (let i = 0; i < this.shimGenerators.length; i++) {
|
||||||
const generator = this.shimGenerators[i];
|
const generator = this.shimGenerators[i];
|
||||||
const originalFile = generator.getOriginalSourceOfShim(fileName);
|
const originalFile = generator.getOriginalSourceOfShim(canonical);
|
||||||
if (originalFile !== null) {
|
if (originalFile !== null) {
|
||||||
// This shim generator has recognized the filename being requested, and is now responsible
|
// This shim generator has recognized the filename being requested, and is now responsible
|
||||||
// for generating its contents, based on the contents of the original file it has requested.
|
// for generating its contents, based on the contents of the original file it has requested.
|
||||||
|
@ -561,6 +561,62 @@ describe('compiler compliance', () => {
|
|||||||
expectEmit(source, OtherDirectiveDefinition, 'Incorrect OtherDirective.ngDirectiveDef');
|
expectEmit(source, OtherDirectiveDefinition, 'Incorrect OtherDirective.ngDirectiveDef');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support components without selector', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, Directive, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({})
|
||||||
|
export class EmptyOutletDirective {}
|
||||||
|
|
||||||
|
@Component({template: '<router-outlet></router-outlet>'})
|
||||||
|
export class EmptyOutletComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [EmptyOutletComponent]})
|
||||||
|
export class MyModule{}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// EmptyOutletDirective definition should be:
|
||||||
|
const EmptyOutletDirectiveDefinition = `
|
||||||
|
…
|
||||||
|
EmptyOutletDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||||
|
type: EmptyOutletDirective,
|
||||||
|
selectors: [],
|
||||||
|
factory: function EmptyOutletDirective_Factory(t) { return new (t || EmptyOutletDirective)(); }
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
// EmptyOutletComponent definition should be:
|
||||||
|
const EmptyOutletComponentDefinition = `
|
||||||
|
…
|
||||||
|
EmptyOutletComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: EmptyOutletComponent,
|
||||||
|
selectors: [["ng-component"]],
|
||||||
|
factory: function EmptyOutletComponent_Factory(t) { return new (t || EmptyOutletComponent)(); },
|
||||||
|
consts: 1,
|
||||||
|
vars: 0,
|
||||||
|
template: function EmptyOutletComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵelement(0, "router-outlet");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encapsulation: 2
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const source = result.source;
|
||||||
|
|
||||||
|
expectEmit(
|
||||||
|
source, EmptyOutletDirectiveDefinition,
|
||||||
|
'Incorrect EmptyOutletDirective.ngDirectiveDefDef');
|
||||||
|
expectEmit(
|
||||||
|
source, EmptyOutletComponentDefinition, 'Incorrect EmptyOutletComponent.ngComponentDef');
|
||||||
|
});
|
||||||
|
|
||||||
it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting',
|
it('should not treat ElementRef, ViewContainerRef, or ChangeDetectorRef specially when injecting',
|
||||||
() => {
|
() => {
|
||||||
const files = {
|
const files = {
|
||||||
|
@ -145,8 +145,10 @@ describe('compiler compliance: bindings', () => {
|
|||||||
type: HostBindingDir,
|
type: HostBindingDir,
|
||||||
selectors: [["", "hostBindingDir", ""]],
|
selectors: [["", "hostBindingDir", ""]],
|
||||||
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
|
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
|
||||||
hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) {
|
hostBindings: function HostBindingDir_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵload(dirIndex).dirId));
|
if (rf & 2) {
|
||||||
|
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.dirId));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
hostVars: 1
|
hostVars: 1
|
||||||
});
|
});
|
||||||
@ -188,8 +190,10 @@ describe('compiler compliance: bindings', () => {
|
|||||||
type: HostBindingComp,
|
type: HostBindingComp,
|
||||||
selectors: [["host-binding-comp"]],
|
selectors: [["host-binding-comp"]],
|
||||||
factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); },
|
factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); },
|
||||||
hostBindings: function HostBindingComp_HostBindings(dirIndex, elIndex) {
|
hostBindings: function HostBindingComp_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵload(dirIndex).id)));
|
if (rf & 2) {
|
||||||
|
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, ctx.id)));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
hostVars: 3,
|
hostVars: 3,
|
||||||
consts: 0,
|
consts: 0,
|
||||||
@ -232,8 +236,10 @@ describe('compiler compliance: bindings', () => {
|
|||||||
type: HostAttributeDir,
|
type: HostAttributeDir,
|
||||||
selectors: [["", "hostAttributeDir", ""]],
|
selectors: [["", "hostAttributeDir", ""]],
|
||||||
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
||||||
hostBindings: function HostAttributeDir_HostBindings(dirIndex, elIndex) {
|
hostBindings: function HostAttributeDir_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementAttribute(elIndex, "required", $r3$.ɵbind($r3$.ɵload(dirIndex).required));
|
if (rf & 2) {
|
||||||
|
$r3$.ɵelementAttribute(elIndex, "required", $r3$.ɵbind(ctx.required));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
hostVars: 1
|
hostVars: 1
|
||||||
});
|
});
|
||||||
|
@ -771,12 +771,16 @@ describe('compiler compliance: styling', () => {
|
|||||||
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
||||||
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
||||||
…
|
…
|
||||||
hostBindings: function MyComponent_HostBindings(dirIndex, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, dirIndex);
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStylingMap(elIndex, $r3$.ɵload(dirIndex).myClass, $r3$.ɵload(dirIndex).myStyle, dirIndex);
|
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||||
$r3$.ɵelementStyleProp(elIndex, 2, $r3$.ɵload(dirIndex).myColorProp, null, dirIndex);
|
}
|
||||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myFooClass, dirIndex);
|
if (rf & 2) {
|
||||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
||||||
|
$r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
|
||||||
|
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
||||||
|
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -825,14 +829,18 @@ describe('compiler compliance: styling', () => {
|
|||||||
const _c0 = ["bar", "foo"];
|
const _c0 = ["bar", "foo"];
|
||||||
const _c1 = ["height", "width"];
|
const _c1 = ["height", "width"];
|
||||||
…
|
…
|
||||||
hostBindings: function MyComponent_HostBindings(dirIndex, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, dirIndex);
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStylingMap(elIndex, $r3$.ɵload(dirIndex).myClasses, $r3$.ɵload(dirIndex).myStyle, dirIndex);
|
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||||
$r3$.ɵelementStyleProp(elIndex, 0, $r3$.ɵload(dirIndex).myHeightProp, "pt", dirIndex);
|
}
|
||||||
$r3$.ɵelementStyleProp(elIndex, 1, $r3$.ɵload(dirIndex).myWidthProp, null, dirIndex);
|
if (rf & 2) {
|
||||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myBarClass, dirIndex);
|
$r3$.ɵelementStylingMap(elIndex, ctx.myClasses, ctx.myStyle, ctx);
|
||||||
$r3$.ɵelementClassProp(elIndex, 1, $r3$.ɵload(dirIndex).myFooClass, dirIndex);
|
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeightProp, "pt", ctx);
|
||||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
$r3$.ɵelementStyleProp(elIndex, 1, ctx.myWidthProp, null, ctx);
|
||||||
|
$r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
|
||||||
|
$r3$.ɵelementClassProp(elIndex, 1, ctx.myFooClass, ctx);
|
||||||
|
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -886,18 +894,26 @@ describe('compiler compliance: styling', () => {
|
|||||||
const _c2 = ["bar"];
|
const _c2 = ["bar"];
|
||||||
const _c3 = ["height"];
|
const _c3 = ["height"];
|
||||||
…
|
…
|
||||||
function WidthDirective_HostBindings(dirIndex, elIndex) {
|
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementStyling(_c0, _c1, null, dirIndex);
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStyleProp(elIndex, 0, $r3$.ɵload(dirIndex).myWidth, null, dirIndex);
|
$r3$.ɵelementStyling(_c0, _c1, null, ctx);
|
||||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myFooClass, dirIndex);
|
}
|
||||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
if (rf & 2) {
|
||||||
|
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
|
||||||
|
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
||||||
|
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
function HeightDirective_HostBindings(dirIndex, elIndex) {
|
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
$r3$.ɵelementStyling(_c2, _c3, null, dirIndex);
|
if (rf & 1) {
|
||||||
$r3$.ɵelementStyleProp(elIndex, 0, $r3$.ɵload(dirIndex).myHeight, null, dirIndex);
|
$r3$.ɵelementStyling(_c2, _c3, null, ctx);
|
||||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myBarClass, dirIndex);
|
}
|
||||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
if (rf & 2) {
|
||||||
|
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
|
||||||
|
$r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
|
||||||
|
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
`;
|
`;
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
import {NgtscTestEnvironment} from './env';
|
import {NgtscTestEnvironment} from './env';
|
||||||
|
|
||||||
|
const trim = (input: string): string => input.replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
describe('ngtsc behavioral tests', () => {
|
describe('ngtsc behavioral tests', () => {
|
||||||
if (!NgtscTestEnvironment.supported) {
|
if (!NgtscTestEnvironment.supported) {
|
||||||
// These tests should be excluded from the non-Bazel build.
|
// These tests should be excluded from the non-Bazel build.
|
||||||
@ -449,6 +451,37 @@ describe('ngtsc behavioral tests', () => {
|
|||||||
expect(jsContents).toContain(`i0.ɵquery(null, ViewContainerRef, true)`);
|
expect(jsContents).toContain(`i0.ɵquery(null, ViewContainerRef, true)`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should generate host listeners for components', () => {
|
||||||
|
env.tsconfig();
|
||||||
|
env.write(`test.ts`, `
|
||||||
|
import {Component, HostListener} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test',
|
||||||
|
template: 'Test'
|
||||||
|
})
|
||||||
|
class FooCmp {
|
||||||
|
@HostListener('document:click', ['$event.target'])
|
||||||
|
onClick(eventTarget: HTMLElement): void {}
|
||||||
|
|
||||||
|
@HostListener('window:scroll')
|
||||||
|
onScroll(event: any): void {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
const hostBindingsFn = `
|
||||||
|
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
||||||
|
if (rf & 1) {
|
||||||
|
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event.target); });
|
||||||
|
i0.ɵlistener("scroll", function FooCmp_scroll_HostBindingHandler($event) { return ctx.onScroll(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
|
||||||
|
});
|
||||||
|
|
||||||
it('should generate host bindings for directives', () => {
|
it('should generate host bindings for directives', () => {
|
||||||
env.tsconfig();
|
env.tsconfig();
|
||||||
env.write(`test.ts`, `
|
env.write(`test.ts`, `
|
||||||
@ -476,39 +509,33 @@ describe('ngtsc behavioral tests', () => {
|
|||||||
|
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
expect(jsContents)
|
const hostBindingsFn = `
|
||||||
.toContain(`i0.ɵelementAttribute(elIndex, "hello", i0.ɵbind(i0.ɵload(dirIndex).foo));`);
|
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
||||||
expect(jsContents)
|
if (rf & 1) {
|
||||||
.toContain(`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵload(dirIndex).bar));`);
|
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event); });
|
||||||
expect(jsContents)
|
i0.ɵlistener("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
|
||||||
.toContain('i0.ɵelementClassProp(elIndex, 0, i0.ɵload(dirIndex).someClass, dirIndex)');
|
i0.ɵelementStyling(_c0, null, null, ctx);
|
||||||
|
}
|
||||||
const factoryDef = `
|
if (rf & 2) {
|
||||||
factory: function FooCmp_Factory(t) {
|
i0.ɵelementAttribute(elIndex, "hello", i0.ɵbind(ctx.foo));
|
||||||
var f = new (t || FooCmp)();
|
i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(ctx.bar));
|
||||||
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) {
|
i0.ɵelementClassProp(elIndex, 0, ctx.someClass, ctx);
|
||||||
return f.onClick($event);
|
i0.ɵelementStylingApply(elIndex, ctx);
|
||||||
});
|
}
|
||||||
i0.ɵlistener("change", function FooCmp_change_HostBindingHandler($event) {
|
|
||||||
return f.onChange(f.arg1, f.arg2, f.arg3);
|
|
||||||
});
|
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
expect(jsContents).toContain(factoryDef.replace(/\s+/g, ' ').trim());
|
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate host listeners for directives with base factories', () => {
|
it('should generate host listeners for directives within hostBindings section', () => {
|
||||||
env.tsconfig();
|
env.tsconfig();
|
||||||
env.write(`test.ts`, `
|
env.write(`test.ts`, `
|
||||||
import {Directive, HostListener} from '@angular/core';
|
import {Directive, HostListener} from '@angular/core';
|
||||||
|
|
||||||
class Base {}
|
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[test]',
|
selector: '[test]',
|
||||||
})
|
})
|
||||||
class Dir extends Base {
|
class Dir {
|
||||||
@HostListener('change', ['arg'])
|
@HostListener('change', ['arg'])
|
||||||
onChange(event: any, arg: any): void {}
|
onChange(event: any, arg: any): void {}
|
||||||
}
|
}
|
||||||
@ -516,17 +543,14 @@ describe('ngtsc behavioral tests', () => {
|
|||||||
|
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
const factoryDef = `
|
const hostBindingsFn = `
|
||||||
factory: function Dir_Factory(t) {
|
hostBindings: function Dir_HostBindings(rf, ctx, elIndex) {
|
||||||
var f = ɵDir_BaseFactory((t || Dir));
|
if (rf & 1) {
|
||||||
i0.ɵlistener("change", function Dir_change_HostBindingHandler($event) {
|
i0.ɵlistener("change", function Dir_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg); });
|
||||||
return f.onChange(f.arg);
|
}
|
||||||
});
|
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
expect(jsContents).toContain(factoryDef.replace(/\s+/g, ' ').trim());
|
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
|
||||||
expect(jsContents).toContain('var ɵDir_BaseFactory = i0.ɵgetInheritedFactory(Dir)');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly recognize local symbols', () => {
|
it('should correctly recognize local symbols', () => {
|
||||||
|
@ -358,9 +358,8 @@ function parserSelectorToR3Selector(selector: CssSelector): R3CssSelector {
|
|||||||
return positive.concat(...negative);
|
return positive.concat(...negative);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseSelectorToR3Selector(selector: string): R3CssSelectorList {
|
export function parseSelectorToR3Selector(selector: string | null): R3CssSelectorList {
|
||||||
const selectors = CssSelector.parse(selector);
|
return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
|
||||||
return selectors.map(parserSelectorToR3Selector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pasted from render3/interfaces/definition since it cannot be referenced directly
|
// Pasted from render3/interfaces/definition since it cannot be referenced directly
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user